内容简介:A lightweight PHP API FrameworkGreat for CRUD applications
Spry
A lightweight PHP API Framework
Great for CRUD applications
@PSR2 and @Symfony Compliant
Documentation
Installation
The best way to install Spry and use it is through the CLI. https://github.com/ggedde/spry-cli
composer global require ggedde/spry-cli
Please reference the Installation Process on the CLI Page.
Then from the command line:
spry new [project_name] cd [project_name]
An enpoint is automatically created for you
public/index.php
include dirname(__DIR__).'/vendor/autoload.php'; include dirname(__DIR__).'/spry/init.php';
Manual Installation
composer require ggedde/spry
You will need to add the composer autoloader if it has not already been added. Then use the run
method and include a path to your config file or include a config object. See
Example:
include_once '/vendor/autoload.php'; Spry\Spry::run('../config.php');
QuickStart
Create a project through the CLI
spry new [project_name] cd [project_name]
Folder Structure
- public/ index.php - spry/ - components/ - logs/ - api.log - php.log - config.php - init.php
To Start the Spry test server run
spry up
Then open a separate termal and run some tests
spry test
Create a Single FIle Component
spry component MyComponent
Now View and Edit spry/components/MyComponent.php
Update Database Schema from New Component to Database
spry migrate
Run Tests again with new Component
spry test
Thats It!
Happy Coding
Well, I guess you might need more info. Most of all your coding will be in the components files you create and the rest will most likely be in the configuration file. See more details below :)
Configuration
Spry requires a config file or a config object.
When using a config file Spry will pass a pre-initialized $config object to the file. You will just need to set the variables within the object
Example Config File:
<?php $config->salt = ''; $config->endpoint = 'http://localhost:8000'; $config->componentsDir = __DIR__.'/components'; ...
Setting | Type | Default | Description |
---|---|---|---|
componentsDir | String | __DIR__.'/components' | Directory where Components are stored. Component Filenames must match the Class name of the Component. |
db | Array | [] | Database Object See Database documentation |
dbProvider | String | 'Spry\\SpryProvider\\SpryDB' | Database Provider Class See Database documentation |
endpoint | String | ' http://localhost:8000 ' | Spry Server Endpoint url. Used for internal requests only. |
logger | Array | [] | Logger Object See Logger documentation |
loggerProvider | Array | 'Spry\\SpryProvider\\SpryLogger' | Logger Provider Class See Logger documentation |
projectPath | String | __DIR__ | Path to Spry Project. Default is to use the same directory the config.php file is in. |
responseCodes | Array | [] | Array of Response Codes See Response Codes documentation |
responseHeaders | Array | [] | Default Response Codes |
routes | Array | [] | Array of Routes See Routes documentation |
salt | String | '' | Salt for Security. Change this to be Unique for each one of your API's. You should use a long and Strong key. DO NOT CHANGE IT ONCE YOU HAVE CREATED DATA. Otherwise things like logins may no longer work. |
tests | Array | [] | Array of Tests See Tests documentation |
Accessing Config Settings
You can access any setting by calling the config() object from Spry
Example:
echo Spry::config()->salt; echo Spry::config()->db['database_name']
Extending Config Settings
You can add your own settings and then access them later in your component, provider or plugin
config.php
$config->mySetting = '123';
MyComponent.php
echo Spry::config()->mySetting;
Components
Spry is mainly used through components added in the componentsDir
set in the.
Components are classes within the SpryComponent Namespace.
Components can set their own Routes, Response Codes, DB Schema and Tests by using built in methods. Or you can configure everything through the config.php
These built in Methods are Optional:
Method | Description |
---|---|
setup | Hook on initial Spry setup |
getCodes | Returns Array of Codes and adds them to the Config |
getRoutes | Returns Array of Routes and adds them to the Config |
getSchema | Returns Array of Table Schema and adds them to the Config |
getTests | Returns Array of Tests and adds them to the Config |
Any Methods that resolve a route is called a Controller and gets passed any Parameters from the route.
Basic Example:
<?php namespace Spry\SpryComponent; use Spry\Spry; class MyComponent { private static $id = 2; // Component ID public static function setup() { Spry::addFilter('configure', 'MyComponent::stuff'); } public static function stuff($config) { // Do Stuff return $config; } public static function getRoutes() { return [ '/items/{id}' => [ 'label' => 'Get Item', 'controller' => 'MyComponent::get', 'access' => 'public', 'methods' => 'GET', 'params' => [ 'id' => [ 'required' => true, 'type' => 'int', ], ], ], ]; } public static function getCodes() { return [ 0 => [ 'success' => ['en' => 'Successfully Retrieved Item'], 'warning' => ['en' => 'No Item with that ID Found'], 'error' => ['en' => 'Error: Retrieving Item'], ], 1 => [ 'info' => ['en' => 'Empty Results'], 'success' => ['en' => 'Successfully Retrieved All Items'], 'error' => ['en' => 'Error: Retrieving All Items'], ], ]; } public static function getSchema() { return [ 'items' => [ 'columns' => [ 'name' => [ 'type' => 'string', ], ], ], ]; } public static function getTests() { return [ 'items_get' => [ 'label' => 'Get Example', 'route' => '/items/123', 'method' => 'GET', 'params' => [], 'expect' => [ 'status' => 'success', ], ], ]; } public static function get($params = []) { $response = Spry::db()->get('items', '*', $params) return Spry::response(self::$id, 01, $response); } }
Routes
When using single file components you can set the routes in the Components by returning the routes in the getRoutes()
method.
Example:
public static function getRoutes() { return [ '/items/{id}' => [ 'label' => 'Get Item', 'controller' => 'MyComponent::get', 'access' => 'public', 'methods' => 'GET', 'params' => [ 'id' => [ 'required' => true, 'type' => 'int', ], ], ], ]; }
Or within the Config Settings
$config->routes[ '/items/{id}' => [ 'label' => 'Get Item', 'controller' => 'MyComponent::get', 'access' => 'public', 'methods' => 'GET', 'params' => [ 'id' => [ 'required' => true, 'type' => 'int', ], ], ], ... ];
Route Options
Setting | Type | Default | Description |
---|---|---|---|
label | String | Path Sanitized | Name used to Label Route |
controller | String | '' | Path to Component Method. Ex. MyComponent::method or NameSpace\\Framework\\Class::someMethod |
access | String | public | Spry has a getRoutes method which will return all public routes. You can remove the route from that method by setting the access to private |
methods | String|Array | POST | Method allowed for route. POST|GET|DELETE|PUT |
params | Array | [] | Allowed Parameters for the Route. Any Parameters passed that do not match will get ignored. You can specify Validation for each Parameter. |
params_trim | Boolean | false | Trim all Params for surrounding whitespace. This can be overwritten per param. |
Param Options
Setting | Type | Default | Description |
---|---|---|---|
type | String | string | Types: integer | number | float | array | cardNumber | date | email | url | ip | domain | string | boolean | password |
between | Array[min,max] | [] | Validates Param is between 2 values |
betweenLength | Array[min,max] | [] | Validates Param length is between 2 values |
callback | Callback | Custom Callback function to return true for valid. Can be array for array(CLASS, METHOD) or closure. |
|
endsWith | String | '' | Validates Param ends with a substr. |
filter | Callback | Closure to filter value. Closure accepts one argument of the value and must return filtered value. | |
has | String | '' | Checks if Param is array and contains value. |
hasLetters | Integer | 1 | Validates Param contains x number of letters [a-zA-Z] |
hasLowercase | Integer | 1 | Validates Param contains x number of lower case letters [a-z] |
hasNumbers | Integer | 1 | Validates Param contains x number of numbers [0-9] |
hasSymbols | Integer | 1 | Validates Param contains x number of Symbols |
hasUppercase | Integer | 1 | Validates Param contains x number of upper case letters [A-Z] |
in | Array | [] | Validates Param is within Array |
length | Interger | Length of Param must match value. Accepts Length of Array or String | |
matches | String | '' | Validates Param matches Value. Strict Type comparison is enabled. |
max | Integer | Maximum count of Array or length of String | |
maxDate | Integer | String | 0 | If Integer then maximum days from today or String of Date formatted |
maxLength | Integer | Maximum length of String for Parameter. Strings only. | |
meta | Boolean | false | If true then the parameter will not be passed back to the controller as a Param, but as a Meta Value instead and returned back to the Controller in the second argument. Useful for route settings like pagination page, etc |
min | Integer | Minimum count of Array or length of String | |
minDate | Integer | String | 0 | If Integer then minimum days from today or String of Date formatted |
minLength | Integer | Maximum length of String for Parameter. Strings only. | |
notEndsWith | String | '' | Validates Param does not end with a substr. |
notMatches | String | '' | Validates Param does not match Value. Strict Type comparison is enabled. |
notStartsWith | String | '' | Validates Param does not start with a substr. |
numbersOnly | Boolean | true | Validates Param only contains numbers. |
required | Boolean | Array | true | Validates Param has a Value. if Array then Only requires if params within required array have value (conditional checking). |
startsWith | String | '' | Validates Param starts with a substr. |
trim | Boolean | true | Trims Param for surrounding whitespace |
validateOnly | Boolean | true | Validates the param, but does not send the value back to the controller. Ex. good for 'confirm_password' which doesn't need to be returned to the controller, but present for 'password' to match it. |
unique | Boolean | true | If Param is Array then duplicate values within array will be removed. |
Database
Spry's default Database Provider is SpryDB based on Medoo
See SpryDB's full documentationThis allows you to swap out the Provider later on without having to change your project code.
Spry::db()->get('items', '*', ['id' => 123]); Spry::db()->select('items', '*', ['date[>]' => '2020-01-01']); Spry::db()->insert('items', ['name' => 'test', 'date' => '2020-01-01']); Spry::db()->update('items', ['name' => 'newtest'], ['id' => 123]); Spry::db()->delete('items', ['id' => 123]);
Spry Config Settings
$config->dbProvider = 'Spry\\SpryProvider\\SpryDB'; $config->db = [... ];
Logger
Spry's default Log Provider is SpryLogger
See SpryLogger's full documentationThis allows you to swap out the Provider later on without having to change your project code.
Spry::log()->message("My Message"); Spry::log()->warning("Warning"); Spry::log()->error("Error");
Spry Configuration
$config->loggerProvider = 'Spry\\SpryProvider\\SpryLogger'; $config->logger = [... ];
Tests
Spry comes with its own pre-built testing solution. You can still use other like PHPUnit, but Spry's Tests utilize's Spry's config for rapid testing. When using single file components you can set the routes in the Components by returning the routes in the getTests()
method.
Example:
public static function getTests() { return [ 'items_insert' => [ 'label' => 'Insert Item', 'route' => '/items/insert', 'method' => 'POST', 'params' => [ 'name' => 'TestData', ], 'expect' => [ 'status' => 'success', ], ], 'items_get' => [ 'label' => 'Get Item', 'route' => '/items/{items_insert.body.id}', 'method' => 'GET', 'params' => [], 'expect' => [ 'status' => 'success', ], ], 'items_delete' => [ 'label' => 'Delete Item', 'route' => '/items/delete/', 'method' => 'POST', 'params' => [ 'id' => '{items_insert.body.id}' ], 'expect' => [ 'status' => 'success', ], ], ]; }
Or within the Config Settings
$config->tests[ 'items_get' => [ 'label' => 'Get Example', 'route' => '/items/123', 'params' => [], 'expect' => [ 'code' => '1-200', ], ], 'items_get' => [ 'label' => 'Get Example', 'route' => '/items/123', 'params' => [], 'expect' => [ 'body.id[>]' => 12, ], ], ... ];
Test Options
Setting | Type | Default | Description |
---|---|---|---|
label | String | '' | Name used to Label Test |
route | String | '' | Route that is enabled in Spry Routes |
method | String | GET | Route that is enabled in Spry Routes |
params | Array | [] | Params passed in Route |
expect | Array | [] | Key and Value of what is expected valid from the Response. Keys accept dot notation and [>], [>=], [<], [<=], [!=], [===], [!==] comparison checks. See above. |
Running Tests with Spry CLI
All Tests
spry test
Specific Test
spry test items_get
More Options
spry test --verbose --repeat 10
Response
Spry has 2 built in functions for building the response ( response
and stop
).
response
Spry::response($data = null, $responseCode = 0, $responseStatus = null, $meta = null, $additionalMessages = []);
You can use this to build the response data and return it from the Controller.
$responseCode: This is the Response code id from the Component. $responseStatus: This can be either null
| info
| success
| warning
| error
If null
then the function will auto detect the status based on the value of $data
Example:
$data = ['id' => 123, 'name' => 'John']; return Spry::response($data, 0);
stop
Spry::stop($responseCode = 0, $responseStatus = null, $data = null, $additionalMessages = [], $privateData = null);
This will immediately kill the application and return the response
Example:
if ($error) { Spry::stop(0); }
* See Response Codes Below to see how this works
ResponseCodes
Single File Component Example:
public static function getCodes() { return [ 0 => [ // Get Single 'success' => ['en' => 'Successfully Retrieved Item'], 'warning' => ['en' => 'No Item with that ID Found'], 'error' => ['en' => 'Error: Retrieving Item'], ], 1 => [ // Get Multiple 'info' => ['en' => 'No Results Found'], 'success' => ['en' => 'Successfully Retrieved Items'], 'error' => ['en' => 'Error: Retrieving Items'], ], 2 => [ // Insert 'success' => ['en' => 'Successfully Created Item'], 'error' => ['en' => 'Error: Creating Item'], ], 3 => [ // Update 'success' => ['en' => 'Successfully Updated Item'], 'error' => ['en' => 'Error: Updating Item'], ], 4 => [ // Delete 'success' => ['en' => 'Successfully Deleted Item'], 'error' => ['en' => 'Error: Deleting Item'], ], ]; }
Config File Example
Notice you will need to add a Group Group Number for the component codes. This is not needed in the Single File Component setup
$config->tests[ 1 => [ 0 => [ // Get Single 'success' => ['en' => 'Successfully Retrieved Item'], 'warning' => ['en' => 'No Item with that ID Found'], 'error' => ['en' => 'Error: Retrieving Item'], ], 1 => [ // Get Multiple 'info' => ['en' => 'No Results Found'], 'success' => ['en' => 'Successfully Retrieved Items'], 'error' => ['en' => 'Error: Retrieving Items'], ], 2 => [ // Insert 'success' => ['en' => 'Successfully Created Item'], 'error' => ['en' => 'Error: Creating Item'], ], 3 => [ // Update 'success' => ['en' => 'Successfully Updated Item'], 'error' => ['en' => 'Error: Updating Item'], ], 4 => [ // Delete 'success' => ['en' => 'Successfully Deleted Item'], 'error' => ['en' => 'Error: Deleting Item'], ], ], 2 => [ 0 => [ // Get Single 'success' => ['en' => 'Successfully Retrieved Other Item'], 'warning' => ['en' => 'No Other Item with that ID Found'], 'error' => ['en' => 'Error: Retrieving Other Item'], ], 1 => [ // Get Multiple 'info' => ['en' => 'No Results Found'], 'success' => ['en' => 'Successfully Retrieved Other Items'], 'error' => ['en' => 'Error: Retrieving Other Items'], ], 2 => [ // Insert 'success' => ['en' => 'Successfully Created Other Item'], 'error' => ['en' => 'Error: Creating Other Item'], ], 3 => [ // Update 'success' => ['en' => 'Successfully Updated Other Item'], 'error' => ['en' => 'Error: Updating Other Item'], ], 4 => [ // Delete 'success' => ['en' => 'Successfully Deleted Other Item'], 'error' => ['en' => 'Error: Deleting Other Item'], ], 5 => ['redirect' = ['en' => 'Depricated: This route is depricated']], 6 => ['error' = ['en' => 'Error: Custom Error Message']], 7 => ['error' = ['en' => 'Error: Another Custom Error Message']], 8 => ['error' = ['en' => 'Error: And Another Custom Error Message']], ], ... ];
Component Group Number
The first number is to separate the Components codes by ID
Ex.
1-200 # Success For Component A 2-200 # Success For Component B
Multi-Language Support
1 => [ 'success' => [ 'en' => 'Success!', 'es' => '¡Éxito!' ] ]
Format - Info, Success, Redirect or Depricated, Client Error and Server Error
The first number in the code represents the code type.
[group_id]-[1]xx - the 1 represents 'Info' or 'Empty'
[group_id]-[2]xx - the 2 represents 'Success'
[group_id]-[3]xx - the 3 represents 'Redirect' or 'Depricated'
[group_id]-[4]xx - the 4 represents 'Client Error', or 'Unkown'
[group_id]-[5]xx - the 5 represents 'Server Error'
When using Spry::response() you can pass just the last 2 digits as the code and the data parameter.
Ex.
Spry::response($data, 1);
If $data is an array but empty then the response will automatically Prepend the code with a 1 and return 1-101 .
If $data has a value and is not empty then the response will automatically Prepend the code with a 2 and return 1-201 .
If $data is an array but not null then the response will automatically Prepend the code with a 4 and return 1-401 .
If $data is false or null then the response will automatically Prepend the code with a 5 and return 1-501 .
Response Status
In the response there will be a key of status
The possible values for this will only be success
, error
, or unknown
info
, success
, and redirect
all represent a successfully response and will return success
warning
and error
represent a failed response and will return error
Lifecycle
Hooks & Filters Lifecycle in order of completion:
Name | Type | Details |
---|---|---|
initialized |
hook | Has access to the initial config, but before any filters or components |
configure |
filter | Runs after all Components and Plugins have been loaded |
configure |
hook | Runs after the configure filter and after the OPTIONS pre-flight response |
getPath |
filter | Runs after route path has been recieved |
setPath |
hook | Runs after route path has been set |
setRoutes |
hook | Runs after routes has been set |
params |
filter | Runs after the Params have been fetched |
setParams |
hook | Runs after the Params have been set |
getRoute |
hook | Runs after the Route has been set |
validateParams |
filter | Runs after Params Validation. |
response |
filter | Runs after the response has been built. |
output |
filter | Runs right before the Output is returned. |
Non Lifecycle Hooks & Filters
Name | Type | Details |
---|---|---|
stop |
hook | This may run at anytime when their is an error or Spry needs to stop |
database |
hook | Called after the Database connection has been made or accessing the DatabaseProvider for the first time |
dbJoin |
filter | Called right before the query is ran. Allows to filter the JOIN statement and will include $meta on the query details |
dbColumns |
filter | Called right before the query is ran. Allows to filter the FROM statement and will include $meta on the query details |
dbWhere |
filter | Called right before the query is ran. Allows to filter the WHERE statement and will include $meta on the query details |
dbData |
filter | Called right before the query is ran. Allows to filter the data for INSERT, UPDATE, REPLACE statement and will include $meta on the query details |
dbGet |
filter | Called right before the get query is ran and includes entire statement object |
dbSelect |
filter | Called right before the select query is ran and includes entire statement object |
dbInsert |
filter | Called right before the insert query is ran and includes entire statement object |
dbUpdate |
filter | Called right before the update query is ran and includes entire statement object |
dbReplace |
filter | Called right before the replace query is ran and includes entire statement object |
dbDelete |
filter | Called right before the delete query is ran and includes entire statement object |
dbHas |
filter | Called right before the has query is ran and includes entire statement object |
dbRand |
filter | Called right before the rand query is ran and includes entire statement object |
dbCount |
filter | Called right before the count query is ran and includes entire statement object |
dbAvg |
filter | Called right before the avg query is ran and includes entire statement object |
dbMax |
filter | Called right before the max query is ran and includes entire statement object |
dbMin |
filter | Called right before the min query is ran and includes entire statement object |
dbSum |
filter | Called right before the sum query is ran and includes entire statement object |
Hooks
Hooks allow you to run your own code at specific times and life cycles. This is how you can run middleware and other life cycle aware code.
Adding Hooks
Spry::addHook(string $hookName, string|callable $controller, [ mixed $extraData = null [, int $order = 0 ]] ) : void
Example:
Spry::addHook('configure', 'Spry\\SpryComponent\\MyComponent::myMethod', ['bar' => 345], 100);
Running Hooks
Spry::runHook(string $hookName, [ mixed $data ] ) : void
Example:
Spry::runHook('configure',['foo' => 123] [[ mixed $data = null ], mixed $meta = null ] );
Your Controller
class MyComponent public static function myMethod($data = null, $meta = null, $extraData = null) { $data // [foo => 123] $meta // null $extraData // [bar => 345] // Do Stuff }
Filters
Filters allow you to filter data at specific times and life cycles. A filter typically requires a return value.
Adding Filters
Spry::addFilter(string $filterName, string|callable $controller, [ mixed $extraData = null [, int $order = 0 ]] ) : void
Example:
Spry::addFilter('configure', 'Spry\\SpryComponent\\MyComponent::myMethod', ['bar' => 345], 100);
Running Filters
Spry::runFilter(string $filterName, [[ mixed $data = null ], mixed $meta = null ] ) : void
Example:
$config = Spry::runFilter('configure', $config, ['component' => 'someComponent', 'var' => 'abc']);
Your Controller
class MyComponent public static function myMethod($config = null, $meta = null, $extraData = null) { $config // $config $meta // ['component' => 'someComponent', 'var' => 'abc'] $extraData // [bar => 345] // Do Stuff return $config; }
Todos
- Add Types and Interfaces to everything
- Review and optimize Performance
- Review PSR-7 for response
- Drink a Beer!
以上所述就是小编给大家介绍的《A lightweight PHP API Framework Great fpr CRUD applications》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。