You are on page 1of 21

Easy Web Services

using PHP reflection API

phplondon, Dec 4th 2008


Reflection?
http://www.flickr.com/photos/bcnbits/368112752/
Reflection
In computer science, reflection is the
process by which a computer
program can observe and modify its
own structure and behaviour.
Reflection in PHP5
reverse-engineer retrieve doc comments for
• classes, • functions,
• interfaces, • classes and
• exceptions
• methods.
• functions
• Methods
• Parameters
• properties
• extensions
Eg. Reflection Method
class ReflectionMethod extends […]
{
public bool isFinal()
    public bool isAbstract()
    public bool isPublic()
    public bool isPrivate()
    public bool isProtected()
    public bool isStatic()
    public bool isConstructor()
    public bool isDestructor()
[…..]
    public string getFileName()
    public int getStartLine()
    public int getEndLine()
    public string getDocComment()
    public array getStaticVariables()
Eg. Reflection Method
class Randomizer
{    
    /**
     * Returns randomly 0 or 1      
* @return  int
     */
    final public static function get()
    {
        return rand( 0, 1);
    }
}

// Create an instance of the ReflectionMethod class
$method = new ReflectionMethod(‘Randomizer', get');

echo $method->isConstructor() ? 'the constructor' : 'a regular method‘;
printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));
Reflection is useful for
• Why reverse engineer comments in code?
Easy to generate documentation.
• Unit tests framework: mockup objects, function
test*, etc.
• Automatic serializations of object: your code
defines your data, your objects can be serialized
automatically, converted in JSON, etc.
$reflectionClass = new ReflectionClass('ClassIsData');
$properties = $reflectionClass->getProperties();

$property->getName();
$property->getValue( $instance );
Reflection is useful for
• Annotations, eg. in PHP Unit

In this example the test will fail because it


doesn’t throw the InvalidArgumentException
Easy Web Services
Simple use case
Watch out, you will see code
in slides!!!
Use case: you have this class in your system:
class Users
{
/**
* @return array the list of all the users
*/
static public function getUsers($limit = 10)
{
// each API method can have different permission requirements
Access::checkUserHasPermission('admin');

return Registry::get('db')->fetchAll(" SELECT *


FROM users
LIMIT $limit");
}
}

You want this class and the public methods to be


automatically available in a REST service, so you can call:
http://myService.net/?module=Users.getUsers&limit=100
And get the result (eg. in XML)
How we want to call it (1/2)
// simple call to the REST API via http
$users = file_get_contents( "http://service/API/" .
"?method=Users.getUsers&limit=100");

// we also want to call it from within our existing php code


FrontController::getInstance()->init(); // init DB, logger, auth, etc.
$request = new Request('method=Users.getUsers&limit=100');
$result = $request->process();
How we want to call it (2/2)
// ResponseBuilder object can convert the data in XML, JSON, CSV
// by converting your API returned value (from array, int, object, etc)
$request = new Request(' method=Users.getUsers
&limit=100
&format=JSON');
$XmlResult = $request->process();

// possible to add token_based authentication: all API methods call helper


// class that checks that the token has the right permissions
$request = new Request('method=Users.getUsers&token_auth=puiwrtertwoc98');
$result = $request->process();
The concept
Request comes in, forwarded to
Proxy that does the Reflection
magic: calls the method on the
right class, with the right
parameters mapped.
class Request
{
$request = new
protected $request;
function __construct($requestString = null) Request( 'method=Users.getUser
{ s&limit=100');
if(is_null($requestString)) {
$request = $_REQUEST; $result = $request->process();
} else {
$request = parse_str($requestString);
}
$this->request = $request;
}

function process()
{
$responseBuilder = new ResponseBuilder($this->request);
try {
// eg. get the "Users.getUsers"
$methodToCall = Helper::sanitizeInputVariable('method', $this->request);
list($className, $method) = explode('.',$methodToCall);
$proxy = Proxy::getInstance();
$returnedValue = $proxy->call($className, $method, $this->request );

// return the response after applying standard filters, converting format,..


$response = $responseBuilder->getResponse($returnedValue);
} catch(Exception $e ) {
// exception is forwarded to ResponseBuilder to be converted in XML/JSON,..
$response = $responseBuilder->getResponseException( $e );
}
return $response;
}
}
class Proxy
{
function call($className, $methodName, $request)
{
$this->setContext($className, $methodName, $request);
$this->loadClassMetadata();
$this->checkMethodExists();
$this->checkParametersAreSet();
$parameterValues = $this->getParametersValues();
$object = call_user_func(array($className, "getInstance"));
$timer = new Timer;
$returnedValue = call_user_func_array( array($object, $methodName),
$parameterValues);

Registry::get('logger_api')->log($className, $methodName,
$parameterValues
$timer->getTimeMs(),$returnedValue);
return $returnedValue;
}
function loadClassMetadata($className)
{
$reflectionClass = new ReflectionClass($className);
foreach($reflectionClass->getMethods() as $method)
{
$this->loadMethodMetadata($className, $method);
}
}
[...]
Conclusion
• Similar pattern as FrontController / Dispatcher
• One entry point to your Models. You can then add:
– Caching
– Logging
– Authentication
• Eg. If your app is data-centric, the ResponseBuilder could
apply set of filters.
You could for example specify custom filters to be apply to
API calls in the RequestString:
$request = new Request(' method=Analytics.getUsers
&filter_sort=name-desc
&filter_search=(pattern)');
... and use the Proxy class to generate your API
documentation (from the code, and by reverse engineering
your method comments)
Questions?
References
• This pattern is used in the open source Piwik project
http://piwik.org
• View the code on
http://dev.piwik.org/svn/trunk/core/API/
• How to design an API: best practises, concepts
http://piwik.org/blog/2008/01/how-to-design-an-api-best-
practises-concepts-technical-aspects/
• PHP: Reflection – Manual
http://uk.php.net/oop5.reflection
• Declarative Development Using Annotations In PHP
http://www.slideshare.net/stubbles/declarative-development-
using-annotations-in-php

Presentation under license #cc-by-sa, by Matthieu Aubry

You might also like