adhocore / phalcon-ext
杂项phalcon适配器、扩展和实用工具
Requires
- php: >=7.0
- adhocore/cli: ^0.8.1
- adhocore/cron-expr: ^1.1
- adhocore/jwt: ^1.0
Requires (Dev)
- phpunit/phpunit: ^6.5 || ^7.5
- swiftmailer/swiftmailer: ^6.0.0
- twig/twig: ^1.3 || ^2.4
This package is auto-updated.
Last update: 2024-09-06 10:13:33 UTC
README
有用的phalcon适配器、中间件、扩展和实用工具!
支持phalcon v4。
安装
composer require adhocore/phalcon-ext
包含内容
缓存
Cli
数据库
依赖注入
Http
日志记录器
邮件
Util
验证
视图
Cache.Redis
扩展 Phalcon\Cache\Backend\Redis
以允许通过底层的redis绑定进行访问。
设置
$di->setShared('redis', function () { return new \PhalconExt\Cache\Redis(new \Phalcon\Cache\Frontend\None(['lifetime' => 0])); }); // Call native \Redis methods like: $di->get('redis')->getConnection()->hGet(); $di->get('redis')->getConnection()->info();
Cli.Extension
请务必检查它在 adhocore/cli 中的工作方式以及它如何在 example/cli 和 example/MainTask.php 中集成和使用。
设置
$di = new PhalconExt\Di\FactoryDefault; $di->setShared('dispatcher', Phalcon\Cli\Dispatcher::class); $di->setShared('router', Phalcon\Cli\Router::class); $di->setShared('config', new Phalcon\Config([ 'console' => [ 'tasks' => [ // Register your tasks here: 'name' => class // You will define their options/arguments in respective `onConstruct()` 'main' => Your\MainTask::class, ], ], ])); $console = new PhalconExt\Cli\Console($di, 'MyApp', 'v1.0.1'); // Or if you have your own Console extends, just use the trait class YourConsole extends \Phalcon\Cli\Console { use PhalconExt\Cli\Extension; }
command(string $command, string $descr = '', bool $allowUnknown = false): Ahc\Cli\Command
您可以在引导中注册命令,或者您可以将它们组织在 SomeTask::onConstruct()
中,如下所示
class MainTask extends Phalcon\Cli\Task { public function onConstruct() { ($console = $this->getDI()->get('console')) ->command('main', 'Main task ...', false) ->arguments('<requiredPath> [optional:default]'); ->option('-s --stuff', 'Description', 'callable:filter', 'default') ->tap($console) // for fluency ->schedule('7-9 * */9 * *') ->command('main:other', ...) ->option('') ->option('') ->tap($console) ->schedule('@5mintues') // @10minutes, @15minutes, @weekly, @daily, @yearly, @hourly ... ; } public function mainAction() { $io = $this->interactor; // Access defined args/opts for writing to terminal: $io->write($this->command->requiredPath, true); $io->write($this->command->stuff, true); } }
现在每次您运行命令 php cli.php main main the-path --stuff whatever
,它将打印 the-path
和 whatever
! cli.php
可以是您选择的任何内容,应与 example/cli 相当。
initTasks(void): self
初始化可加载的任务。如果您在 console.tasks
配置中列出了它们(请参阅上面的设置部分),则自动执行。如果您在过程中动态或较晚加载任务,请手动调用它:$console->initTasks()
。
Cli.MiddlewareTrait
允许您以最简单的方式定义、注册和触发CLI中的中间件!默认注册 PhalconExt/Cli/Middleware/Factory
以方便使用。它将相关的命令实例(Ahc\Cli\Input\Command
)注入到DI中,并在 --help
和 --version
时自动触发。
定义中间件
class HelloConsole { // Prints hello every time you run a console cmd, just before execution public function before(PhalconExt\Cli\Console $console) { $console->getDI()->get('interactor')->bgGreenBold('Hello', true); return true; // Indicates success and no-objection! } }
注册/检索中间件
// Single: $console->middleware(HelloConsole::class); // Multiple: $console->middlewares([HelloConsole::class, Another::class]); // Get em: (It already contains PhalconExt/Cli/Middleware/Factory) $console->middlewares(); // array
触发中间件
您不必这样做。所有中间件的 before
和 after
方法都自动作为控制台生命周期事件调用。
Cli.Task.ScheduleTask
作为 adhocore/phalcon-ext
的工厂特性,它是自动加载的,因此您不需要将其放入配置的 console.tasks
数组中。
它提供了 schedule:list
(或 schedule list
)和 schedule:run
或 schedule run
命令,分别用于列出所有计划的任务和运行所有在该特定时间到期的任务。
将任务注册为计划的任务也很简单。检查上面的 command()
部分,您可以通过流畅接口以这种方式安排一个任务
$console ->command('task:action', ...) ->arguments(...)->option(...) ->tap($console) ->schedule('crontab expression') // You can also use humanly phrases: @daily, @hourly ;
如您所见,您在crontab中需要做的所有事情就是添加以下条目:* * * * * php /path/to/your/phalcon-app-using-phalcon-ext/src/cli.php schedule:run
... 并在这里管理所有内容!
注意
任何通过自动化方式安排运行的作业,最好不定义形式为<name>
的必需参数或选项,因为首先,在作为计划任务运行时它们不会被验证,其次,计划的理念是注册单个crontab脚本* * * * * php /app/src/cli.php schedule:run
,没有任何参数或选项。(如果你想要第三点,这些可以在无人值守的情况下运行!)
然而,如果你坚持这样做,可以在schedule:run
部分之后附加--option-name value
,但这个值会作用于当前所有可运行的待办任务。它们都可以通过$this->command->optionName
读取。
Db.Extension
设置
$di->setShared('config', new \Phalcon\Config([ 'database' => [ 'driver' => 'sqlite', 'dbname' => __DIR__ . '/.var/db.db', // ... other options (see phalcon &/or pdo docs) ], ]); $di->setShared('db', function () { // Can use Mysql or Postgresql too return (new \PhalconExt\Db\Sqlite($this->get('config')->toArray()['database'])); }); // Or if you have your own already, just use the trait class YourDb extends \Phalcon\Db\Adapter { use PhalconExt\Db\Extension; }
upsert(string $table, array $data, array $criteria): bool
根据给定的条件在指定表中插入或更新数据行。
$di->get('db')->upsert('users', ['name' => 'John'], ['username' => 'johnny']);
insertAsBulk(string $table, array $data): bool
一次性插入多个项目 - 在一个查询中 - 无循环。
$di->get('db')->insertAsBulk('table', [ ['name' => 'name1', 'status' => 'status1'], ['details' => 'detail2', 'name' => 'name2'], // columns dont need to be ordered or balanced ]);
countBy(string $table, array $criteria): int
根据条件计算表中的行数。
$di->get('db')->countBy('table', ['name' => 'name1', 'status' => 'ok']);
Db.Logger
作为事件监听器挂钩到数据库,记录所有SQL查询 - 绑定是内插的。
$di->setShared('config', new \Phalcon\Config([ 'sqllogger' => [ 'enabled' => true, 'logPath' => __DIR__ . '/.var/sql/', // directory 'addHeader' => true, 'backtraceLevel' => 5, 'skipFirst' => 2, ], ]); $di->get('db')->registerLogger($di->get('config')->toArray()['sqllogger']);
Di.Extension
前言 整个示例和整个 phalcon-ext
包几乎总是使用 $di->get('service')
而不是 $di->getShared('service')
,这是因为如果你将 'service' 设置为共享的,则 get()
将会一次又一次地返回相同的共享实例,如果没有,则将生成新的实例 - 重点是,如果我们不想要新的实例,为什么不使用 setShared()
?必须自觉地思考是使用 setShared()
还是 set()
而不是使用 getShared()
或 get()
。
设置
$di = new \PhalconExt\Di\FactoryDefault; // Or if you have your own already, just use the trait class YourDi extends \Phalcon\Di { use PhalconExt\Di\Extension; }
registerAliases(array $aliases): self
为di服务注册别名,以便它们可以通过名称 &/ 或类型提示自动解析。
$di->registerAliases([ 'TheAlias' => 'service', \Phalcon\Db\Adapter::class => 'db', ]);
resolve(string $class, array $parameters = []): mixed
递归解析给定类的FQCN的所有依赖项,并返回新实例。
$instance = $di->resolve(\Some\Complex\ClassName::class, $parameters);
replace(array $services): self
覆盖di服务,但保留备份以便在需要时可以恢复(非常适合测试)
$di->replace(['service' => new \MockedService]);
restore(?string $service)
将覆盖的服务恢复到它们的默认值。
$di->restore(); // All $di->restore(['service']); // One
Di.ProvidesDi
di(?string $service): mixed
使用此快捷方式轻松解析di服务。
class AnyClass { use \PhalconExt\Di\ProviesDi; public function anyFn() { $di = $this->di(); $db = $this->di('db'); } }
Http.BaseMiddleware
中间件的基础实现,你可以在此基础上创建自己的中间件。你只需实现一个或两个接收request
和response
对象的before()
&/ 或 after()
方法。请参见Ajax中间件的示例
$di->setShared('config', new \Phalcon\Config([ 'ajax' => [ 'uriPrefix' => '/ajax', ], ]); class Ajax extends \PhalconExt\Http\BaseMiddleware { /** @var string The root key in config having settings for Ajax middleware */ protected $configKey = 'ajax'; /** * For any uri starting with `/ajax`, allow if only it is real ajax request. * * Register as before handler because we will abort before actual exceution if not ajax. * * @return bool */ public function before(Phalcon\Http\Request $request, Phalcon\Http\Response $response): bool { list(, $uri) = $this->getRouteNameUri(); if (\stripos($uri, $this->config['uriPrefix']) !== 0) { return true; } if (!$request->isAjax()) { // Aborts/stops the app. All other middlewares down the line are skipped return $this->abort(400); } return true; } } // Usage is pretty simple: // Create an app! $app = new Phalcon\Mvc\Application($di); // OR micro $app = new Phalcon\Mvc\Micro($di); // Wrap the app with middleware and run it (new PhalconExt\Http\Middlewares([Ajax::class]))->wrap($app);
Http.Middleware.ApiAuth
基于JWT的API身份验证中间件,它拦截POST /api/auth
请求,并根据grant_type
生成或刷新access_token
。对于所有其他请求,它检查Authorization: Bearer <JWT>
,并且只有在它是有效的并且范围满足条件时才允许。你可以根据端点配置范围。你可以通过在整个应用程序中使用以下方式访问当前认证的用户
$di->get('authenticator')->getSubject();
设置
$di->setShared('config', new \Phalcon\Config([ 'apiAuth' => [ // 14 days in seconds (http://stackoverflow.com/questions/15564486/why-do-refresh-tokens-expire-after-14-days) 'refreshMaxAge' => 1209600, // Prefix to use in stored tokens (max 4 chars) 'tokenPrefix' => 'RF/', // The route to generate/refresh access tokens. // genrerate: curl -XPOST -d 'grant_type=password&username=&password=' /api/auth // refresh: curl -XPOST -d 'grant_type=refresh_token&refresh_token=' /api/auth // It can also accept json payload: // -H 'content-type: application/json' -d {"grant_type":"refresh_token","refresh_token":""} 'authUri' => '/api/auth', // The permission scopes required for a route 'scopes' => [ '/some/uri' => 'admin', '/next/uri' => 'user', ], // Json Web tokens configuration. 'jwt' => [ 'keys' => [ // kid => key (first one is default always) 'default' => '*((**@$#@@KJJNN!!#D^G&(U)KOIHIYGTFD', ], 'algo' => 'HS256', // 15 minutes in seconds. 'maxAge' => 900, // Grace time in seconds. 'leeway' => 10, // Only for RS algo. 'passphrase' => '', // Name of the app/project. 'issuer' => '', ], ], ]); // Usage: (new PhalconExt\Http\Middlewares([ PhalconExt\Http\Middleware\ApiAuth::class, ]))->wrap(new Phalcon\Mvc\Micro($di));
Http.Middleware.Cache
通过缓存请求的输出以大幅提高性能。需要redis服务。目前,根据设计,只有GET请求被缓存,这可能会改变。
设置
$di->setShared('config', new \Phalcon\Config([ 'httpCache' => [ // cache life- time to live in mintues 'ttl' => 60, // White listed uri/routes to enable caching 'routes' => [ // for absolute uri, prepend forward `/` '/content/about-us', // or you can use route name without a `/` 'home', ], ], ]);
Http.Middleware.Cors
为配置的源和请求选项启用cors预检。
设置
$di->setShared('config', new \Phalcon\Config([ 'cors' => [ 'exposedHeaders' => [], // Should be in lowercases. 'allowedHeaders' => ['x-requested-with', 'content-type', 'authorization'], // Should be in uppercase. 'allowedMethods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], // Requests originating from here can entertain CORS. 'allowedOrigins' => [ 'http://127.0.0.1:1234', ], // Cache preflight for 7 days (expressed in seconds). 'maxAge' => 604800, ], ]);
Http.Middleware.Throttle
根据你选择的时间和配额限制流量。需要redis服务。
设置
$di->setShared('config', new \Phalcon\Config([ 'throttle' => [ 'maxHits' => [ // Mintues => Max Hits 1 => 10, 60 => 250, 1440 => 4500, ], 'checkUserAgent' => false, // Cache key prefix 'prefix' => '_', ], ]);
使用方法
可以使用PhalconExt\Http\Middlewares
管理器作为应用程序的包装器来使用中间件。
$app = new Phalcon\Mvc\Micro($di); // Set all your middlewares in an array using class FQCN, they are lazily loaded // They are executed in order of their presence // If a middleware returns `false` from its `before()` or `after()` events, // all other middlewares down the line are skipped $middlewares = new PhalconExt\Http\Middlewares([ PhalconExt\Http\Middleware\Throttle::class, PhalconExt\Http\Middleware\ApiAuth::class, PhalconExt\Http\Middleware\Cors::class, PhalconExt\Http\Middleware\Cache::class, ]); // Wrap and run the app! $middlewares->wrap($app); // The app is wrapped and run automatically so you dont have to do: // $app->handle();
Logger.EchoLogger
log(string $message, int $type, array $context = [])
立即回显任何内容 - 但你可以控制格式和日志级别。
$echo = $this->di(\PhalconExt\Logger\EchoLogger::class, ['config' => ['level' => Logger::INFO]]); $echo->log('Message {a}', \Phalcon\Logger::INFO, ['a' => 'ok']);
Logger.LogsToFile
log(string $message, int $type, array $context = [])
将平凡的文件记录任务委托给此特质,从而减少了样板代码。
class AnyClass { use \PhalconExt\Logger\LogsToFile; protected $fileExtension = '.log'; public function anyFn() { $this->activate('/path/to/log/dir/'); $this->log('Some message', \Phalcon\Logger::INFO); } }
Mail.Mailer
Phalcon适配器/桥梁/容器/代理(读取:abcd)到swiftmailer。
设置
$di->setShared('config', new \Phalcon\Config([ 'mail' => [ 'driver' => 'null', 'from' => [ 'name' => 'Test', 'email' => 'test@localhost', ], // for driver 'smtp': 'host' => 'smtp.server.com', 'port' => 425, 'encryption' => true, 'username' => 'user', 'password' => 'pass', // for driver sendmail only (optional) 'sendmail' => '/sendmail/binary', ], ]); $di->setShared('mailer', function () { return new \PhalconExt\Mail\Mailer($this->get('config')->toArray()['mail']); });
Mail.Mail
Swiftmail消息的子类,允许轻松附加附件。
$mail = $di->get('mailer')->newMail(); // Or from view template $mail = $di->get('mailer')->newTemplateMail('view/file.twig', ['view' => 'params']); $mail->setTo('test@localhost')->setSubject('Hi')->setBody('Hello')->mail(); // Attachments: $mail->attachFile('/path/to/file', 'optional attachment name'); $mail->attachFiles(['/path/to/file1', '/path/to/file2']); // OR $mail->attachFiles([ 'attachment name 1' => '/path/to/file1', 'attachment name 2' => '/path/to/file2', ]); $mail->attachRaw('Raw plain text data', 'rawtext.txt', 'text/plain');
Mail.Mailable
mail()
类似于Logger.LogsToFile,但用于邮件。
class AnyClass { use \PhalconExt\Mail\Mailable; public function anyFn() { $this->mail('test@local', 'Hi', ['body' => 'Hello']); $this->mail('test@local', 'Hi', ['template' => 'view/file.twig', 'params' => ['key' => 'value']]); } }
Mail.Logger
自动将所有发送的邮件记录到文件,作为Swiftmailer的事件监听器-您可以选择日志格式:eml | html | json
。
设置
$di->setShared('config', new \Phalcon\Config([ 'mail' => [ 'driver' => 'null', 'from' => [ 'name' => 'Test', 'email' => 'test@localhost', ], 'logger' => [ 'enabled' => true, 'logPath' => __DIR__ . '/.var/mail/', // directory 'type' => 'eml', // options: json, html, eml ], ], ]); // When setting mailer, include config `mail>logger` and it is auto set up. $di->setShared('mailer', function () { return new \PhalconExt\Mail\Mailer($this->get('config')->toArray()['mail']); });
Util.OpcachePrimer
prime(array $paths): int
确保在文件执行之前很好地预热给定路径中的所有文件的opcache。Opcache缓存特定于运行它的sapi。因此,对于Web,您需要有一个端点
$primer = new \PhalconExt\Util\OpcachePrimer; $total = $primer->prime(['/path/to/project/src', '/path/to/project/app/', '/path/to/project/vendor/']);
Validation.Validation
验证数据,就像我们在其他地方做的那样-将规则设置为.well-known字符串或key=>value对(数组)。
设置
$di->setShared('validation', \PhalconExt\Validation\Validation::class);
register(string $ruleName, $handler, string $message = ''): self
注册一个新的验证规则。
$di->get('validation')->register('gmail', function ($data) { // You can access current validation instance with `$this` // You can also access current validator options with `$this->getOption(...)` return stripos($this->getCurrentValue(), '@gmail.com') > 0; }, 'Field :field must be an email with @gmail.com');
registerRules(array $ruleHandlers, array $messages = []): self
一次注册多个新的验证规则。
$di->get('validation')->registerRules([ 'rule1' => function($data) { return true; }, 'rule1' => function($data) { return false; }, ], [ 'rule1' => 'message1', 'rule2' => 'message2' ]);
使用方法
$validation = $this->di('validation'); $rules = [ // Can be string (With `abort` if the field `id` is invalid, following validations are aborted) 'id' => 'required|length:min:1;max:2;|in:domain:1,12,30|abort', // Can be an array too 'email' => [ 'required' => true, 'gmail' => true, // With `abort` if the field `email` is invalid, following validations are aborted 'abort' => true, ], // validate if only exist in dataset 'xyz' => 'length:5|if_exist', ]; // Validate against empty data (can be array or object) $data = []; // OR $data = new \stdClas OR $data = new SomeClass($someData) $validation->run($rules, $data); $pass = $validation->pass(); // false $fail = $validation->fail(); // true $errors = $validation->getErrorMessages(); // array
Validation.Existence
验证是否在数据库中存在某物。您可以可选地设置要检查的表和列。
// Checks `users` table for `id` column with value 1 $rules = ['users' => 'exist']; $data = ['users' => 1]; // Data can be array // Checks `users` table for `username` column with value 'admin' $rules = ['username' => 'exist:table:users']; $data = new User(['username' => 'admin']); // Data can be model/entity // Checks `users` table for `login` column with value 'admin@localhost' $rules = ['email' => 'exist:table:users;column:login']; $data = (object) ['email' => 'admin@localhost']; // Data can be any Object // Run the rules $validation->run($rules, $data);
View.Twig
在Phalcon中原生使用twig视图。
设置
$di->setShared('config', new \Phalcon\Config([ 'view' => [ 'dir' => __DIR__ . '/view/', ], // Required 'twig' => [ 'view_dirs' => [__DIR__ . '/view/'], // array 'auto_reload' => getenv('APP_ENV') !== 'prod', 'cache' => __DIR__ . '/.var/view/', // ... other options (see twig docs) ], ]); // You must have view setup with twig engine enabled. $di->setShared('view', function () { return (new View) ->setViewsDir($this->get('config')->toArray()['view']['dir']) ->registerEngines([ '.twig' => 'twig', ]); }); $di->setShared('twig', function () { $twig = new PhalconExt\View\Twig($this->get('view'), $this); // Here you can: // $twig->addFilter(...) // $twig->addExtension(...) return $twig; });
使用方法
// standalone $di->get('twig')->render('template.twig', ['view' => 'params']); // or as view $di->get('view')->render('template.twig', ['view' => 'params']); // .twig is optional
相关项目
许可
© 2017-2020, Jitendra Adhikari | MIT
鸣谢
此项目由please管理。