Enterprise Web Services Framework for PHP

Apify is a small and powerful open source library that delivers new levels of developer productivity by simplifying the creation of RESTful architectures. Web services are a great way to extend your web application, however, adding a web API to an existing web application can be a tedious and time-consuming task. Apify takes certain common patterns found in most web services and abstracts them so that you can quickly write web APIs without having to write too much code.

Apify exposes similar APIs as the Zend Framework, so if you are familiar with the Zend Framework, then you already know how to use Apify. Take a look at the UsersController class.

Demo

You can see it in action here: http://www.youtube.com/watch?v=7ptoB0yCsDo

Download

Apify is hosted on GitHub. You can fork it or download it in either .tar.gz or .zip formats.

FileSyncTask: Using Phing to synchronize files and directories

I needed to automate the task of synchronizing files from one server to another, so I wrote a Phing task. Finally today I found some time to finish writing the documentation.

Overview

FileSyncTask is a Phing extension for Unix systems which synchronizes files and directories from one location to another while minimizing data transfer. FileSyncTask can copy or display directory contents and copy files, optionally using compression and recursion.

Rather than using FTP or some other form of file transfer, FileSyncTask uses rsync to copy only the diffs of files that have actually changed. Only actual changed pieces of files are transferred, rather than the whole file, which results in transferring only a small amount of data and are very fast. FTP, for example, would transfer the entire file, even if only one byte changed. The tiny pieces of diffs are then compressed on the fly, further saving you file transfer time and reducing the load on the network.

FileSyncTask can be used to synchronize Website trees from staging to production servers and to backup key areas of the filesystems.

Usage

There are 4 different ways of using FileSyncTask:

  1. For copying local files.
  2. For copying from the local machine to a remote machine using a remote shell program as the transport (ssh).
  3. For copying from a remote machine to the local machine using a remote shell program.
  4. For listing files on a remote machine.

The SSH client called by FileSyncTask uses settings from the build.properties file:

sync.source.projectdir=/home/development.com/public
sync.destination.projectdir=/home/staging.com
sync.remote.host=server.com
sync.remote.user=user
sync.destination.backupdir=/home/staging.com/backup
sync.exclude.file=/home/development.com/build/sync.exclude

Listing files

The “listonly” option will cause the modified files to be listed instead of transferred. You must specify a source and a destination, one of which may be remote.

<taskdef name="sync" classname="phing.tasks.ext.FileSyncTask" />
<sync
    sourcedir="${sync.source.projectdir}"
    destinationdir="${sync.destination.projectdir}"
    listonly="true"
    verbose="true" />

Excluding irrelevant files

To exclude files from synchronizations, open and edit the sync.exclude file under the build/ directory. Each line can contain a file, a directory, or a pattern:

*~
.svn
.htaccess
public/index.development.php
public/images/uploads/*
build/*
log/*
tmp/*

Copying files to a remote machine

The following task definition will transfer files from a local source to a remote destination:

<taskdef name="sync" classname="phing.tasks.ext.FileSyncTask" />
<sync
    sourcedir="${sync.source.projectdir}"
    destinationdir="${sync.remote.user}@${sync.remote.host}:${sync.destination.projectdir}"
    excludefile="${sync.exclude.file}"
    verbose="true" />

Example

Directory structure

In order to separate the sync settings from the main build file, I’ve created a file called sync.properties:

development.com
|-- build
|   |-- build.properties
|   |-- build.xml
|   |-- sync.exclude
|   `-- sync.properties
`-- public
    `-- index.php

XML build file

Phing uses XML build files that contain a description of the things to do. The build file is structured into targets that contain the actual commands to perform:

<?xml version="1.0" ?>
<project name="example" basedir="." default="build">
<property name="version" value="1.0" />

<!-- Public targets -->
<target name="sync:list" description="List files">
  <phingcall target="-sync-execute-task">
    <property name="listonly" value="true" />
  </phingcall>
</target>

<target name="sync" description="Copy files">
  <phingcall target="-sync-execute-task">
    <property name="listonly" value="false" />
  </phingcall>
</target>

<!-- Private targets -->
<target name="-init" description="Load main settings">
  <tstamp />
  <property file="build.properties" />
</target>

<target name="-sync-execute-task" depends="-init">
  <property file="sync.properties" />
  <if>
    <not>
      <isset property="sync.verbose" />
    </not>
    <then>
      <property name="sync.verbose" value="true" override="true" />
      <echo message="The value of sync.verbose has been set to true" />
    </then>
  </if>
  <property name="sync.remote.auth" value="${sync.remote.user}@${sync.remote.host}" />
  <taskdef name="sync" classname="phing.tasks.ext.FileSyncTask" />
  <sync
    sourcedir="${sync.source.projectdir}"
    destinationdir="${sync.remote.auth}:${sync.destination.projectdir}"
    backupdir="${sync.remote.auth}:${sync.destination.backupdir}"
    excludefile="${sync.exclude.file}"
    listonly="${listonly}"
    verbose="${sync.verbose}" />
</target>
</project>

Execute task

$ phing sync:list

Outputs:

Buildfile: /home/development.com/build/build.xml

example > sync:list:
[phingcall] Calling Buildfile '/home/development.com/build/build.xml'
            with target '-sync-execute-task'

example > -init:
[property] Loading /home/development.com/build/build.properties

example > -sync-execute-task:
[property] Loading /home/development.com/build/sync.properties
[echo] The value of sync.verbose has been set to true

Execute Command
----------------------------------------
rsync -razv --list-only -b --backup-dir

Sync files to remote server
----------------------------------------
Source:        /home/development.com/public
Destination:   user@server.com:/home/staging.com
Backup:        user@server.com:/home/staging.com/backup

Exclude patterns
----------------------------------------
*~
.svn
.htaccess
public/index.development.php
public/images/uploads/*
build/*
log/*
tmp/*

(list of files that have changed)

BUILD FINISHED
Total time: 1.9763 second

More information

Related Articles

Using Annotations in PHP

Annotations provide data about a program that is not part of the program itself. They have no direct effect on the operation of the code they annotate. In PHP, annotations can only be read reflectively at run time.

Basic concept of Annotations

  • Add meta-data to classes, methods, properties
  • Do not (directly) affect program semantics
  • Can be used by tools or libraries
  • Can be parameterized or simple marker annotations

Addendum is a PHP library that supports single-value and multi-value annotations. There are three annotation types:

Marker

Marker type annotations have no elements, except the annotation name itself.

Example:

class Persistent extends Annotation {}

Usage:

/** @Persistent */
class Person {
    ...
}

// by class name
$reflection = new ReflectionAnnotatedClass('Person');
// by instance
$person = new Person();
$reflection = new ReflectionAnnotatedClass($person);
// true
$reflection->hasAnnotation('Persistent');

Single-value

Single-value type annotations provide a single piece of data only. This can be represented with a data=value pair or, simply with the value (a shortcut syntax) only, within parenthesis.

Example:

class Table extends Annotation {}

Usage:

/** @Table("people") */
class Person {
    ...
}

// by class name
$reflection = new ReflectionAnnotatedClass('Person');
// contains string "people"
$reflection->getAnnnotation('Table')->value;

Multi-value

Multi-value type annotations have multiple data members. Therefore, you must use a full data=value parameter syntax for each member.

Example:

class Secured extends Annotation {
   public $role;
   public $level;
}

Usage:

/** @Secured(role = "admin", level = 2) */
class Administration {
    ...
}

// by class name
$reflection = new ReflectionAnnotatedClass('Administration');
$annotation = $reflection->getAnnnotation('Secured');
// contains string "admin"
$annotation->role;
// contains integer "2"
$annotation->level;

More information

Related Projects

Static Factories vs Public Constructors

Normally, creating an instance of a class is done by calling new, which calls the constructor. Static factory provides a static method that returns an instance of the class. So, you are using static factory instead of the constructor. Providing a static factory method instead of a public constructor has both advantages and disadvantages.

Advantages of static factories over constructors:

  • Static factory instances have names but constructors do not.
  • Static factories can have two methods named differently but taking the same number and type of parameters.
  • Subclassing isn’t possible but inheritance isn’t always the best way to reuse code.

Disadvantages of static factories over constructors:

  • Can’t be subclassed.
  • Poor naming conventions can make it hard to know what’s going on.

Dagfinn Reiersøl wrote an interesting post about this:

I’ve never considered visibility restrictions important enough to be a major argument against those languages that have lacked them (PHP 4). So why would I be sceptical of public constructors? I got the idea after reading Joshua Kerievsky’s book Refactoring to Patterns. One of his refactorings is called Replace Constructors with Creation Methods. In Java, unlike PHP, you can have multiple constructors that are distinguished only by the number and type of arguments. That may be practical sometimes, but as Kerievsky’s example shows, it be more readable to have creation methods with different names instead. Which is what you have to anyway in PHP.

Read More: Public constructors considered harmful

Improving the performance of Zend_Controller

Zend_Controller_Router_Rewrite is the standard framework router. It takes a URI endpoint and decomposes it into parameters to determine which module, controller, and action of that controller should receive the request. Routing occurs only once, when the request is initially received and before the first controller is dispatched.

To use the router you have to instantiate it, add some user defined routes and inject it into the controller:

$routes['blog'] = new Zend_Controller_Router_Route(
	'blog/:year/:month/:day/:title/*',
	array(
		'year'       => date('Y'),
		'month'      => date('m'),
		'day'        => date('d'),
		'module'     => 'blog',
		'controller' => 'index',
		'action'     => 'archive'
	),
	array(
		'year'  => '\d+',
		'month' => '\d+',
		'day'   => '\d+',
		'title' => '([0-9a-zA-Z-]+)',
	)
);

$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$router->addRoutes($routes);
$frontController->dispatch();

Front Controller dispatch mechanism:

o-- Zend_Controller_Front::dispatch()
--> Zend_Controller_Front::setResponse()
--> Zend_Controller_Front::getRouter()
--> Zend_Controller_Router_Rewrite::setParams()
--> Zend_Controller_Router_Rewrite::route()

Zend_Controller_Front gets an instance of Zend_Controller_Router_Rewrite, routes the request, iterates through all the provided routes and matches its definitions to the current request URI.

The problem

The router has to load all the objects into memory, iterate through all the routes and perform regular expressions until it finds a positive match. This means that the more routes you add, the longer the router will take to process them. The problem becomes more critical when using XML or INI configuration files and a site increases in complexity and attracts more traffic.

The Solution

One of the solutions is to split the routes file into different .php or .ini files. For example, instead of loading a single file:

$frontController = Zend_Controller_Front::getInstance();
$frontController->addModuleDirectory(
	'../application/modules'
);
$config = new Zend_Config_Ini(
	'../application/config/routes/config.ini',
	'production'
);
$router = $frontController->getRouter();
$router->addConfig($config, 'routes');

You can use the My_Controller_Router_Rewrite class to set the path to the directory where all the configuration files are located:

$frontController = Zend_Controller_Front::getInstance();
$frontController->addModuleDirectory(
	'../application/modules'
);
$router = new My_Controller_Router_Rewrite();
$frontController->setRouter($router);
$router->setRoutesDirectory(
	'../application/config/routes',
	array('production', 'routes')
);

This allows the router to map the first segment of the URL path to a filename. For example, given the following URI:

http://www.zend-test.com/blog/2008/07/14/test

The router will load the blog.ini file:

application
|-- bootstrap.php
|-- config
|   `-- routes
|       |-- blog.ini
|       |-- eshop.ini
|       |-- forum.ini
|       `-- gallery.ini
|-- layouts
|   `-- main.phtml
`-- modules
    |-- blog
        |-- controllers
        |   `-- IndexController.php
        |-- models
        `-- views

Test Result Data

Zend_Controller_Router_Rewrite (4 routes)

Server Hostname:         www.zend-test.com
Document Path:           /blog/2008/07/14/test
Requests per second 1:   88.24 [#/sec]
Requests per second 2:   89.32 [#/sec]

Zend_Controller_Router_Rewrite (16 routes)

Server Hostname:         www.zend-test.com
Document Path:           /blog/2008/07/14/test
Requests per second 1:   64.45 [#/sec]
Requests per second 2:   64.61 [#/sec]

Zend_Controller_Router_Rewrite (20 routes)

Server Hostname:         www.zend-test.com
Document Path:           /blog/2008/07/14/test
Requests per second 1:   59.17 [#/sec]
Requests per second 2:   59.03 [#/sec]

Now, lets try splitting the config.ini file into separate configuration files:

MyRouter extends Zend_Controller_Router_Rewrite (4 routes)

Server Hostname:         www.zend-test.com
Document Path:           /blog/2008/07/14/test
Requests per second 1:   89.47 [#/sec]
Requests per second 2:   89.32 [#/sec]

MyRouter extends Zend_Controller_Router_Rewrite (40 routes)

Server Hostname:         www.zend-test.com
Document Path:           /blog/2008/07/14/test
Requests per second 1:   89.62 [#/sec]
Requests per second 2:   89.41 [#/sec]

Documentation

Hacking the Core of the Zend Framework

So, I’ve been hacking the core of the Zend Framework again, this time the Zend_Controller component. I spent a couple of hours monitoring the performance and tuning parameters and configuration options to minimize bottlenecks. Needless to say I am very impressed with the overall design of the component. It’s very simple, flexible and covers most of the use cases.

Performance problems typically do not become apparent until someone places an application under an increased load, so measuring the performance of an application (at design time) can help improve the scalability of the application.

The methodology I’m using:

  1. Determine the problem.
  2. Formulate a solution.
  3. Implement the solution.
  4. Analyse the results.
  5. Document everything.

Continue reading

Architecture-Oriented or Feature-Oriented?

Traditionally, there are two fundamental approaches when it comes to organising your development teams: the Architecture-Oriented approach and the Feature-Oriented approach. The first privileges teams that focus on the different architectural layers or components, whereas the second prefers to organise teams around deliverable application features.

How do you organise your development teams?