├── .gitignore ├── .codeclimate.yml ├── documentation ├── resources │ ├── sort.png │ ├── footer.png │ ├── resize.png │ ├── collapsed.png │ ├── inherit.png │ ├── tree-last.png │ ├── tree-cleaner.png │ ├── tree-hasnext.png │ ├── tree-vertical.png │ └── style.css ├── elementlist.js ├── package-CodeIgniter.html ├── package-CodeIgniter.Libraries.html ├── 404.html ├── package-None.html ├── package-CodeIgniter.Rest.html ├── index.html ├── class-Rest_server.html ├── source-class-Rest_server.html ├── class-Welcome.html ├── source-class-Welcome.html ├── class-Example.html ├── source-class-Example.html └── class-Key.html ├── application ├── libraries │ ├── jwt │ │ ├── ExpiredException.php │ │ ├── BeforeValidException.php │ │ ├── SignatureInvalidException.php │ │ └── JWT.php │ ├── index.html │ └── Format.php ├── config │ ├── index.html │ └── routes.php ├── language │ ├── index.html │ ├── bulgarian │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── dutch │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── english │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── french │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── german │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── indonesia │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── italian │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── romanian │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── spanish │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── turkish │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── serbian_cyr │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── serbian_lat │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── traditional-chinese │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── portuguese-brazilian │ │ ├── index.html │ │ └── rest_controller_lang.php │ ├── simplified-chinese │ │ ├── index.html │ │ └── rest_controller_lang.php │ └── greek │ │ └── rest_controller_lang.php ├── views │ ├── index.html │ ├── welcome_message.php │ └── rest_server.php └── controllers │ ├── index.html │ ├── api │ ├── index.html │ ├── Jwt.php │ ├── Example.php │ └── Key.php │ ├── Rest_server.php │ └── Welcome.php ├── composer.json ├── LICENSE ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vendor 3 | .idea -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | exclude_paths: 2 | - "documentation/" 3 | - "application/language/" -------------------------------------------------------------------------------- /documentation/resources/sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/sort.png -------------------------------------------------------------------------------- /documentation/resources/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/footer.png -------------------------------------------------------------------------------- /documentation/resources/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/resize.png -------------------------------------------------------------------------------- /documentation/resources/collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/collapsed.png -------------------------------------------------------------------------------- /documentation/resources/inherit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/inherit.png -------------------------------------------------------------------------------- /documentation/resources/tree-last.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/tree-last.png -------------------------------------------------------------------------------- /documentation/resources/tree-cleaner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/tree-cleaner.png -------------------------------------------------------------------------------- /documentation/resources/tree-hasnext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/tree-hasnext.png -------------------------------------------------------------------------------- /documentation/resources/tree-vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiamk/codeigniter-restserver/HEAD/documentation/resources/tree-vertical.png -------------------------------------------------------------------------------- /application/libraries/jwt/ExpiredException.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/controllers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/libraries/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/controllers/api/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/bulgarian/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/dutch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/english/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/french/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/german/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/indonesia/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/italian/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/romanian/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/spanish/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/turkish/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/serbian_cyr/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/serbian_lat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/traditional-chinese/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/portuguese-brazilian/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/language/simplified-chinese/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/controllers/Rest_server.php: -------------------------------------------------------------------------------- 1 | load->helper('url'); 10 | 11 | $this->load->view('rest_server'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "REST Server for the CodeIgniter framework", 3 | "name": "chriskacerguis/codeigniter-restserver", 4 | "type": "project", 5 | "homepage": "https://github.com/chriskacerguis/codeigniter-restserver", 6 | "authors": [ 7 | { 8 | "name": "Chris Kacerguis", 9 | "role": "Developer" 10 | } 11 | ], 12 | "license": "MIT", 13 | "support": { 14 | "source": "https://github.com/chriskacerguis/codeigniter-restserver" 15 | } 16 | } -------------------------------------------------------------------------------- /application/controllers/Welcome.php: -------------------------------------------------------------------------------- 1 | 19 | * @see https://codeigniter.com/user_guide/general/urls.html 20 | */ 21 | public function index() 22 | { 23 | $this->load->helper('url'); 24 | 25 | $this->load->view('welcome_message'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /application/language/simplified-chinese/rest_controller_lang.php: -------------------------------------------------------------------------------- 1 | my_controller/index 50 | | my-controller/my-method -> my_controller/my_method 51 | */ 52 | $route['default_controller'] = 'welcome'; 53 | $route['404_override'] = ''; 54 | $route['translate_uri_dashes'] = TRUE; 55 | 56 | /* 57 | | ------------------------------------------------------------------------- 58 | | Sample REST API Routes 59 | | ------------------------------------------------------------------------- 60 | */ 61 | $route['api/example/users/(:num)'] = 'api/example/users/id/$1'; // Example 4 62 | $route['api/example/users/(:num)(\.)([a-zA-Z0-9_-]+)(.*)'] = 'api/example/users/id/$1/format/$3$4'; // Example 8 63 | -------------------------------------------------------------------------------- /documentation/package-CodeIgniter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Package CodeIgniter 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 56 |
57 | 58 |
59 | 60 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /documentation/package-CodeIgniter.Libraries.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Package CodeIgniter\Libraries 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 61 |
62 | 63 |
64 | 65 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog: 2 | =========== 3 | 4 | ### UNRELEASED 5 | * Added support for CodeIgniter controller's index methods (index_GET, index_POST...) 6 | * Added exceptions handling when the method could not be found 7 | 8 | ### 2.7.2 9 | 10 | * Added $this->query() in which query parameters can now be obtained regardless of whether a GET request is sent or not 11 | * Added doc comments added to functions 12 | * Added HTTP status constants e.g. REST_Controller::HTTP_OK 13 | * Added new CSV formatting function 14 | * Fixed numerous bug fixes 15 | * Updated API calls limit can be based on API key, routed url or method name 16 | * Updated documentation 17 | * Updated examples (thanks @ivantcholakov and @lagaisse) 18 | * Updated many functions by re-writing (thanks @softwarespot) 19 | * Updated performance increase 20 | 21 | ### 2.7.0 22 | 23 | * Added Blacklist IP option 24 | * Added controller based access controls 25 | * Added support for OPTIONS, PATCH, and HEAD (from boh1996) 26 | * Added logging of the time it takes for a request (rtime column in DB) 27 | * Changed DB schemas to use InnoDB, not MyISAM 28 | * Updated Readme to reflect new developer (Chris Kacerguis) 29 | 30 | ### 2.6.2 31 | 32 | * Update CodeIgniter files to 2.1.3 33 | * Fixed issue #165 34 | 35 | ### 2.6.1 36 | 37 | * Update CodeIgniter files to 2.1.2 38 | * Log Table support for IPv6 & NULL parameters 39 | * Abstract out the processes of firing a controller method within _remap() to an separate method 40 | * Moved GET, POST, PUT, and DELETE parsing to separate methods, allowing them to be overridden as needed 41 | * Small bug-fix for a PHP 5.3 strlen error 42 | * Fixed some PHP 5.4 warnings 43 | * Fix for bug in Format.php's to_html() which failed to detect if $data was really a multidimensional array. 44 | * Fix for empty node on XML output format, for false = 0, true = 1. 45 | 46 | ### 2.6.0 47 | 48 | * Added loads of PHPDoc comments. 49 | * Response where method doesn't exist is now "HTTP 405 Method Not Allowed", not "HTTP 404 Not Found". 50 | * Compatible with PHP 5.4. 51 | * Added support for gzip compression. 52 | * Fix the apache\_request\_header function with CGI. 53 | * Fixed up correctly .foo extensions to work when get arguments provided. 54 | * Allows method emulation via X-HTTP-Method-Override 55 | * Support for Backbone.emulateHTTP improved. 56 | * Combine both URI segment and GET params instead of using one or the other 57 | * Separate each piece of the WWW-Authenticate header for digest requests with a comma. 58 | * Added IP white-list option. 59 | 60 | ### 2.5 61 | 62 | * Instead of just seeing item, item, item, the singular version of the base-node will be used if possible. [Example](http://d.pr/RS46). 63 | * Re-factored to use the Format library, which will soon be merged with CodeIgniter. 64 | * Fixed Limit bug (limit of 5 would allow 6 requests). 65 | * Added logging for invalid API key requests. 66 | * Changed serialize to serialized. 67 | -------------------------------------------------------------------------------- /documentation/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Page not found 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 67 |
68 | 69 |
70 | 71 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /documentation/package-None.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | No package 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 63 |
64 | 65 |
66 | 67 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /documentation/package-CodeIgniter.Rest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Package CodeIgniter\Rest 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 62 |
63 | 64 |
65 | 66 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /documentation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overview 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 65 |
66 | 67 |
68 | 69 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /application/views/welcome_message.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | Welcome to CodeIgniter 10 | 11 | 70 | 71 | 72 | 73 |
74 |

Welcome to CodeIgniter!

75 | 76 |
77 | 78 |

REST Server Tests

79 | 80 | 81 |

REST Server Documentation

82 | 83 | 84 |

The page you are looking at is being generated dynamically by CodeIgniter.

85 | 86 |

If you would like to edit this page you'll find it located at:

87 | application/views/welcome_message.php 88 | 89 |

The corresponding controller for this page is found at:

90 | application/controllers/Welcome.php 91 | 92 | 93 |

If you are exploring CodeIgniter for the very first time, you should start by reading the User Guide.

94 | 95 |
96 | 97 | 98 |
99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /documentation/class-Rest_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Class Rest_server 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 63 |
64 | 65 |
66 | 67 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /documentation/source-class-Rest_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File controllers/Rest_server.php 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 67 |
68 | 69 |
70 | 71 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /documentation/class-Welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Class Welcome 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 63 |
64 | 65 |
66 | 67 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /application/controllers/api/Jwt.php: -------------------------------------------------------------------------------- 1 | 'password', 11 | 'user' => 'password2', 12 | 'demo' => 'password3', 13 | ]; 14 | 15 | public function __construct() 16 | { 17 | parent::__construct(); 18 | } 19 | 20 | //User JWT authentication to get the toekn 21 | public function token_post() 22 | { 23 | $this->load->library('form_validation'); 24 | 25 | $this->form_validation->set_data([ 26 | 'username' => $this->post('username'), 27 | 'password' => $this->post('password'), 28 | ]); 29 | 30 | $this->form_validation->set_rules('username', 'Username', 'required'); 31 | $this->form_validation->set_rules('password', 'Password', 'required'); 32 | 33 | if ($this->form_validation->run() == TRUE) 34 | { 35 | if ($this->login($this->post('username'), $this->post('password'))) 36 | { 37 | $token['username'] = $this->post('username'); 38 | $date = new DateTime(); 39 | $token['iat'] = $date->getTimestamp(); 40 | $token['exp'] = $date->getTimestamp() + $this->config->item('jwt_token_expire'); 41 | $output_data['token'] = $this->jwt_encode($token); 42 | $this->response($output_data, REST_Controller::HTTP_OK); 43 | } 44 | else 45 | { 46 | $output_data[$this->config->item('rest_status_field_name')] = "invalid_credentials"; 47 | $output_data[$this->config->item('rest_message_field_name')] = "Invalid username or password!"; 48 | $this->response($output_data, REST_Controller::HTTP_UNAUTHORIZED); 49 | } 50 | } 51 | else 52 | { 53 | $output_data[$this->config->item('rest_status_field_name')] = "empty_fields"; 54 | $output_data[$this->config->item('rest_message_field_name')] = $this->form_validation->error_array(); 55 | 56 | $this->response($output_data, REST_Controller::HTTP_UNPROCESSABLE_ENTITY); 57 | } 58 | } 59 | 60 | // Refresh the token with new expirey time 61 | public function token_refresh_get() 62 | { 63 | try 64 | { 65 | $decoded = $this->jwt_decode($this->jwt_token()); 66 | 67 | if($this->username_check($decoded['username']) == FALSE) 68 | { 69 | $output_data[$this->config->item('rest_status_field_name')] = "invalid_user"; 70 | $output_data[$this->config->item('rest_message_field_name')] = "The token user id is not exist in the system!"; 71 | $this->response($output_data, REST_Controller::HTTP_UNAUTHORIZED); 72 | } 73 | 74 | $token['username'] = $decoded['username']; 75 | $date = new DateTime(); 76 | $token['iat'] = $date->getTimestamp(); 77 | $token['exp'] = $date->getTimestamp() + $this->config->item('jwt_token_expire'); 78 | $output_data['token'] = $this->jwt_encode($token); 79 | $this->response($output_data, REST_Controller::HTTP_OK); 80 | } 81 | catch (Exception $e) 82 | { 83 | $output_data[$this->config->item('rest_status_field_name')] = "invalid_token"; 84 | $output_data[$this->config->item('rest_message_field_name')] = $e->getMessage(); 85 | $this->response($output_data, REST_Controller::HTTP_UNAUTHORIZED); 86 | } 87 | } 88 | 89 | // JWT test endpoint, it shows the token information (need token authorization) 90 | public function token_info_get() 91 | { 92 | try 93 | { 94 | $output_data = $this->jwt_decode($this->jwt_token()); 95 | $this->response($output_data, REST_Controller::HTTP_OK); 96 | } 97 | catch (Exception $e) 98 | { 99 | $output_data[$this->config->item('rest_status_field_name')] = "invalid_token"; 100 | $output_data[$this->config->item('rest_message_field_name')] = $e->getMessage(); 101 | $this->response($output_data, REST_Controller::HTTP_UNAUTHORIZED); 102 | } 103 | } 104 | 105 | // Login model emulation, login funcation 106 | private function login($username, $password) 107 | { 108 | if(array_key_exists($username, $this->accounts) AND $this->accounts[$username] === $password) 109 | { 110 | return TRUE; 111 | } 112 | return FALSE; 113 | } 114 | 115 | // Login model emulation, check if user exist on database 116 | private function username_check($username) 117 | { 118 | if(array_key_exists($username, $this->accounts)) 119 | { 120 | return TRUE; 121 | } 122 | return FALSE; 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /application/controllers/api/Example.php: -------------------------------------------------------------------------------- 1 | methods['users_get']['limit'] = 500; // 500 requests per hour per user/key 30 | $this->methods['users_post']['limit'] = 100; // 100 requests per hour per user/key 31 | $this->methods['users_delete']['limit'] = 50; // 50 requests per hour per user/key 32 | } 33 | 34 | public function users_get() 35 | { 36 | // Users from a data store e.g. database 37 | $users = [ 38 | ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'], 39 | ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'], 40 | ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]], 41 | ]; 42 | 43 | $id = $this->get('id'); 44 | 45 | // If the id parameter doesn't exist return all the users 46 | 47 | if ($id === NULL) 48 | { 49 | // Check if the users data store contains users (in case the database result returns NULL) 50 | if ($users) 51 | { 52 | // Set the response and exit 53 | $this->response($users, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code 54 | } 55 | else 56 | { 57 | // Set the response and exit 58 | $this->response([ 59 | 'status' => FALSE, 60 | 'message' => 'No users were found' 61 | ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code 62 | } 63 | } 64 | 65 | // Find and return a single record for a particular user. 66 | 67 | $id = (int) $id; 68 | 69 | // Validate the id. 70 | if ($id <= 0) 71 | { 72 | // Invalid id, set the response and exit. 73 | $this->response(NULL, REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code 74 | } 75 | 76 | // Get the user from the array, using the id as key for retrieval. 77 | // Usually a model is to be used for this. 78 | 79 | $user = NULL; 80 | 81 | if (!empty($users)) 82 | { 83 | foreach ($users as $key => $value) 84 | { 85 | if (isset($value['id']) && $value['id'] === $id) 86 | { 87 | $user = $value; 88 | } 89 | } 90 | } 91 | 92 | if (!empty($user)) 93 | { 94 | $this->set_response($user, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code 95 | } 96 | else 97 | { 98 | $this->set_response([ 99 | 'status' => FALSE, 100 | 'message' => 'User could not be found' 101 | ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code 102 | } 103 | } 104 | 105 | public function users_post() 106 | { 107 | // $this->some_model->update_user( ... ); 108 | $message = [ 109 | 'id' => 100, // Automatically generated by the model 110 | 'name' => $this->post('name'), 111 | 'email' => $this->post('email'), 112 | 'message' => 'Added a resource' 113 | ]; 114 | 115 | $this->set_response($message, REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code 116 | } 117 | 118 | public function users_delete() 119 | { 120 | $id = (int) $this->get('id'); 121 | 122 | // Validate the id. 123 | if ($id <= 0) 124 | { 125 | // Set the response and exit 126 | $this->response(NULL, REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code 127 | } 128 | 129 | // $this->some_model->delete_something($id); 130 | $message = [ 131 | 'id' => $id, 132 | 'message' => 'Deleted the resource' 133 | ]; 134 | 135 | $this->set_response($message, REST_Controller::HTTP_NO_CONTENT); // NO_CONTENT (204) being the HTTP response code 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /documentation/source-class-Welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File controllers/Welcome.php 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 67 |
68 | 69 |
70 | 71 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /application/views/rest_server.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | REST Server Tests 10 | 11 | 70 | 71 | 72 | 73 |
74 |

REST Server Tests

75 | 76 |
77 | 78 |

Home

79 | 80 |

81 | See the article 82 | 83 | http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/ 84 | 85 |

86 | 87 |

88 | The master project repository is 89 | 90 | https://github.com/chriskacerguis/codeigniter-restserver 91 | 92 |

93 | 94 |

95 | Click on the links to check whether the REST server is working. 96 |

97 | 98 |
    99 |
  1. Users - defaulting to JSON
  2. 100 |
  3. Users - get it in CSV
  4. 101 |
  5. User #1 - defaulting to JSON (users/id/1)
  6. 102 |
  7. User #1 - defaulting to JSON (users/1)
  8. 103 |
  9. User #1 - get it in XML (users/id/1.xml)
  10. 104 |
  11. User #1 - get it in XML (users/id/1/format/xml)
  12. 105 |
  13. User #1 - get it in XML (users/id/1?format=xml)
  14. 106 |
  15. User #1 - get it in XML (users/1.xml)
  16. 107 |
  17. Users - get it in JSON (AJAX request)
  18. 108 |
  19. Users - get it in HTML (users.html)
  20. 109 |
  21. Users - get it in HTML (users/format/html)
  22. 110 |
  23. Users - get it in HTML (users?format=html)
  24. 111 |
112 | 113 |
114 | 115 | 116 |
117 | 118 | 119 | 120 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeIgniter Rest Server with JWT Authentication 2 | 3 | [![Gitter chat](https://badges.gitter.im/chriskacerguis/codeigniter-restserver.png)](https://gitter.im/codeigniter-restserver/Lobby) 4 | 5 | A fully RESTful server implementation for CodeIgniter using one library, one 6 | config file and one controller. 7 | 8 | ## Requirements 9 | 10 | 1. PHP 5.4 or greater 11 | 2. CodeIgniter 3.0+ 12 | 13 | _Note: for 1.7.x support download v2.2 from Downloads tab_ 14 | 15 | ## Important Update on 4.0.0 16 | 17 | Please note that version 4.0.0 is in the works, and is considered a breaking change (per SemVer). As CI 3.1.0 now has native support for Composer, this library will be moving to be composer based. 18 | 19 | Take a look at the "development" branch to see what's up. 20 | 21 | ## Installation 22 | 23 | Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. To use `require_once` it at the top of your controllers to load it into the scope. Additionally, copy the **rest.php** file from **application/config** in your application's configuration directory. 24 | 25 | ## Handling Requests 26 | 27 | When your controller extends from `REST_Controller`, the method names will be appended with the HTTP method used to access the request. If you're making an HTTP `GET` call to `/books`, for instance, it would call a `Books#index_get()` method. 28 | 29 | This allows you to implement a RESTful interface easily: 30 | 31 | ```php 32 | class Books extends REST_Controller 33 | { 34 | public function index_get() 35 | { 36 | // Display all books 37 | } 38 | 39 | public function index_post() 40 | { 41 | // Create a new book 42 | } 43 | } 44 | ``` 45 | 46 | `REST_Controller` also supports `PUT` and `DELETE` methods, allowing you to support a truly RESTful interface. 47 | 48 | 49 | Accessing parameters is also easy. Simply use the name of the HTTP verb as a method: 50 | 51 | ```php 52 | $this->get('blah'); // GET param 53 | $this->post('blah'); // POST param 54 | $this->put('blah'); // PUT param 55 | ``` 56 | 57 | The HTTP spec for DELETE requests precludes the use of parameters. For delete requests, you can add items to the URL 58 | 59 | ```php 60 | public function index_delete($id) 61 | { 62 | $this->response([ 63 | 'returned from delete:' => $id, 64 | ]); 65 | } 66 | ``` 67 | 68 | If query parameters are passed via the URL, regardless of whether it's a GET request, can be obtained by the query method: 69 | 70 | ```php 71 | $this->query('blah'); // Query param 72 | ``` 73 | 74 | ## Content Types 75 | 76 | `REST_Controller` supports a bunch of different request/response formats, including XML, JSON and serialised PHP. By default, the class will check the URL and look for a format either as an extension or as a separate segment. 77 | 78 | This means your URLs can look like this: 79 | ``` 80 | http://example.com/books.json 81 | http://example.com/books?format=json 82 | ``` 83 | 84 | This can be flaky with URI segments, so the recommend approach is using the HTTP `Accept` header: 85 | 86 | ```bash 87 | $ curl -H "Accept: application/json" http://example.com 88 | ``` 89 | 90 | Any responses you make from the class (see [responses](#responses) for more on this) will be serialised in the designated format. 91 | 92 | ## Responses 93 | 94 | The class provides a `response()` method that allows you to return data in the user's requested response format. 95 | 96 | Returning any object / array / string / whatever is easy: 97 | 98 | ```php 99 | public function index_get() 100 | { 101 | $this->response($this->db->get('books')->result()); 102 | } 103 | ``` 104 | 105 | This will automatically return an `HTTP 200 OK` response. You can specify the status code in the second parameter: 106 | 107 | ```php 108 | public function index_post() 109 | { 110 | // ...create new book 111 | $this->response($book, 201); // Send an HTTP 201 Created 112 | } 113 | ``` 114 | 115 | If you don't specify a response code, and the data you respond with `== FALSE` (an empty array or string, for instance), the response code will automatically be set to `404 Not Found`: 116 | 117 | ```php 118 | $this->response([]); // HTTP 404 Not Found 119 | ``` 120 | 121 | ## Multilingual Support 122 | 123 | If your application uses language files to support multiple locales, `REST_Controller` will automatically parse the HTTP `Accept-Language` header and provide the language(s) in your actions. This information can be found in the `$this->response->lang` object: 124 | 125 | ```php 126 | public function __construct() 127 | { 128 | parent::__construct(); 129 | 130 | if (is_array($this->response->lang)) 131 | { 132 | $this->load->language('application', $this->response->lang[0]); 133 | } 134 | else 135 | { 136 | $this->load->language('application', $this->response->lang); 137 | } 138 | } 139 | ``` 140 | 141 | ## Authentication 142 | 143 | This class also provides rudimentary support for HTTP basic authentication and/or the securer HTTP digest access authentication. 144 | 145 | You can enable basic authentication by setting the `$config['rest_auth']` to `'basic'`. The `$config['rest_valid_logins']` directive can then be used to set the usernames and passwords able to log in to your system. The class will automatically send all the correct headers to trigger the authentication dialogue: 146 | 147 | ```php 148 | $config['rest_valid_logins'] = ['username' => 'password', 'other_person' => 'secure123']; 149 | ``` 150 | 151 | Enabling digest auth is similarly easy. Configure your desired logins in the config file like above, and set `$config['rest_auth']` to `'digest'`. The class will automatically send out the headers to enable digest auth. 152 | 153 | If you're tying this library into an AJAX endpoint where clients authenticate using PHP sessions then you may not like either of the digest nor basic authentication methods. In that case, you can tell the REST Library what PHP session variable to check for. If the variable exists, then the user is authorized. It will be up to your application to set that variable. You can define the variable in ``$config['auth_source']``. Then tell the library to use a php session variable by setting ``$config['rest_auth']`` to ``session``. 154 | 155 | All three methods of authentication can be secured further by using an IP white-list. If you enable `$config['rest_ip_whitelist_enabled']` in your config file, you can then set a list of allowed IPs. 156 | 157 | Any client connecting to your API will be checked against the white-listed IP array. If they're on the list, they'll be allowed access. If not, sorry, no can do hombre. The whitelist is a comma-separated string: 158 | 159 | ```php 160 | $config['rest_ip_whitelist'] = '123.456.789.0, 987.654.32.1'; 161 | ``` 162 | 163 | Your localhost IPs (`127.0.0.1` and `0.0.0.0`) are allowed by default. 164 | 165 | ## API Keys 166 | 167 | In addition to the authentication methods above, the `REST_Controller` class also supports the use of API keys. Enabling API keys is easy. Turn it on in your **config/rest.php** file: 168 | 169 | ```php 170 | $config['rest_enable_keys'] = TRUE; 171 | ``` 172 | 173 | You'll need to create a new database table to store and access the keys. `REST_Controller` will automatically assume you have a table that looks like this: 174 | 175 | ```sql 176 | CREATE TABLE `keys` ( 177 | `id` INT(11) NOT NULL AUTO_INCREMENT, 178 | `key` VARCHAR(40) NOT NULL, 179 | `level` INT(2) NOT NULL, 180 | `ignore_limits` TINYINT(1) NOT NULL DEFAULT '0', 181 | `date_created` INT(11) NOT NULL, 182 | PRIMARY KEY (`id`) 183 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 184 | ``` 185 | 186 | The class will look for an HTTP header with the API key on each request. An invalid or missing API key will result in an `HTTP 403 Forbidden`. 187 | 188 | By default, the HTTP will be `X-API-KEY`. This can be configured in **config/rest.php**. 189 | 190 | ```bash 191 | $ curl -X POST -H "X-API-KEY: some_key_here" http://example.com/books 192 | ``` 193 | 194 | ## Other Documentation / Tutorials 195 | 196 | * [NetTuts: Working with RESTful Services in CodeIgniter](http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/) 197 | 198 | ## Contributions 199 | 200 | This project was originally written by Phil Sturgeon, however his involvement has shifted 201 | as he is no longer using it. As of 2013/11/20 further development and support will be done by Chris Kacerguis. 202 | 203 | Pull Requests are the best way to fix bugs or add features. I know loads of you use this, so please 204 | contribute if you have improvements to be made and I'll keep releasing versions over time. 205 | 206 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/chriskacerguis/codeigniter-restserver/master/LICENSE) 207 | -------------------------------------------------------------------------------- /application/controllers/api/Key.php: -------------------------------------------------------------------------------- 1 | ['level' => 10, 'limit' => 10], 24 | 'index_delete' => ['level' => 10], 25 | 'level_post' => ['level' => 10], 26 | 'regenerate_post' => ['level' => 10], 27 | ]; 28 | 29 | /** 30 | * Insert a key into the database 31 | * 32 | * @access public 33 | * @return void 34 | */ 35 | public function index_put() 36 | { 37 | // Build a new key 38 | $key = $this->_generate_key(); 39 | 40 | // If no key level provided, provide a generic key 41 | $level = $this->put('level') ? $this->put('level') : 1; 42 | $ignore_limits = ctype_digit($this->put('ignore_limits')) ? (int) $this->put('ignore_limits') : 1; 43 | 44 | // Insert the new key 45 | if ($this->_insert_key($key, ['level' => $level, 'ignore_limits' => $ignore_limits])) 46 | { 47 | $this->response([ 48 | 'status' => TRUE, 49 | 'key' => $key 50 | ], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code 51 | } 52 | else 53 | { 54 | $this->response([ 55 | 'status' => FALSE, 56 | 'message' => 'Could not save the key' 57 | ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code 58 | } 59 | } 60 | 61 | /** 62 | * Remove a key from the database to stop it working 63 | * 64 | * @access public 65 | * @return void 66 | */ 67 | public function index_delete() 68 | { 69 | $key = $this->delete('key'); 70 | 71 | // Does this key exist? 72 | if (!$this->_key_exists($key)) 73 | { 74 | // It doesn't appear the key exists 75 | $this->response([ 76 | 'status' => FALSE, 77 | 'message' => 'Invalid API key' 78 | ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code 79 | } 80 | 81 | // Destroy it 82 | $this->_delete_key($key); 83 | 84 | // Respond that the key was destroyed 85 | $this->response([ 86 | 'status' => TRUE, 87 | 'message' => 'API key was deleted' 88 | ], REST_Controller::HTTP_NO_CONTENT); // NO_CONTENT (204) being the HTTP response code 89 | } 90 | 91 | /** 92 | * Change the level 93 | * 94 | * @access public 95 | * @return void 96 | */ 97 | public function level_post() 98 | { 99 | $key = $this->post('key'); 100 | $new_level = $this->post('level'); 101 | 102 | // Does this key exist? 103 | if (!$this->_key_exists($key)) 104 | { 105 | // It doesn't appear the key exists 106 | $this->response([ 107 | 'status' => FALSE, 108 | 'message' => 'Invalid API key' 109 | ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code 110 | } 111 | 112 | // Update the key level 113 | if ($this->_update_key($key, ['level' => $new_level])) 114 | { 115 | $this->response([ 116 | 'status' => TRUE, 117 | 'message' => 'API key was updated' 118 | ], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code 119 | } 120 | else 121 | { 122 | $this->response([ 123 | 'status' => FALSE, 124 | 'message' => 'Could not update the key level' 125 | ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code 126 | } 127 | } 128 | 129 | /** 130 | * Suspend a key 131 | * 132 | * @access public 133 | * @return void 134 | */ 135 | public function suspend_post() 136 | { 137 | $key = $this->post('key'); 138 | 139 | // Does this key exist? 140 | if (!$this->_key_exists($key)) 141 | { 142 | // It doesn't appear the key exists 143 | $this->response([ 144 | 'status' => FALSE, 145 | 'message' => 'Invalid API key' 146 | ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code 147 | } 148 | 149 | // Update the key level 150 | if ($this->_update_key($key, ['level' => 0])) 151 | { 152 | $this->response([ 153 | 'status' => TRUE, 154 | 'message' => 'Key was suspended' 155 | ], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code 156 | } 157 | else 158 | { 159 | $this->response([ 160 | 'status' => FALSE, 161 | 'message' => 'Could not suspend the user' 162 | ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code 163 | } 164 | } 165 | 166 | /** 167 | * Regenerate a key 168 | * 169 | * @access public 170 | * @return void 171 | */ 172 | public function regenerate_post() 173 | { 174 | $old_key = $this->post('key'); 175 | $key_details = $this->_get_key($old_key); 176 | 177 | // Does this key exist? 178 | if (!$key_details) 179 | { 180 | // It doesn't appear the key exists 181 | $this->response([ 182 | 'status' => FALSE, 183 | 'message' => 'Invalid API key' 184 | ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code 185 | } 186 | 187 | // Build a new key 188 | $new_key = $this->_generate_key(); 189 | 190 | // Insert the new key 191 | if ($this->_insert_key($new_key, ['level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits])) 192 | { 193 | // Suspend old key 194 | $this->_update_key($old_key, ['level' => 0]); 195 | 196 | $this->response([ 197 | 'status' => TRUE, 198 | 'key' => $new_key 199 | ], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code 200 | } 201 | else 202 | { 203 | $this->response([ 204 | 'status' => FALSE, 205 | 'message' => 'Could not save the key' 206 | ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code 207 | } 208 | } 209 | 210 | /* Helper Methods */ 211 | 212 | private function _generate_key() 213 | { 214 | do 215 | { 216 | // Generate a random salt 217 | $salt = base_convert(bin2hex($this->security->get_random_bytes(64)), 16, 36); 218 | 219 | // If an error occurred, then fall back to the previous method 220 | if ($salt === FALSE) 221 | { 222 | $salt = hash('sha256', time() . mt_rand()); 223 | } 224 | 225 | $new_key = substr($salt, 0, config_item('rest_key_length')); 226 | } 227 | while ($this->_key_exists($new_key)); 228 | 229 | return $new_key; 230 | } 231 | 232 | /* Private Data Methods */ 233 | 234 | private function _get_key($key) 235 | { 236 | return $this->rest->db 237 | ->where(config_item('rest_key_column'), $key) 238 | ->get(config_item('rest_keys_table')) 239 | ->row(); 240 | } 241 | 242 | private function _key_exists($key) 243 | { 244 | return $this->rest->db 245 | ->where(config_item('rest_key_column'), $key) 246 | ->count_all_results(config_item('rest_keys_table')) > 0; 247 | } 248 | 249 | private function _insert_key($key, $data) 250 | { 251 | $data[config_item('rest_key_column')] = $key; 252 | $data['date_created'] = function_exists('now') ? now() : time(); 253 | 254 | return $this->rest->db 255 | ->set($data) 256 | ->insert(config_item('rest_keys_table')); 257 | } 258 | 259 | private function _update_key($key, $data) 260 | { 261 | return $this->rest->db 262 | ->where(config_item('rest_key_column'), $key) 263 | ->update(config_item('rest_keys_table'), $data); 264 | } 265 | 266 | private function _delete_key($key) 267 | { 268 | return $this->rest->db 269 | ->where(config_item('rest_key_column'), $key) 270 | ->delete(config_item('rest_keys_table')); 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /documentation/resources/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 13px/1.5 Verdana, 'Geneva CE', lucida, sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | background: #ffffff; 6 | color: #333333; 7 | } 8 | 9 | h1, h2, h3, h4, caption { 10 | font-family: 'Trebuchet MS', 'Geneva CE', lucida, sans-serif; 11 | color: #053368; 12 | } 13 | 14 | h1 { 15 | color: #1e5eb6; 16 | font-size: 230%; 17 | font-weight: normal; 18 | margin: .3em 0; 19 | } 20 | 21 | h2 { 22 | color: #1e5eb6; 23 | font-size: 150%; 24 | font-weight: normal; 25 | margin: -.3em 0 .3em 0; 26 | } 27 | 28 | h3 { 29 | font-size: 1.6em; 30 | font-weight: normal; 31 | margin-bottom: 2px; 32 | } 33 | 34 | h4 { 35 | font-size: 100%; 36 | font-weight: bold; 37 | padding: 0; 38 | margin: 0; 39 | } 40 | 41 | caption { 42 | border: 1px solid #cccccc; 43 | background: #ecede5; 44 | font-weight: bold; 45 | font-size: 1.2em; 46 | padding: 3px 5px; 47 | text-align: left; 48 | margin-bottom: 0; 49 | } 50 | 51 | p { 52 | margin: .7em 0 1em; 53 | padding: 0; 54 | } 55 | 56 | hr { 57 | margin: 2em 0 1em; 58 | border: none; 59 | border-top: 1px solid #cccccc; 60 | height: 0; 61 | } 62 | 63 | a { 64 | color: #006aeb; 65 | padding: 3px 1px; 66 | text-decoration: none; 67 | } 68 | 69 | h1 a { 70 | color: #1e5eb6; 71 | } 72 | 73 | a:hover, a:active, a:focus, a:hover b, a:hover var { 74 | background-color: #006aeb; 75 | color: #ffffff !important; 76 | } 77 | 78 | code, var, pre { 79 | font-family: monospace; 80 | } 81 | 82 | var { 83 | font-weight: bold; 84 | font-style: normal; 85 | color: #ca8a04; 86 | } 87 | 88 | pre { 89 | margin: 0; 90 | } 91 | 92 | code a b { 93 | color: #000000; 94 | } 95 | 96 | .deprecated { 97 | text-decoration: line-through; 98 | opacity: .5; 99 | } 100 | 101 | .invalid { 102 | color: #e71818; 103 | } 104 | 105 | .hidden { 106 | display: none; 107 | } 108 | 109 | /* Left side */ 110 | #left { 111 | overflow: auto; 112 | width: 270px; 113 | height: 100%; 114 | position: fixed; 115 | } 116 | 117 | /* Menu */ 118 | #menu { 119 | padding: 10px; 120 | } 121 | 122 | #menu ul { 123 | list-style: none; 124 | padding: 0; 125 | margin: 0; 126 | } 127 | 128 | #menu ul ul { 129 | padding-left: 10px; 130 | } 131 | 132 | #menu li { 133 | white-space: nowrap; 134 | position: relative; 135 | } 136 | 137 | #menu a { 138 | display: block; 139 | padding: 0 2px; 140 | } 141 | 142 | #menu .active > a, #menu > span { 143 | color: #333333; 144 | background: none; 145 | font-weight: bold; 146 | } 147 | 148 | #menu .active > a.invalid { 149 | color: #e71818; 150 | } 151 | 152 | #menu .active > a:hover, #menu .active > a:active, #menu .active > a:focus { 153 | background-color: #006aeb; 154 | } 155 | 156 | #menu #groups span { 157 | position: absolute; 158 | top: 4px; 159 | right: 2px; 160 | cursor: pointer; 161 | display: block; 162 | width: 12px; 163 | height: 12px; 164 | background: url('collapsed.png') transparent 0 0 no-repeat; 165 | } 166 | 167 | #menu #groups span:hover { 168 | background-position: -12px 0; 169 | } 170 | 171 | #menu #groups span.collapsed { 172 | background-position: 0 -12px; 173 | } 174 | 175 | #menu #groups span.collapsed:hover { 176 | background-position: -12px -12px; 177 | } 178 | 179 | #menu #groups ul.collapsed { 180 | display: none; 181 | } 182 | 183 | /* Right side */ 184 | #right { 185 | overflow: auto; 186 | margin-left: 275px; 187 | height: 100%; 188 | position: relative; 189 | left: 0; 190 | right: 0; 191 | } 192 | 193 | #rightInner { 194 | max-width: 1000px; 195 | min-width: 350px; 196 | } 197 | 198 | /* Search */ 199 | #search { 200 | float: right; 201 | margin: 3px 8px; 202 | } 203 | 204 | #search input.text { 205 | padding: 3px 5px; 206 | width: 250px; 207 | } 208 | 209 | /* Autocomplete */ 210 | .ac_results { 211 | padding: 0; 212 | border: 1px solid #cccccc; 213 | background-color: #ffffff; 214 | overflow: hidden; 215 | z-index: 99999; 216 | } 217 | 218 | .ac_results ul { 219 | width: 100%; 220 | list-style-position: outside; 221 | list-style: none; 222 | padding: 0; 223 | margin: 0; 224 | } 225 | 226 | .ac_results li { 227 | margin: 0; 228 | padding: 2px 5px; 229 | cursor: default; 230 | display: block; 231 | font: 12px 'Trebuchet MS', 'Geneva CE', lucida, sans-serif; 232 | line-height: 16px; 233 | overflow: hidden; 234 | white-space: nowrap; 235 | } 236 | 237 | .ac_results li strong { 238 | color: #000000; 239 | } 240 | 241 | .ac_odd { 242 | background-color: #eeeeee; 243 | } 244 | 245 | .ac_over { 246 | background-color: #006aeb; 247 | color: #ffffff; 248 | } 249 | 250 | .ac_results li.ac_over strong { 251 | color: #ffffff; 252 | } 253 | 254 | /* Navigation */ 255 | #navigation { 256 | padding: 3px 8px; 257 | background-color: #f6f6f4; 258 | height: 26px; 259 | } 260 | 261 | #navigation ul { 262 | list-style: none; 263 | margin: 0 8px 4px 0; 264 | padding: 0; 265 | overflow: hidden; 266 | float: left; 267 | } 268 | 269 | #navigation ul + ul { 270 | border-left: 1px solid #000000; 271 | padding-left: 8px; 272 | } 273 | 274 | #navigation ul li { 275 | float: left; 276 | margin: 2px; 277 | padding: 0 3px; 278 | font-family: Verdana, 'Geneva CE', lucida, sans-serif; 279 | color: #808080; 280 | } 281 | 282 | #navigation ul li.active { 283 | background-color: #053368; 284 | color: #ffffff; 285 | font-weight: bold; 286 | } 287 | 288 | #navigation ul li a { 289 | color: #000000; 290 | font-weight: bold; 291 | padding: 0; 292 | } 293 | 294 | #navigation ul li span { 295 | float: left; 296 | padding: 0 3px; 297 | } 298 | 299 | #navigation ul li a:hover span, #navigation ul li a:active span, #navigation ul li a:focus span { 300 | background-color: #006aeb; 301 | } 302 | 303 | /* Content */ 304 | #content { 305 | clear: both; 306 | padding: 5px 15px; 307 | } 308 | 309 | .description pre { 310 | padding: .6em; 311 | background: #fcfcf7; 312 | } 313 | 314 | #content > .description { 315 | background: #ecede5; 316 | padding: 1px 8px; 317 | margin: 1.2em 0; 318 | } 319 | 320 | #content > .description pre { 321 | margin: .5em 0; 322 | } 323 | 324 | dl.tree { 325 | margin: 1.2em 0; 326 | } 327 | 328 | dl.tree dd { 329 | margin: 0; 330 | padding: 0; 331 | } 332 | 333 | .info { 334 | margin: 1.2em 0; 335 | } 336 | 337 | .summary { 338 | border: 1px solid #cccccc; 339 | border-collapse: collapse; 340 | font-size: 1em; 341 | width: 100%; 342 | margin: 1.2em 0 2.4em; 343 | } 344 | 345 | .summary caption { 346 | border-width: 1px 1px 0; 347 | } 348 | 349 | .summary caption.switchable { 350 | background: #ecede5 url('sort.png') no-repeat center right; 351 | cursor: pointer; 352 | } 353 | 354 | .summary td { 355 | border: 1px solid #cccccc; 356 | margin: 0; 357 | padding: 3px 10px; 358 | font-size: 1em; 359 | vertical-align: top; 360 | } 361 | 362 | .summary td:first-child { 363 | text-align: right; 364 | } 365 | 366 | .summary td hr { 367 | margin: 3px -10px; 368 | } 369 | 370 | #packages.summary td:first-child, #namespaces.summary td:first-child, .inherited.summary td:first-child, .used.summary td:first-child { 371 | text-align: left; 372 | } 373 | 374 | .summary tr:hover td { 375 | background: #f6f6f4; 376 | } 377 | 378 | .summary .description pre { 379 | border: .5em solid #ecede5; 380 | } 381 | 382 | .summary .description p { 383 | margin: 0; 384 | } 385 | 386 | .summary .description p + p, .summary .description ul { 387 | margin: 3px 0 0 0; 388 | } 389 | 390 | .summary .description.detailed h4 { 391 | margin-top: 3px; 392 | } 393 | 394 | .summary dl { 395 | margin: 0; 396 | } 397 | 398 | .summary dd { 399 | margin: 0 0 0 25px; 400 | } 401 | 402 | .name, .attributes { 403 | white-space: nowrap; 404 | } 405 | 406 | .value code { 407 | white-space: pre-wrap; 408 | } 409 | 410 | td.name, td.attributes { 411 | width: 1%; 412 | } 413 | 414 | td.attributes { 415 | width: 1%; 416 | } 417 | 418 | .class .methods .name, .class .properties .name, .class .constants .name { 419 | width: auto; 420 | white-space: normal; 421 | } 422 | 423 | .class .methods .name > div > code { 424 | white-space: pre-wrap; 425 | } 426 | 427 | .class .methods .name > div > code span, .function .value > code { 428 | white-space: nowrap; 429 | } 430 | 431 | .class .methods td.name > div, .class td.value > div { 432 | position: relative; 433 | padding-right: 1em; 434 | } 435 | 436 | .anchor { 437 | position: absolute; 438 | top: 0; 439 | right: 0; 440 | line-height: 1; 441 | font-size: 85%; 442 | margin: 0; 443 | color: #006aeb !important; 444 | } 445 | 446 | .list { 447 | margin: 0 0 5px 25px; 448 | } 449 | 450 | div.invalid { 451 | background-color: #fae4e0; 452 | padding: 10px; 453 | } 454 | 455 | /* Splitter */ 456 | #splitter { 457 | position: fixed; 458 | height: 100%; 459 | width: 5px; 460 | left: 270px; 461 | background: #1e5eb6 url('resize.png') left center no-repeat; 462 | cursor: e-resize; 463 | } 464 | 465 | #splitter.active { 466 | opacity: .5; 467 | } 468 | 469 | /* Footer */ 470 | #footer { 471 | border-top: 1px solid #e9eeef; 472 | clear: both; 473 | color: #a7a7a7; 474 | font-size: 8pt; 475 | text-align: center; 476 | padding: 20px 0 0; 477 | margin: 3em 0 0; 478 | height: 90px; 479 | background: #ffffff url('footer.png') no-repeat center top; 480 | } 481 | 482 | /* Tree */ 483 | div.tree ul { 484 | list-style: none; 485 | background: url('tree-vertical.png') left repeat-y; 486 | padding: 0; 487 | margin-left: 20px; 488 | } 489 | 490 | div.tree li { 491 | margin: 0; 492 | padding: 0; 493 | } 494 | 495 | div.tree div { 496 | padding-left: 30px; 497 | } 498 | 499 | div.tree div.notlast { 500 | background: url('tree-hasnext.png') left 10px no-repeat; 501 | } 502 | 503 | div.tree div.last { 504 | background: url('tree-last.png') left -240px no-repeat; 505 | } 506 | 507 | div.tree li.last { 508 | background: url('tree-cleaner.png') left center repeat-y; 509 | } 510 | 511 | div.tree span.padding { 512 | padding-left: 15px; 513 | } 514 | 515 | /* Source code */ 516 | .php-keyword1 { 517 | color: #e71818; 518 | font-weight: bold; 519 | } 520 | 521 | .php-keyword2 { 522 | font-weight: bold; 523 | } 524 | 525 | .php-var { 526 | color: #d59401; 527 | font-weight: bold; 528 | } 529 | 530 | .php-num { 531 | color: #cd0673; 532 | } 533 | 534 | .php-quote { 535 | color: #008000; 536 | } 537 | 538 | .php-comment { 539 | color: #929292; 540 | } 541 | 542 | .xlang { 543 | color: #ff0000; 544 | font-weight: bold; 545 | } 546 | 547 | span.l { 548 | display: block; 549 | } 550 | 551 | span.l.selected { 552 | background: #f6f6f4; 553 | } 554 | 555 | span.l a { 556 | color: #333333; 557 | } 558 | 559 | span.l a:hover, div.l a:active, div.l a:focus { 560 | background: transparent; 561 | color: #333333 !important; 562 | } 563 | 564 | span.l .php-var a { 565 | color: #d59401; 566 | } 567 | 568 | span.l .php-var a:hover, span.l .php-var a:active, span.l .php-var a:focus { 569 | color: #d59401 !important; 570 | } 571 | 572 | span.l a.l { 573 | padding-left: 2px; 574 | color: #c0c0c0; 575 | } 576 | 577 | span.l a.l:hover, span.l a.l:active, span.l a.l:focus { 578 | background: transparent; 579 | color: #c0c0c0 !important; 580 | } 581 | 582 | #rightInner.medium #navigation { 583 | height: 52px; 584 | } 585 | 586 | #rightInner.medium #navigation ul:first-child + ul { 587 | clear: left; 588 | border: none; 589 | padding: 0; 590 | } 591 | 592 | #rightInner.medium .name, #rightInner.medium .attributes { 593 | white-space: normal; 594 | } 595 | 596 | #rightInner.small #search { 597 | float: left; 598 | } 599 | 600 | #rightInner.small #navigation { 601 | height: 78px; 602 | } 603 | 604 | #rightInner.small #navigation ul:first-child { 605 | clear: both; 606 | } 607 | 608 | /* global style */ 609 | .left, .summary td.left { 610 | text-align: left; 611 | } 612 | .right, .summary td.right { 613 | text-align: right; 614 | } 615 | -------------------------------------------------------------------------------- /application/libraries/jwt/JWT.php: -------------------------------------------------------------------------------- 1 | 18 | * @author Anant Narayanan 19 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 20 | * @link https://github.com/firebase/php-jwt 21 | */ 22 | class JWT 23 | { 24 | 25 | /** 26 | * When checking nbf, iat or expiration times, 27 | * we want to provide some extra leeway time to 28 | * account for clock skew. 29 | */ 30 | public static $leeway = 0; 31 | 32 | /** 33 | * Allow the current timestamp to be specified. 34 | * Useful for fixing a value within unit testing. 35 | * 36 | * Will default to PHP time() value if null. 37 | */ 38 | public static $timestamp = null; 39 | 40 | public static $supported_algs = array( 41 | 'HS256' => array('hash_hmac', 'SHA256'), 42 | 'HS512' => array('hash_hmac', 'SHA512'), 43 | 'HS384' => array('hash_hmac', 'SHA384'), 44 | 'RS256' => array('openssl', 'SHA256'), 45 | ); 46 | 47 | /** 48 | * Decodes a JWT string into a PHP object. 49 | * 50 | * @param string $jwt The JWT 51 | * @param string|array $key The key, or map of keys. 52 | * If the algorithm used is asymmetric, this is the public key 53 | * @param array $allowed_algs List of supported verification algorithms 54 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256' 55 | * 56 | * @return object The JWT's payload as a PHP object 57 | * 58 | * @throws UnexpectedValueException Provided JWT was invalid 59 | * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed 60 | * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' 61 | * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' 62 | * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim 63 | * 64 | * @uses jsonDecode 65 | * @uses urlsafeB64Decode 66 | */ 67 | public static function decode($jwt, $key, $allowed_algs = array()) 68 | { 69 | $timestamp = is_null(static::$timestamp) ? time() : static::$timestamp; 70 | 71 | if (empty($key)) { 72 | throw new InvalidArgumentException('Key may not be empty'); 73 | } 74 | if (!is_array($allowed_algs)) { 75 | throw new InvalidArgumentException('Algorithm not allowed'); 76 | } 77 | $tks = explode('.', $jwt); 78 | if (count($tks) != 3) { 79 | throw new UnexpectedValueException('Wrong number of segments'); 80 | } 81 | list($headb64, $bodyb64, $cryptob64) = $tks; 82 | if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) { 83 | throw new UnexpectedValueException('Invalid header encoding'); 84 | } 85 | if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) { 86 | throw new UnexpectedValueException('Invalid claims encoding'); 87 | } 88 | $sig = static::urlsafeB64Decode($cryptob64); 89 | 90 | if (empty($header->alg)) { 91 | throw new UnexpectedValueException('Empty algorithm'); 92 | } 93 | if (empty(static::$supported_algs[$header->alg])) { 94 | throw new UnexpectedValueException('Algorithm not supported'); 95 | } 96 | if (!in_array($header->alg, $allowed_algs)) { 97 | throw new UnexpectedValueException('Algorithm not allowed'); 98 | } 99 | if (is_array($key) || $key instanceof \ArrayAccess) { 100 | if (isset($header->kid)) { 101 | $key = $key[$header->kid]; 102 | } else { 103 | throw new UnexpectedValueException('"kid" empty, unable to lookup correct key'); 104 | } 105 | } 106 | 107 | // Check the signature 108 | if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) { 109 | throw new SignatureInvalidException('Signature verification failed'); 110 | } 111 | 112 | // Check if the nbf if it is defined. This is the time that the 113 | // token can actually be used. If it's not yet that time, abort. 114 | if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) { 115 | throw new BeforeValidException( 116 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf) 117 | ); 118 | } 119 | 120 | // Check that this token has been created before 'now'. This prevents 121 | // using tokens that have been created for later use (and haven't 122 | // correctly used the nbf claim). 123 | if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) { 124 | throw new BeforeValidException( 125 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat) 126 | ); 127 | } 128 | 129 | // Check if this token has expired. 130 | if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) { 131 | throw new ExpiredException('Expired token'); 132 | } 133 | 134 | return $payload; 135 | } 136 | 137 | /** 138 | * Converts and signs a PHP object or array into a JWT string. 139 | * 140 | * @param object|array $payload PHP object or array 141 | * @param string $key The secret key. 142 | * If the algorithm used is asymmetric, this is the private key 143 | * @param string $alg The signing algorithm. 144 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256' 145 | * @param mixed $keyId 146 | * @param array $head An array with header elements to attach 147 | * 148 | * @return string A signed JWT 149 | * 150 | * @uses jsonEncode 151 | * @uses urlsafeB64Encode 152 | */ 153 | public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null) 154 | { 155 | $header = array('typ' => 'JWT', 'alg' => $alg); 156 | if ($keyId !== null) { 157 | $header['kid'] = $keyId; 158 | } 159 | if ( isset($head) && is_array($head) ) { 160 | $header = array_merge($head, $header); 161 | } 162 | $segments = array(); 163 | $segments[] = static::urlsafeB64Encode(static::jsonEncode($header)); 164 | $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload)); 165 | $signing_input = implode('.', $segments); 166 | 167 | $signature = static::sign($signing_input, $key, $alg); 168 | $segments[] = static::urlsafeB64Encode($signature); 169 | 170 | return implode('.', $segments); 171 | } 172 | 173 | /** 174 | * Sign a string with a given key and algorithm. 175 | * 176 | * @param string $msg The message to sign 177 | * @param string|resource $key The secret key 178 | * @param string $alg The signing algorithm. 179 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256' 180 | * 181 | * @return string An encrypted message 182 | * 183 | * @throws DomainException Unsupported algorithm was specified 184 | */ 185 | public static function sign($msg, $key, $alg = 'HS256') 186 | { 187 | if (empty(static::$supported_algs[$alg])) { 188 | throw new DomainException('Algorithm not supported'); 189 | } 190 | list($function, $algorithm) = static::$supported_algs[$alg]; 191 | switch($function) { 192 | case 'hash_hmac': 193 | return hash_hmac($algorithm, $msg, $key, true); 194 | case 'openssl': 195 | $signature = ''; 196 | $success = openssl_sign($msg, $signature, $key, $algorithm); 197 | if (!$success) { 198 | throw new DomainException("OpenSSL unable to sign data"); 199 | } else { 200 | return $signature; 201 | } 202 | } 203 | } 204 | 205 | /** 206 | * Verify a signature with the message, key and method. Not all methods 207 | * are symmetric, so we must have a separate verify and sign method. 208 | * 209 | * @param string $msg The original message (header and body) 210 | * @param string $signature The original signature 211 | * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key 212 | * @param string $alg The algorithm 213 | * 214 | * @return bool 215 | * 216 | * @throws DomainException Invalid Algorithm or OpenSSL failure 217 | */ 218 | private static function verify($msg, $signature, $key, $alg) 219 | { 220 | if (empty(static::$supported_algs[$alg])) { 221 | throw new DomainException('Algorithm not supported'); 222 | } 223 | 224 | list($function, $algorithm) = static::$supported_algs[$alg]; 225 | switch($function) { 226 | case 'openssl': 227 | $success = openssl_verify($msg, $signature, $key, $algorithm); 228 | if (!$success) { 229 | throw new DomainException("OpenSSL unable to verify data: " . openssl_error_string()); 230 | } else { 231 | return $signature; 232 | } 233 | case 'hash_hmac': 234 | default: 235 | $hash = hash_hmac($algorithm, $msg, $key, true); 236 | if (function_exists('hash_equals')) { 237 | return hash_equals($signature, $hash); 238 | } 239 | $len = min(static::safeStrlen($signature), static::safeStrlen($hash)); 240 | 241 | $status = 0; 242 | for ($i = 0; $i < $len; $i++) { 243 | $status |= (ord($signature[$i]) ^ ord($hash[$i])); 244 | } 245 | $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash)); 246 | 247 | return ($status === 0); 248 | } 249 | } 250 | 251 | /** 252 | * Decode a JSON string into a PHP object. 253 | * 254 | * @param string $input JSON string 255 | * 256 | * @return object Object representation of JSON string 257 | * 258 | * @throws DomainException Provided string was invalid JSON 259 | */ 260 | public static function jsonDecode($input) 261 | { 262 | if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) { 263 | /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you 264 | * to specify that large ints (like Steam Transaction IDs) should be treated as 265 | * strings, rather than the PHP default behaviour of converting them to floats. 266 | */ 267 | $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING); 268 | } else { 269 | /** Not all servers will support that, however, so for older versions we must 270 | * manually detect large ints in the JSON string and quote them (thus converting 271 | *them to strings) before decoding, hence the preg_replace() call. 272 | */ 273 | $max_int_length = strlen((string) PHP_INT_MAX) - 1; 274 | $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input); 275 | $obj = json_decode($json_without_bigints); 276 | } 277 | 278 | if (function_exists('json_last_error') && $errno = json_last_error()) { 279 | static::handleJsonError($errno); 280 | } elseif ($obj === null && $input !== 'null') { 281 | throw new DomainException('Null result with non-null input'); 282 | } 283 | return $obj; 284 | } 285 | 286 | /** 287 | * Encode a PHP object into a JSON string. 288 | * 289 | * @param object|array $input A PHP object or array 290 | * 291 | * @return string JSON representation of the PHP object or array 292 | * 293 | * @throws DomainException Provided object could not be encoded to valid JSON 294 | */ 295 | public static function jsonEncode($input) 296 | { 297 | $json = json_encode($input); 298 | if (function_exists('json_last_error') && $errno = json_last_error()) { 299 | static::handleJsonError($errno); 300 | } elseif ($json === 'null' && $input !== null) { 301 | throw new DomainException('Null result with non-null input'); 302 | } 303 | return $json; 304 | } 305 | 306 | /** 307 | * Decode a string with URL-safe Base64. 308 | * 309 | * @param string $input A Base64 encoded string 310 | * 311 | * @return string A decoded string 312 | */ 313 | public static function urlsafeB64Decode($input) 314 | { 315 | $remainder = strlen($input) % 4; 316 | if ($remainder) { 317 | $padlen = 4 - $remainder; 318 | $input .= str_repeat('=', $padlen); 319 | } 320 | return base64_decode(strtr($input, '-_', '+/')); 321 | } 322 | 323 | /** 324 | * Encode a string with URL-safe Base64. 325 | * 326 | * @param string $input The string you want encoded 327 | * 328 | * @return string The base64 encode of what you passed in 329 | */ 330 | public static function urlsafeB64Encode($input) 331 | { 332 | return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); 333 | } 334 | 335 | /** 336 | * Helper method to create a JSON error. 337 | * 338 | * @param int $errno An error number from json_last_error() 339 | * 340 | * @return void 341 | */ 342 | private static function handleJsonError($errno) 343 | { 344 | $messages = array( 345 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 346 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 347 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON' 348 | ); 349 | throw new DomainException( 350 | isset($messages[$errno]) 351 | ? $messages[$errno] 352 | : 'Unknown JSON error: ' . $errno 353 | ); 354 | } 355 | 356 | /** 357 | * Get the number of bytes in cryptographic strings. 358 | * 359 | * @param string 360 | * 361 | * @return int 362 | */ 363 | private static function safeStrlen($str) 364 | { 365 | if (function_exists('mb_strlen')) { 366 | return mb_strlen($str, '8bit'); 367 | } 368 | return strlen($str); 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /application/libraries/Format.php: -------------------------------------------------------------------------------- 1 | _CI = &get_instance(); 87 | 88 | // Load the inflector helper 89 | $this->_CI->load->helper('inflector'); 90 | 91 | // If the provided data is already formatted we should probably convert it to an array 92 | if ($from_type !== NULL) 93 | { 94 | if (method_exists($this, '_from_'.$from_type)) 95 | { 96 | $data = call_user_func([$this, '_from_'.$from_type], $data); 97 | } 98 | else 99 | { 100 | throw new Exception('Format class does not support conversion from "'.$from_type.'".'); 101 | } 102 | } 103 | 104 | // Set the member variable to the data passed 105 | $this->_data = $data; 106 | } 107 | 108 | /** 109 | * Create an instance of the format class 110 | * e.g: echo $this->format->factory(['foo' => 'bar'])->to_csv(); 111 | * 112 | * @param mixed $data Data to convert/parse 113 | * @param string $from_type Type to convert from e.g. json, csv, html 114 | * 115 | * @return object Instance of the format class 116 | */ 117 | public function factory($data, $from_type = NULL) 118 | { 119 | // $class = __CLASS__; 120 | // return new $class(); 121 | 122 | return new static($data, $from_type); 123 | } 124 | 125 | // FORMATTING OUTPUT --------------------------------------------------------- 126 | 127 | /** 128 | * Format data as an array 129 | * 130 | * @param mixed|NULL $data Optional data to pass, so as to override the data passed 131 | * to the constructor 132 | * @return array Data parsed as an array; otherwise, an empty array 133 | */ 134 | public function to_array($data = NULL) 135 | { 136 | // If no data is passed as a parameter, then use the data passed 137 | // via the constructor 138 | if ($data === NULL && func_num_args() === 0) 139 | { 140 | $data = $this->_data; 141 | } 142 | 143 | // Cast as an array if not already 144 | if (is_array($data) === FALSE) 145 | { 146 | $data = (array) $data; 147 | } 148 | 149 | $array = []; 150 | foreach ((array) $data as $key => $value) 151 | { 152 | if (is_object($value) === TRUE || is_array($value) === TRUE) 153 | { 154 | $array[$key] = $this->to_array($value); 155 | } 156 | else 157 | { 158 | $array[$key] = $value; 159 | } 160 | } 161 | 162 | return $array; 163 | } 164 | 165 | /** 166 | * Format data as XML 167 | * 168 | * @param mixed|NULL $data Optional data to pass, so as to override the data passed 169 | * to the constructor 170 | * @param NULL $structure 171 | * @param string $basenode 172 | * @return mixed 173 | */ 174 | public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') 175 | { 176 | if ($data === NULL && func_num_args() === 0) 177 | { 178 | $data = $this->_data; 179 | } 180 | 181 | if ($structure === NULL) 182 | { 183 | $structure = simplexml_load_string("<$basenode />"); 184 | } 185 | 186 | // Force it to be something useful 187 | if (is_array($data) === FALSE && is_object($data) === FALSE) 188 | { 189 | $data = (array) $data; 190 | } 191 | 192 | foreach ($data as $key => $value) 193 | { 194 | 195 | //change false/true to 0/1 196 | if (is_bool($value)) 197 | { 198 | $value = (int) $value; 199 | } 200 | 201 | // no numeric keys in our xml please! 202 | if (is_numeric($key)) 203 | { 204 | // make string key... 205 | $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; 206 | } 207 | 208 | // replace anything not alpha numeric 209 | $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); 210 | 211 | if ($key === '_attributes' && (is_array($value) || is_object($value))) 212 | { 213 | $attributes = $value; 214 | if (is_object($attributes)) 215 | { 216 | $attributes = get_object_vars($attributes); 217 | } 218 | 219 | foreach ($attributes as $attribute_name => $attribute_value) 220 | { 221 | $structure->addAttribute($attribute_name, $attribute_value); 222 | } 223 | } 224 | // if there is another array found recursively call this function 225 | elseif (is_array($value) || is_object($value)) 226 | { 227 | $node = $structure->addChild($key); 228 | 229 | // recursive call. 230 | $this->to_xml($value, $node, $key); 231 | } 232 | else 233 | { 234 | // add single node. 235 | $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8'); 236 | 237 | $structure->addChild($key, $value); 238 | } 239 | } 240 | 241 | return $structure->asXML(); 242 | } 243 | 244 | /** 245 | * Format data as HTML 246 | * 247 | * @param mixed|NULL $data Optional data to pass, so as to override the data passed 248 | * to the constructor 249 | * @return mixed 250 | */ 251 | public function to_html($data = NULL) 252 | { 253 | // If no data is passed as a parameter, then use the data passed 254 | // via the constructor 255 | if ($data === NULL && func_num_args() === 0) 256 | { 257 | $data = $this->_data; 258 | } 259 | 260 | // Cast as an array if not already 261 | if (is_array($data) === FALSE) 262 | { 263 | $data = (array) $data; 264 | } 265 | 266 | // Check if it's a multi-dimensional array 267 | if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE)) 268 | { 269 | // Multi-dimensional array 270 | $headings = array_keys($data[0]); 271 | } 272 | else 273 | { 274 | // Single array 275 | $headings = array_keys($data); 276 | $data = [$data]; 277 | } 278 | 279 | // Load the table library 280 | $this->_CI->load->library('table'); 281 | 282 | $this->_CI->table->set_heading($headings); 283 | 284 | foreach ($data as $row) 285 | { 286 | // Suppressing the "array to string conversion" notice 287 | // Keep the "evil" @ here 288 | $row = @array_map('strval', $row); 289 | 290 | $this->_CI->table->add_row($row); 291 | } 292 | 293 | return $this->_CI->table->generate(); 294 | } 295 | 296 | /** 297 | * @link http://www.metashock.de/2014/02/create-csv-file-in-memory-php/ 298 | * @param mixed|NULL $data Optional data to pass, so as to override the data passed 299 | * to the constructor 300 | * @param string $delimiter The optional delimiter parameter sets the field 301 | * delimiter (one character only). NULL will use the default value (,) 302 | * @param string $enclosure The optional enclosure parameter sets the field 303 | * enclosure (one character only). NULL will use the default value (") 304 | * @return string A csv string 305 | */ 306 | public function to_csv($data = NULL, $delimiter = ',', $enclosure = '"') 307 | { 308 | // Use a threshold of 1 MB (1024 * 1024) 309 | $handle = fopen('php://temp/maxmemory:1048576', 'w'); 310 | if ($handle === FALSE) 311 | { 312 | return NULL; 313 | } 314 | 315 | // If no data is passed as a parameter, then use the data passed 316 | // via the constructor 317 | if ($data === NULL && func_num_args() === 0) 318 | { 319 | $data = $this->_data; 320 | } 321 | 322 | // If NULL, then set as the default delimiter 323 | if ($delimiter === NULL) 324 | { 325 | $delimiter = ','; 326 | } 327 | 328 | // If NULL, then set as the default enclosure 329 | if ($enclosure === NULL) 330 | { 331 | $enclosure = '"'; 332 | } 333 | 334 | // Cast as an array if not already 335 | if (is_array($data) === FALSE) 336 | { 337 | $data = (array) $data; 338 | } 339 | 340 | // Check if it's a multi-dimensional array 341 | if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE)) 342 | { 343 | // Multi-dimensional array 344 | $headings = array_keys($data[0]); 345 | } 346 | else 347 | { 348 | // Single array 349 | $headings = array_keys($data); 350 | $data = [$data]; 351 | } 352 | 353 | // Apply the headings 354 | fputcsv($handle, $headings, $delimiter, $enclosure); 355 | 356 | foreach ($data as $record) 357 | { 358 | // If the record is not an array, then break. This is because the 2nd param of 359 | // fputcsv() should be an array 360 | if (is_array($record) === FALSE) 361 | { 362 | break; 363 | } 364 | 365 | // Suppressing the "array to string conversion" notice. 366 | // Keep the "evil" @ here. 367 | $record = @ array_map('strval', $record); 368 | 369 | // Returns the length of the string written or FALSE 370 | fputcsv($handle, $record, $delimiter, $enclosure); 371 | } 372 | 373 | // Reset the file pointer 374 | rewind($handle); 375 | 376 | // Retrieve the csv contents 377 | $csv = stream_get_contents($handle); 378 | 379 | // Close the handle 380 | fclose($handle); 381 | 382 | return $csv; 383 | } 384 | 385 | /** 386 | * Encode data as json 387 | * 388 | * @param mixed|NULL $data Optional data to pass, so as to override the data passed 389 | * to the constructor 390 | * @return string Json representation of a value 391 | */ 392 | public function to_json($data = NULL) 393 | { 394 | // If no data is passed as a parameter, then use the data passed 395 | // via the constructor 396 | if ($data === NULL && func_num_args() === 0) 397 | { 398 | $data = $this->_data; 399 | } 400 | 401 | // Get the callback parameter (if set) 402 | $callback = $this->_CI->input->get('callback'); 403 | 404 | if (empty($callback) === TRUE) 405 | { 406 | return json_encode($data); 407 | } 408 | 409 | // We only honour a jsonp callback which are valid javascript identifiers 410 | elseif (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) 411 | { 412 | // Return the data as encoded json with a callback 413 | return $callback.'('.json_encode($data).');'; 414 | } 415 | 416 | // An invalid jsonp callback function provided. 417 | // Though I don't believe this should be hardcoded here 418 | $data['warning'] = 'INVALID JSONP CALLBACK: '.$callback; 419 | 420 | return json_encode($data); 421 | } 422 | 423 | /** 424 | * Encode data as a serialized array 425 | * 426 | * @param mixed|NULL $data Optional data to pass, so as to override the data passed 427 | * to the constructor 428 | * @return string Serialized data 429 | */ 430 | public function to_serialized($data = NULL) 431 | { 432 | // If no data is passed as a parameter, then use the data passed 433 | // via the constructor 434 | if ($data === NULL && func_num_args() === 0) 435 | { 436 | $data = $this->_data; 437 | } 438 | 439 | return serialize($data); 440 | } 441 | 442 | /** 443 | * Format data using a PHP structure 444 | * 445 | * @param mixed|NULL $data Optional data to pass, so as to override the data passed 446 | * to the constructor 447 | * @return mixed String representation of a variable 448 | */ 449 | public function to_php($data = NULL) 450 | { 451 | // If no data is passed as a parameter, then use the data passed 452 | // via the constructor 453 | if ($data === NULL && func_num_args() === 0) 454 | { 455 | $data = $this->_data; 456 | } 457 | 458 | return var_export($data, TRUE); 459 | } 460 | 461 | // INTERNAL FUNCTIONS 462 | 463 | /** 464 | * @param string $data XML string 465 | * @return array XML element object; otherwise, empty array 466 | */ 467 | protected function _from_xml($data) 468 | { 469 | return $data ? (array) simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA) : []; 470 | } 471 | 472 | /** 473 | * @param string $data CSV string 474 | * @param string $delimiter The optional delimiter parameter sets the field 475 | * delimiter (one character only). NULL will use the default value (,) 476 | * @param string $enclosure The optional enclosure parameter sets the field 477 | * enclosure (one character only). NULL will use the default value (") 478 | * @return array A multi-dimensional array with the outer array being the number of rows 479 | * and the inner arrays the individual fields 480 | */ 481 | protected function _from_csv($data, $delimiter = ',', $enclosure = '"') 482 | { 483 | // If NULL, then set as the default delimiter 484 | if ($delimiter === NULL) 485 | { 486 | $delimiter = ','; 487 | } 488 | 489 | // If NULL, then set as the default enclosure 490 | if ($enclosure === NULL) 491 | { 492 | $enclosure = '"'; 493 | } 494 | 495 | return str_getcsv($data, $delimiter, $enclosure); 496 | } 497 | 498 | /** 499 | * @param string $data Encoded json string 500 | * @return mixed Decoded json string with leading and trailing whitespace removed 501 | */ 502 | protected function _from_json($data) 503 | { 504 | return json_decode(trim($data)); 505 | } 506 | 507 | /** 508 | * @param string $data Data to unserialize 509 | * @return mixed Unserialized data 510 | */ 511 | protected function _from_serialize($data) 512 | { 513 | return unserialize(trim($data)); 514 | } 515 | 516 | /** 517 | * @param string $data Data to trim leading and trailing whitespace 518 | * @return string Data with leading and trailing whitespace removed 519 | */ 520 | protected function _from_php($data) 521 | { 522 | return trim($data); 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /documentation/class-Example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Class Example 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 62 |
63 | 64 |
65 | 66 | 454 | 455 | 456 | 457 | 458 | -------------------------------------------------------------------------------- /documentation/source-class-Example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File controllers/api/Example.php 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 67 |
68 | 69 |
70 | 71 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /documentation/class-Key.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Class Key 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 62 |
63 | 64 |
65 | 66 | 509 | 510 | 511 | 512 | 513 | --------------------------------------------------------------------------------