├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── phpunit.xml.dist
├── src
└── Flintstones
│ └── Rest
│ ├── PimpleDecoderProvider.php
│ └── ServiceProvider.php
└── tests
├── Flintstones
└── Rest
│ └── Tests
│ └── ServiceProviderTest.php
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Igor Wiedler
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do 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
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flintstones RestServiceProvider
2 |
3 | Adding some REST capabilities to [Silex][1], so you can
4 | more easily build RESTful APIs. 110% Buzzword-Driven.
5 |
6 | You get accept header support and request body decoding.
7 |
8 | ## Registering
9 |
10 | $app->register(new Flintstones\Rest\ServiceProvider(), array(
11 | 'rest.fos.class_path' => __DIR__.'/vendor',
12 | 'rest.serializer.class_path' => __DIR__.'/vendor',
13 | ));
14 |
15 | ## Running the tests
16 |
17 | $ curl -s https://getcomposer.org/installer | php
18 | $ php composer.phar install
19 | $ phpunit
20 |
21 | ## Credits
22 |
23 | * [FOSRestBundle][2]
24 | * [Symfony2 Serializer Component][3]
25 |
26 | ## License
27 |
28 | The RestServiceProvider is licensed under the MIT license.
29 |
30 | [1]: http://silex-project.org
31 | [2]: https://github.com/FriendsOfSymfony/FOSRestBundle
32 | [3]: https://github.com/symfony/Serializer
33 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flintstones/rest",
3 | "description": "Adding some REST capabilities to Silex, so you can more easily build RESTful APIs. 110% Buzzword-Driven.",
4 | "keywords": ["rest", "silex"],
5 | "authors": [
6 | {
7 | "name": "Igor Wiedler",
8 | "email": "igor@wiedler.ch"
9 | }
10 | ],
11 | "require": {
12 | "friendsofsymfony/rest-bundle": "*",
13 | "symfony/serializer": "2.1.x",
14 | "silex/silex": "1.0.*"
15 | },
16 | "autoload": {
17 | "psr-0": { "Flintstones": "src" }
18 | },
19 | "minimum-stability": "dev"
20 | }
21 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "hash": "fbaad4ca06268918d11410b6f0e70525",
3 | "packages": [
4 | {
5 | "package": "doctrine/common",
6 | "version": "2.3.x-dev",
7 | "source-reference": "2ec4bc6db13db6a0976a16b71058083c55cbcdbd",
8 | "commit-date": "1343847076"
9 | },
10 | {
11 | "package": "friendsofsymfony/rest",
12 | "version": "dev-master",
13 | "alias-pretty-version": "0.7.x-dev",
14 | "alias-version": "0.7.9999999.9999999-dev"
15 | },
16 | {
17 | "package": "friendsofsymfony/rest",
18 | "version": "dev-master",
19 | "source-reference": "5ea703f5a3f5c7110cadeccb91b212c0eb7ea2d0",
20 | "commit-date": "1339701893"
21 | },
22 | {
23 | "package": "friendsofsymfony/rest-bundle",
24 | "version": "dev-master",
25 | "alias-pretty-version": "0.8.x-dev",
26 | "alias-version": "0.8.9999999.9999999-dev"
27 | },
28 | {
29 | "package": "friendsofsymfony/rest-bundle",
30 | "version": "dev-master",
31 | "source-reference": "d3f15d6c913d25fe5b29185e6e6be510afd09c73",
32 | "commit-date": "1345237156"
33 | },
34 | {
35 | "package": "jms/metadata",
36 | "version": "dev-master",
37 | "alias-pretty-version": "1.2.x-dev",
38 | "alias-version": "1.2.9999999.9999999-dev"
39 | },
40 | {
41 | "package": "jms/metadata",
42 | "version": "dev-master",
43 | "source-reference": "7c58d49ff13de62464837c3c7d91aff1deb2c442",
44 | "commit-date": "1345552810"
45 | },
46 | {
47 | "package": "jms/serializer-bundle",
48 | "version": "dev-master",
49 | "alias-pretty-version": "0.9.x-dev",
50 | "alias-version": "0.9.9999999.9999999-dev"
51 | },
52 | {
53 | "package": "jms/serializer-bundle",
54 | "version": "dev-master",
55 | "source-reference": "1f308a587742246e87e580290f3bae23073d1d70",
56 | "commit-date": "1343641450"
57 | },
58 | {
59 | "package": "pimple/pimple",
60 | "version": "dev-master",
61 | "alias-pretty-version": "1.0.x-dev",
62 | "alias-version": "1.0.9999999.9999999-dev"
63 | },
64 | {
65 | "package": "pimple/pimple",
66 | "version": "dev-master",
67 | "source-reference": "db836b8cfadc0f39dacafa2bf311a1ab603600bb",
68 | "commit-date": "1343051648"
69 | },
70 | {
71 | "package": "sensio/framework-extra-bundle",
72 | "version": "dev-master",
73 | "alias-pretty-version": "2.1.x-dev",
74 | "alias-version": "2.1.9999999.9999999-dev"
75 | },
76 | {
77 | "package": "sensio/framework-extra-bundle",
78 | "version": "dev-master",
79 | "source-reference": "ade2d53b20a9fc3428f04c863b3fd4fca0b62181",
80 | "commit-date": "1344072374"
81 | },
82 | {
83 | "package": "silex/silex",
84 | "version": "dev-master",
85 | "alias-pretty-version": "1.0.x-dev",
86 | "alias-version": "1.0.9999999.9999999-dev"
87 | },
88 | {
89 | "package": "silex/silex",
90 | "version": "dev-master",
91 | "source-reference": "6d7cf048c704c0c58276b01eccfacab4dd11d013",
92 | "commit-date": "1344453846"
93 | },
94 | {
95 | "package": "symfony/config",
96 | "version": "dev-master",
97 | "alias-pretty-version": "2.1.x-dev",
98 | "alias-version": "2.1.9999999.9999999-dev"
99 | },
100 | {
101 | "package": "symfony/config",
102 | "version": "dev-master",
103 | "source-reference": "f68ad44f3ee1603ce96387bacba0ecba488c2a33",
104 | "commit-date": "1344599303"
105 | },
106 | {
107 | "package": "symfony/dependency-injection",
108 | "version": "dev-master",
109 | "alias-pretty-version": "2.1.x-dev",
110 | "alias-version": "2.1.9999999.9999999-dev"
111 | },
112 | {
113 | "package": "symfony/dependency-injection",
114 | "version": "dev-master",
115 | "source-reference": "98fa735a7ef5dc07c43bfe6faa4367a983b6ba6f",
116 | "commit-date": "1344599303"
117 | },
118 | {
119 | "package": "symfony/event-dispatcher",
120 | "version": "dev-master",
121 | "alias-pretty-version": "2.1.x-dev",
122 | "alias-version": "2.1.9999999.9999999-dev"
123 | },
124 | {
125 | "package": "symfony/event-dispatcher",
126 | "version": "dev-master",
127 | "source-reference": "76c76f62702b09e0f182ae618be0f1d79e2a711f",
128 | "commit-date": "1345066712"
129 | },
130 | {
131 | "package": "symfony/filesystem",
132 | "version": "dev-master",
133 | "alias-pretty-version": "2.1.x-dev",
134 | "alias-version": "2.1.9999999.9999999-dev"
135 | },
136 | {
137 | "package": "symfony/filesystem",
138 | "version": "dev-master",
139 | "source-reference": "72acf65c2390c9066e1174d269a4e146ffad9296",
140 | "commit-date": "1344875591"
141 | },
142 | {
143 | "package": "symfony/framework-bundle",
144 | "version": "dev-master",
145 | "alias-pretty-version": "2.1.x-dev",
146 | "alias-version": "2.1.9999999.9999999-dev"
147 | },
148 | {
149 | "package": "symfony/framework-bundle",
150 | "version": "dev-master",
151 | "source-reference": "4f0d296b4414cb5322e7ee45af117e5234bc75d0",
152 | "commit-date": "1345278333"
153 | },
154 | {
155 | "package": "symfony/http-foundation",
156 | "version": "dev-master",
157 | "alias-pretty-version": "2.1.x-dev",
158 | "alias-version": "2.1.9999999.9999999-dev"
159 | },
160 | {
161 | "package": "symfony/http-foundation",
162 | "version": "dev-master",
163 | "source-reference": "8d5e7f909fa519853e71bcfc57a1da8c637ebcbb",
164 | "commit-date": "1345191498"
165 | },
166 | {
167 | "package": "symfony/http-kernel",
168 | "version": "dev-master",
169 | "alias-pretty-version": "2.1.x-dev",
170 | "alias-version": "2.1.9999999.9999999-dev"
171 | },
172 | {
173 | "package": "symfony/http-kernel",
174 | "version": "dev-master",
175 | "source-reference": "0d0ee371ab0425f3399dba9e25d7ad98ca5c0d62",
176 | "commit-date": "1344074675"
177 | },
178 | {
179 | "package": "symfony/routing",
180 | "version": "dev-master",
181 | "alias-pretty-version": "2.1.x-dev",
182 | "alias-version": "2.1.9999999.9999999-dev"
183 | },
184 | {
185 | "package": "symfony/routing",
186 | "version": "dev-master",
187 | "source-reference": "v2.1.0-RC1",
188 | "commit-date": "1343744077"
189 | },
190 | {
191 | "package": "symfony/serializer",
192 | "version": "dev-master",
193 | "alias-pretty-version": "2.1.x-dev",
194 | "alias-version": "2.1.9999999.9999999-dev"
195 | },
196 | {
197 | "package": "symfony/serializer",
198 | "version": "dev-master",
199 | "source-reference": "v2.1.0-RC1",
200 | "commit-date": "1343307511"
201 | },
202 | {
203 | "package": "symfony/templating",
204 | "version": "dev-master",
205 | "alias-pretty-version": "2.1.x-dev",
206 | "alias-version": "2.1.9999999.9999999-dev"
207 | },
208 | {
209 | "package": "symfony/templating",
210 | "version": "dev-master",
211 | "source-reference": "v2.1.0-RC1",
212 | "commit-date": "1343982509"
213 | },
214 | {
215 | "package": "symfony/translation",
216 | "version": "dev-master",
217 | "alias-pretty-version": "2.1.x-dev",
218 | "alias-version": "2.1.9999999.9999999-dev"
219 | },
220 | {
221 | "package": "symfony/translation",
222 | "version": "dev-master",
223 | "source-reference": "d7a6083e76a3af3f247bff523bc47b41e7208a87",
224 | "commit-date": "1344955085"
225 | }
226 | ],
227 | "packages-dev": null,
228 | "aliases": [
229 |
230 | ],
231 | "minimum-stability": "dev",
232 | "stability-flags": [
233 |
234 | ]
235 | }
236 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | ./Tests
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Flintstones/Rest/PimpleDecoderProvider.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Flintstones\Rest;
13 |
14 | use FOS\Rest\Decoder\DecoderProviderInterface;
15 |
16 | class PimpleDecoderProvider implements DecoderProviderInterface
17 | {
18 | private $container;
19 |
20 | private $decoders;
21 |
22 | public function __construct(\Pimple $container, array $decoders)
23 | {
24 | $this->container = $container;
25 | $this->decoders = $decoders;
26 | }
27 |
28 | public function supports($format)
29 | {
30 | return isset($this->container[$this->decoders[$format]]);
31 | }
32 |
33 | public function getDecoder($format)
34 | {
35 | if (!$this->supports($format)) {
36 | throw new \InvalidArgumentException(sprintf("Format '%s' is not supported by PimpleDecoderProvider.", $format));
37 | }
38 |
39 | return $this->container[$this->decoders[$format]];
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Flintstones/Rest/ServiceProvider.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Flintstones\Rest;
13 |
14 | use FOS\RestBundle\EventListener\BodyListener;
15 | use FOS\RestBundle\EventListener\FormatListener;
16 | use FOS\Rest\Util\FormatNegotiator;
17 | use FOS\Rest\Decoder\JsonDecoder;
18 | use FOS\Rest\Decoder\XmlDecoder;
19 |
20 | use Silex\Application;
21 | use Silex\ServiceProviderInterface;
22 |
23 | use Symfony\Component\HttpKernel\KernelEvents as HttpKernelEvents;
24 | use Symfony\Component\Serializer\Serializer;
25 | use Symfony\Component\Serializer\Encoder\JsonEncoder;
26 | use Symfony\Component\Serializer\Encoder\XmlEncoder;
27 |
28 | class ServiceProvider implements ServiceProviderInterface
29 | {
30 | public function register(Application $app)
31 | {
32 | $app['rest.serializer'] = $app->share(function () {
33 | $encoders = array (
34 | 'json' => new JsonEncoder(),
35 | 'xml' => new XmlEncoder()
36 | );
37 | $serializer = new Serializer(array(), $encoders);
38 | return $serializer;
39 | });
40 |
41 | if (!isset($app['rest.priorities'])) {
42 | $app['rest.priorities'] = array('json', 'xml');
43 | }
44 |
45 | $app['rest.format_negotiator'] = function ($app) {
46 | return new FormatNegotiator($app['request'], $app['rest.priorities']);
47 | };
48 |
49 | $app['rest.decoder.json'] = function ($app) {
50 | return new JsonDecoder();
51 | };
52 |
53 | $app['rest.decoder.xml'] = function ($app) {
54 | return new XmlDecoder();
55 | };
56 |
57 | $app['rest.decoders'] = isset($app['rest.decoders']) ? $app['rest.decoders'] : array(
58 | 'json' => 'rest.decoder.json',
59 | 'xml' => 'rest.decoder.xml',
60 | );
61 |
62 | if (isset($app['rest.fos.class_path'])) {
63 | $app['autoloader']->registerNamespace('FOS\RestBundle', $app['rest.fos.class_path']);
64 | }
65 |
66 | if (isset($app['rest.serializer.class_path'])) {
67 | $app['autoloader']->registerNamespace('Symfony\Component\Serializer', $app['rest.serializer.class_path']);
68 | }
69 |
70 | $listener = new BodyListener(new PimpleDecoderProvider($app, $app['rest.decoders']));
71 | $app['dispatcher']->addListener(HttpKernelEvents::REQUEST, array($listener, 'onKernelRequest'));
72 |
73 | $listener = new FormatListener($app['rest.format_negotiator'], 'html', $app['rest.priorities']);
74 | $app['dispatcher']->addListener(HttpKernelEvents::CONTROLLER, array($listener, 'onKernelController'), 10);
75 | }
76 |
77 | /**
78 | * Bootstraps the application.
79 | *
80 | * This method is called after all services are registers
81 | * and should be used for "dynamic" configuration (whenever
82 | * a service must be requested).
83 | */
84 | public function boot(Application $app) {
85 | // TODO: Implement boot() method.
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/Flintstones/Rest/Tests/ServiceProviderTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Flintstones\Rest\Tests;
13 |
14 | use Flintstones\Rest\ServiceProvider as RestServiceProvider;
15 |
16 | use Silex\Application;
17 |
18 | use Symfony\Component\HttpFoundation\Request;
19 | use Symfony\Component\HttpKernel\HttpKernelInterface;
20 |
21 | /**
22 | * RestExtension test cases.
23 | *
24 | * @author Igor Wiedler
25 | */
26 | class ServiceProviderTest extends \PHPUnit_Framework_TestCase
27 | {
28 | public function setUp()
29 | {
30 | if (!is_file(__DIR__.'/../../../../vendor/friendsofsymfony/rest-bundle/FOS/RestBundle/FOSRestBundle.php')) {
31 | $this->markTestSkipped('FOS\RestBundle submodule was not installed.');
32 | }
33 | }
34 |
35 | public function testRegister()
36 | {
37 | $app = new Application();
38 |
39 | $app->register(new RestServiceProvider(), array(
40 | 'rest.fos.class_path' => __DIR__.'/../../../../vendor',
41 | 'rest.serializer.class_path' => __DIR__.'/../../../../vendor',
42 | ));
43 |
44 | $this->assertInstanceOf('Symfony\Component\Serializer\Serializer', $app['rest.serializer']);
45 |
46 | return $app;
47 | }
48 |
49 | /**
50 | * @depends testRegister
51 | */
52 | public function testDecodingOfRequestBody(Application $app)
53 | {
54 | $app->put('/api/user/{id}', function ($id) use ($app) {
55 | return $app['request']->get('name');
56 | });
57 |
58 | $request = Request::create('/api/user/1', 'put', array(), array(), array(), array(), '{"name":"igor"}');
59 | $request->headers->set('Content-Type', 'application/json');
60 | $response = $app->handle($request, HttpKernelInterface::MASTER_REQUEST, false);
61 |
62 | $this->assertEquals('igor', $response->getContent());
63 | }
64 |
65 | /**
66 | * @depends testRegister
67 | */
68 | public function testFormatDetection(Application $app)
69 | {
70 | $app->get('/api/user/{id}', function ($id) use ($app) {
71 | return $app['request']->getRequestFormat();
72 | });
73 |
74 | $request = Request::create('/api/user/1');
75 | $request->headers->set('Accept', 'application/json');
76 | $response = $app->handle($request, HttpKernelInterface::MASTER_REQUEST, false);
77 |
78 | $this->assertEquals('json', $response->getContent());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | add('FlintstonesRest', __DIR__.'/../src');
5 | $loader->add('Flintstones\Tests\Rest', __DIR__);
6 |
--------------------------------------------------------------------------------