├── LICENSE ├── README.md ├── composer.json └── jsonAPI ├── JsonApiMiddleware.php └── JsonApiView.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jonathan Tavares 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slim-jsonAPI 2 | [![Latest Stable Version](https://poser.pugx.org/entomb/slim-json-api/v/stable.png)](https://packagist.org/packages/entomb/slim-json-api) 3 | [![Total Downloads](https://poser.pugx.org/entomb/slim-json-api/downloads.png)](https://packagist.org/packages/entomb/slim-json-api) 4 | 5 | This is an extension to the [SLIM framework](https://github.com/codeguy/Slim) to implement json API's with great ease. 6 | 7 | ## Installation 8 | Using composer you can add use this as your composer.json 9 | 10 | ```json 11 | { 12 | "require": { 13 | "slim/slim": "2.3.*", 14 | "entomb/slim-json-api": "dev-master" 15 | } 16 | } 17 | 18 | ``` 19 | 20 | ## Usage 21 | To include the middleware and view you just have to load them using the default _Slim_ way. 22 | Read more about Slim Here (https://github.com/codeguy/Slim#getting-started) 23 | 24 | ```php 25 | require 'vendor/autoload.php'; 26 | 27 | $app = new \Slim\Slim(); 28 | 29 | $app->view(new \JsonApiView()); 30 | $app->add(new \JsonApiMiddleware()); 31 | ``` 32 | 33 | ### .htaccess sample 34 | Here's an .htaccess sample for simple RESTful API's 35 | ``` 36 | RewriteEngine On 37 | RewriteCond %{REQUEST_FILENAME} !-f 38 | RewriteRule ^ index.php [QSA,L] 39 | ``` 40 | 41 | ### example method 42 | all your requests will be returning a JSON output. 43 | the usage will be `$app->render( (int)$HTTP_CODE, (array)$DATA);` 44 | 45 | #### example Code 46 | ```php 47 | 48 | $app->get('/', function() use ($app) { 49 | $app->render(200,array( 50 | 'msg' => 'welcome to my API!', 51 | )); 52 | }); 53 | 54 | ``` 55 | 56 | 57 | #### example output 58 | ```json 59 | { 60 | "msg":"welcome to my API!", 61 | "error":false, 62 | "status":200 63 | } 64 | 65 | ``` 66 | 67 | ## Errors 68 | To display an error just set the `error => true` in your data array. 69 | All requests will have an `error` param that defaults to false. 70 | 71 | ```php 72 | 73 | $app->get('/user/:id', function($id) use ($app) { 74 | 75 | //your code here 76 | 77 | $app->render(404,array( 78 | 'error' => TRUE, 79 | 'msg' => 'user not found', 80 | )); 81 | }); 82 | 83 | ``` 84 | ```json 85 | { 86 | "msg":"user not found", 87 | "error":true, 88 | "status":404 89 | } 90 | 91 | ``` 92 | 93 | You can optionally throw exceptions, the middleware will catch all exceptions and display error messages. 94 | 95 | ```php 96 | 97 | $app->get('/user/:id', function($id) use ($app) { 98 | 99 | //your code here 100 | 101 | if(...){ 102 | throw new Exception("Something wrong with your request!"); 103 | } 104 | }); 105 | 106 | ``` 107 | ```json 108 | { 109 | "error": true, 110 | "msg": "ERROR: Something wrong with your request!", 111 | "status": 500 112 | } 113 | 114 | ``` 115 | 116 | ## Embedding response data and metadata in separate containers 117 | It is possible to separate response metadata and business information in separate containers. 118 | 119 | #### To make it possible just init JsonApiView with containers names 120 | ```php 121 | require 'vendor/autoload.php'; 122 | 123 | $app = new \Slim\Slim(); 124 | 125 | $app->view(new \JsonApiView("data", "meta")); 126 | $app->add(new \JsonApiMiddleware()); 127 | ``` 128 | 129 | #### Response 130 | ```json 131 | { 132 | "data":{ 133 | "msg":"welcome to my API!" 134 | }, 135 | "meta":{ 136 | "error":false, 137 | "status":200 138 | } 139 | } 140 | ``` 141 | 142 | 143 | ## routing specific requests to the API 144 | If your site is using regular HTML responses and you just want to expose an API point on a specific route, 145 | you can use Slim router middlewares to define this. 146 | 147 | ```php 148 | function APIrequest(){ 149 | $app = \Slim\Slim::getInstance(); 150 | $app->view(new \JsonApiView()); 151 | $app->add(new \JsonApiMiddleware()); 152 | } 153 | 154 | 155 | $app->get('/home',function() use($app){ 156 | //regular html response 157 | $app->render("template.tpl"); 158 | }); 159 | 160 | $app->get('/api','APIrequest',function() use($app){ 161 | //this request will have full json responses 162 | 163 | $app->render(200,array( 164 | 'msg' => 'welcome to my API!', 165 | )); 166 | }); 167 | ``` 168 | 169 | 170 | ## middleware 171 | The middleware will set some static routes for default requests. 172 | **if you dont want to use it**, you can copy its content code into your bootstrap file. 173 | 174 | ***IMPORTANT: remember to use `$app->config('debug', false);` or errors will still be printed in HTML*** 175 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "entomb/slim-json-api", 3 | "type": "library", 4 | "version": "1.2.3", 5 | "description": "Slim extension to implement fast JSON API's", 6 | "keywords": ["slim","json","API","view", "middleware"], 7 | "homepage": "https://github.com/entomb/slim-json-api", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Jonathan Tavares", 12 | "email": "the.entomb@gmail.com", 13 | "homepage": "https://github.com/entomb", 14 | "role": "Developer" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.3.0" 19 | }, 20 | "autoload": { 21 | "classmap": ["jsonAPI/"] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /jsonAPI/JsonApiMiddleware.php: -------------------------------------------------------------------------------- 1 | 8 | * @license GNU General Public License, version 3 9 | * @filesource 10 | * 11 | * 12 | */ 13 | 14 | /** 15 | * JsonApiMiddleware - Middleware that sets a bunch of static routes for easy bootstrapping of json API's 16 | * 17 | * @package Slim 18 | * @subpackage View 19 | * @author Jonathan Tavares 20 | * @license GNU General Public License, version 3 21 | * @filesource 22 | */ 23 | class JsonApiMiddleware extends \Slim\Middleware { 24 | 25 | 26 | /** 27 | * Sets a buch of static API calls 28 | * 29 | */ 30 | function __construct(){ 31 | 32 | $app = \Slim\Slim::getInstance(); 33 | $app->config('debug', false); 34 | 35 | // Mirrors the API request 36 | $app->get('/return', function() use ($app) { 37 | 38 | $app->render(200,array( 39 | 'method' => $app->request()->getMethod(), 40 | 'name' => $app->request()->get('name'), 41 | 'headers' => $app->request()->headers(), 42 | 'params' => $app->request()->params(), 43 | )); 44 | }); 45 | 46 | // Generic error handler 47 | $app->error(function (Exception $e) use ($app) { 48 | 49 | if ($e->getCode()) { 50 | $errorCode = $e->getCode(); 51 | } else { 52 | $errorCode = 500; 53 | } 54 | 55 | // Log error with the same message 56 | $message = \JsonApiMiddleware::_errorType($e->getCode()) .": ". $e->getMessage(); 57 | $app->getLog()->error($message . ' in ' . $e->getFile() . ' at line ' . $e->getLine()); 58 | 59 | $app->render($errorCode,array( 60 | 'msg' => $message, 61 | )); 62 | }); 63 | 64 | // Not found handler (invalid routes, invalid method types) 65 | $app->notFound(function() use ($app) { 66 | $app->render(404,array( 67 | 'msg' => 'Invalid route', 68 | )); 69 | }); 70 | 71 | // Handle Empty response body 72 | $app->hook('slim.after.router', function () use ($app) { 73 | //Fix sugested by: https://github.com/bdpsoft 74 | //Will allow download request to flow 75 | if($app->response()->header('Content-Type')==='application/octet-stream'){ 76 | return; 77 | } 78 | 79 | if (strlen($app->response()->body()) == 0) { 80 | $app->render(500,array( 81 | 'msg' => 'Empty response', 82 | )); 83 | } 84 | }); 85 | 86 | } 87 | 88 | /** 89 | * Call next 90 | */ 91 | function call(){ 92 | return $this->next->call(); 93 | } 94 | 95 | static function _errorType($type=1){ 96 | switch($type) 97 | { 98 | default: 99 | case E_ERROR: // 1 // 100 | return 'ERROR'; 101 | case E_WARNING: // 2 // 102 | return 'WARNING'; 103 | case E_PARSE: // 4 // 104 | return 'PARSE'; 105 | case E_NOTICE: // 8 // 106 | return 'NOTICE'; 107 | case E_CORE_ERROR: // 16 // 108 | return 'CORE_ERROR'; 109 | case E_CORE_WARNING: // 32 // 110 | return 'CORE_WARNING'; 111 | case E_CORE_ERROR: // 64 // 112 | return 'COMPILE_ERROR'; 113 | case E_CORE_WARNING: // 128 // 114 | return 'COMPILE_WARNING'; 115 | case E_USER_ERROR: // 256 // 116 | return 'USER_ERROR'; 117 | case E_USER_WARNING: // 512 // 118 | return 'USER_WARNING'; 119 | case E_USER_NOTICE: // 1024 // 120 | return 'USER_NOTICE'; 121 | case E_STRICT: // 2048 // 122 | return 'STRICT'; 123 | case E_RECOVERABLE_ERROR: // 4096 // 124 | return 'RECOVERABLE_ERROR'; 125 | case E_DEPRECATED: // 8192 // 126 | return 'DEPRECATED'; 127 | case E_USER_DEPRECATED: // 16384 // 128 | return 'USER_DEPRECATED'; 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /jsonAPI/JsonApiView.php: -------------------------------------------------------------------------------- 1 | 8 | * @license GNU General Public License, version 3 9 | * @filesource 10 | * 11 | * 12 | */ 13 | 14 | 15 | /** 16 | * JsonApiView - view wrapper for json responses (with error code). 17 | * 18 | * @package Slim 19 | * @subpackage View 20 | * @author Jonathan Tavares 21 | * @license GNU General Public License, version 3 22 | * @filesource 23 | */ 24 | class JsonApiView extends \Slim\View { 25 | 26 | /** 27 | * Bitmask consisting of JSON_HEX_QUOT, 28 | * JSON_HEX_TAG, 29 | * JSON_HEX_AMP, 30 | * JSON_HEX_APOS, 31 | * JSON_NUMERIC_CHECK, 32 | * JSON_PRETTY_PRINT, 33 | * JSON_UNESCAPED_SLASHES, 34 | * JSON_FORCE_OBJECT, 35 | * JSON_UNESCAPED_UNICODE. 36 | * The behaviour of these constants is described on 37 | * the JSON constants page. 38 | * @var int 39 | */ 40 | public $encodingOptions = 0; 41 | 42 | /** 43 | * Content-Type sent through the HTTP header. 44 | * Default is set to "application/json", 45 | * append ";charset=UTF-8" to force the charset 46 | * @var string 47 | */ 48 | public $contentType = 'application/json'; 49 | 50 | /** 51 | * 52 | * @var string 53 | */ 54 | private $dataWraper; 55 | 56 | /** 57 | * 58 | * @var string 59 | */ 60 | private $metaWrapper; 61 | 62 | /** 63 | * 64 | * @var bool 65 | */ 66 | private $dataOnly = false; 67 | 68 | 69 | /** 70 | * Construct JsonApiView instance 71 | * @param type $dataWrapper (optional) Wrapper for data in response 72 | * @param type $metaWrapper (optional) Wrapper for metadata in response 73 | */ 74 | public function __construct($dataWrapper = NULL, $metaWrapper = NULL) { 75 | parent::__construct(); 76 | $this->dataWraper = $dataWrapper; 77 | $this->metaWrapper = $metaWrapper; 78 | } 79 | 80 | public function render($status = 200, $data = NULL) { 81 | $app = \Slim\Slim::getInstance(); 82 | 83 | $status = intval($status); 84 | 85 | if ($this->dataWraper) { 86 | $response[$this->dataWraper] = $this->all(); 87 | } else { 88 | $response = $this->all(); 89 | } 90 | 91 | if (! $this->dataOnly) { 92 | //append error bool 93 | if ($status<400) { 94 | if ($this->metaWrapper) { 95 | $response[$this->metaWrapper]['error'] = false; 96 | } else { 97 | $response['error'] = false; 98 | } 99 | } else { 100 | if ($this->metaWrapper) { 101 | $response[$this->metaWrapper]['error'] = true; 102 | } else { 103 | $response['error'] = true; 104 | } 105 | } 106 | 107 | //append status code 108 | if ($this->metaWrapper) { 109 | $response[$this->metaWrapper]['status'] = $status; 110 | } else { 111 | $response['status'] = $status; 112 | } 113 | 114 | //add flash messages 115 | if (isset($this->data->flash) && is_object($this->data->flash)) { 116 | $flash = $this->data->flash->getMessages(); 117 | if ($this->dataWraper) { 118 | unset($response[$this->dataWraper]['flash']); 119 | } else { 120 | unset($response['flash']); 121 | } 122 | if (count($flash)) { 123 | if ($this->metaWrapper) { 124 | $response[$this->metaWrapper]['flash'] = $flash; 125 | } else { 126 | $response['flash'] = $flash; 127 | } 128 | } 129 | } 130 | } else { 131 | unset($response['flash'], $response['status'], $response['error']); 132 | } 133 | 134 | $app->response()->status($status); 135 | $app->response()->header('Content-Type', $this->contentType); 136 | 137 | $jsonp_callback = $app->request->get('callback', null); 138 | 139 | if($jsonp_callback !== null){ 140 | $app->response()->body($jsonp_callback.'('.json_encode($response, $this->encodingOptions).')'); 141 | } else { 142 | $app->response()->body(json_encode($response, $this->encodingOptions)); 143 | } 144 | 145 | $app->stop(); 146 | } 147 | 148 | /** 149 | * set whether to return only the data 150 | * 151 | * @param bool $dataOnly 152 | */ 153 | public function dataOnly($dataOnly = true) 154 | { 155 | $this->dataOnly = $dataOnly; 156 | } 157 | } 158 | --------------------------------------------------------------------------------