Download - Easy rest service using PHP reflection api

Transcript
Page 1: Easy rest service using PHP reflection api

Easy Web Services using PHP reflection API

phplondon, Dec 4th 2008

Page 2: Easy rest service using PHP reflection api

Reflection?

Page 3: Easy rest service using PHP reflection api

http://www.flickr.com/photos/bcnbits/368112752/

Page 4: Easy rest service using PHP reflection api

ReflectionIn computer science, reflection is the

process by which a computer program can observe and modify its own structure and behaviour.

Page 5: Easy rest service using PHP reflection api

Reflection in PHP5reverse-engineer• classes, • interfaces, • exceptions• functions• Methods• Parameters• properties• extensions

retrieve doc comments for • functions, • classes and • methods.

Page 6: Easy rest service using PHP reflection api

Eg. Reflection Methodclass 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()

Page 7: Easy rest service using PHP reflection api

Eg. Reflection Methodclass 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));

Page 8: Easy rest service using PHP reflection api

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 );

Page 9: Easy rest service using PHP reflection api

Reflection is useful for • Annotations, eg. in PHP Unit

In this example the test will fail because it doesn’t throw the InvalidArgumentException

Page 10: Easy rest service using PHP reflection api

Easy Web Services Simple use case

Page 11: Easy rest service using PHP reflection api

Watch out, you will see code in slides!!!

Page 12: Easy rest service using PHP reflection api

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"); }}

Use case: you have this class in your system:

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)

Page 13: Easy rest service using PHP reflection api

// 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 (1/2)

Page 14: Easy rest service using PHP reflection api

// 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();

How we want to call it (2/2)

Page 15: Easy rest service using PHP reflection api

The conceptRequest comes in, forwarded to Proxy that does the Reflection magic: calls the method on the right class, with the right parameters mapped.

Page 16: Easy rest service using PHP reflection api

class Request { protected $request; function __construct($requestString = null) { if(is_null($requestString)) { $request = $_REQUEST; } 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; }}

$request = new Request( 'method=Users.getUsers&limit=100');

$result = $request->process();

Page 17: Easy rest service using PHP reflection api

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); } } [...]}

Page 18: Easy rest service using PHP reflection api

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)');

Page 19: Easy rest service using PHP reflection api

... and use the Proxy class to generate your API documentation (from the code, and by reverse engineering your method comments)

Page 20: Easy rest service using PHP reflection api

Questions?

Page 21: Easy rest service using PHP reflection api

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 PHPhttp://www.slideshare.net/stubbles/declarative-development-using-annotations-in-php

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