Blog Archives
Enqueue Symfony’s process components with PHP and ZeroMQ
Today I’d like to play with ZeroMQ. ZeroMQ is a great tool to work with sockets. I will show you the problem that I want to solve: One web application needs to execute background processes but I need to execute those processes in order. Two users cannot execute one process at the same time. OK, if we face to this problem we can use Gearman. I’ve written various posts about Gearman (here and here for example). But today I want to play with ZeroMQ.
I’m going to use one great library called React. With react (reactor pattern implementation in PHP) we can do various thing. One of them are ZeroMQ bindings.
In this simple example we are going to build a simple server and client. The client will send to the server one string that the server will enqueue and executes using the Symfony’s Process component.
Here is the client:
<?php
include __DIR__ . '/../vendor/autoload.php';
use Zmqlifo\Client;
$queue = Client::factory('tcp://127.0.0.1:4444');
echo $queue->run("ls -latr")->getOutput();
echo $queue->run("pwd")->getOutput();
And finally the server:
<?php
include __DIR__ . '/../vendor/autoload.php';
use Symfony\Component\Process\Process;
use Zmqlifo\Server;
$server = Server::factory('tcp://127.0.0.1:4444');
$server->registerOnMessageCallback(function ($msg) {
$process = new Process($msg);
$process->setTimeout(3600);
$process->run();
return $process->getOutput();
});
$server->run();
You can see the working example here:
you can check the full code of the library in github and Packagist.
UPDATE
As Igor Wiedler said React is not necessary here.
ZMQ is used for blocking sends
and blocking tasks, having an event loop does not really make much sense.
github repository updated (thanks!).
Sign-in with Twitter in a Silex application.
I’ve working in a pet-project with Silex and I wanted to perform a Sign-in with Twitter. Implementing Sign in with Twitter is pretty straightforward and it’s also well explained in the Twitter’s developers site. Now we only need to implement those HTTP client requests within PHP. We can create the REST client with curl but nowadays I prefer to use the great library called Guzzle to perform those kind of opperations. So let’s start.
The idea is to create something reusable. I don’t want to spend too much time including the Sign-in with Twitter in my proyects, so my first idea was to create a class with all the needed code and mount this class as group of Silex controllers (as it’s defined here). I also want to keep the class as standard as possible and avoiding the usage of any other external dependencies (except Guzzle)..
Imagine a simple Silex application:
<?php
// www/index.php
include __DIR__ . "/../vendor/autoload.php";
$app = new Silex\Application();
$app->get('/', function () {
return 'Hello';
});
$app->run();
Now I want to use a Sign-in with Twitter, so I will change the application to:
<?php
include __DIR__ . "/../vendor/autoload.php";
$app = new Silex\Application();
$app->register(new Silex\Provider\SessionServiceProvider());
$consumerKey = "***";
$consumerSecret = "***";
$twitterLoggin = new SilexTwitterLogin($app, 'twitter');
$twitterLoggin->setConsumerKey($consumerKey);
$twitterLoggin->setConsumerSecret($consumerSecret);
$twitterLoggin->registerOnLoggin(function () use ($app, $twitterLoggin) {
$app['session']->set($twitterLoggin->getSessionId(), [
'user_id' => $twitterLoggin->getUserId(),
'screen_name' => $twitterLoggin->getScreenName(),
'oauth_token' => $twitterLoggin->getOauthToken(),
'oauth_token_secret' => $twitterLoggin->getOauthTokenSecret()
]);
});
$twitterLoggin->mountOn('/login', function () {
return '<a href="/login/requestToken">login</a>';
});
$app->get('/', function () use ($app){
return 'Hello ' . $app['session']->get('twitter')['screen_name'];
});
$app->run();
The application will redirects all requests (without the correct session) to the route “/login”. The login page has a simple link to the route: “/login/requestToken” (we can create a fancy template with Twig if we want, indeed). This route redirects the request to Twitter’s login page and after a successful login it will redirects back to the route that we have defined within our Twitter application. The library assumes that this callback’s url is “/login/callbackUrl”. All this default routes can be defined by the user using the proper setters of the class.
When the sign-in is finished the application will trigger the callback defined in registerOnLoggin function and will redirects to the route “/”. This route (called internally “redirectOnSuccess”) is also customizable with a setter.
And that’s all. Library available at github and packagist
{
"require": {
"gonzalo123/silex-twitter-login": "dev-master"
}
}
Scaling Silex applications (part II). Using RouteCollection
In the post Scaling Silex applications I wanted to organize a one Silex application. In one comment Igor Wiedler recommended us to use RouteCollections instead of define the routes with a Symfony’s Dependency Injection Container. Because of that I started to hack a little bit about it and here I show you my outcomes:
I want to build an imaginary application with silex. This application has also one Api and one little blog. I want to organize those parts. Our index.php file
<?php
// www/index.php
require_once __DIR__ . '/../vendor/autoload.php';
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
use Silex\Application;
$app = new Application();
$app['routes'] = $app->extend('routes', function (RouteCollection $routes, Application $app) {
$loader = new YamlFileLoader(new FileLocator(__DIR__ . '/../config'));
$collection = $loader->load('routes.yml');
$routes->addCollection($collection);
return $routes;
});
$app->run();
Now our routes.yml file:
# config/routes.yml
home:
path: /
defaults: { _controller: 'Gonzalo123\AppController::homeAction' }
hello:
path: /hello/{name}
defaults: { _controller: 'Gonzalo123\AppController::helloAction' }
api:
prefix: /api
resource: api.yml
blog:
prefix: /blog
resource: blog.yml
As we can see we have separated the main routing file into different files: api.yml (for the Api) and blog.yml (for the blog)
# config/api.yml
api.list:
path: /list
defaults: { _controller: 'Gonzalo123\ApiController::listAction' }
# blog.yml
blog.home:
path: /
defaults: { _controller: 'Gonzalo123\BlogController::homeAction' }
And now we can create our controllers:
<?php
// lib/Gonzalo123/AppController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
use Silex\Application;
class AppController
{
public function homeAction()
{
return new Response("AppController::homeAction");
}
public function helloAction(Application $app, $name)
{
return new Response("Hello" . $app->escape($name));
}
}
<?php
// lib/Gonzalo123/ApiController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
class ApiController
{
public function listAction()
{
return new Response("AppController::listAction");
}
}
<?php
// lib/Gonzalo123/BlogController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
class BlogController
{
public function homeAction()
{
return new Response("BlogController::homeAction");
}
}
And that’s all. Here also the needed dependencies within our composer.json file
{
"require":{
"silex/silex":"1.0.*@dev",
"symfony/yaml":"v2.2.0",
"symfony/config":"v2.2.0"
},
"autoload":{
"psr-0":{
"":"lib/"
}
}
}
source code at github.
Scaling Silex applications
In my humble opinion Silex is great. It’s perfect to create prototypes, but when our application grows up it turns into a mess. That was what I thought until the last month, when I attended to a great talk about Silex with Javier Eguiluz. OK. Scaling Silex it’s not the same than with a Symfony application, but it’s possible.
It’s pretty straightforward to create a Silex application with composer:
{
"require": {
"silex/silex": "1.0.*"
},
"minimum-stability": "dev"
}
But there’s a better way. We can use the Fabien Potencier’s skeleton. With this skeleton we can organize our code better.
We also can use classes as controllers instead of using a closure with all the code. Igor Wiedler has a great post about this. You can read it here.
Today I’m playing with Silex and I want to show you something. Let’s start:
Probably you know that I’m a big fan of Symfony’s Dependency Injection Container (you can read about it here and here), but Silex uses Pimple. In fact the Silex application extends Pimple Class. My idea is the following one:
In the Igor’s post we can see how to use things like that:
$app->match('/video/{id}', 'Gonzalo123\ApiController::indexAction')->method('GET')->bind('video_info');
My idea is to store this information within a Service Container (we will use Symfony’s DIC). For example here we can see our routes.yml:
routes:
video_info:
pattern: /video/{id}
controller: Gonzalo123\ApiController::initAction
requirements:
_method: GET
As we can see we need to implement one Extension for the alias “routes”. We only will implement the needed functions for YAML files in this example.
<?php
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class SilexRouteExtension implements ExtensionInterface
{
/**
* Loads a specific configuration.
*
* @param array $config An array of configuration values
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @throws InvalidArgumentException When provided tag is not defined in this extension
*
* @api
*/
public function load(array $config, ContainerBuilder $container)
{
}
/**
* Returns the namespace to be used for this extension (XML namespace).
*
* @return string The XML namespace
*
* @api
*/
public function getNamespace()
{
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*
* @api
*/
public function getXsdValidationBasePath()
{
}
/**
* Returns the recommended alias to use in XML.
*
* This alias is also the mandatory prefix to use when using YAML.
*
* @return string The alias
*
* @api
*/
public function getAlias()
{
return "routes";
}
}
And now we only need to prepare the DIC. According to Fabien’s recommendation in his Silex skeleton, we only need to change the src/controllers.php
<?php
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
// Set up container
$container = new ContainerBuilder();
$container->registerExtension(new SilexRouteExtension);
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../config/'));
// load configuration
$loader->load('routes.yml');
$app['container'] = $container;
$app->mount('/api', include 'controllers/myApp.php');
$container->compile();
$app->error(function (\Exception $e, $code) use ($app) {
if ($app['debug']) {
return;
}
$page = 404 == $code ? '404.html' : '500.html';
return new Response($app['twig']->render($page, array('code' => $code)), $code);
});
and now we define the config/routes.yml
routes:
video_info:
pattern: /video/{videoId}
controller: Gonzalo123\ApiController::initAction
requirements:
_method: GET
And finally the magic in our controllers/myApp.php:
<?php
$myApp = $app['controllers_factory'];
foreach ($container->getExtensionConfig('routes')[0] as $name => $route) {
$controller = $myApp->match($route['pattern'], $route['controller']);
$controller->method($route['requirements']['_method']);
$controller->bind($name);
}
return $myApp;
The class for this example is: src/Gonzalo123/ApiController.php
<?php
namespace Gonzalo123;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
class ApiController
{
public function initAction(Request $request, Application $app)
{
return new JsonResponse(array(1, 1, $request->get('id')));
}
}
As you can see the idea is to use classes as controllers, define them within the service container and build the silex needed code iterating over the configuration. What do you think?
How to configure Symfony’s Service Container to use Twitter API
Keeping on with the series about Symfony’s Services container (another posts here and here), now we will use the service container to use Twitter API from a service.
To use Twitter API we need to handle http requests. I’ve written several post about http request with PHP (example1, example2), but today we will use one amazing library to build clients: Guzzle. Guzzle is amazing. We can easily build a Twitter client with it. There’s one example is its landing page:
<?php
$client = new Client('https://api.twitter.com/{version}', array('version' => '1.1'));
$oauth = new OauthPlugin(array(
'consumer_key' => '***',
'consumer_secret' => '***',
'token' => '***',
'token_secret' => '***'
));
$client->addSubscriber($oauth);
echo $client->get('/statuses/user_timeline.json')->send()->getBody();
If we are working within a Symfony2 application or a PHP application that uses the Symfony’s Dependency injection container component you can easily integrate this simple script in the service container. I will show you the way that I use to do it. Let’s start:
The idea is simple. First we include guzzle within our composer.json and execute composer update:
"require": {
"guzzle/guzzle":"dev-master"
}
Then we will create two files, one to store our Twitter credentials and another one to configure the service container:
# twitter.conf.yml
parameters:
twitter.baseurl: https://api.twitter.com/1.1
twitter.config:
consumer_key: ***
consumer_secret: ***
token: ***
token_secret: ***
# twitter.yml
parameters:
class.guzzle.response: Guzzle\Http\Message\Response
class.guzzle.client: Guzzle\Http\Client
class.guzzle.oauthplugin: Guzzle\Plugin\Oauth\OauthPlugin
services:
guzzle.twitter.client:
class: %class.guzzle.client%
arguments: [%twitter.baseurl%]
calls:
- [addSubscriber, [@guzzle.twitter.oauthplugin]]
guzzle.twitter.oauthplugin:
class: %class.guzzle.oauthplugin%
arguments: [%twitter.config%]
And finally we include those files in our services.yml:
# services.yml
imports:
- { resource: twitter.conf.yml }
- { resource: twitter.yml }
And that’s all. Now we can use the service without problems:
<?php
namespace Gonzalo123\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction($name)
{
$twitterClient = $this->container->get('guzzle.twitter.client');
$status = $twitterClient->get('statuses/user_timeline.json')
->send()->getBody();
return $this->render('AppBundle:Default:index.html.twig', array(
'status' => $status
));
}
}
Handling several PDO Database connections in Symfony2 through the Dependency Injection Container with PHP
I’m not a big fan of ORMs, especially in PHP world when all dies at the end of each request. Plain SQL is easy to understand and very powerful. Anyway in PHP we have Doctrine. Doctrine is a amazing project, probably (with permission of Symfony2) the most advanced PHP project, but I normally prefer to work with SQL instead of Doctrine.
Symfony framework is closely coupled to Doctrine and it’s very easy to use the ORM from our applications. But as I said before I prefer not to use it. By the other hand I have another problem. Due to my daily work I need to connect to different databases (not only one) in my applications. In Symfony2 we normally configure the default database in our parameters.yml file:
# parameters.yml
parameters:
database_driver: pdo_pgsql
database_host: localhost
database_port: 5432
database_name: symfony
database_user: username
database_password: password
Ok. If we want to use PDO objects with different databases, we can use something like that:
# parameters.yml parameters: database.db1.dsn: sqlite::memory: database.db1.username: username database.db1.password: password database.db2.dsn: pgsql:host=127.0.0.1;port=5432;dbname=testdb database.db2.username: username database.db2.password: password
And now create the PDO objects within our code with new \PDO():
$dsn = $this->container->getParameter('database.db1.dsn');
$username = $this->container->getParameter('database.db1.username');
$password = $this->container->getParameter('database.db1.password')
$pdo = new \PDO($dsn, $username, $password);
It works, but it’s awful. We store the database credentials in the service container but we aren’t using the service container properly. So we can do one small improvement. We will create a new configuration file called databases.yml and we will include this new file within the services.yml:
# services.yml
imports:
- { resource: databases.yml }
And create our databases.yml:
# databases.yml
parameters:
db.class: Gonzalo123\AppBundle\Db\Db
database.db1.dsn: sqlite::memory:
database.db1.username: username
database.db1.password: password
database.db2.dsn: pgsql:host=127.0.0.1;port=5432;dbname=testdb
database.db2.username: username
database.db2.password: password
services:
db1:
class: %db.class%
calls:
- [setDsn, [%database.db1.dsn%]]
- [setUsername, [%database.db1.username%]]
- [setPassword, [%database.db1.password%]]
db2:
class: %db.class%
calls:
- [setDsn, [%database.db2.dsn%]]
- [setUsername, [%database.db2.username%]]
- [setPassword, [%database.db2.password%]]
As we can see we have created two new services in the dependency injection container called db1 (sqlite in memory) and db2 (one postgreSql database) that use the same class (in this case ‘Gonzalo123\AppBundle\Db\Db’). So we need to create our Db class:
<?php
namespace Gonzalo123\AppBundle\Db;
class Db
{
private $dsn;
private $username;
private $password;
public function setDsn($dsn)
{
$this->dsn = $dsn;
}
public function setPassword($password)
{
$this->password = $password;
}
public function setUsername($username)
{
$this->username = $username;
}
/** @return \PDO */
public function getPDO()
{
$options = array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION);
return new \PDO($this->dsn, $this->username, $this->password, $options);
}
}
And that’s all. Now we can get a new PDO object from our service container with:
$this->container->get('db1')->getPDO();
Better, isn’t it? But it’s still ugly. We need one extra class (Gonzalo123\AppBundle\Db\Db) and this class creates a new instance of PDO object (with getPDO()). Do we really need this class? the answer is no. We can change our service container to:
# databases.yml
parameters:
pdo.class: PDO
pdo.attr_errmode: 3
pdo.erromode_exception: 2
pdo.options:
%pdo.attr_errmode%: %pdo.erromode_exception%
database.db1.dsn: sqlite::memory:
database.db1.username: username
database.db1.password: password
database.db2.dsn: pgsql:host=127.0.0.1;port=5432;dbname=testdb
database.db2.username: username
database.db2.password: password
services:
db1:
class: %pdo.class%
arguments:
- %database.db1.dsn%
- %database.db1.username%
- %database.db1.password%
- %pdo.options%
db2:
class: %pdo.class%
arguments:
- %database.db2.dsn%
- %database.db2.username%
- %database.db2.password%
- %pdo.options%
Now we don’t need getPDO() and we can get the PDO object directly from service container with:
$this->container->get('db1');
And we can use something like this within our controllers (or maybe better in models):
<?php
namespace Gonzalo123\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction($name)
{
// this code should be out from controller, in a model object.
// It is only an example
$pdo = $this->container->get('db1');
$pdo->exec("CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY, title TEXT, message TEXT)");
$pdo->exec("INSERT INTO messages(id, title, message) VALUES (1, 'title', 'message')");
$data = $pdo->query("SELECT * FROM messages")->fetchAll();
//
return $this->render('AppBundle:Default:index.html.twig', array('usuario' => $data));
}
}
Multiple inheritance with PHP and Traits
Multiple inheritance isn’t allowed in PHP. With Python we can do things like that:
class ClassName(Base1, Base2):
....
That’s no possible with PHP (in Java is not possible either), but today we can do something similar (is not the exactly the same) with Traits. Let me explain that: Instead of classes we can create Traits:
<?php
trait Base1
{
public function hello1($name)
{
return "Hello1 {$name}";
}
}
trait Base2
{
public function hello2($name)
{
return "Hello2 {$name}";
}
}
And now we can use those traits (instead of to extend multiple classes)
<?php
class ClassName
{
use Base1, Base2;
}
The main reason because multiple inheritance isn’t allowed in PHP, Java and another languages is is due to the collisions. If we extends from two classes with the same method, which is the good one?
Python solves this problem with a easy solution: The first one is the good one:
class Base1:
def hello1(self, name):
return "Hello1 " + name
class Base2:
def hello1(self, name):
return "Hello2 " + name
class ClassName(Base1, Base2):
pass
c = ClassName()
print c.hello1("Gonzalo")
Will output “Hello1 Gonzalo” but if change the inheritance order to:
class ClassName(Base2, Base1):
pass
The output will be “Hello2 Gonzalo”
Traits in PHP doesn’t solve the problem “out of the box”. If we use the following script:
<?php
trait Base1
{
public function hello1($name)
{
return "Hello1 {$name}";
}
}
trait Base2
{
public function hello1($name)
{
return "Hello2 {$name}";
}
}
class ClassName
{
use Base1, Base2;
}
$class = new ClassName();
echo $class->hello1("Gonzalo");
Our script will throw a Fatal error:
PHP Fatal error: Trait method hello1 has not been applied, because there are collisions with other trait methods on ClassName on line 23
If we want to avoid collisions, we need to describe explicitly what function we will use. It’s not difficult:
<?php
trait Base1
{
public function hello1($name)
{
return "Hello1 {$name}";
}
}
trait Base2
{
public function hello1($name)
{
return "Hello2 {$name}";
}
}
class ClassName
{
use Base1, Base2 {
Base1::hello1 insteadof Base2;
}
}
$class = new ClassName();
echo $class->hello1("Gonzalo");
Sending sockets from PostgreSQL triggers with Python
Picture this: We want to notify to one external service each time that one record is inserted in the database. We can find the place where the insert statement is done and create a TCP client there, but: What happens if the application that inserts the data within the database is a legacy application?, or maybe it is too hard to do?. If your database is PostgreSQL it’s pretty straightforward. With the “default” procedural language of PostgreSQL (pgplsql) we cannot do it, but PostgreSQL allows us to use more procedural languages than plpgsql, for example Python. With plpython we can use sockets in the same way than we use it within Python scripts. It’s very simple. Let me show you how to do it.
First we need to create one plpython with our TCP client
CREATE OR REPLACE FUNCTION dummy.sendsocket(msg character varying, host character varying, port integer)
RETURNS integer AS
$BODY$
import _socket
try:
s = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
s.connect((host, port))
s.sendall(msg)
s.close()
return 1
except:
return 0
$BODY$
LANGUAGE plpython VOLATILE
COST 100;
ALTER FUNCTION dummy.sendsocket(character varying, character varying, integer)
OWNER TO username;
Now we create the trigger that use our socket client.
CREATE OR REPLACE FUNCTION dummy.myTriggerToSendSockets()
RETURNS trigger AS
$BODY$
import json
stmt = plpy.prepare("select dummy.sendSocket($1, $2, $3)", ["text", "text", "int"])
rv = plpy.execute(stmt, [json.dumps(TD), "host", 26200])
$BODY$
LANGUAGE plpython VOLATILE
COST 100;
As you can see in my example we are sending all the record as a JSON string in the socket body.
And finally we attach the trigger to one table (or maybe we need to do it to more than one table)
CREATE TRIGGER myTrigger AFTER INSERT OR UPDATE OR DELETE ON dummy.myTable FOR EACH ROW EXECUTE PROCEDURE dummy.myTriggerToSendSockets();
And that’s all. Now we can use one simple TCP socket server to handle those requests. Let me show you different examples of TCP servers with different languages. As we can see all are different implementations of Reactor pattern. We can use, for example:
node.js:
var net = require('net');
var host = 'localhost';
var port = 26200;
var server = net.createServer(function (socket) {
socket.on('data', function(buffer) {
// do whatever that we want with buffer
});
});
server.listen(port, host);
python (with Twisted):
from twisted.internet import reactor, protocol
HOST = 'localhost'
PORT = 26200
class MyServer(protocol.Protocol):
def dataReceived(self, data):
# do whatever that we want with data
pass
class MyServerFactory(protocol.Factory):
def buildProtocol(self, addr):
return MyServer()
reactor.listenTCP(PORT, MyServerFactory(), interface=HOST)
reactor.run()
(I know that we can create the Python’s TCP server without Twisted, but if don’t use it maybe someone will angry with me. Probably he is angry right now because I put the node.js example first
)
php (with react):
<?php
include __DIR__ . '/vendor/autoload.php';
$host = 'localhost';
$port = 26200;
$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$socket->on('connection', function ($conn) {
$conn->on('data', function ($data) {
// do whatever we want with data
}
);
});
$socket->listen($port, $host);
$loop->run();
You also can use xinet.d to handle the TCP inbound connections.
How to call shell programs as functions with PHP
I’m a big fan of Symfony’s Process Component. I’ve used intensively this component within a project and I noticed that I needed a wrapper to avoid to write again and again the same code. Suddenly a cool python library came to my head: sh. With python’s sh we can call any program as if it were a function:
from sh import ifconfig
print(ifconfig("wlan0"))
Outputs:
wlan0 Link encap:Ethernet HWaddr 00:00:00:00:00:00
inet addr:192.168.1.100 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: ffff::ffff:ffff:ffff:fff/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0 GB) TX bytes:0 (0 GB)
So I decided to develop something similar in PHP. This library is not exactly the same than python one. Python’s sh allows more cool things such as non-blocking processes, baking, … not available in my PHP one’s, but at least I can call shell programs as functions in a simple way (and that’s was my scope). Let’s start.
One simple example of Process:
use Symfony\Component\Process\Process;
$process = new Process('-latr ~');
$process->setTimeout(3600);
$process->run();
echo $process->getOutput();
With sh library we can do:
use Sh/Sh;
$sh = new Sh();
echo $sh->ls('-latr ~');
You can check the source code in github, but it’s very simple one. Basically it’s a parser that creates the command line string, and another class that calls to the parser and sends the output to Process component. Whit the magic function __call we can use shell commands as functions.
The command’s arguments can be one string ‘-latr ~’ or one array ['-latr', '~']. You can see more example in the unit tests here
Symfony/Process also allows us to get feedback in real time:
use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
$process->run(function ($type, $buffer) {
if ('err' === $type) {
echo 'ERR > '.$buffer;
} else {
echo 'OUT > '.$buffer;
}
});
Sh uses this feature, so we can do things like that:
$sh->tail('/var/log/messages', function ($buffer) {
echo $buffer;
});
We can see more examples here:
<?php
error_reporting(-1);
include __DIR__ . '/../vendor/autoload.php';
use Sh\Sh;
echo Sh::factory()->runCommnad('notify-send', ['-t', 5000, 'title', 'HOLA']);
$sh = new Sh();
echo $sh->ifconfig("eth0");
echo $sh->ls('-latr ~');
echo $sh->ls(['-latr', '~']);
$sh->tail('-f /var/log/apache2/access.log', function ($buffer) {
echo $buffer;
});
As I said before the library is in github and also you can use with composer:
require: "gonzalo123/sh": "dev-master"
Updated!
Now Sh library supports chained arguments (baking)
// chainable commands (baking)
$sh->ssh(array('myserver.com', '-p' => 1393))->whoami();
// executes: ssh myserver.com -p 1393 whoami
$sh->ssh(array('myserver.com', '-p' => 1393))->tail(array("/var/log/dumb_daemon.log", 'n' => 100));
// executes: ssh myserver.com -p 1393 tail /var/log/dumb_daemon.log -n 100
});
Building a FTP client library with PHP
In my daily work I need to connect very often to FTP servers. Put files, read, list and things like that. I normally use the standard PHP functions for Ftp it’s pretty straight forward to use them. Just enable it within our installation (–enable-ftp) and it’s ready to use them. But last Sunday it was raining again and I start with this simple library.
Lets connect to a FTP server, switch to passive mode, put a file from one real file stored in our local filesystem and delete it.
use FtpLib\Ftp,
FtpLib\File;
list($host, $user, $pass) = include __DIR__ . "/credentials.php";
$ftp = new Ftp($host, $user, $pass);
$ftp->connect();
$ftp->setPasv();
$file = $ftp->putFileFromPath(__DIR__ . '/fixtures/foo');
echo $file->getName();
echo $file->getContent();
$file->delete();
Now the same, but without a real file. We are going to create the file on-the-fly from one string:
use FtpLib\Ftp,
FtpLib\File;
list($host, $user, $pass) = include __DIR__ . "/credentials.php";
$ftp = new Ftp($host, $user, $pass);
$ftp->connect();
$ftp->setPasv();
$file = $ftp->putFileFromString('bar', 'bla, bla, bla');
echo $file->getName();
echo $file->getContent();
$file->delete();
We also can create directories, change the working directory and delete folders in the FTP server with a fluent interface (I love fluent interfaces, indeed):
$ftp->mkdir('directory')
->chdir('directory')
->putFileFromString('newFile', 'bla, bla')
->delete();
$ftp->rmdir('directory');
And finally we can iterate files in the FTP (I must admit that this feature was the main purpose of the library)
$ftp->getFiles(function (File $file) use ($ftp) {
switch($file->getName()) {
case 'file1':
$file->delete();
break;
case 'file2':
$ftp->mkdir('backup')->chdir('backup')->putFileFromString($file->getName(), $file->getContent());
break;
}
});
And that’s all. You can find the library in github and you also can use it with composer.
"gonzalo123/ftplib": "dev-master"
You can also see usage examples within the unit tests























