ggedde/spry

PHP API 框架

安装: 117

依赖项: 0

建议者: 0

安全性: 0

星标: 6

关注者: 3

分支: 0

开放问题: 0

类型:框架

1.1.11 2020-09-16 10:19 UTC

README

轻量级的 PHP API 框架
非常适合 CRUD 应用

GitHub release (latest by date including pre-releases)   GitHub   PHP from Packagist

@PSR2 和 @Symfony 兼容

文档

安装

安装 Spry 并使用它的最佳方式是通过 CLI。 https://github.com/ggedde/spry-cli

composer global require ggedde/spry-cli

请参考 CLI 页面上的 安装过程
然后在命令行中

spry new [project_name]
cd [project_name]

将为您自动创建一个端点

public/index.php

include dirname(__DIR__).'/vendor/autoload.php';
include dirname(__DIR__).'/spry/init.php';

手动安装

composer require ggedde/spry

如果您尚未添加,需要添加 composer 自动加载器。然后使用 run 方法并包含您的配置文件路径或包含一个配置对象。请参阅 配置

示例

include_once '/vendor/autoload.php';
Spry\Spry::run('../config.php');

快速入门

通过 CLI 创建项目

spry new [project_name]
cd [project_name]

文件夹结构

- public/
    index.php
- spry/
    - components/
    - logs/
        - api.log
        - php.log
    - config.php
    - init.php

要启动 Spry 测试服务器,请运行

spry up

然后在单独的终端中运行一些测试

spry test

创建单个文件组件

spry component MyComponent

现在查看和编辑 spry/components/MyComponent.php

从新组件更新数据库模式到数据库

spry migrate

使用新组件再次运行测试

spry test

就这样!
编码愉快

嗯,我想你可能还需要更多信息。您的大部分编码将在您创建的组件文件中,其余的可能在配置文件中。下面有更多详细信息:)


配置

Spry 需要一个配置文件或配置对象。

当使用配置文件时,Spry 将传递一个预先初始化的 $config 对象到文件。您只需设置对象内的变量即可

示例配置文件

<?php
$config->salt = '';
$config->endpoint = 'https://:8000';
$config->componentsDir = __DIR__.'/components';
...

访问配置设置

您可以通过从 Spry 调用 config() 对象来访问任何设置

示例

echo Spry::config()->salt;
echo Spry::config()->db['database_name']

扩展配置设置

您可以在组件、提供者或插件中添加自己的设置,然后在稍后访问它们

config.php

$config->mySetting = '123';

MyComponent.php

echo Spry::config()->mySetting;

组件

Spry 主要通过在 Spry 配置 中设置的 componentsDir 添加的组件来使用。

组件是 SpryComponent 命名空间内的类。
组件可以通过使用内置方法设置自己的路由、响应代码、数据库模式和测试。或者您可以通过配置.php 配置一切

这些内置方法都是可选的

任何解析路由的方法称为控制器,并接收来自路由的任何参数。

基本示例

<?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);
    }
}

路由

当使用单个文件组件时,您可以通过在 getRoutes() 方法中返回路由来设置组件中的路由。

示例

public static function getRoutes() {
    return [
        '/items/{id}' => [
            'label' => 'Get Item',
            'controller' => 'MyComponent::get',
            'access' => 'public',
            'methods' => 'GET',
            'params' => [
                'id' => [
                    'required' => true,
                    'type' => 'int',
                ],
            ],
        ],
    ];
}

或者是在配置设置中

$config->routes[
    '/items/{id}' => [
        'label' => 'Get Item',
        'controller' => 'MyComponent::get',
        'access' => 'public',
        'methods' => 'GET',
        'params' => [
            'id' => [
                'required' => true,
                'type' => 'int',
            ],
        ],
    ],
    ...
];

路由选项

参数选项

数据库

Spry的默认数据库提供者是基于Medoo的SpryDB
查看SpryDB的完整文档

这允许您在不更改项目代码的情况下,稍后更换提供者。

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->dbProvider = 'Spry\\SpryProvider\\SpryDB';
$config->db = [... ];

日志

Spry的默认日志提供者是SpryLogger
查看SpryLogger的完整文档

这允许您在不更改项目代码的情况下,稍后更换提供者。

Spry::log()->message("My Message");
Spry::log()->warning("Warning");
Spry::log()->error("Error");

Spry配置

$config->loggerProvider = 'Spry\\SpryProvider\\SpryLogger';
$config->logger = [... ];

响应

Spry有两个内置函数用于构建响应(responsestop)。

response

Spry::response($data = null, $responseCode = 0, $responseStatus = null, $meta = null, $additionalMessages = []);

您可以使用此功能构建响应数据,并从控制器返回它。

$responseCode: 这是组件的响应代码ID。 $responseStatus: 可以是 null | info | success | warning | error
如果为 null,则函数将根据 $data 的值自动检测状态

示例

$data = ['id' => 123, 'name' => 'John'];
return Spry::response($data, 0);

stop

Spry::stop($responseCode = 0, $responseStatus = null, $data = null, $additionalMessages = [], $privateData = null);

这将立即终止应用程序并返回响应

示例

if ($error) {
    Spry::stop(0);
}

* 请参阅下面的响应代码以了解其工作方式


响应代码

单个文件组件示例

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->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']],
    ],
    ...
];

组件组编号

第一个数字用于通过ID分隔组件代码
例如:

1-200   # Success For Component A 
2-200   # Success For Component B 

多语言支持

1 => [
    'success' => [
        'en' => 'Success!',
        'es' => '¡Éxito!'
    ]
]

格式 - 信息、成功、重定向或已弃用、客户端错误和服务器错误

代码中的第一个数字表示代码类型。

[group_id]-[1]xx - 1 表示 '信息' 或 '空'
[group_id]-[2]xx - 2 表示 '成功'
[group_id]-[3]xx - 3 表示 '重定向' 或 '已弃用'
[group_id]-[4]xx - 4 表示 '客户端错误',或 '未知'
[group_id]-[5]xx - 5 表示 '服务器错误'

当使用Spry::response()时,您只需传递最后两位数字作为代码和数据参数。

例如:

Spry::response($data, 1); 

如果 $data 是数组但 为空,则响应将自动在代码前加上 1 并返回 1-101
如果 $data 值且 不为空,则响应将自动在代码前加上 2 并返回 1-201
如果 $data 是数组但不是 null,则响应将自动在代码前加上 4 并返回 1-401
如果 $data 是 falsenull,则响应将自动在代码前加上 5 并返回 1-501

响应状态

在响应中,将有一个名为 status 的键
此键的可能值只有 successerrorunknown

infosuccessredirect 都表示成功的响应,将返回 success
warningerror 表示失败的响应,将返回 error


测试

Spry自带预构建的测试解决方案。您仍然可以使用PHPUnit等工具,但Spry的测试利用Spry的配置进行快速测试。当使用单个文件组件时,您可以通过在getTests()方法中返回路由来设置组件中的路由。

示例

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',
            ],
        ],
    ];
}

或者是在配置设置中

$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,
        ],
    ],
    ...
];

测试选项

使用Spry CLI运行测试

所有测试

spry test

特定测试

spry test items_get

更多选项

spry test --verbose --repeat 10

速率限制

Spry的默认速率限制是SpryRateLimits
查看完整文档

这允许您在不更改项目代码的情况下,稍后更换提供者。

添加全局速率限制

$config->rateLimits = [
  'driver' => 'file',
  'fileDirectory' => __DIR__.'/rate_limits',
  'excludeTests' => false,
  'default' => [
      'by' => 'ip',
      'limit' => 10,
      'within' => 1,
      'hook' => 'configure',
      'excludeTests' => false
  ]
];

按路由添加限制

$config->routes = [
    '/auth/login' => [
        'label' => 'Auth Login',
        'controller' => 'Auth::login',
        'access' => 'public',
        'methods' => 'POST',
        'limits' => [
            'by' => 'ip',
            'limit' => 1,
            'within' => 3,
            'excludeTests' => false
        ],
        'params' => [
            'email' => [
                'required' => true,
                'type' => 'string',
            ],
            'password' => [
                'required' => true,
                'type' => 'string',
            ],
        ],
    ],
];

查看完整文档


生命周期

按完成顺序排列钩子和过滤器生命周期

非生命周期钩子和过滤器

钩子

钩子允许你在特定的时间和生命周期中运行自己的代码。这是运行中间件和其他生命周期感知代码的方式。

添加钩子

Spry::addHook(string $hookName, string|callable $controller, [ mixed $extraData = null [, int $order = 0 ]] ) : void

示例

Spry::addHook('configure', 'Spry\\SpryComponent\\MyComponent::myMethod', ['bar' => 345], 100);

运行钩子

Spry::runHook(string $hookName, [ mixed $data ] ) : void

示例

Spry::runHook('configure',['foo' => 123] [[ mixed $data = null ], mixed $meta = null ] );

您的控制器

class MyComponent

public static function myMethod($data = null, $meta = null, $extraData = null) {
    $data // [foo => 123]
    $meta // null
    $extraData // [bar => 345]
    // Do Stuff
}

过滤器

过滤器允许你在特定的时间和生命周期中过滤数据。通常,过滤器需要一个返回值。

添加过滤器

Spry::addFilter(string $filterName, string|callable $controller, [ mixed $extraData = null [, int $order = 0 ]] ) : void

示例

Spry::addFilter('configure', 'Spry\\SpryComponent\\MyComponent::myMethod', ['bar' => 345], 100);

运行过滤器

Spry::runFilter(string $filterName, [[ mixed $data = null ], mixed $meta = null ] ) : void

示例

$config = Spry::runFilter('configure', $config, ['component' => 'someComponent', 'var' => 'abc']);

您的控制器

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;
}

待办事项

  • 添加PHPUnit测试
  • 添加类型和接口
  • 审查和优化性能
  • 审查PSR-7响应规范
  • 喝一杯啤酒!