├── .gitignore ├── .travis.yml ├── Decoder ├── DecoderInterface.php ├── DecoderProviderInterface.php ├── JsonDecoder.php ├── JsonToFormDecoder.php └── XmlDecoder.php ├── README.md ├── Tests ├── Decoder │ └── JsonToFormDecoderTest.php └── bootstrap.php ├── Util └── Codes.php ├── composer.json └── phpunit.xml.dist /.gitignore: -------------------------------------------------------------------------------- 1 | /phpunit.xml 2 | 3 | composer.phar 4 | /composer.lock 5 | /vendor/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | env: 9 | - SYMFONY_VERSION=2.0.* 10 | - SYMFONY_VERSION=2.1.* 11 | - SYMFONY_VERSION=2.2.* 12 | - SYMFONY_VERSION=2.3.* 13 | - SYMFONY_VERSION=dev-master 14 | 15 | before_script: 16 | - composer require symfony/http-foundation:${SYMFONY_VERSION} 17 | 18 | script: phpunit --coverage-text 19 | 20 | notifications: 21 | email: 22 | - friendsofsymfony-dev@googlegroups.com 23 | -------------------------------------------------------------------------------- /Decoder/DecoderInterface.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 FOS\Rest\Decoder; 13 | 14 | /** 15 | * Defines the interface of decoders 16 | * 17 | * @author Jordi Boggiano 18 | */ 19 | interface DecoderInterface 20 | { 21 | /** 22 | * Decodes a string into PHP data 23 | * 24 | * @param string $data data to decode 25 | * @return array|Boolean false in case the content could not be decoded, else an array 26 | */ 27 | function decode($data); 28 | } 29 | -------------------------------------------------------------------------------- /Decoder/DecoderProviderInterface.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 FOS\Rest\Decoder; 13 | 14 | /** 15 | * Defines the interface of decoder providers 16 | * 17 | * @author Igor Wiedler 18 | */ 19 | interface DecoderProviderInterface 20 | { 21 | /** 22 | * Check if a certain format is supported. 23 | * 24 | * @param string $format Format for the requested decoder. 25 | * @return Boolean 26 | */ 27 | function supports($format); 28 | 29 | /** 30 | * Provides decoders, possibly lazily. 31 | * 32 | * @param string $format Format for the requested decoder. 33 | * @return FOS\Rest\Decoder\DecoderInterface 34 | */ 35 | function getDecoder($format); 36 | } 37 | -------------------------------------------------------------------------------- /Decoder/JsonDecoder.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 FOS\Rest\Decoder; 13 | 14 | /** 15 | * Decodes JSON data 16 | * 17 | * @author Jordi Boggiano 18 | */ 19 | class JsonDecoder implements DecoderInterface 20 | { 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function decode($data) 25 | { 26 | return @json_decode($data, true); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Decoder/JsonToFormDecoder.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 FOS\Rest\Decoder; 13 | 14 | use FOS\Rest\Decoder\DecoderInterface; 15 | 16 | /** 17 | * Decodes JSON data and make it compliant with application/x-www-form-encoded style 18 | * 19 | * @author Kévin Dunglas 20 | */ 21 | class JsonToFormDecoder implements DecoderInterface 22 | { 23 | 24 | /** 25 | * Makes data decoded from JSON application/x-www-form-encoded compliant 26 | * 27 | * @param array $data 28 | */ 29 | private function xWwwFormEncodedLike(&$data) 30 | { 31 | foreach ($data as $key => &$value) { 32 | if (is_array($value)) { 33 | // Encode recursively 34 | $this->xWwwFormEncodedLike($value); 35 | } elseif (false === $value) { 36 | // Checkbox-like behavior: remove false data 37 | unset($data[$key]); 38 | } elseif (!is_string($value)) { 39 | // Convert everyting to string 40 | // true values will be converted to '1', this is the default checkbox behavior 41 | $value = strval($value); 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function decode($data) 50 | { 51 | $decodedData = @json_decode($data, true); 52 | if ($decodedData) { 53 | $this->xWwwFormEncodedLike($decodedData); 54 | } 55 | 56 | return $decodedData; 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /Decoder/XmlDecoder.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 FOS\Rest\Decoder; 13 | 14 | /** 15 | * Decodes XML data 16 | * 17 | * @author Jordi Boggiano 18 | * @author John Wards 19 | * @author Fabian Vogler 20 | */ 21 | class XmlDecoder implements DecoderInterface 22 | { 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function decode($data) 27 | { 28 | $xml = @simplexml_load_string($data); 29 | if (!$xml) { 30 | return; 31 | } 32 | 33 | if (!$xml->count()) { 34 | if (!$xml->attributes()) { 35 | return (string) $xml; 36 | } 37 | $data = array(); 38 | foreach ($xml->attributes() as $attrkey => $attr) { 39 | $data['@'.$attrkey] = (string) $attr; 40 | } 41 | $data['#'] = (string) $xml; 42 | 43 | return $data; 44 | } 45 | 46 | return $this->parseXml($xml); 47 | } 48 | 49 | /** 50 | * Parse the input SimpleXmlElement into an array 51 | * 52 | * @param \SimpleXmlElement $node xml to parse 53 | * @return array 54 | */ 55 | private function parseXml(\SimpleXmlElement $node) 56 | { 57 | $data = array(); 58 | if ($node->attributes()) { 59 | foreach ($node->attributes() as $attrkey => $attr) { 60 | $data['@'.$attrkey] = (string) $attr; 61 | } 62 | } 63 | foreach ($node->children() as $key => $subnode) { 64 | if ($subnode->count()) { 65 | $value = $this->parseXml($subnode); 66 | } elseif ($subnode->attributes()) { 67 | $value = array(); 68 | foreach ($subnode->attributes() as $attrkey => $attr) { 69 | $value['@'.$attrkey] = (string) $attr; 70 | } 71 | $value['#'] = (string) $subnode; 72 | } else { 73 | $value = (string) $subnode; 74 | } 75 | 76 | if ($key === 'item') { 77 | if (isset($value['@key'])) { 78 | $data[(string)$value['@key']] = $value['#']; 79 | } elseif (isset($data['item'])) { 80 | $tmp = $data['item']; 81 | unset($data['item']); 82 | $data[] = $tmp; 83 | $data[] = $value; 84 | } 85 | } elseif (array_key_exists($key, $data)) { 86 | if ((false === is_array($data[$key])) || (false === isset($data[$key][0]))) { 87 | $data[$key] = array($data[$key]); 88 | } 89 | $data[$key][] = $value; 90 | } else { 91 | $data[$key] = $value; 92 | } 93 | } 94 | 95 | return $data; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FOSRest 2 | ======= 3 | 4 | This library provides various tools to rapidly develop RESTful APIs: 5 | http://github.com/FriendsOfSymfony/FOSRest 6 | 7 | The main goals for now are to provide ways for decoding request bodies 8 | and request format negotiation (inspired by Apache mod_negotiation) 9 | 10 | [![Build Status](https://secure.travis-ci.org/FriendsOfSymfony/FOSRest.png)](http://travis-ci.org/FriendsOfSymfony/FOSRest) 11 | 12 | Deprecated in favor of https://github.com/willdurand/Negotiation and https://github.com/FriendsOfSymfony/FOSRestBundle 13 | -------------------------------------------------------------------------------- /Tests/Decoder/JsonToFormDecoderTest.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 FOS\Rest\Tests\Decoder; 13 | 14 | use FOS\Rest\Decoder\JsonToFormDecoder; 15 | 16 | /** 17 | * Tests the form like encoder 18 | * 19 | * @author Kévin Dunglas 20 | */ 21 | class JsonToFormDecoderTest extends \PHPUnit_Framework_TestCase 22 | { 23 | 24 | public function testDecode() 25 | { 26 | $data = array( 27 | 'arrayKey' => array( 28 | 'falseKey' => false, 29 | 'stringKey' => 'foo' 30 | ), 31 | 'falseKey' => false, 32 | 'trueKey' => true, 33 | 'intKey' => 69, 34 | 'floatKey' => 3.14, 35 | 'stringKey' => 'bar', 36 | ); 37 | $decoder = new JsonToFormDecoder(); 38 | $decoded = $decoder->decode(json_encode($data)); 39 | 40 | $this->assertTrue(is_array($decoded)); 41 | $this->assertTrue(is_array($decoded['arrayKey'])); 42 | $this->assertArrayNotHasKey('falseKey', $decoded['arrayKey']); 43 | $this->assertEquals('foo', $decoded['arrayKey']['stringKey']); 44 | $this->assertArrayNotHasKey('falseKey', $decoded); 45 | $this->assertEquals('1', $decoded['trueKey']); 46 | $this->assertEquals('69', $decoded['intKey']); 47 | $this->assertEquals('3.14', $decoded['floatKey']); 48 | $this->assertEquals('bar', $decoded['stringKey']); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /Tests/bootstrap.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 | $file = __DIR__.'/../vendor/autoload.php'; 13 | if (!file_exists($file)) { 14 | throw new RuntimeException('Install dependencies to run test suite.'); 15 | } 16 | 17 | $autoload = require_once $file; 18 | -------------------------------------------------------------------------------- /Util/Codes.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 FOS\Rest\Util; 13 | 14 | /** 15 | * List of HTTP response status codes. 16 | * 17 | * The list of codes is complete according to the 18 | * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry} 19 | * (last updated 2012-02-13). 20 | * 21 | * Unless otherwise noted, the status code is defined in RFC2616. 22 | * 23 | * Note: Symfony 2.4 will have the same constants in the Response class: 24 | * https://github.com/symfony/symfony/pull/8820 25 | * 26 | * @author Jordi Boggiano 27 | * @author Markus Lanthaler 28 | */ 29 | class Codes 30 | { 31 | const HTTP_CONTINUE = 100; 32 | const HTTP_SWITCHING_PROTOCOLS = 101; 33 | const HTTP_PROCESSING = 102; // RFC2518 34 | const HTTP_OK = 200; 35 | const HTTP_CREATED = 201; 36 | const HTTP_ACCEPTED = 202; 37 | const HTTP_NON_AUTHORITATIVE_INFORMATION = 203; 38 | const HTTP_NO_CONTENT = 204; 39 | const HTTP_RESET_CONTENT = 205; 40 | const HTTP_PARTIAL_CONTENT = 206; 41 | const HTTP_MULTI_STATUS = 207; // RFC4918 42 | const HTTP_ALREADY_REPORTED = 208; // RFC5842 43 | const HTTP_IM_USED = 226; // RFC3229 44 | const HTTP_MULTIPLE_CHOICES = 300; 45 | const HTTP_MOVED_PERMANENTLY = 301; 46 | const HTTP_FOUND = 302; 47 | const HTTP_SEE_OTHER = 303; 48 | const HTTP_NOT_MODIFIED = 304; 49 | const HTTP_USE_PROXY = 305; 50 | const HTTP_RESERVED = 306; 51 | const HTTP_TEMPORARY_REDIRECT = 307; 52 | const HTTP_PERMANENTLY_REDIRECT = 308; // RFC-reschke-http-status-308-07 53 | const HTTP_BAD_REQUEST = 400; 54 | const HTTP_UNAUTHORIZED = 401; 55 | const HTTP_PAYMENT_REQUIRED = 402; 56 | const HTTP_FORBIDDEN = 403; 57 | const HTTP_NOT_FOUND = 404; 58 | const HTTP_METHOD_NOT_ALLOWED = 405; 59 | const HTTP_NOT_ACCEPTABLE = 406; 60 | const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; 61 | const HTTP_REQUEST_TIMEOUT = 408; 62 | const HTTP_CONFLICT = 409; 63 | const HTTP_GONE = 410; 64 | const HTTP_LENGTH_REQUIRED = 411; 65 | const HTTP_PRECONDITION_FAILED = 412; 66 | const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; 67 | const HTTP_REQUEST_URI_TOO_LONG = 414; 68 | const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; 69 | const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; 70 | const HTTP_EXPECTATION_FAILED = 417; 71 | const HTTP_I_AM_A_TEAPOT = 418; // RFC2324 72 | const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918 73 | const HTTP_LOCKED = 423; // RFC4918 74 | const HTTP_FAILED_DEPENDENCY = 424; // RFC4918 75 | const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817 76 | const HTTP_UPGRADE_REQUIRED = 426; // RFC2817 77 | const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585 78 | const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585 79 | const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585 80 | const HTTP_INTERNAL_SERVER_ERROR = 500; 81 | const HTTP_NOT_IMPLEMENTED = 501; 82 | const HTTP_BAD_GATEWAY = 502; 83 | const HTTP_SERVICE_UNAVAILABLE = 503; 84 | const HTTP_GATEWAY_TIMEOUT = 504; 85 | const HTTP_VERSION_NOT_SUPPORTED = 505; 86 | const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295 87 | const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918 88 | const HTTP_LOOP_DETECTED = 508; // RFC5842 89 | const HTTP_NOT_EXTENDED = 510; // RFC2774 90 | const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585 91 | } 92 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "friendsofsymfony/rest", 3 | "type": "library", 4 | "description": "This library provides various tools to develop RESTful API's", 5 | "keywords": ["rest"], 6 | "homepage": "http://friendsofsymfony.github.com", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Lukas Kahwe Smith", 11 | "email": "smith@pooteeweet.org" 12 | }, 13 | { 14 | "name": "FriendsOfSymfony Community", 15 | "homepage": "https://github.com/FriendsOfSymfony/FOSRest/contributors" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=5.3.3" 20 | }, 21 | "autoload": { 22 | "psr-0": { "FOS\\Rest": "" } 23 | }, 24 | "target-dir": "FOS/Rest", 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.0-dev" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ./Tests 8 | 9 | 10 | 11 | 12 | 13 | ./ 14 | 15 | ./Tests 16 | ./vendor 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------