Blog Archives
Google App Engine, PHP and Silex. Setting up a Login Provider
Last week Google announced the PHP support for Google App Engine (GAE). PHPStorm, the great IDE for PHP development, also announced support for Google App Engine PHP. Because of that now is time to hack a little bit with this new toy.
I’ve worked in a couple of projects with Google App Engine in the past (with Python). With PHP the process is almost the same. First we need to define our application in the app.yaml file. In our example we are going to redirect all requests to main.php, where our Silex application is defined.
application: silexgae version: 1 runtime: php api_version: 1 threadsafe: true handlers: - url: .* script: main.php
To build a simple Silex application over Google App Engine is pretty straightforward (more info here). Because of that we’re going to go a little further. We are going to use the log-in framework provided by GAE to log-in with our Goggle account within our Silex application. In fact we can use the standard OAuth authentication process but Google provides a simple way to use our gmail account.
Now we’re going to build a LoginProvider to make this process simpler. Our base Silex application will be the following one:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Silex\Application;
use Gae\LoginProvider;
use Gae\Auth;
$app = new Application();
$app->register(new LoginProvider(), array(
'auth.onlogin.callback.url' => '/private',
'auth.onlogout.callback.url' => '/loggedOut',
));
/** @var Auth $auth */
$auth = $app['gae.auth']();
$app->get('/', function () use ($app, $auth) {
return $auth->isLogged() ?
$app->redirect("/private") :
"<a href='" . $auth->getLoginUrl() . "'>login</a>";
});
$app->get('/private', function () use ($app, $auth) {
return $auth->isLogged() ?
"Hello " . $auth->getUser()->getNickname() .
" <a href='" . $auth->getLogoutUrl() . "'>logout</a>" :
$auth->getRedirectToLogin();
});
$app->get('/loggedOut', function () use ($app) {
return "Thank you!";
});
$app->run();
Our LoginProvider is a simple Class that implements Silex\ServiceProviderInterface
<?php
namespace Gae;
require_once 'google/appengine/api/users/UserService.php';
use google\appengine\api\users\UserService;
use Gae\Auth;
use Silex\Application;
use Silex\ServiceProviderInterface;
class LoginProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['gae.auth'] = $app->protect(function () use ($app) {
return new Auth($app, UserService::getCurrentUser());
});
}
public function boot(Application $app)
{
}
}
As you can see our Provider class proviedes us an instance of Gae\Auth class
<?php
namespace Gae;
require_once 'google/appengine/api/users/UserService.php';
use google\appengine\api\users\User;
use google\appengine\api\users\UserService;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Silex\Application;
class Auth
{
private $user = null;
private $loginUrl;
private $logoutUrl;
private $logged;
public function __construct(Application $app, User $user=null)
{
$this->user = $user;
if (is_null($user)) {
$this->loginUrl = UserService::createLoginUrl($app['auth.onlogin.callback.url']);
$this->logged = false;
} else {
$this->logged = true;
$this->logoutUrl = UserService::createLogoutUrl($app['auth.onlogout.callback.url']);
}
}
/**
* @return RedirectResponse
*/
public function getRedirectToLogin()
{
return new RedirectResponse($this->getLoginUrl());
}
/**
* @return boolean
*/
public function isLogged()
{
return $this->logged;
}
/**
* @return string
*/
public function getLoginUrl()
{
return $this->loginUrl;
}
/**
* @return string
*/
public function getLogoutUrl()
{
return $this->logoutUrl;
}
/**
* @return \google\appengine\api\users\User|null
*/
public function getUser()
{
return $this->user;
}
}
And that’s all. Full code is available in my github account and you can also use composer to include this provider within your projects.
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 DBAL Database connections in Symfony2 through the Dependency Injection Container with PHP
(This post is the second part of my previous post: Handling several PDO Database connections in Symfony2 through the Dependency Injection Container with PHP. You can read it here)
OK. We can handle PDOs connections inside a Symfony2 application, but what happens if we prefer DBAL. As we know DBAL is built over PDO and adds a set of “extra” features to our database connection. It’s something like PDO with steroids.
If we read the documentation, we will see how to use DBAL:
<?php
$config = new \Doctrine\DBAL\Configuration();
//..
$connectionParams = array(
'dbname' => 'mydb',
'user' => 'user',
'password' => 'secret',
'host' => 'localhost',
'driver' => 'pdo_mysql',
);
$conn = DriverManager::getConnection($connectionParams, $config);
As we can see to obtain a DBAL connection we use a factory method in DriverManager class. We can easily implements it in our service container:
# databases.yml
parameters:
doctrine.dbal.configuration: Doctrine\DBAL\Configuration
doctrine.dbal.drivermanager: Doctrine\DBAL\DriverManager
database.db1:
driver: pdo_sqlite
memory: true
database.db2:
driver: pdo_pgsql
dbname: testdb
user: username
password: password
host: 127.0.0.1
services:
dbal_configuartion:
class: %doctrine.dbal.configuration%
db1:
factory_class: %doctrine.dbal.drivermanager%
factory_method: getConnection
arguments: [%database.db1%]
db2:
factory_class: %doctrine.dbal.drivermanager%
factory_method: getConnection
arguments: [%database.db2%]
But if we run again our example Symfony will throws us one error:
RuntimeException: Please add the class to service “db1″ even if it is constructed by a factory since we might need to add method calls based on compile-time checks.
If we use this service container configuration outside Symfony2 application it works (remember we can use Symfony’s Dependency Injection Container outside Symfony application as a component. Example here). But if we want to use it with Symfony2 we need to set the “class”, even here when we only need the static constructor, so we change it to:
# databases.yml
parameters:
doctrine.dbal.configuration: Doctrine\DBAL\Configuration
doctrine.dbal.drivermanager: Doctrine\DBAL\DriverManager
database.db1:
driver: pdo_sqlite
memory: true
database.db2:
driver: pdo_pgsql
dbname: testdb
user: username
password: password
host: 127.0.0.1
services:
dbal_configuartion:
class: %doctrine.dbal.configuration%
db1:
class: %doctrine.dbal.drivermanager%
factory_class: %doctrine.dbal.drivermanager%
factory_method: getConnection
arguments: [%database.db1%]
db2:
class: %doctrine.dbal.drivermanager%
factory_class: %doctrine.dbal.drivermanager%
factory_method: getConnection
arguments: [%database.db2%]
And that’s all. We can use DBAL instead of PDO in our database connections.
UPDATE:
After publishing this post someone comment me Doctrine allows us to do it “out of the box” within Symfony with its DoctrineBundle:
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));
}
}
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
});
Managing Windows services with Symfony/Process and PHP
Sometimes I need to stop/start remote Windows services with PHP. It’s quite easy to do it with net commnand. This command is a tool for administration of Samba and remote CIFS servers. It’s pretty straightforward to handle them from Linux command line:
net rpc service --help
Usage:
net rpc service list
View configured Win32 services
net rpc service start
Start a service
net rpc service stop
Stop a service
net rpc service pause
Pause a service
net rpc service resume
Resume a service
net rpc service status
View current status of a service
net rpc service delete
Deletes a service
net rpc service create
Creates a service
Today we are going to create a PHP wrapper for this tool. Our NetService library will have two classes: One Parser and one Service class.
The Parser’s responsibility will be create the command line instruction. I will use Behat in the developing process of Parser class. Here we can see the feature file:
Feature: command line parser
Scenario: net service list
Given windows server host called "windowshost.com"
And credentials are "myDomanin/user%password"
And action is "list"
Then command line is "net rpc service list -S windowshost.com -U myDomanin/user%password"
Scenario: net service start
Given windows server host called "windowshost.com"
And service name called "ServiceName"
And credentials are "myDomanin/user%password"
And action is "start"
Then command line is "net rpc service start ServiceName -S windowshost.com -U myDomanin/user%password"
Scenario: net service stop
Given windows server host called "windowshost.com"
And service name called "ServiceName"
And credentials are "myDomanin/user%password"
And action is "stop"
Then command line is "net rpc service stop ServiceName -S windowshost.com -U myDomanin/user%password"
Scenario: net service pause
Given windows server host called "windowshost.com"
And service name called "ServiceName"
And credentials are "myDomanin/user%password"
And action is "pause"
Then command line is "net rpc service pause ServiceName -S windowshost.com -U myDomanin/user%password"
Scenario: net service resume
Given windows server host called "windowshost.com"
And service name called "ServiceName"
And credentials are "myDomanin/user%password"
And action is "resume"
Then command line is "net rpc service resume ServiceName -S windowshost.com -U myDomanin/user%password"
Scenario: net service status
Given windows server host called "windowshost.com"
And service name called "ServiceName"
And credentials are "myDomanin/user%password"
And action is "status"
Then command line is "net rpc service status ServiceName -S windowshost.com -U myDomanin/user%password"
The implementation of the feature file:
namespace NetService;
class Parser
{
private $host;
private $credentials;
public function __construct($host, $credentials)
{
$this->host = $host;
$this->credentials = $credentials;
}
public function getCommandLineForAction($action, $service = NULL)
{
if (!is_null($service)) $service = " {$service}";
return "net rpc service {$action}{$service} -S {$this->host} -U {$this->credentials}";
}
}
and finally our Service class:
namespace NetService;
use Symfony\Component\Process\Process,
NetService\Parser;
class Service
{
private $parser;
private $timeout;
const START = 'start';
const STOP = 'stop';
const STATUS = 'status';
const LIST_SERVICES = 'list';
const PAUSE = 'pause';
const RESUME = 'resume';
const DEFAULT_TIMEOUT = 3600;
public function __construct(Parser $parser)
{
$this->parser = $parser;
$this->timeout = self::DEFAULT_TIMEOUT;
}
public function start($service)
{
return $this->runProcess($this->parser->getCommandLineForAction(self::START, $service));
}
public function stop($service)
{
return $this->runProcess($this->parser->getCommandLineForAction(self::STOP, $service));
}
public function pause($service)
{
return $this->runProcess($this->parser->getCommandLineForAction(self::PAUSE, $service));
}
public function resume($service)
{
return $this->runProcess($this->parser->getCommandLineForAction(self::RESUME, $service));
}
public function status($service)
{
return $this->runProcess($this->parser->getCommandLineForAction(self::STATUS, $service));
}
public function listServices()
{
return $this->runProcess($this->parser->getCommandLineForAction(self::LIST_SERVICES));
}
public function isRunning($service)
{
$status = explode("\n", $this->status($service));
if (isset($status[0]) && strpos(strtolower($status[0]), "running") !== FALSE) {
return TRUE;
} else {
return FALSE;
}
}
public function setTimeout($timeout)
{
$this->timeout = $timeout;
}
private function runProcess($commandLine)
{
$process = new Process($commandLine);
$process->setTimeout($this->timeout);
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException($process->getErrorOutput());
}
return $process->getOutput();
}
private function parseStatus($status)
{
return explode("\n", $status);
}
}
And that’s all. Now a couple of examples:
include __DIR__ . '/../vendor/autoload.php';
use NetService\Service,
NetService\Parser;
$host = 'windowshost.com';
$serviceName = 'ServiceName';
$credentials = '{domain}/{user}%{password}';
$service = new Service(new Parser($host, $credentials));
if ($service->isRunning($serviceName)) {
echo "Service is running. Let's stop";
$service->stop($serviceName);
} else {
echo "Service isn't running. Let's start";
$service->start($serviceName);
}
//dumps status output
echo $service->status($serviceName);
include __DIR__ . '/../vendor/autoload.php';
use NetService\Service,
NetService\Parser;
$host = 'windowshost.com';
$credentials = '{domain}/{user}%{password}';
$service = new Service(new Parser($host, $credentials));
echo $service->listServices();
You can see the full code in github here. The package is also available for composer at Packaist.
Dependency Injection Containers with PHP. When Pimple is not enough.
Two months ago I wrote an article about Dependency Injection with PHP and Pimple. After the post I was speaking about it with a group of colleagues and someone threw a question:
What happens if your container grows up? Does Pimple scale well?
The answer is not so easy. Pimple is really simple and good for small projects, but it becomes a little mess when we need to scale. Normally when I face a problem like that I like to checkout the Symfony2 code. It usually implements a good solution. There’s something I really like from Symfony2: it’s a brilliant component library and we can use those components within our projects instead of using the full stack framework. So why don’t we only use the Dependency Injection component from SF2 instead Pimple to solve the problem? Let’s start:
We are going to use composer to load our dependencies so we start writing our composer.json file. We want to use yaml files so we need to add “symfony/yaml” and “symfony/config” in addition to “symfony/dependency-injection”:
{
"require": {
"symfony/dependency-injection": "dev-master",
"symfony/yaml": "dev-master",
"symfony/config": "dev-master"
},
"autoload":{
"psr-0":{
"":"lib/"
}
},
}
Now we can run “composer install” command and we already have our vendors and our autolader properly set.
We are going to build exactly the same example than in the previous post:
<?php
class App
{
private $proxy;
public function __construct(Proxy $proxy)
{
echo "App::__construct\n";
$this->proxy = $proxy;
}
public function hello()
{
return $this->proxy->hello();
}
}
class Proxy
{
private $curl;
public function __construct(Curl $curl)
{
$this->curl = $curl;
}
public function hello()
{
echo "Proxy::__construct\n";
return $this->curl->doGet();
}
}
class Curl
{
public function doGet()
{
echo "Curl::doGet\n";
return "Hello";
}
}
Now we create our file “services.yml” describing our dependency injection container behaviour:
services:
app:
class: App
arguments: [@Proxy]
proxy:
class: Proxy
arguments: [@Curl]
curl:
class: Curl
and finally we can build the script:
<?php
include __DIR__ . "/vendor/autoload.php";
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.yml');
$container->get('app')->hello();
IMHO is as simple a Pimple but much more flexible, customizable and it’s also well documented. For example we can split our yaml files into different ones and load them:
<?php
include __DIR__ . "/vendor/autoload.php";
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services1.yml');
$loader->load('services2.yml');
$container->get('app')->hello();
An we also can use imports in our yaml files:
imports:
- { resource: services2.yml }
services:
app:
class: App
arguments: [@Proxy]
If you don’t like yaml syntax and you prefer XML (I know. It looks insane
) you can use it, or even programatically with PHP.
What do you think?
Source code in github (see the README to install the vendors)























