├── .gitignore
├── tests
└── Doctrine
│ └── Tests
│ ├── DoctrineTestCase.php
│ ├── TestInit.php
│ └── REST
│ ├── AllTests.php
│ ├── FunctionalTest.php
│ └── ClientTest.php
├── .travis.yml
├── composer.json
├── phpunit.xml.dist
├── client.php
├── LICENSE
├── server.php
├── js
├── README.markdown
├── jActiveResource.js
└── example.html
├── lib
└── Doctrine
│ └── REST
│ ├── Client
│ ├── URLGenerator
│ │ ├── ApontadorURLGenerator.php
│ │ ├── AbstractURLGenerator.php
│ │ └── StandardURLGenerator.php
│ ├── ResponseTransformer
│ │ ├── AbstractResponseTransformer.php
│ │ └── StandardResponseTransformer.php
│ ├── Request.php
│ ├── Client.php
│ ├── Entity.php
│ ├── Manager.php
│ └── EntityConfiguration.php
│ └── Server
│ ├── PHPRequestParser.php
│ ├── Action
│ ├── GetAction.php
│ ├── DeleteAction.php
│ ├── UpdateAction.php
│ ├── InsertAction.php
│ ├── ListAction.php
│ └── AbstractAction.php
│ ├── Request.php
│ ├── Server.php
│ ├── Response.php
│ └── RequestHandler.php
├── README.markdown
└── composer.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | phpunit.xml
3 |
--------------------------------------------------------------------------------
/tests/Doctrine/Tests/DoctrineTestCase.php:
--------------------------------------------------------------------------------
1 | =5.3.2",
13 | "doctrine/common": "*"
14 | },
15 | "require-dev": {
16 | "doctrine/orm": "~2.4.2",
17 | "doctrine/dbal": "~2.4.2",
18 | "phpunit/phpunit": "~3.7"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Doctrine\\REST\\": "lib/Doctrine/REST"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Doctrine/Tests/REST/AllTests.php:
--------------------------------------------------------------------------------
1 | addTestSuite('Doctrine\Tests\REST\ClientTest');
23 | $suite->addTestSuite('Doctrine\Tests\REST\FunctionalTest');
24 |
25 | return $suite;
26 | }
27 | }
28 |
29 | if (PHPUnit_MAIN_METHOD == 'AllTests::main') {
30 | AllTests::main();
31 | }
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 |
22 | ./tests/Doctrine/
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/client.php:
--------------------------------------------------------------------------------
1 | register();
13 |
14 | $client = new Client();
15 |
16 | $manager = new Manager($client);
17 | $manager->registerEntity('User');
18 |
19 | Entity::setManager($manager);
20 |
21 | class User extends Entity
22 | {
23 | public $id;
24 | public $username;
25 | public $password;
26 |
27 | public static function configure(EntityConfiguration $entityConfiguration)
28 | {
29 | $entityConfiguration->setUrl('http://localhost/rest/server.php');
30 | $entityConfiguration->setName('user');
31 | }
32 | }
33 |
34 | $user = User::find(9);
35 | $user->username = 'teetertertsting';
36 | $user->password = 'w00t';
37 | $user->save();
38 | print_r($user);
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2006-2014 Doctrine Project
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 | register();
10 |
11 | $classLoader = new ClassLoader('Doctrine', '/Users/jwage/Sites/doctrine2git/lib');
12 | $classLoader->register();
13 |
14 | $config = new \Doctrine\ORM\Configuration();
15 | $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
16 | $config->setProxyDir('/tmp');
17 | $config->setProxyNamespace('Proxies');
18 | $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver());
19 |
20 | $connectionOptions = array(
21 | 'driver' => 'pdo_mysql',
22 | 'dbname' => 'rest_test',
23 | 'user' => 'root'
24 | );
25 |
26 | $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
27 |
28 | $parser = new \Doctrine\REST\Server\PHPRequestParser();
29 | $requestData = $parser->getRequestArray();
30 |
31 | class TestAction
32 | {
33 | public function executeDBAL()
34 | {
35 | return array('test' => 'test');
36 | }
37 | }
38 |
39 | $server = new \Doctrine\REST\Server\Server($em->getConnection(), $requestData);
40 | $server->addEntityAction('user', 'test', 'TestAction');
41 | $server->execute();
42 | $server->getResponse()->send();
--------------------------------------------------------------------------------
/js/README.markdown:
--------------------------------------------------------------------------------
1 | # Javascript REST Client
2 |
3 | The Javascript REST client is an ActiveRecord style API for working with REST
4 | services. It is built on top of jQuery and is easy to use.
5 |
6 | All you need to do is make sure you require jQuery and jActiveResource:
7 |
8 |
9 |
10 |
11 | Now you can get started by defining new entity:
12 |
13 | jActiveResource.define('User', {
14 | url: 'http://localhost/rest/server.php/user',
15 |
16 | username: null,
17 | password: null,
18 |
19 | toString: function () {
20 | return 'username=' + this.username + '&password=' + this.password;
21 | }
22 | });
23 |
24 |
25 | You can start creating instances and saving them:
26 |
27 | var user = User.create();
28 | user.username = 'jwage';
29 | user.password = 'password';
30 |
31 | user.save(function (user) {
32 | alert(user.username + ' saved!');
33 | });
34 |
35 | You can easily retrieve all the users with findAll():
36 |
37 | var users = User.findAll(null, function(users) {
38 | alert(users.length + ' users returned');
39 | });
40 |
41 | If you want to retrieve a single User you can use the find() method:
42 |
43 | var user = User.find(1);
44 | user.username = 'jon';
45 | user.save(function (user) {
46 | alert(user.id + ' updated');
47 | });
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/URLGenerator/ApontadorURLGenerator.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class ApontadorURLGenerator extends AbstractURLGenerator
11 | {
12 | public function generate(array $options)
13 | {
14 | $id = isset($options['id']) ? $options['id'] : null;
15 | $action = isset($options['action']) ? $options['action'] : null;
16 | $parameters = isset($options['parameters']) ? $options['parameters'] : array();
17 |
18 | $parameters['type'] = $this->_entityConfiguration->getResponseType();
19 | if ($id)
20 | {
21 | if ($action !== null)
22 | {
23 | $path = sprintf('/%s/%s', $id, $action);
24 | } else {
25 | $path = sprintf('/%s', $id);
26 | }
27 | } else {
28 | if ($action !== null)
29 | {
30 | $path = sprintf('/%s', $action);
31 | } else {
32 | $path = '';
33 | }
34 | }
35 | $url = $this->_entityConfiguration->getUrl() . '/' . $this->_entityConfiguration->getName() . $path;
36 | if (is_array($parameters) && $parameters) {
37 | foreach ($this->_entityConfiguration->getProperties() as $field) {
38 | unset($parameters[$field]);
39 | }
40 | if ($parameters) {
41 | $url .= '?' . http_build_query($parameters);
42 | }
43 | }
44 | return $url;
45 | }
46 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/PHPRequestParser.php:
--------------------------------------------------------------------------------
1 | $entity,
43 | '_id' => $id,
44 | '_action' => $action,
45 | '_format' => $format
46 | ), $_REQUEST);
47 |
48 | return $data;
49 | }
50 | }
--------------------------------------------------------------------------------
/js/jActiveResource.js:
--------------------------------------------------------------------------------
1 | var jActiveResource = jQuery.extend({
2 | create: function() {
3 | return jQuery.extend(this);
4 | },
5 |
6 | define: function (name, definition) {
7 | var instance = jQuery.extend(this, definition);
8 | eval(name + ' = instance;');
9 | eval(name + 'Factory = jQuery.extend(jActiveResourceFactory);')
10 | },
11 |
12 | find: function(id, callback) {
13 | this.execute('get', this.getUrl(id), null, callback);
14 | },
15 |
16 | findAll: function(data, callback) {
17 | this.execute('get', this.getUrl(), data, callback);
18 | },
19 |
20 | destroy: function(id, callback) {
21 | this.execute('delete', this.getUrl(this.id), null, callback);
22 | },
23 |
24 | save: function(callback) {
25 | this.execute('post', this.getUrl(this.id), this.toString(), callback);
26 | },
27 |
28 | getUrl: function(id, action, parameters) {
29 | if (id) {
30 | return this.url + '/' + id + '.json';
31 | } else {
32 | return this.url + '.json';
33 | }
34 | },
35 |
36 | execute: function(method, url, data, callback) {
37 | $.ajax({
38 | type: method,
39 | dataType: 'json',
40 | url: url,
41 | data: data,
42 | success: function(data) {
43 | if (data.length > 0) {
44 | var results = new Array();
45 | for (i = 0; i < data.length; i++) {
46 | results[i] = jQuery.extend(this.prototype, data[i]);
47 | }
48 | callback(results)
49 | } else {
50 | callback(jQuery.extend(this.prototype, data));
51 | }
52 | }
53 | });
54 | }
55 | });
56 |
57 | var jActiveResourceFactory = jActiveResource.extend({
58 | destroy: function(id, callback) {
59 | this.execute('delete', this.getUrl(id), null, callback);
60 | }
61 | });
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/URLGenerator/AbstractURLGenerator.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client\URLGenerator;
23 |
24 | use Doctrine\REST\Client\EntityConfiguration;
25 |
26 | /**
27 | * Abstract URL generator for REST services
28 | *
29 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
30 | * @link www.doctrine-project.org
31 | * @since 2.0
32 | * @version $Revision$
33 | * @author Jonathan H. Wage
34 | */
35 | abstract class AbstractURLGenerator
36 | {
37 | protected $_entityConfiguration;
38 |
39 | public function __construct(EntityConfiguration $entityConfiguration)
40 | {
41 | $this->_entityConfiguration = $entityConfiguration;
42 | }
43 |
44 | abstract public function generate(array $options);
45 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Action/GetAction.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server\Action;
23 |
24 | /**
25 | * REST server get action.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class GetAction extends AbstractAction
34 | {
35 | public function execute()
36 | {
37 | $entity = $this->_findEntityById();
38 |
39 | if ( ! $entity) {
40 | throw new \InvalidArgumentException(sprintf('Could not find the "%s" with an id of "%s"', $this->_request['_entity'], implode(', ', (array) $this->_request['_id'])));
41 | }
42 |
43 | return $entity;
44 | }
45 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/ResponseTransformer/AbstractResponseTransformer.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client\ResponseTransformer;
23 |
24 | use Doctrine\REST\Client\EntityConfiguration;
25 |
26 | /**
27 | * Abstract REST request response transformer
28 | *
29 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
30 | * @link www.doctrine-project.org
31 | * @since 2.0
32 | * @version $Revision$
33 | * @author Jonathan H. Wage
34 | */
35 | abstract class AbstractResponseTransformer
36 | {
37 | protected $_entityConfiguration;
38 |
39 | public function __construct(EntityConfiguration $entityConfiguration)
40 | {
41 | $this->_entityConfiguration = $entityConfiguration;
42 | }
43 |
44 | abstract public function transform($data);
45 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Action/DeleteAction.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server\Action;
23 |
24 | /**
25 | * REST server delete action.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class DeleteAction extends AbstractAction
34 | {
35 | public function executeORM()
36 | {
37 | if ($entity = $this->_findEntityById()) {
38 | $this->_source->remove($entity);
39 | $this->_source->flush();
40 | return $entity;
41 | }
42 | }
43 |
44 | public function executeDBAL()
45 | {
46 | if ($entity = $this->_findEntityById()) {
47 | $this->_source->delete($this->_getEntity(), array(
48 | $this->_getEntityIdentifierKey() => $this->_request['_id']
49 | ));
50 | return $entity;
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Action/UpdateAction.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server\Action;
23 |
24 | /**
25 | * REST server update action.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class UpdateAction extends AbstractAction
34 | {
35 | public function executeORM()
36 | {
37 | if ($entity = $this->_findEntityById()) {
38 | $this->_updateEntityInstance($entity);
39 | $this->_source->flush();
40 | }
41 |
42 | return $entity;
43 | }
44 |
45 | public function executeDBAL()
46 | {
47 | $entity = $this->_getEntity();
48 | $identifierKey = $this->_getEntityIdentifierKey($entity);
49 |
50 | $data = $this->_gatherData();
51 | $this->_source->update($entity, $data, array(
52 | $identifierKey => $this->_request['_id']
53 | ));
54 |
55 | return $this->_findEntityById();
56 | }
57 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Action/InsertAction.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server\Action;
23 |
24 | /**
25 | * REST server insert action.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class InsertAction extends AbstractAction
34 | {
35 | public function executeORM()
36 | {
37 | $entity = $this->_getEntity();
38 |
39 | $instance = new $entity();
40 | $this->_updateEntityInstance($instance);
41 | $this->_source->persist($instance);
42 | $this->_source->flush();
43 |
44 | return $instance;
45 | }
46 |
47 | public function executeDBAL()
48 | {
49 | $entity = $this->_getEntity();
50 | $identifierKey = $this->_getEntityIdentifierKey();
51 |
52 | $data = $this->_gatherData();
53 |
54 | unset($data['id']);
55 | $this->_source->insert($entity, $data);
56 | $data = array_merge(
57 | array($identifierKey => $this->_source->lastInsertId()),
58 | $data
59 | );
60 |
61 | return $data;
62 | }
63 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Request.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server;
23 |
24 | /**
25 | * Class that represents a REST server request.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class Request implements \ArrayAccess
34 | {
35 | private $_data;
36 |
37 | public function __construct(array $request)
38 | {
39 | $this->_data = $request;
40 | $this->_data['_format'] = isset($this->_data['_format']) ? $this->_data['_format'] : 'json';
41 | }
42 |
43 | public function getData()
44 | {
45 | return $this->_data;
46 | }
47 |
48 | public function offsetSet($key, $value)
49 | {
50 | $this->_data[$key] = $value;
51 | }
52 |
53 | public function offsetGet($key)
54 | {
55 | return isset($this->_data[$key]) ? $this->_data[$key] : null;
56 | }
57 |
58 | public function offsetUnset($key)
59 | {
60 | unset($this->_data[$key]);
61 | }
62 |
63 | public function offsetExists($key)
64 | {
65 | return isset($this->_data[$key]);
66 | }
67 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Action/ListAction.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server\Action;
23 |
24 | /**
25 | * REST server list action.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class ListAction extends AbstractAction
34 | {
35 | public function executeORM()
36 | {
37 | $entity = $this->_getEntity();
38 | $qb = $this->_source->createQueryBuilder()
39 | ->select('a')
40 | ->from($entity, 'a');
41 |
42 | $data = $this->_gatherData();
43 | foreach ($data as $key => $value) {
44 | $qb->andWhere("a.$key = :$key");
45 | $qb->setParameter($key, $value);
46 | }
47 |
48 | $query = $qb->getQuery();
49 | $this->_setQueryFirstAndMax($query);
50 | $results = $query->execute();
51 |
52 | return $results;
53 | }
54 |
55 | public function executeDBAL()
56 | {
57 | $entity = $this->_getEntity();
58 |
59 | $params = array();
60 | $query = sprintf('SELECT * FROM %s', $entity);
61 | if ($data = $this->_gatherData()) {
62 | $query .= ' WHERE ';
63 | foreach ($data as $key => $value) {
64 | $query .= $key . ' = ? AND ';
65 | $params[] = $value;
66 | }
67 | $query = substr($query, 0, strlen($query) - 5);
68 | }
69 | $query = $this->_setQueryFirstAndMax($query);
70 | $results = $this->_source->fetchAll($query, $params);
71 |
72 | return $results;
73 | }
74 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/URLGenerator/StandardURLGenerator.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client\URLGenerator;
23 |
24 | /**
25 | * Standard REST request URL generator
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class StandardURLGenerator extends AbstractURLGenerator
34 | {
35 | public function generate(array $options)
36 | {
37 | $id = isset($options['id']) ? $options['id'] : null;
38 | $action = isset($options['action']) ? $options['action'] : null;
39 | $parameters = isset($options['parameters']) ? $options['parameters'] : array();
40 |
41 | if ($id)
42 | {
43 | if ($action !== null)
44 | {
45 | $path = sprintf('/%s/%s.' . $this->_entityConfiguration->getResponseType(), $id, $action);
46 | } else {
47 | $path = sprintf('/%s.' . $this->_entityConfiguration->getResponseType(), $id);
48 | }
49 | } else {
50 | if ($action !== null)
51 | {
52 | $path = sprintf('/%s.' . $this->_entityConfiguration->getResponseType(), $action);
53 | } else {
54 | $path = '.' . $this->_entityConfiguration->getResponseType();
55 | }
56 | }
57 | $url = $this->_entityConfiguration->getUrl() . '/' . $this->_entityConfiguration->getName() . $path;
58 | if (is_array($parameters) && $parameters) {
59 | foreach ($this->_entityConfiguration->getProperties() as $field) {
60 | unset($parameters[$field]);
61 | }
62 | if ($parameters) {
63 | $url .= '?' . http_build_query($parameters);
64 | }
65 | }
66 | return $url;
67 | }
68 | }
--------------------------------------------------------------------------------
/js/example.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | jAct demo
5 |
6 |
7 |
8 |
9 |
56 |
57 |
58 |
59 |
60 | Create User
61 |
67 |
68 | Users
69 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Server.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server;
23 |
24 | use Doctrine\ORM\EntityManager,
25 | Doctrine\ORM\Connection;
26 |
27 | /**
28 | * Simple REST server facade.
29 | *
30 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
31 | * @link www.doctrine-project.org
32 | * @since 2.0
33 | * @version $Revision$
34 | * @author Jonathan H. Wage
35 | */
36 | class Server
37 | {
38 | private $_requestHandler;
39 | private $_request;
40 | private $_response;
41 |
42 | public function __construct($source, array $requestData = array())
43 | {
44 | $this->_request = new Request($requestData);
45 | $this->_response = new Response($this->_request);
46 | $this->_requestHandler = new RequestHandler($source, $this->_request, $this->_response);
47 | }
48 |
49 | public function execute()
50 | {
51 | $this->_requestHandler->execute();
52 | return $this->_requestHandler->getResponse()->getContent();
53 | }
54 |
55 | public function setEntityIdentifierKey($entity, $identifierKey)
56 | {
57 | $this->_requestHandler->setEntityIdentifierKey($entity, $identifierKey);
58 | }
59 |
60 | public function setEntityAlias($entity, $alias)
61 | {
62 | $this->_requestHandler->setEntityAlias($entity, $alias);
63 | }
64 |
65 | public function registerAction($action, $className)
66 | {
67 | $this->_requestHandler->registerAction($action, $className);
68 | }
69 |
70 | public function addEntityAction($entity, $action, $className)
71 | {
72 | $this->_requestHandler->addEntityAction($entity, $action, $className);
73 | }
74 |
75 | public function setUsername($username)
76 | {
77 | $this->_requestHandler->setUsername($username);
78 | }
79 |
80 | public function setPassword($password)
81 | {
82 | $this->_requestHandler->setPassword($password);
83 | }
84 |
85 | public function getResponse()
86 | {
87 | return $this->_response;
88 | }
89 |
90 | public function getRequest()
91 | {
92 | return $this->_request;
93 | }
94 |
95 | public function getRequestHandler()
96 | {
97 | return $this->_requestHandler;
98 | }
99 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/Request.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client;
23 |
24 | /**
25 | * Class that represents a request to a REST service through the raw HTTP client.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class Request
34 | {
35 | private $_url;
36 | private $_method = Client::GET;
37 | private $_parameters = array();
38 | private $_username;
39 | private $_password;
40 | private $_responseType = 'xml';
41 | private $_responseTransformerImpl;
42 |
43 | public function setUrl($url)
44 | {
45 | $this->_url = $url;
46 | }
47 |
48 | public function getUrl()
49 | {
50 | return $this->_url;
51 | }
52 |
53 | public function setMethod($method)
54 | {
55 | $this->_method = $method;
56 | }
57 |
58 | public function getMethod()
59 | {
60 | return $this->_method;
61 | }
62 |
63 | public function setParameters($parameters)
64 | {
65 | $this->_parameters = $parameters;
66 | }
67 |
68 | public function getParameters()
69 | {
70 | return $this->_parameters;
71 | }
72 |
73 | public function setResponseType($responseType)
74 | {
75 | $this->_responseType = $responseType;
76 | }
77 |
78 | public function getResponseType()
79 | {
80 | return $this->_responseType;
81 | }
82 |
83 | public function setUsername($username)
84 | {
85 | $this->_username = $username;
86 | }
87 |
88 | public function getUsername()
89 | {
90 | return $this->_username;
91 | }
92 |
93 | public function setPassword($password)
94 | {
95 | $this->_password = $password;
96 | }
97 |
98 | public function getPassword()
99 | {
100 | return $this->_password;
101 | }
102 |
103 | public function setResponseTransformerImpl($responseTransformerImpl)
104 | {
105 | $this->_responseTransformerImpl = $responseTransformerImpl;
106 | }
107 |
108 | public function getResponseTransformerImpl()
109 | {
110 | return $this->_responseTransformerImpl;
111 | }
112 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/ResponseTransformer/StandardResponseTransformer.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client\ResponseTransformer;
23 |
24 | /**
25 | * Standard REST request response handler. Converts a standard REST service response
26 | * to an array for easy manipulation. Works for both xml and json response types.
27 | *
28 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
29 | * @link www.doctrine-project.org
30 | * @since 2.0
31 | * @version $Revision$
32 | * @author Jonathan H. Wage
33 | */
34 | class StandardResponseTransformer extends AbstractResponseTransformer
35 | {
36 | public function transform($data)
37 | {
38 | switch ($this->_entityConfiguration->getResponseType()) {
39 | case 'xml':
40 | return $this->xmlToArray($data);
41 | case 'json':
42 | return $this->jsonToArray($data);
43 | break;
44 | }
45 | }
46 |
47 | public function xmlToArray($object, &$array = array())
48 | {
49 | if (is_string($object)) {
50 | $object = new \SimpleXMLElement($object);
51 | }
52 | $children = $object->children();
53 | $executed = false;
54 | foreach ($children as $elementName => $node) {
55 | if (isset($array[$elementName]) && $array[$elementName] !== null) {
56 | if (isset($array[$elementName][0]) && $array[$elementName][0] !== null) {
57 | $i = count($array[$elementName]);
58 | $this->xmlToArray($node, $array[$elementName][$i]);
59 | } else {
60 | $tmp = $array[$elementName];
61 | $array[$elementName] = array();
62 | $array[$elementName][0] = $tmp;
63 | $i = count($array[$elementName]);
64 | $this->xmlToArray($node, $array[$elementName][$i]);
65 | }
66 | } else {
67 | $array[$elementName] = array();
68 | $this->xmlToArray($node, $array[$elementName]);
69 | }
70 | $executed = true;
71 | }
72 | if ( ! $executed && ! $children->getName()) {
73 | $array = (string) $object;
74 | }
75 | return $array;
76 | }
77 |
78 | public function jsonToArray($json)
79 | {
80 | return (array) json_decode($json);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # THIS PROJECT IS NOT MAINTAINED
2 |
3 | # Doctrine 2 REST Server and Client
4 |
5 | The Doctrine 2 REST server and client component is both an easy way to spin up
6 | REST services for your Doctrine 2 entities as well as a way to work with REST
7 | services via an ActiveRecord style implementation similiar to ActiveResource in
8 | Ruby on Rails!
9 |
10 | ## Introduction
11 |
12 | The basic concept is simple, you have a REST service (http://api.people.com/person)
13 | and you want to interact with it through a simple ActiveRecord style interface.
14 |
15 | First we can retrieve a person:
16 |
17 | $person = Person::find(1); // GET http://api.people.com/person/1.xml
18 |
19 | Now we can change some properties of that person:
20 |
21 | $person->setName('Jonathan H. Wage');
22 |
23 | Once we're done we can simply save it and the appropriate REST call will be made:
24 |
25 | $person->save(); // POST http://api.people.com/person/1.xml (name=Jonathan H. Wage)
26 |
27 | ## Client
28 |
29 | The REST client is an ActiveRecord style implementation for working with REST
30 | services. All you need to do is define some PHP classes that are mapped to some
31 | REST service on the web. Here is an example where we map a Person to
32 | http://api.people.com/person:
33 |
34 | setUrl('http://api.people.com');
48 | $entityConfiguration->setName('person');
49 | }
50 |
51 | public function getId()
52 | {
53 | return $this->id;
54 | }
55 |
56 | public function setName($name)
57 | {
58 | $this->name = $name;
59 | }
60 |
61 | public function getName()
62 | {
63 | return $this->name;
64 | }
65 | }
66 |
67 | Now when we perform some actions it will generate the appropriate REST request,
68 | execute it, transform the response and hydrate the results to your PHP objects.
69 |
70 | $person = new Person();
71 | $person->setName('Jonathan H. Wage');
72 | $person->save(); // PUT http://api.people.com/person.xml (name=Jonathan H. Wage)
73 |
74 | We can retrieve that person again now:
75 |
76 | $person = Person::find($person->getId()); // GET http://api.people.com/person/1.xml
77 |
78 | Or you can retrieve all Person objects:
79 |
80 | $persons = Person::findAll();
81 |
82 | ## Server
83 |
84 | The Doctrine 2 REST server allows you to easily expose your entities through some
85 | REST services. This is the raw low level server and does not include any routing
86 | or URL parsing so you would need to implement in some existing framework that
87 | has routing like Symfony or Zend Framework.
88 |
89 | All you need to do is create a new REST server instance and pass it the instance
90 | of your EntityManager you want to expose the entities for and an array representing
91 | the server request you want to process:
92 |
93 | $request = array(
94 | '_method' => 'get',
95 | '_format' => 'xml',
96 | '_entity' => 'user',
97 | '_action' => 'get',
98 | '_id' => 1
99 | );
100 |
101 | $server = new \Doctrine\REST\Server\Server($em, $request);
102 | $server->setEntityAlias('Entities\User', 'user');
103 |
104 | $xml = $server->execute();
105 |
106 | The above would retrieve the User with the id of 1 and return an XML document
107 | like the following:
108 |
109 |
110 | 1
111 | jwage
112 |
113 |
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/Client.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client;
23 |
24 | /**
25 | * Basic class for issuing HTTP requests via PHP curl.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class Client
34 | {
35 | const POST = 'POST';
36 | const GET = 'GET';
37 | const PUT = 'PUT';
38 | const DELETE = 'DELETE';
39 |
40 | public function post(Request $request)
41 | {
42 | $request->setMethod(Client::POST);
43 | return $this->execute($request);
44 | }
45 |
46 | public function get(Request $request)
47 | {
48 | $request->setMethod(Client::GET);
49 | return $this->execute($request);
50 | }
51 |
52 | public function put(Request $request)
53 | {
54 | $request->setMethod(Client::PUT);
55 | return $this->execute($request);
56 | }
57 |
58 | public function delete(Request $request)
59 | {
60 | $request->setMethod(Client::DELETE);
61 | return $this->execute($request);
62 | }
63 |
64 | public function execute(Request $request)
65 | {
66 | $ch = curl_init();
67 | curl_setopt($ch, CURLOPT_URL, $request->getUrl());
68 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
69 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
70 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
71 |
72 | $username = $request->getUsername();
73 | $password = $request->getPassword();
74 |
75 | if ($username && $password) {
76 | curl_setopt ($ch, CURLOPT_USERPWD, $username . ':' . $password);
77 | }
78 |
79 | switch ($request->getMethod()) {
80 | case self::POST:
81 | case self::PUT:
82 | curl_setopt($ch, CURLOPT_POST, 1);
83 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request->getParameters()));
84 | break;
85 | case self::DELETE:
86 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
87 | break;
88 | case self::GET:
89 | default:
90 | break;
91 | }
92 |
93 | $result = curl_exec($ch);
94 |
95 | if ( ! $result) {
96 | $errorNumber = curl_errno($ch);
97 | $error = curl_error($ch);
98 | curl_close($ch);
99 |
100 | throw new \Exception($errorNumber . ': ' . $error);
101 | }
102 |
103 | curl_close($ch);
104 |
105 | return $request->getResponseTransformerImpl()->transform($result);
106 | }
107 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/Entity.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client;
23 |
24 | /**
25 | * Abstract entity class for REST entities to extend from to give ActiveRecord
26 | * style interface for working with REST services.
27 | *
28 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
29 | * @link www.doctrine-project.org
30 | * @since 2.0
31 | * @version $Revision$
32 | * @author Jonathan H. Wage
33 | */
34 | abstract class Entity
35 | {
36 | protected static $_manager;
37 |
38 | public static function setManager(Manager $manager)
39 | {
40 | self::$_manager = $manager;
41 | }
42 |
43 | public function toArray()
44 | {
45 | return get_object_vars($this);
46 | }
47 |
48 | public function exists()
49 | {
50 | return self::$_manager->entityExists($this);
51 | }
52 |
53 | public function getIdentifier()
54 | {
55 | return self::$_manager->getEntityIdentifier($this);
56 | }
57 |
58 | public static function generateUrl(array $options = array())
59 | {
60 | $configuration = self::$_manager->getEntityConfiguration(get_called_class());
61 | return $configuration->generateUrl($options);
62 | }
63 |
64 | public static function find($id, $action = null)
65 | {
66 | return self::$_manager->execute(
67 | get_called_class(),
68 | self::generateUrl(get_defined_vars()),
69 | Client::GET
70 | );
71 | }
72 |
73 | public static function findAll($action = null, $parameters = null)
74 | {
75 | return self::$_manager->execute(
76 | get_called_class(),
77 | self::generateUrl(get_defined_vars()),
78 | Client::GET, $parameters
79 | );
80 | }
81 |
82 | public function save($action = null)
83 | {
84 | $parameters = $this->toArray();
85 | $exists = $this->exists();
86 | $method = $exists ? Client::POST : Client::PUT;
87 | $id = $exists ? $this->getIdentifier() : null;
88 | $path = $this->generateUrl(get_defined_vars());
89 | return self::$_manager->execute($this, $path, $method, $parameters, $action);
90 | }
91 |
92 | public function delete($action = null)
93 | {
94 | $id = $this->getIdentifier();
95 | return self::$_manager->execute(
96 | $this, $this->generateUrl(get_defined_vars()), Client::DELETE
97 | );
98 | }
99 |
100 | public function post($action = null)
101 | {
102 | $id = $this->getIdentifier();
103 | return self::$_manager->execute(
104 | $this, $this->generateUrl(get_defined_vars()),
105 | Client::POST, $this->toArray()
106 | );
107 | }
108 |
109 | public function get($action = null)
110 | {
111 | return self::$_manager->execute(
112 | $this, $this->generateUrl(get_defined_vars()),
113 | Client::GET, $this->toArray()
114 | );
115 | }
116 |
117 | public function put($action = null)
118 | {
119 | return self::$_manager->execute(
120 | $this, $this->generateUrl(get_defined_vars()),
121 | Client::PUT, $this->toArray()
122 | );
123 | }
124 |
125 | public static function execute($method, $action, $parameters = null)
126 | {
127 | return self::$_manager->execute(
128 | get_called_class(),
129 | self::generateUrl(get_defined_vars()),
130 | $method, $parameters
131 | );
132 | }
133 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Action/AbstractAction.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server\Action;
23 |
24 | use Doctrine\REST\Server\RequestHandler,
25 | Doctrine\ORM\EntityManager;
26 |
27 | /**
28 | * Abstract server action class for REST server actions to extend from.
29 | *
30 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
31 | * @link www.doctrine-project.org
32 | * @since 2.0
33 | * @version $Revision$
34 | * @author Jonathan H. Wage
35 | */
36 | abstract class AbstractAction
37 | {
38 | protected $_requestHandler;
39 | protected $_source;
40 | protected $_request;
41 |
42 | public function __construct(RequestHandler $requestHandler)
43 | {
44 | $this->_requestHandler = $requestHandler;
45 | $this->_source = $requestHandler->getSource();
46 | $this->_request = $requestHandler->getRequest();
47 | }
48 |
49 | public function executeORM()
50 | {
51 | }
52 |
53 | public function executeDBAL()
54 | {
55 | }
56 |
57 | protected function _getEntity()
58 | {
59 | return $this->_requestHandler->getEntity();
60 | }
61 |
62 | protected function _getEntityIdentifierKey()
63 | {
64 | return $this->_requestHandler->getEntityIdentifierKey($this->_getEntity());
65 | }
66 |
67 | protected function _setQueryFirstAndMax($q)
68 | {
69 | if ( ! isset($this->_request['_page']) && ! isset($this->_request['_first']) && ! isset($this->_request['_max'])) {
70 | $this->_request['_page'] = '1';
71 | }
72 | $maxPerPage = isset($this->_request['_max_per_page']) ? $this->_request['_max_per_page'] : 20;
73 | if (isset($this->_request['_page'])) {
74 | $page = $this->_request['_page'];
75 | $first = ($page - 1) * $maxPerPage;
76 | } else {
77 | if (isset($this->_request['_first'])) {
78 | $first = $this->_request['_first'];
79 | } else {
80 | $first = 0;
81 | }
82 | if (isset($this->_request['_max'])) {
83 | $maxPerPage = $this->_request['_max'];
84 | }
85 | }
86 |
87 | if ($this->_source instanceof EntityManager) {
88 | $q->setFirstResult($first);
89 | $q->setMaxResults($maxPerPage);
90 | } else {
91 | $platform = $this->_source->getDatabasePlatform();
92 | return $platform->modifyLimitQuery($q, $maxPerPage, $first);
93 | }
94 | }
95 |
96 | protected function _findEntityById()
97 | {
98 | if ($this->_source instanceof EntityManager) {
99 | $entity = $this->_getEntity();
100 | $id = $this->_request['_id'];
101 |
102 | $qb = $this->_source->createQueryBuilder()
103 | ->select('a')
104 | ->from($entity, 'a')
105 | ->where('a.id = ?1')
106 | ->setParameter('1', $id);
107 | $query = $qb->getQuery();
108 |
109 | return $query->getSingleResult();
110 | } else {
111 | $entity = $this->_getEntity();
112 | $identifierKey = $this->_getEntityIdentifierKey($entity);
113 |
114 | $query = sprintf('SELECT * FROM %s WHERE %s = ?', $entity, $identifierKey);
115 |
116 | return $this->_source->fetchAssoc($query, array($this->_request['_id']));
117 | }
118 | }
119 |
120 | protected function _updateEntityInstance($entity)
121 | {
122 | $data = $this->_gatherData($this->_request->getData());
123 | foreach ($data as $key => $value) {
124 | $setter = 'set' . ucfirst($key);
125 | if (is_callable(array($entity, $setter))) {
126 | $entity->$setter($value);
127 | }
128 | }
129 | return $entity;
130 | }
131 |
132 | protected function _gatherData()
133 | {
134 | $data = array();
135 | foreach ($this->_request->getData() as $key => $value) {
136 | if ($key[0] == '_') {
137 | continue;
138 | }
139 | $data[$key] = $value;
140 | }
141 | return $data;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/Manager.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client;
23 |
24 | /**
25 | * Class responsible for managing the entities registered for REST services.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class Manager
34 | {
35 | private $_client;
36 | private $_entityConfigurations = array();
37 | private $_identityMap = array();
38 |
39 | public function __construct(Client $client)
40 | {
41 | $this->_client = $client;
42 | }
43 |
44 | public function registerEntity($entity)
45 | {
46 | $this->_entityConfigurations[$entity] = $entity;
47 | }
48 |
49 | public function getEntityConfiguration($entity)
50 | {
51 | if ( ! isset($this->_entityConfigurations[$entity])) {
52 | throw new \InvalidArgumentException(
53 | sprintf('Could not find entity configuration for "%s"', $entity)
54 | );
55 | }
56 | if (is_string($this->_entityConfigurations[$entity])) {
57 | $entityConfiguration = new EntityConfiguration($entity);
58 | call_user_func_array(
59 | array($entity, 'configure'),
60 | array($entityConfiguration)
61 | );
62 | $this->_entityConfigurations[$entity] = $entityConfiguration;
63 | }
64 | return $this->_entityConfigurations[$entity];
65 | }
66 |
67 | public function entityExists($entity)
68 | {
69 | return $this->getEntityIdentifier($entity) ? true : false;
70 | }
71 |
72 | public function getEntityIdentifier($entity)
73 | {
74 | $configuration = $this->getEntityConfiguration(get_class($entity));
75 | $identifierKey = $configuration->getIdentifierKey();
76 | return $configuration->getValue($entity, $identifierKey);
77 | }
78 |
79 | public function execute($entity, $url = null, $method = Client::GET, $parameters = null)
80 | {
81 | if (is_object($entity)) {
82 | $className = get_class($entity);
83 | } else {
84 | $className = $entity;
85 | }
86 | $configuration = $this->getEntityConfiguration($className);
87 |
88 | $request = new Request();
89 | $request->setUrl($url);
90 | $request->setMethod($method);
91 | $request->setParameters($parameters);
92 | $request->setUsername($configuration->getUsername());
93 | $request->setPassword($configuration->getPassword());
94 | $request->setResponseType($configuration->getResponseType());
95 | $request->setResponseTransformerImpl($configuration->getResponseTransformerImpl());
96 |
97 | $result = $this->_client->execute($request);
98 |
99 | if (is_array($result))
100 | {
101 | $name = $configuration->getName();
102 |
103 | $identifierKey = $configuration->getIdentifierKey();
104 | $className = $configuration->getClass();
105 | if (isset($result[$name]) && is_array($result[$name]))
106 | {
107 | $collection = array();
108 | foreach ($result[$name] as $data) {
109 | $identifier = $data[$identifierKey];
110 | if (isset($this->_identityMap[$className][$identifier]))
111 | {
112 | $instance = $this->_identityMap[$className][$identifier];
113 | } else {
114 | $instance = $configuration->newInstance();
115 | $this->_identityMap[$className][$identifier] = $instance;
116 | }
117 | $collection[] = $this->_hydrate(
118 | $configuration, $instance, $data
119 | );
120 | }
121 | return $collection;
122 | } else if ($result) {
123 |
124 | if (is_object($entity))
125 | {
126 | $instance = $entity;
127 | $this->_hydrate($configuration, $instance, $result);
128 | $identifier = $this->getEntityIdentifier($instance);
129 | $this->_identityMap[$className][$identifier] = $instance;
130 | } else {
131 | $identifier = $result[$identifierKey];
132 | if (isset($this->_identityMap[$className][$identifier]))
133 | {
134 | $instance = $this->_identityMap[$className][$identifier];
135 | } else {
136 | $instance = $configuration->newInstance();
137 | $this->_identityMap[$className][$identifier] = $instance;
138 | }
139 | $this->_hydrate($configuration, $instance, $result);
140 | }
141 | return $instance;
142 | }
143 | } else {
144 | return array();
145 | }
146 | }
147 |
148 | private function _hydrate($configuration, $instance, $data)
149 | {
150 | foreach ($data as $key => $value) {
151 | if (is_array($value))
152 | {
153 | $configuration->setValue($instance, $key, (string) $value);
154 | } else {
155 | $configuration->setValue($instance, $key, $value);
156 | }
157 | }
158 |
159 | return $instance;
160 | }
161 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Client/EntityConfiguration.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Client;
23 |
24 | use Doctrine\REST\Client\URLGenerator\StandardURLGenerator,
25 | Doctrine\REST\Client\ResponseTransformer\StandardResponseTransformer;
26 |
27 | /**
28 | * Entity configuration class holds all the configuration information for a PHP5
29 | * object entity that maps to a REST service.
30 | *
31 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
32 | * @link www.doctrine-project.org
33 | * @since 2.0
34 | * @version $Revision$
35 | * @author Jonathan H. Wage
36 | */
37 | class EntityConfiguration
38 | {
39 | private $_prototype;
40 | private $_reflection;
41 | private $_reflectionProperties = array();
42 |
43 | private $_attributes = array(
44 | 'class' => null,
45 | 'url' => null,
46 | 'name' => null,
47 | 'username' => null,
48 | 'password' => null,
49 | 'identifierKey' => 'id',
50 | 'responseType' => 'xml',
51 | 'urlGeneratorImpl' => null,
52 | 'responseTransformerImpl' => null,
53 | );
54 |
55 | public function __construct($class)
56 | {
57 | $this->_attributes['class'] = $class;
58 | $this->_attributes['urlGeneratorImpl'] = new StandardURLGenerator($this);
59 | $this->_attributes['responseTransformerImpl'] = new StandardResponseTransformer($this);
60 |
61 | $this->_reflection = new \ReflectionClass($class);
62 | foreach ($this->_reflection->getProperties() as $property) {
63 | if ($property->getDeclaringClass()->getName() == $class) {
64 | $property->setAccessible(true);
65 | $this->_reflectionProperties[$property->getName()] = $property;
66 | $this->_properties[] = $property->getName();
67 | }
68 | }
69 | }
70 |
71 | public function getReflection()
72 | {
73 | return $this->_reflection;
74 | }
75 |
76 | public function getReflectionProperties()
77 | {
78 | return $this->_reflectionProperties;
79 | }
80 |
81 | public function getProperties()
82 | {
83 | return $this->_properties;
84 | }
85 |
86 | public function setValue($entity, $field, $value)
87 | {
88 | if (isset($this->_reflectionProperties[$field])) {
89 | $this->_reflectionProperties[$field]->setValue($entity, $value);
90 | } else {
91 | $entity->$field = $value;
92 | }
93 | }
94 |
95 | public function getValue($entity, $field)
96 | {
97 | return $this->_reflectionProperties[$field]->getValue($entity);
98 | }
99 |
100 | public function generateUrl(array $options)
101 | {
102 | return $this->_attributes['urlGeneratorImpl']->generate($options);
103 | }
104 |
105 | public function setUrl($url)
106 | {
107 | $this->_attributes['url'] = rtrim($url, '/');
108 | }
109 |
110 | public function getUrl()
111 | {
112 | return $this->_attributes['url'];
113 | }
114 |
115 | public function setClass($class)
116 | {
117 | $this->_attributes['class'] = $class;
118 | }
119 |
120 | public function getClass()
121 | {
122 | return $this->_attributes['class'];
123 | }
124 |
125 | public function setName($name)
126 | {
127 | $this->_attributes['name'] = $name;
128 | }
129 |
130 | public function getName()
131 | {
132 | return $this->_attributes['name'];
133 | }
134 |
135 | public function setUsername($username)
136 | {
137 | $this->_attributes['username'] = $username;
138 | }
139 |
140 | public function getUsername()
141 | {
142 | return $this->_attributes['username'];
143 | }
144 |
145 | public function setPassword($password)
146 | {
147 | $this->_attributes['password'] = $password;
148 | }
149 |
150 | public function getPassword()
151 | {
152 | return $this->_attributes['password'];
153 | }
154 |
155 | public function setIdentifierKey($identifierKey)
156 | {
157 | $this->_attributes['identifierKey'] = $identifierKey;
158 | }
159 |
160 | public function getIdentifierKey()
161 | {
162 | return $this->_attributes['identifierKey'];
163 | }
164 |
165 | public function setResponseType($responseType)
166 | {
167 | $this->_attributes['responseType'] = $responseType;
168 | }
169 |
170 | public function getResponseType()
171 | {
172 | return $this->_attributes['responseType'];
173 | }
174 |
175 | public function setURLGeneratorImpl($urlGeneratorImpl)
176 | {
177 | $this->_attributes['urlGeneratorImpl'] = $urlGeneratorImpl;
178 | }
179 |
180 | public function getURLGeneratorImpl()
181 | {
182 | return $this->_attributes['urlGeneratorImpl'];
183 | }
184 |
185 | public function setResponseTransformerImpl($responseHandlerImpl)
186 | {
187 | $this->_attributes['responseTransformerImpl'] = $responseHandlerImpl;
188 | }
189 |
190 | public function getResponseTransformerImpl()
191 | {
192 | return $this->_attributes['responseTransformerImpl'];
193 | }
194 |
195 | public function newInstance()
196 | {
197 | if ($this->_prototype === null) {
198 | $this->_prototype = unserialize(sprintf(
199 | 'O:%d:"%s":0:{}',
200 | strlen($this->_attributes['class']),
201 | $this->_attributes['class']
202 | ));
203 | }
204 | return clone $this->_prototype;
205 | }
206 | }
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/Response.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server;
23 |
24 | /**
25 | * Class that represents a REST server response.
26 | *
27 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28 | * @link www.doctrine-project.org
29 | * @since 2.0
30 | * @version $Revision$
31 | * @author Jonathan H. Wage
32 | */
33 | class Response
34 | {
35 | private $_requestHandler;
36 | private $_request;
37 | private $_responseData;
38 |
39 | public function __construct(Request $request)
40 | {
41 | $this->_request = $request;
42 | }
43 |
44 | public function setRequestHandler(RequestHandler $requestHandler)
45 | {
46 | $this->_requestHandler = $requestHandler;
47 | }
48 |
49 | public function setError($error)
50 | {
51 | $this->_responseData = array();
52 | $this->_responseData['error'] = $error;
53 | }
54 |
55 | public function setResponseData($responseData)
56 | {
57 | $this->_responseData = $responseData;
58 | }
59 |
60 | public function send()
61 | {
62 | $this->_sendHeaders();
63 | echo $this->getContent();
64 | }
65 |
66 | public function getContent()
67 | {
68 | $data = $this->_responseData;
69 |
70 | switch ($this->_request['_format']) {
71 | case 'php':
72 | return serialize($data);
73 | break;
74 |
75 | case 'json':
76 | return json_encode($data);
77 | break;
78 |
79 | case 'xml':
80 | default:
81 | return $this->_arrayToXml($data, $this->_request['_entity']);
82 | }
83 | }
84 |
85 | private function _sendHeaders()
86 | {
87 | if ($this->_requestHandler->getUsername()) {
88 | if ( ! isset($_SERVER['PHP_AUTH_USER'])) {
89 | header('WWW-Authenticate: Basic realm="Doctrine REST API"');
90 | header('HTTP/1.0 401 Unauthorized');
91 | } else {
92 | if ( ! $this->_requestHandler->hasValidCredentials()) {
93 | $this->setError('Invalid credentials specified.');
94 | }
95 | }
96 | }
97 |
98 | switch ($this->_request['_format']) {
99 | case 'php':
100 | header('Content-type: text/html;');
101 | break;
102 |
103 | case 'json':
104 | header('Content-type: text/json;');
105 | header('Content-Disposition: attachment; filename="' . $this->_request['_action'] . '.json"');
106 | break;
107 |
108 | case 'xml':
109 | default:
110 | header('Content-type: application/xml;');
111 | }
112 | }
113 |
114 | private function _arrayToXml($array, $rootNodeName = 'doctrine', $xml = null, $charset = null)
115 | {
116 | if ($xml === null) {
117 | $xml = new \SimpleXmlElement("<$rootNodeName/>");
118 | }
119 |
120 | foreach($array as $key => $value) {
121 | if (is_numeric($key)) {
122 | $key = $rootNodeName . $key;
123 | }
124 | $key = preg_replace('/[^A-Za-z_]/i', '', $key);
125 |
126 | if (is_array($value) && ! empty($value)) {
127 | $node = $xml->addChild($key);
128 | $this->_arrayToXml($value, $rootNodeName, $node, $charset);
129 | } else if ($value) {
130 | $charset = $charset ? $charset : 'utf-8';
131 | if (strcasecmp($charset, 'utf-8') !== 0 && strcasecmp($charset, 'utf8') !== 0) {
132 | $value = iconv($charset, 'UTF-8', $value);
133 | }
134 | $value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
135 | $xml->addChild($key, $value);
136 | }
137 | }
138 |
139 | return $this->_formatXml($xml);
140 | }
141 |
142 | private function _formatXml($simpleXml)
143 | {
144 | $xml = $simpleXml->asXml();
145 |
146 | // add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries)
147 | $xml = preg_replace('/(>)(<)(\/*)/', "$1\n$2$3", $xml);
148 |
149 | // now indent the tags
150 | $token = strtok($xml, "\n");
151 | $result = ''; // holds formatted version as it is built
152 | $pad = 0; // initial indent
153 | $matches = array(); // returns from preg_matches()
154 |
155 | // test for the various tag states
156 | while ($token !== false) {
157 | // 1. open and closing tags on same line - no change
158 | if (preg_match('/.+<\/\w[^>]*>$/', $token, $matches)) {
159 | $indent = 0;
160 | // 2. closing tag - outdent now
161 | } else if (preg_match('/^<\/\w/', $token, $matches)) {
162 | $pad = $pad - 4;
163 | // 3. opening tag - don't pad this one, only subsequent tags
164 | } elseif (preg_match('/^<\w[^>]*[^\/]>.*$/', $token, $matches)) {
165 | $indent = 4;
166 | // 4. no indentation needed
167 | } else {
168 | $indent = 0;
169 | }
170 |
171 | // pad the line with the required number of leading spaces
172 | $line = str_pad($token, strlen($token)+$pad, ' ', STR_PAD_LEFT);
173 | $result .= $line . "\n"; // add to the cumulative result, with linefeed
174 | $token = strtok("\n"); // get the next token
175 | $pad += $indent; // update the pad size for subsequent lines
176 | }
177 | return $result;
178 | }
179 | }
--------------------------------------------------------------------------------
/tests/Doctrine/Tests/REST/FunctionalTest.php:
--------------------------------------------------------------------------------
1 | setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
23 | $config->setProxyDir('/tmp');
24 | $config->setProxyNamespace('Proxies');
25 | $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver());
26 |
27 | $connectionOptions = array(
28 | 'driver' => 'pdo_sqlite',
29 | 'memory' => true
30 | );
31 |
32 | $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
33 | $classes = array($em->getMetadataFactory()->getMetadataFor('Doctrine\Tests\REST\DoctrineUser'));
34 |
35 | $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($em);
36 | $schemaTool->dropSchema($classes);
37 | $schemaTool->createSchema($classes);
38 |
39 | if ($type === 'orm') {
40 | $this->_client = new TestFunctionalClient('user', $em);
41 | } else {
42 | $this->_client = new TestFunctionalClient('user', $em->getConnection());
43 | }
44 |
45 | $this->_manager = new Manager($this->_client);
46 | $this->_manager->registerEntity('Doctrine\Tests\REST\User');
47 |
48 | Entity::setManager($this->_manager);
49 | }
50 |
51 | public function testOrm()
52 | {
53 | $this->setUpRest('orm');
54 | $this->_testActiveRecordApi();
55 | }
56 |
57 | public function testDbal()
58 | {
59 | $this->setUpRest('dbal');
60 | $this->_testActiveRecordApi();
61 | }
62 |
63 | private function _testActiveRecordApi()
64 | {
65 | $user1 = new User();
66 | $user1->setUsername('jwage');
67 | $user1->save();
68 |
69 | $this->assertEquals(1, $user1->getId());
70 |
71 | $user2 = new User();
72 | $user2->setUsername('fabpot');
73 | $user2->save();
74 |
75 | $this->assertEquals(2, $user2->getId());
76 |
77 | $user3 = new User();
78 | $user3->setUsername('romanb');
79 | $user3->save();
80 |
81 | $this->assertEquals(3, $user3->getId());
82 |
83 | $user3->setUsername('romanb_new');
84 | $user3->save();
85 |
86 | $user3test = User::find($user3->getId());
87 | $this->assertEquals('romanb_new', $user3test->getUsername());
88 |
89 | $test = User::findAll();
90 | $this->assertEquals(3, count($test));
91 | $this->assertTrue($user1 === $test[0]);
92 | $this->assertTrue($user2 === $test[1]);
93 | $this->assertTrue($user3 === $test[2]);
94 |
95 | $user3->delete();
96 |
97 | $test = User::findAll();
98 |
99 | $this->assertEquals(2, count($test));
100 | }
101 | }
102 |
103 | class TestFunctionalClient extends Client
104 | {
105 | public $name;
106 | public $source;
107 | public $data = array();
108 | public $count = 0;
109 |
110 | public function __construct($name, $source)
111 | {
112 | $this->name = $name;
113 | $this->source = $source;
114 | }
115 |
116 | public function execServer($request, $requestArray, $parameters = array(), $responseType = 'xml')
117 | {
118 | $requestArray = array_merge($requestArray, (array) $parameters);
119 | $server = new Server($this->source, $requestArray);
120 | if ($this->source instanceof EntityManager) {
121 | $server->setEntityAlias('Doctrine\Tests\REST\DoctrineUser', 'user');
122 | }
123 | $response = $server->getRequestHandler()->execute();
124 | $data = $request->getResponseTransformerImpl()->transform($response->getContent());
125 | return $data;
126 | }
127 |
128 | public function execute(Request $request)
129 | {
130 | $url = $request->getUrl();
131 | $method = $request->getMethod();
132 | $parameters = $request->getParameters();
133 | $responseType = $request->getResponseType();
134 |
135 | // GET api/user/1.xml (get)
136 | if ($method === 'GET' && preg_match_all('/api\/' . $this->name . '\/([0-9]).xml/', $url, $matches)) {
137 | $id = $matches[1][0];
138 | return $this->execServer($request, array(
139 | '_method' => $method,
140 | '_format' => $responseType,
141 | '_entity' => $this->name,
142 | '_action' => 'get',
143 | '_id' => $id
144 | ), $parameters, $responseType);
145 | }
146 |
147 | // GET api/user.xml (list)
148 | if ($method === 'GET' && preg_match_all('/api\/' . $this->name . '.xml/', $url, $matches)) {
149 | return $this->execServer($request, array(
150 | '_method' => $method,
151 | '_format' => $responseType,
152 | '_entity' => $this->name,
153 | '_action' => 'list'
154 | ), $parameters, $responseType);
155 | }
156 |
157 | // PUT api/user.xml (insert)
158 | if ($method === 'PUT' && preg_match_all('/api\/' . $this->name . '.xml/', $url, $matches)) {
159 | return $this->execServer($request, array(
160 | '_method' => $method,
161 | '_format' => $responseType,
162 | '_entity' => $this->name,
163 | '_action' => 'insert'
164 | ), $parameters, $responseType);
165 | }
166 |
167 |
168 | // POST api/user/1.xml (update)
169 | if ($method === 'POST' && preg_match_all('/api\/' . $this->name . '\/([0-9]).xml/', $url, $matches)) {
170 | return $this->execServer($request, array(
171 | '_method' => $method,
172 | '_format' => $responseType,
173 | '_entity' => $this->name,
174 | '_action' => 'update',
175 | '_id' => $parameters['id']
176 | ), $parameters, $responseType);
177 | }
178 |
179 | // DELETE api/user/1.xml (delete)
180 | if ($method === 'DELETE' && preg_match_all('/api\/' . $this->name . '\/([0-9]).xml/', $url, $matches)) {
181 | return $this->execServer($request, array(
182 | '_method' => $method,
183 | '_format' => $responseType,
184 | '_entity' => $this->name,
185 | '_action' => 'delete',
186 | '_id' => $matches[1][0]
187 | ), $parameters, $responseType);
188 | }
189 | }
190 | }
191 |
192 | class User extends Entity
193 | {
194 | protected $id;
195 | protected $username;
196 |
197 | public static function configure(EntityConfiguration $entityConfiguration)
198 | {
199 | $entityConfiguration->setUrl('api');
200 | $entityConfiguration->setName('user');
201 | }
202 |
203 | public function getId()
204 | {
205 | return $this->id;
206 | }
207 |
208 | public function getUsername()
209 | {
210 | return $this->username;
211 | }
212 |
213 | public function setUsername($username)
214 | {
215 | $this->username = $username;
216 | }
217 | }
218 |
219 | /**
220 | * @Entity
221 | * @Table(name="user")
222 | */
223 | class DoctrineUser
224 | {
225 | /**
226 | * @Id @Column(type="integer")
227 | * @GeneratedValue(strategy="AUTO")
228 | */
229 | private $id;
230 |
231 | /**
232 | * @Column(type="string", length=255, unique=true)
233 | */
234 | private $username;
235 |
236 | public function setUsername($username)
237 | {
238 | $this->username = $username;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/tests/Doctrine/Tests/REST/ClientTest.php:
--------------------------------------------------------------------------------
1 | client = new TestClient();
17 |
18 | $manager = new Manager($this->client);
19 | $manager->registerEntity('Doctrine\Tests\REST\ClientArticleTest');
20 | $manager->registerEntity('Doctrine\Tests\REST\Status');
21 |
22 | Entity::setManager($manager);
23 | }
24 |
25 | public function testGetPath()
26 | {
27 | $this->assertEquals('http://api.people.com/article/1.xml', ClientArticleTest::generateUrl(array('id' => 1)));
28 | $this->assertEquals('http://api.people.com/article/1/test.xml', ClientArticleTest::generateUrl(array('id' => 1, 'action' => 'test')));
29 |
30 | $this->assertEquals('http://api.people.com/article.xml', ClientArticleTest::generateUrl());
31 | $this->assertEquals('http://api.people.com/article/test.xml', ClientArticleTest::generateUrl(array('action' => 'test')));
32 |
33 | $this->assertEquals('http://api.people.com/article.xml?test=test', ClientArticleTest::generateUrl(array('parameters' => array('test' => 'test'))));
34 | }
35 |
36 | public function testInsert()
37 | {
38 | $test = new ClientArticleTest();
39 | $test->setTitle('testing');
40 | $test->save();
41 |
42 | $this->assertEquals(1, $test->getId());
43 | $this->assertEquals('http://api.people.com/article.xml', $this->client->last['url']);
44 | $this->assertEquals('PUT', $this->client->last['method']);
45 | }
46 |
47 | public function testUpdate()
48 | {
49 | $test = new ClientArticleTest();
50 | $test->setId(1);
51 | $test->setTitle('test');
52 | $test->save();
53 |
54 | $this->assertEquals('test', $test->getTitle());
55 | $this->assertEquals('http://api.people.com/article/1.xml', $this->client->last['url']);
56 | $this->assertEquals('POST', $this->client->last['method']);
57 | }
58 |
59 | public function testDelete()
60 | {
61 | $test = new ClientArticleTest();
62 | $test->setId(1);
63 | $test->delete();
64 |
65 | $this->assertEquals('http://api.people.com/article/1.xml', $this->client->last['url']);
66 | $this->assertEquals('DELETE', $this->client->last['method']);
67 | }
68 |
69 | public function testFind()
70 | {
71 | $test = ClientArticleTest::find(1);
72 |
73 | $this->assertEquals('test', $test->getTitle());
74 | $this->assertEquals('http://api.people.com/article/1.xml', $this->client->last['url']);
75 | $this->assertEquals('GET', $this->client->last['method']);
76 | }
77 |
78 | public function testFindWithAction()
79 | {
80 | $test = ClientArticleTest::find(1, 'test');
81 |
82 | $this->assertEquals('http://api.people.com/article/1/test.xml', $this->client->last['url']);
83 | $this->assertEquals('GET', $this->client->last['method']);
84 | }
85 |
86 | public function testFindAll()
87 | {
88 | $test = ClientArticleTest::findAll();
89 | $this->assertEquals(2, count($test));
90 |
91 | $one = $test[0];
92 | $two = $test[1];
93 |
94 | $this->assertEquals(1, $one->getId());
95 | $this->assertEquals('test1', $one->getTitle());
96 |
97 | $this->assertEquals(2, $two->getId());
98 | $this->assertEquals('test2', $two->getTitle());
99 |
100 | $this->assertEquals('http://api.people.com/article.xml', $this->client->last['url']);
101 | $this->assertEquals('GET', $this->client->last['method']);
102 | }
103 |
104 | public function testFindAllWithAction()
105 | {
106 | $test = ClientArticleTest::findAll('test');
107 |
108 | $this->assertEquals('http://api.people.com/article/test.xml', $this->client->last['url']);
109 | $this->assertEquals('GET', $this->client->last['method']);
110 | }
111 |
112 | public function testFindAllWithParameters()
113 | {
114 | $test = ClientArticleTest::findAll(null, array('test' => 'test'));
115 |
116 | $this->assertEquals('http://api.people.com/article.xml?test=test', $this->client->last['url']);
117 | $this->assertEquals('GET', $this->client->last['method']);
118 | }
119 |
120 | public function testExecute()
121 | {
122 | $test = ClientArticleTest::execute(Client::GET, 'test', array('test' => 'test'));
123 |
124 | $this->assertEquals('http://api.people.com/article/test.xml?test=test', $this->client->last['url']);
125 | $this->assertEquals('GET', $this->client->last['method']);
126 | }
127 |
128 | public function testTwitterStatus()
129 | {
130 | $test = Status::execute(Client::POST, 'update', array('status' => 'updating my status'));
131 |
132 | $this->assertEquals('http://twitter.com/statuses/update.xml', $this->client->last['url']);
133 | $this->assertEquals('POST', $this->client->last['method']);
134 | $this->assertEquals(array('status' => 'updating my status'), $this->client->last['parameters']);
135 | $this->assertEquals('username', $this->client->last['username']);
136 | $this->assertEquals('password', $this->client->last['password']);
137 | }
138 | }
139 |
140 | class Status extends Entity
141 | {
142 | private $id;
143 | private $status;
144 | private $text;
145 |
146 | public static function configure(EntityConfiguration $entityConfiguration)
147 | {
148 | $entityConfiguration->setUrl('http://twitter.com');
149 | $entityConfiguration->setName('statuses');
150 | $entityConfiguration->setUsername('username');
151 | $entityConfiguration->setPassword('password');
152 | }
153 | }
154 |
155 | class ClientArticleTest extends Entity
156 | {
157 | private $id;
158 | private $title;
159 |
160 | public static function configure(EntityConfiguration $entityConfiguration)
161 | {
162 | $entityConfiguration->setUrl('http://api.people.com');
163 | $entityConfiguration->setName('article');
164 | }
165 |
166 | public function setId($id)
167 | {
168 | $this->id = $id;
169 | }
170 |
171 | public function getId()
172 | {
173 | return $this->id;
174 | }
175 |
176 | public function setTitle($title)
177 | {
178 | $this->title = $title;
179 | }
180 |
181 | public function getTitle()
182 | {
183 | return $this->title;
184 | }
185 | }
186 |
187 | class TestClient extends Client
188 | {
189 | public $last;
190 |
191 | public function execute(Request $request)
192 | {
193 | $url = $request->getUrl();
194 | $method = $request->getMethod();
195 | $parameters = $request->getParameters();
196 | $username = $request->getUsername();
197 | $password = $request->getPassword();
198 |
199 | $this->last = get_defined_vars();
200 |
201 | if ($url === 'http://api.people.com/article.xml') {
202 | if ($method === Client::PUT)
203 | {
204 | return array('id' => 1, 'title' => 'test');
205 | } else if ($method === Client::POST) {
206 | return $parameters;
207 | } else if ($method === Client::GET) {
208 | return array(
209 | 'article' => array(
210 | array(
211 | 'id' => 1,
212 | 'title' => 'test1'
213 | ),
214 | array(
215 | 'id' => 2,
216 | 'title' => 'test2'
217 | )
218 | )
219 | );
220 | }
221 | return array();
222 | } else if ($url === 'http://api.people.com/article/1.xml') {
223 | if ($method === Client::DELETE) {
224 | return array('id' => 1, 'title' => 'test');
225 | } else if ($method === Client::GET) {
226 | return array('id' => 1, 'title' => 'test');
227 | }
228 | }
229 | return array();
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/lib/Doctrine/REST/Server/RequestHandler.php:
--------------------------------------------------------------------------------
1 | .
20 | */
21 |
22 | namespace Doctrine\REST\Server;
23 |
24 | use Doctrine\ORM\EntityManager,
25 | Doctrine\DBAL\Connection;
26 |
27 | /**
28 | * Class responsible for transforming a REST server request to a response.
29 | *
30 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
31 | * @link www.doctrine-project.org
32 | * @since 2.0
33 | * @version $Revision$
34 | * @author Jonathan H. Wage
35 | */
36 | class RequestHandler
37 | {
38 | private $_source;
39 | private $_request;
40 | private $_response;
41 | private $_username;
42 | private $_password;
43 | private $_credentialsCallback;
44 | private $_entities = array();
45 |
46 | private $_actions = array(
47 | 'delete' => 'Doctrine\\REST\\Server\\Action\\DeleteAction',
48 | 'get' => 'Doctrine\\REST\\Server\\Action\\GetAction',
49 | 'insert' => 'Doctrine\\REST\\Server\\Action\\InsertAction',
50 | 'update' => 'Doctrine\\REST\\Server\\Action\\UpdateAction',
51 | 'list' => 'Doctrine\\REST\\Server\\Action\\ListAction'
52 | );
53 |
54 | public function __construct($source, Request $request, Response $response)
55 | {
56 | $this->_source = $source;
57 | $this->_request = $request;
58 | $this->_response = $response;
59 | $this->_response->setRequestHandler($this);
60 | $this->_credentialsCallback = array($this, 'checkCredentials');
61 | }
62 |
63 | public function configureEntity($entity, $configuration)
64 | {
65 | $this->_entities[$entity] = $configuration;
66 | }
67 |
68 | public function setEntityAlias($entity, $alias)
69 | {
70 | $this->_entities[$entity]['alias'] = $alias;
71 | }
72 |
73 | public function addEntityAction($entity, $action, $className)
74 | {
75 | $this->_entities[$entity]['actions'][$action] = $className;
76 | }
77 |
78 | public function setEntityIdentifierKey($entity, $identifierKey)
79 | {
80 | $this->_entities[$entity]['identifierKey'] = $identifierKey;
81 | }
82 |
83 | public function getEntityIdentifierKey($entity)
84 | {
85 | return isset($this->_entities[$entity]['identifierKey']) ? $this->_entities[$entity]['identifierKey'] : 'id';
86 | }
87 |
88 | public function resolveEntityAlias($alias)
89 | {
90 | foreach ($this->_entities as $entity => $configuration) {
91 | if (isset($configuration['alias']) && $configuration['alias'] === $alias) {
92 | return $entity;
93 | }
94 | }
95 | return $alias;
96 | }
97 |
98 | public function setCredentialsCallback($callback)
99 | {
100 | $this->_credentialsCallback = $callback;
101 | }
102 |
103 | public function registerAction($action, $className)
104 | {
105 | $this->_actions[$action] = $className;
106 | }
107 |
108 | public function isSecure()
109 | {
110 | return ($this->_username && $this->_password) ? true : false;
111 | }
112 |
113 | public function checkCredentials($username, $password)
114 | {
115 | if ( ! $this->isSecure()) {
116 | return true;
117 | }
118 |
119 | if ($this->_username == $username && $this->_password == $password) {
120 | return true;
121 | } else {
122 | return false;
123 | }
124 | }
125 |
126 | public function hasValidCredentials()
127 | {
128 | $args = array($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
129 | return call_user_func_array($this->_credentialsCallback, $args);
130 | }
131 |
132 | public function getUsername()
133 | {
134 | return $this->_username;
135 | }
136 |
137 | public function setUsername($username)
138 | {
139 | $this->_username = $username;
140 | }
141 |
142 | public function getPassword()
143 | {
144 | return $this->_password;
145 | }
146 |
147 | public function setPassword($password)
148 | {
149 | $this->_password = $password;
150 | }
151 |
152 | public function getActions()
153 | {
154 | return $this->_actions;
155 | }
156 |
157 | public function getSource()
158 | {
159 | return $this->_source;
160 | }
161 |
162 | public function getRequest()
163 | {
164 | return $this->_request;
165 | }
166 |
167 | public function getResponse()
168 | {
169 | return $this->_response;
170 | }
171 |
172 | public function getEntity()
173 | {
174 | return $this->resolveEntityAlias($this->_request['_entity']);
175 | }
176 |
177 | public function execute()
178 | {
179 | try {
180 | $entity = $this->getEntity();
181 | $actionInstance = $this->getAction($entity, $this->_request['_action']);
182 |
183 | if (method_exists($actionInstance, 'execute')) {
184 | $result = $actionInstance->execute();
185 | } else {
186 | if ($this->_source instanceof EntityManager) {
187 | $result = $actionInstance->executeORM();
188 | } else {
189 | $result = $actionInstance->executeDBAL();
190 | }
191 | }
192 |
193 | $this->_response->setResponseData(
194 | $this->_transformResultForResponse($result)
195 | );
196 | } catch (\Exception $e) {
197 | $this->_response->setError($this->_getExceptionErrorMessage($e));
198 | }
199 | return $this->_response;
200 | }
201 |
202 | public function getAction($entity, $actionName)
203 | {
204 | if (isset($this->_actions[$actionName])) {
205 | if ( ! is_object($this->_actions[$actionName])) {
206 | $actionClassName = $this->_actions[$actionName];
207 | $this->_actions[$actionName] = new $actionClassName($this);
208 | }
209 | return $this->_actions[$actionName];
210 | }
211 | if (isset($this->_entities[$entity]['actions'][$actionName])) {
212 | if ( ! is_object($this->_entities[$entity]['actions'][$actionName])) {
213 | $actionClassName = $this->_entities[$entity]['actions'][$actionName];
214 | $this->_entities[$entity]['actions'][$actionName] = new $actionClassName($this);
215 | }
216 | return $this->_entities[$entity]['actions'][$actionName];
217 | }
218 | }
219 |
220 | private function _getExceptionErrorMessage(\Exception $e)
221 | {
222 | $message = $e->getMessage();
223 |
224 | if ($e instanceof \PDOException) {
225 | $message = preg_replace("/SQLSTATE\[.*\]: (.*)/", "$1", $message);
226 | }
227 |
228 | return $message;
229 | }
230 |
231 | private function _transformResultForResponse($result, $array = null)
232 | {
233 | if ( ! $array) {
234 | $array = array();
235 | }
236 | if (is_object($result)) {
237 | $entityName = get_class($result);
238 | if ($this->_source instanceof EntityManager) {
239 | $class = $this->_source->getMetadataFactory()->getMetadataFor($entityName);
240 | foreach ($class->fieldMappings as $fieldMapping) {
241 | $array[$fieldMapping['fieldName']] = $class->getReflectionProperty($fieldMapping['fieldName'])->getValue($result);
242 | }
243 | } else {
244 | $vars = get_object_vars($result);
245 | foreach ($vars as $key => $value) {
246 | $array[$key] = $value;
247 | }
248 | }
249 | } else if (is_array($result)) {
250 | foreach ($result as $key => $value) {
251 | if (is_object($value) || is_array($value)) {
252 | if (is_object($value)) {
253 | $key = $this->_request['_entity'] . $key;
254 | }
255 | $array[$key] = $this->_transformResultForResponse($value, isset($array[$key]) ? $array[$key] : array());
256 | } else {
257 | $array[$key] = $value;
258 | }
259 | }
260 | } else if (is_string($result)) {
261 | $array = $result;
262 | }
263 | return $array;
264 | }
265 | }
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "hash": "0f0ebd9b8bc386447ed1b2b077b59f1d",
8 | "packages": [
9 | {
10 | "name": "doctrine/annotations",
11 | "version": "v1.2.1",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/doctrine/annotations.git",
15 | "reference": "6a6bec0670bb6e71a263b08bc1b98ea242928633"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/doctrine/annotations/zipball/6a6bec0670bb6e71a263b08bc1b98ea242928633",
20 | "reference": "6a6bec0670bb6e71a263b08bc1b98ea242928633",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "doctrine/lexer": "1.*",
25 | "php": ">=5.3.2"
26 | },
27 | "require-dev": {
28 | "doctrine/cache": "1.*",
29 | "phpunit/phpunit": "4.*"
30 | },
31 | "type": "library",
32 | "extra": {
33 | "branch-alias": {
34 | "dev-master": "1.3.x-dev"
35 | }
36 | },
37 | "autoload": {
38 | "psr-0": {
39 | "Doctrine\\Common\\Annotations\\": "lib/"
40 | }
41 | },
42 | "notification-url": "https://packagist.org/downloads/",
43 | "license": [
44 | "MIT"
45 | ],
46 | "authors": [
47 | {
48 | "name": "Roman Borschel",
49 | "email": "roman@code-factory.org"
50 | },
51 | {
52 | "name": "Benjamin Eberlei",
53 | "email": "kontakt@beberlei.de"
54 | },
55 | {
56 | "name": "Guilherme Blanco",
57 | "email": "guilhermeblanco@gmail.com"
58 | },
59 | {
60 | "name": "Jonathan Wage",
61 | "email": "jonwage@gmail.com"
62 | },
63 | {
64 | "name": "Johannes Schmitt",
65 | "email": "schmittjoh@gmail.com"
66 | }
67 | ],
68 | "description": "Docblock Annotations Parser",
69 | "homepage": "http://www.doctrine-project.org",
70 | "keywords": [
71 | "annotations",
72 | "docblock",
73 | "parser"
74 | ],
75 | "time": "2014-09-25 16:45:30"
76 | },
77 | {
78 | "name": "doctrine/cache",
79 | "version": "v1.3.1",
80 | "source": {
81 | "type": "git",
82 | "url": "https://github.com/doctrine/cache.git",
83 | "reference": "cf483685798a72c93bf4206e3dd6358ea07d64e7"
84 | },
85 | "dist": {
86 | "type": "zip",
87 | "url": "https://api.github.com/repos/doctrine/cache/zipball/cf483685798a72c93bf4206e3dd6358ea07d64e7",
88 | "reference": "cf483685798a72c93bf4206e3dd6358ea07d64e7",
89 | "shasum": ""
90 | },
91 | "require": {
92 | "php": ">=5.3.2"
93 | },
94 | "conflict": {
95 | "doctrine/common": ">2.2,<2.4"
96 | },
97 | "require-dev": {
98 | "phpunit/phpunit": ">=3.7",
99 | "satooshi/php-coveralls": "~0.6"
100 | },
101 | "type": "library",
102 | "extra": {
103 | "branch-alias": {
104 | "dev-master": "1.4.x-dev"
105 | }
106 | },
107 | "autoload": {
108 | "psr-0": {
109 | "Doctrine\\Common\\Cache\\": "lib/"
110 | }
111 | },
112 | "notification-url": "https://packagist.org/downloads/",
113 | "license": [
114 | "MIT"
115 | ],
116 | "authors": [
117 | {
118 | "name": "Roman Borschel",
119 | "email": "roman@code-factory.org"
120 | },
121 | {
122 | "name": "Benjamin Eberlei",
123 | "email": "kontakt@beberlei.de"
124 | },
125 | {
126 | "name": "Guilherme Blanco",
127 | "email": "guilhermeblanco@gmail.com"
128 | },
129 | {
130 | "name": "Jonathan Wage",
131 | "email": "jonwage@gmail.com"
132 | },
133 | {
134 | "name": "Johannes Schmitt",
135 | "email": "schmittjoh@gmail.com"
136 | }
137 | ],
138 | "description": "Caching library offering an object-oriented API for many cache backends",
139 | "homepage": "http://www.doctrine-project.org",
140 | "keywords": [
141 | "cache",
142 | "caching"
143 | ],
144 | "time": "2014-09-17 14:24:04"
145 | },
146 | {
147 | "name": "doctrine/collections",
148 | "version": "v1.2",
149 | "source": {
150 | "type": "git",
151 | "url": "https://github.com/doctrine/collections.git",
152 | "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2"
153 | },
154 | "dist": {
155 | "type": "zip",
156 | "url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2",
157 | "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2",
158 | "shasum": ""
159 | },
160 | "require": {
161 | "php": ">=5.3.2"
162 | },
163 | "type": "library",
164 | "extra": {
165 | "branch-alias": {
166 | "dev-master": "1.2.x-dev"
167 | }
168 | },
169 | "autoload": {
170 | "psr-0": {
171 | "Doctrine\\Common\\Collections\\": "lib/"
172 | }
173 | },
174 | "notification-url": "https://packagist.org/downloads/",
175 | "license": [
176 | "MIT"
177 | ],
178 | "authors": [
179 | {
180 | "name": "Jonathan Wage",
181 | "email": "jonwage@gmail.com",
182 | "homepage": "http://www.jwage.com/",
183 | "role": "Creator"
184 | },
185 | {
186 | "name": "Guilherme Blanco",
187 | "email": "guilhermeblanco@gmail.com",
188 | "homepage": "http://www.instaclick.com"
189 | },
190 | {
191 | "name": "Roman Borschel",
192 | "email": "roman@code-factory.org"
193 | },
194 | {
195 | "name": "Benjamin Eberlei",
196 | "email": "kontakt@beberlei.de"
197 | },
198 | {
199 | "name": "Johannes Schmitt",
200 | "email": "schmittjoh@gmail.com",
201 | "homepage": "https://github.com/schmittjoh",
202 | "role": "Developer of wrapped JMSSerializerBundle"
203 | }
204 | ],
205 | "description": "Collections Abstraction library",
206 | "homepage": "http://www.doctrine-project.org",
207 | "keywords": [
208 | "array",
209 | "collections",
210 | "iterator"
211 | ],
212 | "time": "2014-02-03 23:07:43"
213 | },
214 | {
215 | "name": "doctrine/common",
216 | "version": "v2.4.2",
217 | "source": {
218 | "type": "git",
219 | "url": "https://github.com/doctrine/common.git",
220 | "reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b"
221 | },
222 | "dist": {
223 | "type": "zip",
224 | "url": "https://api.github.com/repos/doctrine/common/zipball/5db6ab40e4c531f14dad4ca96a394dfce5d4255b",
225 | "reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b",
226 | "shasum": ""
227 | },
228 | "require": {
229 | "doctrine/annotations": "1.*",
230 | "doctrine/cache": "1.*",
231 | "doctrine/collections": "1.*",
232 | "doctrine/inflector": "1.*",
233 | "doctrine/lexer": "1.*",
234 | "php": ">=5.3.2"
235 | },
236 | "require-dev": {
237 | "phpunit/phpunit": "~3.7"
238 | },
239 | "type": "library",
240 | "extra": {
241 | "branch-alias": {
242 | "dev-master": "2.4.x-dev"
243 | }
244 | },
245 | "autoload": {
246 | "psr-0": {
247 | "Doctrine\\Common\\": "lib/"
248 | }
249 | },
250 | "notification-url": "https://packagist.org/downloads/",
251 | "license": [
252 | "MIT"
253 | ],
254 | "authors": [
255 | {
256 | "name": "Jonathan Wage",
257 | "email": "jonwage@gmail.com",
258 | "homepage": "http://www.jwage.com/",
259 | "role": "Creator"
260 | },
261 | {
262 | "name": "Guilherme Blanco",
263 | "email": "guilhermeblanco@gmail.com",
264 | "homepage": "http://www.instaclick.com"
265 | },
266 | {
267 | "name": "Roman Borschel",
268 | "email": "roman@code-factory.org"
269 | },
270 | {
271 | "name": "Benjamin Eberlei",
272 | "email": "kontakt@beberlei.de"
273 | },
274 | {
275 | "name": "Johannes Schmitt",
276 | "email": "schmittjoh@gmail.com",
277 | "homepage": "https://github.com/schmittjoh",
278 | "role": "Developer of wrapped JMSSerializerBundle"
279 | }
280 | ],
281 | "description": "Common Library for Doctrine projects",
282 | "homepage": "http://www.doctrine-project.org",
283 | "keywords": [
284 | "annotations",
285 | "collections",
286 | "eventmanager",
287 | "persistence",
288 | "spl"
289 | ],
290 | "time": "2014-05-21 19:28:51"
291 | },
292 | {
293 | "name": "doctrine/inflector",
294 | "version": "v1.0",
295 | "source": {
296 | "type": "git",
297 | "url": "https://github.com/doctrine/inflector.git",
298 | "reference": "54b8333d2a5682afdc690060c1cf384ba9f47f08"
299 | },
300 | "dist": {
301 | "type": "zip",
302 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/54b8333d2a5682afdc690060c1cf384ba9f47f08",
303 | "reference": "54b8333d2a5682afdc690060c1cf384ba9f47f08",
304 | "shasum": ""
305 | },
306 | "require": {
307 | "php": ">=5.3.2"
308 | },
309 | "type": "library",
310 | "autoload": {
311 | "psr-0": {
312 | "Doctrine\\Common\\Inflector\\": "lib/"
313 | }
314 | },
315 | "notification-url": "https://packagist.org/downloads/",
316 | "license": [
317 | "MIT"
318 | ],
319 | "authors": [
320 | {
321 | "name": "Jonathan Wage",
322 | "email": "jonwage@gmail.com",
323 | "homepage": "http://www.jwage.com/",
324 | "role": "Creator"
325 | },
326 | {
327 | "name": "Guilherme Blanco",
328 | "email": "guilhermeblanco@gmail.com",
329 | "homepage": "http://www.instaclick.com"
330 | },
331 | {
332 | "name": "Roman Borschel",
333 | "email": "roman@code-factory.org"
334 | },
335 | {
336 | "name": "Benjamin Eberlei",
337 | "email": "kontakt@beberlei.de"
338 | },
339 | {
340 | "name": "Johannes Schmitt",
341 | "email": "schmittjoh@gmail.com",
342 | "homepage": "https://github.com/schmittjoh",
343 | "role": "Developer of wrapped JMSSerializerBundle"
344 | }
345 | ],
346 | "description": "Common String Manipulations with regard to casing and singular/plural rules.",
347 | "homepage": "http://www.doctrine-project.org",
348 | "keywords": [
349 | "inflection",
350 | "pluarlize",
351 | "singuarlize",
352 | "string"
353 | ],
354 | "time": "2013-01-10 21:49:15"
355 | },
356 | {
357 | "name": "doctrine/lexer",
358 | "version": "v1.0",
359 | "source": {
360 | "type": "git",
361 | "url": "https://github.com/doctrine/lexer.git",
362 | "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb"
363 | },
364 | "dist": {
365 | "type": "zip",
366 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/2f708a85bb3aab5d99dab8be435abd73e0b18acb",
367 | "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb",
368 | "shasum": ""
369 | },
370 | "require": {
371 | "php": ">=5.3.2"
372 | },
373 | "type": "library",
374 | "autoload": {
375 | "psr-0": {
376 | "Doctrine\\Common\\Lexer\\": "lib/"
377 | }
378 | },
379 | "notification-url": "https://packagist.org/downloads/",
380 | "license": [
381 | "MIT"
382 | ],
383 | "authors": [
384 | {
385 | "name": "Guilherme Blanco",
386 | "email": "guilhermeblanco@gmail.com",
387 | "homepage": "http://www.instaclick.com"
388 | },
389 | {
390 | "name": "Roman Borschel",
391 | "email": "roman@code-factory.org"
392 | },
393 | {
394 | "name": "Johannes Schmitt",
395 | "email": "schmittjoh@gmail.com",
396 | "homepage": "https://github.com/schmittjoh",
397 | "role": "Developer of wrapped JMSSerializerBundle"
398 | }
399 | ],
400 | "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
401 | "homepage": "http://www.doctrine-project.org",
402 | "keywords": [
403 | "lexer",
404 | "parser"
405 | ],
406 | "time": "2013-01-12 18:59:04"
407 | }
408 | ],
409 | "packages-dev": [
410 | {
411 | "name": "doctrine/dbal",
412 | "version": "v2.4.3",
413 | "source": {
414 | "type": "git",
415 | "url": "https://github.com/doctrine/dbal.git",
416 | "reference": "0368bc031976126e5d36d37d2c56155092b6575b"
417 | },
418 | "dist": {
419 | "type": "zip",
420 | "url": "https://api.github.com/repos/doctrine/dbal/zipball/0368bc031976126e5d36d37d2c56155092b6575b",
421 | "reference": "0368bc031976126e5d36d37d2c56155092b6575b",
422 | "shasum": ""
423 | },
424 | "require": {
425 | "doctrine/common": "~2.4",
426 | "php": ">=5.3.2"
427 | },
428 | "require-dev": {
429 | "phpunit/phpunit": "3.7.*",
430 | "symfony/console": "~2.0"
431 | },
432 | "suggest": {
433 | "symfony/console": "For helpful console commands such as SQL execution and import of files."
434 | },
435 | "type": "library",
436 | "autoload": {
437 | "psr-0": {
438 | "Doctrine\\DBAL\\": "lib/"
439 | }
440 | },
441 | "notification-url": "https://packagist.org/downloads/",
442 | "license": [
443 | "MIT"
444 | ],
445 | "authors": [
446 | {
447 | "name": "Roman Borschel",
448 | "email": "roman@code-factory.org"
449 | },
450 | {
451 | "name": "Benjamin Eberlei",
452 | "email": "kontakt@beberlei.de"
453 | },
454 | {
455 | "name": "Guilherme Blanco",
456 | "email": "guilhermeblanco@gmail.com"
457 | },
458 | {
459 | "name": "Jonathan Wage",
460 | "email": "jonwage@gmail.com"
461 | }
462 | ],
463 | "description": "Database Abstraction Layer",
464 | "homepage": "http://www.doctrine-project.org",
465 | "keywords": [
466 | "database",
467 | "dbal",
468 | "persistence",
469 | "queryobject"
470 | ],
471 | "time": "2014-10-16 11:56:49"
472 | },
473 | {
474 | "name": "doctrine/orm",
475 | "version": "v2.4.6",
476 | "source": {
477 | "type": "git",
478 | "url": "https://github.com/doctrine/doctrine2.git",
479 | "reference": "bebacf79d8d4dae9168f0f9bc6811e6c2cb6a4d9"
480 | },
481 | "dist": {
482 | "type": "zip",
483 | "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/bebacf79d8d4dae9168f0f9bc6811e6c2cb6a4d9",
484 | "reference": "bebacf79d8d4dae9168f0f9bc6811e6c2cb6a4d9",
485 | "shasum": ""
486 | },
487 | "require": {
488 | "doctrine/collections": "~1.1",
489 | "doctrine/dbal": "~2.4",
490 | "ext-pdo": "*",
491 | "php": ">=5.3.2",
492 | "symfony/console": "~2.0"
493 | },
494 | "require-dev": {
495 | "satooshi/php-coveralls": "dev-master",
496 | "symfony/yaml": "~2.1"
497 | },
498 | "suggest": {
499 | "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
500 | },
501 | "bin": [
502 | "bin/doctrine",
503 | "bin/doctrine.php"
504 | ],
505 | "type": "library",
506 | "extra": {
507 | "branch-alias": {
508 | "dev-master": "2.4.x-dev"
509 | }
510 | },
511 | "autoload": {
512 | "psr-0": {
513 | "Doctrine\\ORM\\": "lib/"
514 | }
515 | },
516 | "notification-url": "https://packagist.org/downloads/",
517 | "license": [
518 | "MIT"
519 | ],
520 | "authors": [
521 | {
522 | "name": "Roman Borschel",
523 | "email": "roman@code-factory.org"
524 | },
525 | {
526 | "name": "Benjamin Eberlei",
527 | "email": "kontakt@beberlei.de"
528 | },
529 | {
530 | "name": "Guilherme Blanco",
531 | "email": "guilhermeblanco@gmail.com"
532 | },
533 | {
534 | "name": "Jonathan Wage",
535 | "email": "jonwage@gmail.com"
536 | }
537 | ],
538 | "description": "Object-Relational-Mapper for PHP",
539 | "homepage": "http://www.doctrine-project.org",
540 | "keywords": [
541 | "database",
542 | "orm"
543 | ],
544 | "time": "2014-10-06 13:22:50"
545 | },
546 | {
547 | "name": "phpunit/php-code-coverage",
548 | "version": "1.2.18",
549 | "source": {
550 | "type": "git",
551 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
552 | "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b"
553 | },
554 | "dist": {
555 | "type": "zip",
556 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
557 | "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
558 | "shasum": ""
559 | },
560 | "require": {
561 | "php": ">=5.3.3",
562 | "phpunit/php-file-iterator": ">=1.3.0@stable",
563 | "phpunit/php-text-template": ">=1.2.0@stable",
564 | "phpunit/php-token-stream": ">=1.1.3,<1.3.0"
565 | },
566 | "require-dev": {
567 | "phpunit/phpunit": "3.7.*@dev"
568 | },
569 | "suggest": {
570 | "ext-dom": "*",
571 | "ext-xdebug": ">=2.0.5"
572 | },
573 | "type": "library",
574 | "extra": {
575 | "branch-alias": {
576 | "dev-master": "1.2.x-dev"
577 | }
578 | },
579 | "autoload": {
580 | "classmap": [
581 | "PHP/"
582 | ]
583 | },
584 | "notification-url": "https://packagist.org/downloads/",
585 | "include-path": [
586 | ""
587 | ],
588 | "license": [
589 | "BSD-3-Clause"
590 | ],
591 | "authors": [
592 | {
593 | "name": "Sebastian Bergmann",
594 | "email": "sb@sebastian-bergmann.de",
595 | "role": "lead"
596 | }
597 | ],
598 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
599 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
600 | "keywords": [
601 | "coverage",
602 | "testing",
603 | "xunit"
604 | ],
605 | "time": "2014-09-02 10:13:14"
606 | },
607 | {
608 | "name": "phpunit/php-file-iterator",
609 | "version": "1.3.4",
610 | "source": {
611 | "type": "git",
612 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
613 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
614 | },
615 | "dist": {
616 | "type": "zip",
617 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
618 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
619 | "shasum": ""
620 | },
621 | "require": {
622 | "php": ">=5.3.3"
623 | },
624 | "type": "library",
625 | "autoload": {
626 | "classmap": [
627 | "File/"
628 | ]
629 | },
630 | "notification-url": "https://packagist.org/downloads/",
631 | "include-path": [
632 | ""
633 | ],
634 | "license": [
635 | "BSD-3-Clause"
636 | ],
637 | "authors": [
638 | {
639 | "name": "Sebastian Bergmann",
640 | "email": "sb@sebastian-bergmann.de",
641 | "role": "lead"
642 | }
643 | ],
644 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
645 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
646 | "keywords": [
647 | "filesystem",
648 | "iterator"
649 | ],
650 | "time": "2013-10-10 15:34:57"
651 | },
652 | {
653 | "name": "phpunit/php-text-template",
654 | "version": "1.2.0",
655 | "source": {
656 | "type": "git",
657 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
658 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
659 | },
660 | "dist": {
661 | "type": "zip",
662 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
663 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
664 | "shasum": ""
665 | },
666 | "require": {
667 | "php": ">=5.3.3"
668 | },
669 | "type": "library",
670 | "autoload": {
671 | "classmap": [
672 | "Text/"
673 | ]
674 | },
675 | "notification-url": "https://packagist.org/downloads/",
676 | "include-path": [
677 | ""
678 | ],
679 | "license": [
680 | "BSD-3-Clause"
681 | ],
682 | "authors": [
683 | {
684 | "name": "Sebastian Bergmann",
685 | "email": "sb@sebastian-bergmann.de",
686 | "role": "lead"
687 | }
688 | ],
689 | "description": "Simple template engine.",
690 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
691 | "keywords": [
692 | "template"
693 | ],
694 | "time": "2014-01-30 17:20:04"
695 | },
696 | {
697 | "name": "phpunit/php-timer",
698 | "version": "1.0.5",
699 | "source": {
700 | "type": "git",
701 | "url": "https://github.com/sebastianbergmann/php-timer.git",
702 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
703 | },
704 | "dist": {
705 | "type": "zip",
706 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
707 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
708 | "shasum": ""
709 | },
710 | "require": {
711 | "php": ">=5.3.3"
712 | },
713 | "type": "library",
714 | "autoload": {
715 | "classmap": [
716 | "PHP/"
717 | ]
718 | },
719 | "notification-url": "https://packagist.org/downloads/",
720 | "include-path": [
721 | ""
722 | ],
723 | "license": [
724 | "BSD-3-Clause"
725 | ],
726 | "authors": [
727 | {
728 | "name": "Sebastian Bergmann",
729 | "email": "sb@sebastian-bergmann.de",
730 | "role": "lead"
731 | }
732 | ],
733 | "description": "Utility class for timing",
734 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
735 | "keywords": [
736 | "timer"
737 | ],
738 | "time": "2013-08-02 07:42:54"
739 | },
740 | {
741 | "name": "phpunit/php-token-stream",
742 | "version": "1.2.2",
743 | "source": {
744 | "type": "git",
745 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
746 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32"
747 | },
748 | "dist": {
749 | "type": "zip",
750 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32",
751 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32",
752 | "shasum": ""
753 | },
754 | "require": {
755 | "ext-tokenizer": "*",
756 | "php": ">=5.3.3"
757 | },
758 | "type": "library",
759 | "extra": {
760 | "branch-alias": {
761 | "dev-master": "1.2-dev"
762 | }
763 | },
764 | "autoload": {
765 | "classmap": [
766 | "PHP/"
767 | ]
768 | },
769 | "notification-url": "https://packagist.org/downloads/",
770 | "include-path": [
771 | ""
772 | ],
773 | "license": [
774 | "BSD-3-Clause"
775 | ],
776 | "authors": [
777 | {
778 | "name": "Sebastian Bergmann",
779 | "email": "sb@sebastian-bergmann.de",
780 | "role": "lead"
781 | }
782 | ],
783 | "description": "Wrapper around PHP's tokenizer extension.",
784 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
785 | "keywords": [
786 | "tokenizer"
787 | ],
788 | "time": "2014-03-03 05:10:30"
789 | },
790 | {
791 | "name": "phpunit/phpunit",
792 | "version": "3.7.38",
793 | "source": {
794 | "type": "git",
795 | "url": "https://github.com/sebastianbergmann/phpunit.git",
796 | "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6"
797 | },
798 | "dist": {
799 | "type": "zip",
800 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6",
801 | "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6",
802 | "shasum": ""
803 | },
804 | "require": {
805 | "ext-ctype": "*",
806 | "ext-dom": "*",
807 | "ext-json": "*",
808 | "ext-pcre": "*",
809 | "ext-reflection": "*",
810 | "ext-spl": "*",
811 | "php": ">=5.3.3",
812 | "phpunit/php-code-coverage": "~1.2",
813 | "phpunit/php-file-iterator": "~1.3",
814 | "phpunit/php-text-template": "~1.1",
815 | "phpunit/php-timer": "~1.0",
816 | "phpunit/phpunit-mock-objects": "~1.2",
817 | "symfony/yaml": "~2.0"
818 | },
819 | "require-dev": {
820 | "pear-pear.php.net/pear": "1.9.4"
821 | },
822 | "suggest": {
823 | "phpunit/php-invoker": "~1.1"
824 | },
825 | "bin": [
826 | "composer/bin/phpunit"
827 | ],
828 | "type": "library",
829 | "extra": {
830 | "branch-alias": {
831 | "dev-master": "3.7.x-dev"
832 | }
833 | },
834 | "autoload": {
835 | "classmap": [
836 | "PHPUnit/"
837 | ]
838 | },
839 | "notification-url": "https://packagist.org/downloads/",
840 | "include-path": [
841 | "",
842 | "../../symfony/yaml/"
843 | ],
844 | "license": [
845 | "BSD-3-Clause"
846 | ],
847 | "authors": [
848 | {
849 | "name": "Sebastian Bergmann",
850 | "email": "sebastian@phpunit.de",
851 | "role": "lead"
852 | }
853 | ],
854 | "description": "The PHP Unit Testing framework.",
855 | "homepage": "http://www.phpunit.de/",
856 | "keywords": [
857 | "phpunit",
858 | "testing",
859 | "xunit"
860 | ],
861 | "time": "2014-10-17 09:04:17"
862 | },
863 | {
864 | "name": "phpunit/phpunit-mock-objects",
865 | "version": "1.2.3",
866 | "source": {
867 | "type": "git",
868 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
869 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875"
870 | },
871 | "dist": {
872 | "type": "zip",
873 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875",
874 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875",
875 | "shasum": ""
876 | },
877 | "require": {
878 | "php": ">=5.3.3",
879 | "phpunit/php-text-template": ">=1.1.1@stable"
880 | },
881 | "suggest": {
882 | "ext-soap": "*"
883 | },
884 | "type": "library",
885 | "autoload": {
886 | "classmap": [
887 | "PHPUnit/"
888 | ]
889 | },
890 | "notification-url": "https://packagist.org/downloads/",
891 | "include-path": [
892 | ""
893 | ],
894 | "license": [
895 | "BSD-3-Clause"
896 | ],
897 | "authors": [
898 | {
899 | "name": "Sebastian Bergmann",
900 | "email": "sb@sebastian-bergmann.de",
901 | "role": "lead"
902 | }
903 | ],
904 | "description": "Mock Object library for PHPUnit",
905 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
906 | "keywords": [
907 | "mock",
908 | "xunit"
909 | ],
910 | "time": "2013-01-13 10:24:48"
911 | },
912 | {
913 | "name": "symfony/console",
914 | "version": "v2.5.6",
915 | "target-dir": "Symfony/Component/Console",
916 | "source": {
917 | "type": "git",
918 | "url": "https://github.com/symfony/Console.git",
919 | "reference": "6f177fca24200a5b97aef5ce7a5c98124a0f0db0"
920 | },
921 | "dist": {
922 | "type": "zip",
923 | "url": "https://api.github.com/repos/symfony/Console/zipball/6f177fca24200a5b97aef5ce7a5c98124a0f0db0",
924 | "reference": "6f177fca24200a5b97aef5ce7a5c98124a0f0db0",
925 | "shasum": ""
926 | },
927 | "require": {
928 | "php": ">=5.3.3"
929 | },
930 | "require-dev": {
931 | "psr/log": "~1.0",
932 | "symfony/event-dispatcher": "~2.1"
933 | },
934 | "suggest": {
935 | "psr/log": "For using the console logger",
936 | "symfony/event-dispatcher": ""
937 | },
938 | "type": "library",
939 | "extra": {
940 | "branch-alias": {
941 | "dev-master": "2.5-dev"
942 | }
943 | },
944 | "autoload": {
945 | "psr-0": {
946 | "Symfony\\Component\\Console\\": ""
947 | }
948 | },
949 | "notification-url": "https://packagist.org/downloads/",
950 | "license": [
951 | "MIT"
952 | ],
953 | "authors": [
954 | {
955 | "name": "Symfony Community",
956 | "homepage": "http://symfony.com/contributors"
957 | },
958 | {
959 | "name": "Fabien Potencier",
960 | "email": "fabien@symfony.com"
961 | }
962 | ],
963 | "description": "Symfony Console Component",
964 | "homepage": "http://symfony.com",
965 | "time": "2014-10-05 13:57:04"
966 | },
967 | {
968 | "name": "symfony/yaml",
969 | "version": "v2.5.6",
970 | "target-dir": "Symfony/Component/Yaml",
971 | "source": {
972 | "type": "git",
973 | "url": "https://github.com/symfony/Yaml.git",
974 | "reference": "2d9f527449cabfa8543dd7fa3a466d6ae83d6726"
975 | },
976 | "dist": {
977 | "type": "zip",
978 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/2d9f527449cabfa8543dd7fa3a466d6ae83d6726",
979 | "reference": "2d9f527449cabfa8543dd7fa3a466d6ae83d6726",
980 | "shasum": ""
981 | },
982 | "require": {
983 | "php": ">=5.3.3"
984 | },
985 | "type": "library",
986 | "extra": {
987 | "branch-alias": {
988 | "dev-master": "2.5-dev"
989 | }
990 | },
991 | "autoload": {
992 | "psr-0": {
993 | "Symfony\\Component\\Yaml\\": ""
994 | }
995 | },
996 | "notification-url": "https://packagist.org/downloads/",
997 | "license": [
998 | "MIT"
999 | ],
1000 | "authors": [
1001 | {
1002 | "name": "Symfony Community",
1003 | "homepage": "http://symfony.com/contributors"
1004 | },
1005 | {
1006 | "name": "Fabien Potencier",
1007 | "email": "fabien@symfony.com"
1008 | }
1009 | ],
1010 | "description": "Symfony Yaml Component",
1011 | "homepage": "http://symfony.com",
1012 | "time": "2014-10-01 05:50:18"
1013 | }
1014 | ],
1015 | "aliases": [],
1016 | "minimum-stability": "stable",
1017 | "stability-flags": [],
1018 | "prefer-stable": false,
1019 | "platform": {
1020 | "php": ">=5.3.2"
1021 | },
1022 | "platform-dev": []
1023 | }
1024 |
--------------------------------------------------------------------------------