teodoroleckie / framework
已弃用 - php 框架
Requires
- php: >=7.2
- ext-json: *
- beberlei/assert: ^3.2
- doctrine/orm: ^2.7
- firebase/php-jwt: ^5.2
- laminas/laminas-diactoros: ^2
- psr/container: ^1.0
- psr/http-factory: ^1
- psr/http-message: ^1
- psr/log: ^1
- psr/simple-cache: ^1
- sunrise/http-message: *
- symfony/console: ^4.4
- vlucas/phpdotenv: ^2.6
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.14
- infection/infection: ^0.12.2
- mikey179/vfsstream: ^1.6
- phpstan/phpstan: ^0.11
- phpstan/phpstan-phpunit: ^0.11
- phpunit/phpunit: ^8.5
Suggests
- ext-apcu: The extension required to use this pool.
This package is auto-updated.
Last update: 2021-05-26 12:54:37 UTC
README
项目结构
目录 config 是必需的,并且必须提供给 Kernel 对象以初始化配置。
在 dependencies.php 文件中,您应该初始化您的依赖项容器,该文件的名称可以是您选择的。
在 routes.php 文件中,您将定义哪些控制器或闭包应该处理每种类型的请求,该文件的名称可以是您选择的。
在 config.php 文件中,初始化 Config 对象,例如,它必须读取依赖于环境的变量。 该文件名必须保留,不能是其他名称。
MyProject/
|-config/
|-dependencies.php
|-routes.php
|-config.php
|-public/
|-vendor/
|-src/
|-Controller/
|-Infrastructure/
|-Domain/Model/
|-Application/
|-tpl/
位于 config 目录内的所有文件,都有一个 $di
变量可用,它是我们的依赖项管理器。一个 config.php 文件的示例可能如下所示
<?php use Framework\Container\DiInterface; use Framework\Config; /** @var $di DiInterface */ $di->getShared('config')->merge( new Config( [ 'hostname' => getenv('HOSTNAME') ] ) );
在此阶段,我们的依赖项容器已创建一个 Config
对象的实例,并将其保留以供我们创建另一个对象,以便我们可以将其与在执行时创建的配置合并。请注意,config 对象仅对 shared
可用,因为我们总是想要相同的实例。
依赖项管理器
namespace Framework\Container\Di
注册依赖项有两种方式,setShared 和 set。获取依赖项我们将通过 getShared 和 get。setShared 方法注册作为 singleton 行为的实例,而 set 注册不作为 singleton 行为的实例,即每次调用都会返回一个新的实例。
可以通过类名注册
第一个参数将是用于注册依赖项的名称,第二个参数是 className
use Framework\Container\Di; use Framework\Http\ServerRequestFactory; $di = new Di(); $di->setShared( 'request.factory' , ServerRequestFactory::class ); // o $di->set( 'request.factory' , ServerRequestFactory::class );
要获取实例,我们将通过调用注册名称来获取。
$di->getShared('request.factory'); // o $di->get('request.factory');
如果使用 setShared 注册一个键,则只能通过 getShared 获取,同样,如果使用 set 注册,则只能通过 get 获取实例。
通过闭包注册
$di->setShared( 'request' , static function () use ($di) { return $di->getShared('request.factory')::fromGlobals(); } );
通过构造函数参数注册
$di->setShared( 'home.controller' , [ 'className' => HomeController::class , 'arguments' => [ [ 'type' => 'service' , 'value' => 'request' ] , [ 'type' => 'service' , 'value' => 'response.factory' ] ] ] );
通过方法调用注册
$di->setShared( 'my.class' , [ 'className' => MyClass::class ,'calls' => [ [ 'arguments' => [ [ 'type' => 'parameter', 'value' => 5 ] ] , 'method' => 'firstMethod' ] ,[ 'arguments' => [ [ 'type' => 'service', 'value' => 'response.factory' ] ] , 'method' => 'secondMethod' ] ] ] );
请注意,第一个调用使用定义为 'type' => 'parameter' 的参数,但第二个调用使用定义为 'type' => 'service' 的服务,该服务也必须定义。在我们的例子中 'response.factory'。
路由管理器
路由管理器必须在 PHP 文件中定义。在其中,您可以通过正则表达式指定路径以及它接受的请求方法。
以下是一个路由定义的示例
<?php use Framework\Container\DiInterface; use Framework\Routes\Item; use Framework\Routes\Router; $routes = new Router(); /** @var $di DiInterface */ $di->setShared('routes', $routes); /** home routes , atiende petiociones get, post y options para el path dado */ /* Será atendida por la controladora 'home.controller' definida en nuestro gestor de dependencias */ $routes->add(new Item( 'routesName' , '/home/(?<id>([0-9]{1,2}))/?' , '/home/%(id)s/' ,['get','post','options'] ) , ['home.controller','homeAction'] );
将 Item
的实例添加到 routes,第一个参数是控制器和 action 的数组。
Item
对象需要三个必填参数
1. 路由名称。
2. 用于与我们的 URL 路径匹配的正则表达式。
3. 当我们根据路由名称和参数构建 URL 时使用的表达式。
4º 可选,一个支持该路由的请求方法数组,如果没有指定任何,将处理所有类型的请求,如get、post、options等。
请注意,控制器的定义是通过我们依赖管理器中的一个命名来实现的。
请记住,在我们的例子中,action(一个名为homeAction
的方法)接收以下参数
ServerRequestInterface $request // objeto reques de la petición
ResponseFactoryInterface $responseFactory // factory para componer el response
Params $params // parámetros definidos en el routes
Params
对象将是我们路由中定义的参数,使用正则表达式。
例如,对于url /home/18/
,我们可以在action中通过以下方式获取18(这是我们定义为id
的参数)的值
<?php namespace MyProject\Controller; use Framework\Controller; use Psr\Http\Message\ServerRequestInterface; use Framework\Routes\Params; use Psr\Http\Message\ResponseFactoryInterface; /** * Class HomeController * @package MyProject\Controller * @author Teodoro Leckie Westberg <teodoroleckie@gmail.com> */ class HomeController extends Controller { /** * @param ServerRequestInterface $request * @param ResponseFactoryInterface $responseFactory * @param Params $params * @return mixed */ public function index( ServerRequestInterface $request , ResponseFactoryInterface $responseFactory , Params $params ) { var_dump($params->id); } }
您还可以定义一个处理请求的Closure,这在API Rest中非常有用,其定义方式如下
/** @var RouterInterface $routes */ use Framework\Routes\RouterInterface;$routes->add(new Item( 'routesName' , '/home/(?<id>([0-9]{1,2}))/?' , '/home/%(id)s/' ,['get','post','options'] ) , static function( ServerRequestInterface $request , ResponseFactoryInterface $responseFactory , Params $params ){ var_dump($params->id); } );
您还可以定义一个由依赖注入容器定义的Closure来处理请求
首先,在我们 的依赖管理器dependencies.php
中定义该Closure
$di->setShared('my.controller', static function(){ return static function( ServerRequestInterface $request , ResponseFactoryInterface $responseFactory , Params $params ){ var_dump($params); }; });
然后,在routes.php
中定义我们的处理器
$routes->add(new Item( 'routesName' , '/home/(?<id>([0-9]{1,2}))/?' , '/home/%(id)s/' ) ,['my.controller'] );
组装url或反向url
例如,可以通过以下方式反转url以显示url或将它分配给您首选的模板管理器
例如,从以下定义的路由开始
$routes->add(new Item( 'routesName' , '/home/(?<param1>([a-z]+))/(?<param2>([0-9]{1,}))/?' , '/home/%(param1)s/%(param2)s/' ) , ['home.controller','home'] );
例如,您可以在控制器内部这样做
/** @var Router $routes */ $routes = $this->getContainer()->getShared('routes'); var_dump($routes->reverse('routesName',['param1'=> 'otherpath', 'param2'=> 555])); // tendrá la siguiente salida: /home/otherpath/555/
控制器
我们可以定义必要的控制器及其相应的actions。我们只需要扩展Framework\Controller
类即可。这些控制器实现了Framework\Container\Injectable
接口,因此我们可以在控制器中使用依赖注入容器。
<?php namespace MyProject\Controller; use Framework\Controller; use Psr\Http\Message\ServerRequestInterface; use Framework\Routes\Params; use Psr\Http\Message\ResponseFactoryInterface; /** * Class HomeController * @package MyProject\Controller * @author Teodoro Leckie Westberg <teodoroleckie@gmail.com> */ class HomeController extends Controller { public function homeAction( ServerRequestInterface $request , ResponseFactoryInterface $responseFactory , Params $params ) { // $responseFactory = $this->getContainer()->getShared('response.factory'); // o $responseFactory = $this->di->getShared('response.factory'); return $responseFactory->createHtmlResponse('hola', 200) ->withHeader( 'Access-Control-Allow-Headers' , 'X-Requested-With , Content-Type , Accept, Origin, Authorization' ); } }
我们只需要将控制器注册到依赖注入容器中的/config/dependencies.php
文件中,如下所示
$di->setShared( 'home.controller' , [ 'className' => HomeController::class ] );
并在/config/routes.php
中按如下方式定义
$routes->add(new Item( 'routesName' , '/home/(?<id>([0-9]{1,2}))/?' , '/home/%(id)s/' ,['get','post','options'] ) , ['home.controller','homeAction'] );
配置
我们将使用config(/config/config.php
)来存储任何类型的对象或值,例如,存储依赖于项目环境的值。
<?php use Framework\Container\DiInterface; use Framework\Config; /** @var $di DiInterface */ $di->getShared('config')->merge( new Config( [ 'hostname' => getenv('HOSTNAME') ,'environment' => getenv('ENV') ,'cacheTtl' => getenv('CACHE_TTL') ,'db' => [ 'dbuser' => getenv('DB_USER') ,'dbhost' => getenv('DB_HOST') ,'dbpass' => getenv('DB_PASS') ] ] ) );
然后,在我们的项目中,我们可以通过在依赖管理器中使用键config
来访问它。
/** @var $config Framework\Config */ $config = $this->di->getShared('config'); var_dmp( $config->db->dbuser ); var_dmp( $config->db->toArray() );
index.php
在我们的入口点,我们需要初始化.env文件,创建Kernel实例并调用处理器,传入当前url作为参数。
<?php include_once '../vendor/autoload.php'; use Framework\Kernel; (new Dotenv\Dotenv(__DIR__ . '/../'))->load(); (new Kernel($_SERVER['REQUEST_METHOD'])) ->changePath(dirname(__DIR__) . DIRECTORY_SEPARATOR) ->handle($_SERVER['REDIRECT_URL'] ?? '/');
日志记录器
您可以使用您偏好的库,只要它实现了psr\log
即可。在这种情况下,建议使用Monolog\Logger
。它必须以以下方式初始化在依赖注入容器中
use Monolog\Logger; use Monolog\Handler\StreamHandler; /** logger */ $di->setShared('log' , static function () use ($di) { $log = new Logger('app'); return $log->pushHandler(new StreamHandler('../log/myfile.log')); } );
要获取logger对象,只需向容器请求它即可
$this->di->getShared('log')->error(new Exception('My exception));
我们有以下方法可用
/** @var $log Psr\Log\LoggerInterface */ $log->error('my message'); $log->alert('my message'); $log->critical('my message'); $log->warning('my message'); $log->notice('my message'); $log->info('my message'); $log->debug('my message');
控制台
Console类的目的是初始化依赖项。您可以使用Symfony\Component\Console\*
库来管理命令。以下是一个示例
在dependencies.php
文件中定义我们的命令和app
$di->set( 'command:multiply' , [ 'className' => \Mynamespace\Command\MyCommand::class ] ); /** command loader */ $di->set('command.loader' , static function () use ($di) { return new Symfony\Component\Console\CommandLoader\FactoryCommandLoader( [ 'command:multiply' => static function () use ($di) { return $di->get('command:multiply'); } ] ); }); /** command app */ $di->set('command.app' , static function () use ($di) { $application = new Symfony\Component\Console\Application(); $application->setCommandLoader($di->get('command.loader')); return $application; } );
然后,我们需要在项目中创建一个名为bin/console
的文件,内容如下(请记得为该文件设置执行权限)
#!/usr/bin/env php <?php require __DIR__ . '/../vendor/autoload.php'; (new Dotenv\Dotenv(__DIR__ . '/../'))->load(); // load dependencies $command = new Framework\Console(); $command->changePath(dirname(__DIR__) . DIRECTORY_SEPARATOR); $di = $command->getContainer(); $log = $di->getShared('log'); try{ // invocar al app para que se inicialicen las dependencias $di->get('command.app')->run(); }catch(Exception $exception){ // log error $log->error($exception); }
命令可能如下所示
<?php namespace Mynamespace\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** * Class MyCommand * * @package Mynamespace\Command * @author Teodoro Leckie Westberg <teodoroleckie@gmail.com> */ class MyCommand extends Command { protected function configure() { $this ->setName('command:multiply') ->setDescription('Primer comando') ->addArgument('number', InputArgument::REQUIRED, 'Es requerido un número') ->addArgument('otherNumber', InputArgument::REQUIRED, 'Es requerido un número'); } /** * @param InputInterface $input * @param OutputInterface $output * @return int|void */ protected function execute(InputInterface $input, OutputInterface $output) { $number = $input->getArgument('number'); $otherNumber = $input->getArgument('otherNumber'); $output->writeln( sprintf( 'Hola, el resultado de multiplicar %s x %s = %s!' , $number , $otherNumber , ($number * $otherNumber) ) ); } }
要执行命令,只需按照以下方式引用其名称
$ ./bin/console command:multiply 5 8
生成新的项目结构
从框架(一旦克隆并执行了composer install)中,您可以生成项目结构。只需执行命令app:skeleton
并指定您想创建项目的地方的路径
$ bin/console app:skeleton "/var/www/mysite"
如果一切顺利,您将看到以下输出
Creado el directorio: /bin/
Creado el directorio: /src/
Creado el directorio: /src/Controller/
Creado el directorio: /src/tpl/
Creado el directorio: /src/Infrastructure/
Creado el directorio: /src/Application/
Creado el directorio: /src/Domain/Model//
Creado el directorio: /tests/
Creado el directorio: /config/
Creado el directorio: /public/
Creado el composer.json
Console creado.
Index creado.
Controlador creado
Routes creado.
Dependencias creadas.
Config creado.
Env creado.