From e6e42e6b799d8875370f601cd8a1750e6360a751 Mon Sep 17 00:00:00 2001 From: mr5 Date: Wed, 17 Jun 2015 15:15:48 +0800 Subject: [PATCH 1/2] Refactor module manager --- .gitignore | 4 +- .phpstorm.meta.php | 39 + Module.php | 72 + bootstrap.php | 402 +++ composer.json | 8 +- config/config.cache.php | 96 + config/config.database.php | 45 + config/config.php | 47 + config/di.php | 376 +++ config/routes.backend.php | 10 + config/routes.cli.php | 11 + config/routes.frontend.php | 9 + di.php | 0 phpunit.php | 49 + src/EvaEngine/Application.php | 133 + .../CLI/Formatter/OutputFormatter.php | 239 -- .../Formatter/OutputFormatterInterface.php | 83 - .../CLI/Formatter/OutputFormatterStyle.php | 235 -- .../OutputFormatterStyleInterface.php | 72 - .../Formatter/OutputFormatterStyleStack.php | 121 - src/EvaEngine/CLI/Output/BufferedOutput.php | 48 - src/EvaEngine/CLI/Output/ConsoleOutput.php | 170 - .../CLI/Output/ConsoleOutputInterface.php | 35 - src/EvaEngine/CLI/Output/NullOutput.php | 113 - src/EvaEngine/CLI/Output/Output.php | 168 - src/EvaEngine/CLI/Output/OutputInterface.php | 113 - src/EvaEngine/CLI/Output/StreamOutput.php | 107 - src/EvaEngine/Cache/CacheAdapterCreator.php | 87 + src/EvaEngine/Cache/RedisDisabled.php | 3001 +++++++++++++++++ src/EvaEngine/Db/DbAdapterCreator.php | 50 + src/EvaEngine/DevTool/IDE/Facade/Alias.php | 277 ++ .../DevTool/IDE/Facade/Generator.php | 209 ++ src/EvaEngine/DevTool/IDE/Facade/Method.php | 277 ++ src/EvaEngine/Engine.php | 25 +- src/EvaEngine/Error/CLIErrorHandler.php | 8 +- src/EvaEngine/Error/DispatchListener.php | 22 + src/EvaEngine/Facades/Annotations.php | 18 + src/EvaEngine/Facades/Config.php | 18 + src/EvaEngine/Facades/Cookies.php | 18 + src/EvaEngine/Facades/Crypt.php | 18 + src/EvaEngine/Facades/Db.php | 18 + src/EvaEngine/Facades/Dispatcher.php | 19 + src/EvaEngine/Facades/Escaper.php | 18 + src/EvaEngine/Facades/Event.php | 18 + src/EvaEngine/Facades/FacadeBase.php | 222 ++ src/EvaEngine/Facades/FlashSession.php | 17 + src/EvaEngine/Facades/GlobalCache.php | 18 + src/EvaEngine/Facades/ModelsCache.php | 17 + src/EvaEngine/Facades/ModelsManager.php | 18 + src/EvaEngine/Facades/ModelsMetadata.php | 18 + src/EvaEngine/Facades/Request.php | 17 + src/EvaEngine/Facades/Response.php | 17 + src/EvaEngine/Facades/Router.php | 18 + src/EvaEngine/Facades/Security.php | 18 + src/EvaEngine/Facades/Session.php | 17 + src/EvaEngine/Facades/Tag.php | 18 + src/EvaEngine/Facades/TransactionManager.php | 18 + src/EvaEngine/Facades/Url.php | 18 + src/EvaEngine/Facades/View.php | 17 + src/EvaEngine/Facades/ViewCache.php | 17 + src/EvaEngine/Foundation/AdapterCreator.php | 64 + src/EvaEngine/IoC.php | 2 +- .../Listeners/DbQueryLoggingListener.php | 37 + src/EvaEngine/Listeners/DispatchListener.php | 20 + src/EvaEngine/Logger/Multiple.php | 47 + src/EvaEngine/Module/AbstractModule.php | 18 + src/EvaEngine/Module/Manager.bak.php | 615 ++++ src/EvaEngine/Module/Manager.php | 582 +--- src/EvaEngine/Module/Module.bak.php | 104 + src/EvaEngine/Module/Module.php | 345 ++ src/EvaEngine/Module/ModuleParser.php | 267 ++ .../Mvc/Controller/ControllerBase.php | 4 +- .../Model/ModelsMetadataAdapterCreator.php | 44 + .../Session/SessionAdapterCreator.php | 77 + src/EvaEngine/Tasks/TaskBase.php | 4 +- src/EvaEngine/View/ViewHelper.php | 219 ++ src/EvaEngine/helpers.php | 54 +- test.php | 16 + tests/EvaEngineTest/Module/ModuleTest.php | 18 + views/ide/helper.phtml | 39 + views/ide/meta.phtml | 22 + 81 files changed, 8054 insertions(+), 1935 deletions(-) create mode 100644 .phpstorm.meta.php create mode 100644 Module.php create mode 100644 bootstrap.php create mode 100644 config/config.cache.php create mode 100644 config/config.database.php create mode 100644 config/config.php create mode 100644 config/di.php create mode 100644 config/routes.backend.php create mode 100644 config/routes.cli.php create mode 100644 config/routes.frontend.php create mode 100644 di.php create mode 100644 phpunit.php create mode 100644 src/EvaEngine/Application.php delete mode 100755 src/EvaEngine/CLI/Formatter/OutputFormatter.php delete mode 100755 src/EvaEngine/CLI/Formatter/OutputFormatterInterface.php delete mode 100755 src/EvaEngine/CLI/Formatter/OutputFormatterStyle.php delete mode 100755 src/EvaEngine/CLI/Formatter/OutputFormatterStyleInterface.php delete mode 100755 src/EvaEngine/CLI/Formatter/OutputFormatterStyleStack.php delete mode 100755 src/EvaEngine/CLI/Output/BufferedOutput.php delete mode 100755 src/EvaEngine/CLI/Output/ConsoleOutput.php delete mode 100755 src/EvaEngine/CLI/Output/ConsoleOutputInterface.php delete mode 100755 src/EvaEngine/CLI/Output/NullOutput.php delete mode 100755 src/EvaEngine/CLI/Output/Output.php delete mode 100755 src/EvaEngine/CLI/Output/OutputInterface.php delete mode 100755 src/EvaEngine/CLI/Output/StreamOutput.php create mode 100644 src/EvaEngine/Cache/CacheAdapterCreator.php create mode 100644 src/EvaEngine/Cache/RedisDisabled.php create mode 100644 src/EvaEngine/Db/DbAdapterCreator.php create mode 100644 src/EvaEngine/DevTool/IDE/Facade/Alias.php create mode 100644 src/EvaEngine/DevTool/IDE/Facade/Generator.php create mode 100644 src/EvaEngine/DevTool/IDE/Facade/Method.php create mode 100644 src/EvaEngine/Error/DispatchListener.php create mode 100644 src/EvaEngine/Facades/Annotations.php create mode 100644 src/EvaEngine/Facades/Config.php create mode 100644 src/EvaEngine/Facades/Cookies.php create mode 100644 src/EvaEngine/Facades/Crypt.php create mode 100644 src/EvaEngine/Facades/Db.php create mode 100644 src/EvaEngine/Facades/Dispatcher.php create mode 100644 src/EvaEngine/Facades/Escaper.php create mode 100644 src/EvaEngine/Facades/Event.php create mode 100644 src/EvaEngine/Facades/FacadeBase.php create mode 100644 src/EvaEngine/Facades/FlashSession.php create mode 100644 src/EvaEngine/Facades/GlobalCache.php create mode 100644 src/EvaEngine/Facades/ModelsCache.php create mode 100644 src/EvaEngine/Facades/ModelsManager.php create mode 100644 src/EvaEngine/Facades/ModelsMetadata.php create mode 100644 src/EvaEngine/Facades/Request.php create mode 100644 src/EvaEngine/Facades/Response.php create mode 100644 src/EvaEngine/Facades/Router.php create mode 100644 src/EvaEngine/Facades/Security.php create mode 100644 src/EvaEngine/Facades/Session.php create mode 100644 src/EvaEngine/Facades/Tag.php create mode 100644 src/EvaEngine/Facades/TransactionManager.php create mode 100644 src/EvaEngine/Facades/Url.php create mode 100644 src/EvaEngine/Facades/View.php create mode 100644 src/EvaEngine/Facades/ViewCache.php create mode 100644 src/EvaEngine/Foundation/AdapterCreator.php create mode 100644 src/EvaEngine/Listeners/DbQueryLoggingListener.php create mode 100644 src/EvaEngine/Listeners/DispatchListener.php create mode 100644 src/EvaEngine/Logger/Multiple.php create mode 100644 src/EvaEngine/Module/Manager.bak.php create mode 100644 src/EvaEngine/Module/Module.bak.php create mode 100644 src/EvaEngine/Module/Module.php create mode 100644 src/EvaEngine/Module/ModuleParser.php create mode 100644 src/EvaEngine/Mvc/Model/ModelsMetadataAdapterCreator.php create mode 100644 src/EvaEngine/Session/SessionAdapterCreator.php create mode 100644 src/EvaEngine/View/ViewHelper.php create mode 100644 test.php create mode 100644 tests/EvaEngineTest/Module/ModuleTest.php create mode 100644 views/ide/helper.phtml create mode 100644 views/ide/meta.phtml diff --git a/.gitignore b/.gitignore index 65cade7..9d50eba 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ vendor/* log/* .idea* *.swp -/.idea/ \ No newline at end of file +/.idea/ +/.phpstorm.meta.php +*-ide-helper.php \ No newline at end of file diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php new file mode 100644 index 0000000..c2f4698 --- /dev/null +++ b/.phpstorm.meta.php @@ -0,0 +1,39 @@ + [ + 'annotations' instanceof \Phalcon\Annotations\Adapter\Memory, + 'config' instanceof \Phalcon\Config, + 'cookies' instanceof \Phalcon\Http\Response\Cookies, + 'crypt' instanceof \Phalcon\Crypt, + 'dispatcher' instanceof \Phalcon\Mvc\Dispatcher, + 'escaper' instanceof \Phalcon\Escaper, + 'eventsManager' instanceof \Phalcon\Events\Manager, + 'flashSession' instanceof \Phalcon\Flash\Session, + 'globalCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, + 'modelsCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, + 'modelsManager' instanceof \Eva\EvaEngine\Mvc\Model\Manager, + 'modelsMetadata' instanceof \Phalcon\Mvc\Model\MetaData\Files, + 'request' instanceof \Phalcon\Http\Request, + 'response' instanceof \Phalcon\Http\Response, + 'router' instanceof \Phalcon\Mvc\Router, + 'security' instanceof \Phalcon\Security, + 'session' instanceof \Phalcon\Session\Adapter\Files, + 'tag' instanceof \Eva\EvaEngine\Tag, + 'transactionManager' instanceof \Phalcon\Mvc\Model\Transaction\Manager, + 'url' instanceof \Eva\EvaEngine\Mvc\Url, + 'viewCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, + 'view' instanceof \Eva\EvaEngine\Mvc\View, + ], + ]; +} \ No newline at end of file diff --git a/Module.php b/Module.php new file mode 100644 index 0000000..4933b6f --- /dev/null +++ b/Module.php @@ -0,0 +1,72 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/5/22 下午5:19 +// +---------------------------------------------------------------------- +// + Module.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine; + +use Eva\EvaEngine\Module\AbstractModule; +use Eva\EvaEngine\Module\Manager as ModuleManager; +use Eva\EvaEngine\Module\StandardInterface; +use Phalcon\Events\Manager as EventsManager; + +class Module implements StandardInterface +{ + /** + * Registers an autoloader related to the module + * + * @param \Phalcon\DiInterface $dependencyInjector + */ + public function registerAutoloaders() + { + // TODO: Implement registerAutoloaders() method. + } + + /** + * Registers services related to the module + * + * @param \Phalcon\DiInterface $dependencyInjector + */ + public function registerServices($dependencyInjector) + { + // TODO: Implement registerServices() method. + } + + /** + * @return void + */ + public static function registerGlobalAutoloaders() + { + // TODO: Implement registerGlobalAutoloaders() method. + } + + /** + * @return void + */ + public static function registerGlobalEventListeners() + { + // TODO: Implement registerGlobalEventListeners() method. + } + + /** + * @return void + */ + public static function registerGlobalViewHelpers() + { + // TODO: Implement registerGlobalViewHelpers() method. + } + + /** + * @return array + */ + public static function registerGlobalRelations() + { + return array(); + } +} \ No newline at end of file diff --git a/bootstrap.php b/bootstrap.php new file mode 100644 index 0000000..7f9aedf --- /dev/null +++ b/bootstrap.php @@ -0,0 +1,402 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/11 下午3:31 +// +---------------------------------------------------------------------- +// + bootstrap.php +// +---------------------------------------------------------------------- + +/** @var \Phalcon\DI $di */ +$di->setShared( + 'eventsManager', + function () use ($di) { + $eventsManager = new \Phalcon\Events\Manager(); + $eventsManager->enablePriorities(true); + + return $eventsManager; + } +); +$di->setShared('fastCache', function () use ($di) { + $config = $this->getDI()->getConfig(); + if (!($config->cache->fastCache->enable)) { + return false; + } + + $redis = new \Redis(); + $redis->connect( + $config->cache->fastCache->host, + $config->cache->fastCache->port, + $config->cache->fastCache->timeout + ); + + return $redis; +}); + + +/* +|-------------------------------------------------------------------------- +| 视图缓存 +|-------------------------------------------------------------------------- +| 主要用于存储页面级别的缓存,默认地,EvaEngine 内置的 RouterCache 使用的是 ViewCache +| +*/ +$di->setShared('viewCache', function () use ($di) { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig($di->getConfig(), 'viewCache'); + +}); + +/* +|-------------------------------------------------------------------------- +| 模型缓存 +|-------------------------------------------------------------------------- +| 主要用于存储数据库结果集 +| +*/ +$di->setShared('modelsCache', function () use ($di) { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig($di->getConfig(), 'modelsCache'); +}); + +/* +|-------------------------------------------------------------------------- +| API 缓存 +|-------------------------------------------------------------------------- +| 主要缓存 API 结果 +| +*/ +$di->setShared('apiCache', function () use ($di) { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig($di->getConfig(), 'apiCache'); +}); +/* +|-------------------------------------------------------------------------- +| 通用缓存 +|-------------------------------------------------------------------------- +| 当上面定义的缓存服务不适用当前语境时,可以使用通用缓存,或者通过 +| `\Eva\EvaEngine\Cache\CacheAdapterCreator` 创建一个自己的缓存服务 +| +*/ +$di->setShared('globalCache', function () use ($di) { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig($di->getConfig(), 'globalCache'); +}); + +/* +|-------------------------------------------------------------------------- +| 配置服务 +|-------------------------------------------------------------------------- +| //TODO 完善「配置」的文档 +| +| +*/ +$di->setShared( + 'config', + function () use ($self) { + return $self->diConfig(); + } +); +/* +|-------------------------------------------------------------------------- +| 默认的路由服务 +|-------------------------------------------------------------------------- +| //TODO 完善「路由」的文档 +| +| +*/ +$di->setShared( + 'router', + function () use ($self) { + return $self->diRouter(); + } +); +/* +|-------------------------------------------------------------------------- +| 默认的分发器服务 +|-------------------------------------------------------------------------- +| //TODO 完善「分发器」的文档 +| +| +*/ +$di->setShared( + 'dispatcher', + function () use ($di) { + $dispatcher = new \Phalcon\Mvc\Dispatcher(); + $dispatcher->setEventsManager($di->getEventsManager()); + + return $dispatcher; + } +); + +$di->setShared( + 'modelsMetadata', + function () use ($di) { + $config = $di->getConfig(); + $metadataConfig = $config->modelsMetadata; + + return (new \Eva\EvaEngine\Mvc\Model\ModelsMetadataAdapterCreator()) + ->create($metadataConfig->adapter, $metadataConfig->options->toArray()); + } +); + +$di->setShared( + 'modelsManager', + function () use ($di) { + $config = $di->getConfig(); + //for solving db master/slave under static find method + \Eva\EvaEngine\Mvc\Model\Manager::setDefaultPrefix($config->dbAdapter->prefix); + $modelsManager = new \Eva\EvaEngine\Mvc\Model\Manager(); + + return $modelsManager; + } +); + +$di->setShared( + 'view', + function () use ($di) { + $view = new \Eva\EvaEngine\Mvc\View(); + $view->setViewsDir(__DIR__ . '/views/'); + $view->setEventsManager($di->getEventsManager()); + + return $view; + } +); + +$di->setShared( + 'session', + function () use ($di) { + $config = $di->getConfig(); + $sessionConfig = $config->session; + + return (new \Eva\EvaEngine\Session\SessionAdapterCreator())->create( + $sessionConfig->adapter, + $sessionConfig->options->toArray(), + $sessionConfig->session_name, + $sessionConfig->cookie_params + ); + } +); + +$di->setShared( + 'tokenStorage', + function () use ($self) { + return $self->diTokenStorage(); + } +); + + +$di->setShared( + 'dbMaster', + function () use ($di) { + $config = $di->getConfig(); + if (!isset($config->dbAdapter->master->adapter) || !$config->dbAdapter->master) { + throw new \Eva\EvaEngine\Exception\RuntimeException(sprintf('No DB Master options found')); + } + + return (new \Eva\EvaEngine\Db\DbAdapterCreator())->create( + $config->dbAdapter->master->adapter, + $config->dbAdapter->master->options->toArray(), + $di->getEventsManager() + ); + } +); + +$di->setShared( + 'dbSlave', + function () use ($di) { + $config = $di->getConfig(); + if (!isset($config->dbAdapter->slave->adapter) || !$config->dbAdapter->slave) { + throw new \Eva\EvaEngine\Exception\RuntimeException(sprintf('No DB Master options found')); + } + + return (new \Eva\EvaEngine\Db\DbAdapterCreator())->create( + $config->dbAdapter->slave->adapter, + $config->dbAdapter->slave->options->toArray(), + $di->getEventsManager() + ); + } +); + +$di->setShared( + 'transactions', + function () use ($di) { + $transactions = new \Phalcon\Mvc\Model\Transaction\Manager(); + $transactions->setDbService('dbMaster'); + + return $transactions; + } +); + + +$di->setShared( + 'queue', + function () use ($di) { + $config = $di->getConfig(); + $client = new \GearmanClient(); + $client->setTimeout(1000); + foreach ($config->queue->servers as $key => $server) { + $client->addServer($server->host, $server->port); + } + + return $client; + } +); + +$di->setShared( + 'worker', + function () use ($di) { + $config = $di->getConfig(); + $worker = new \GearmanWorker(); + foreach ($config->queue->servers as $key => $server) { + $worker->addServer($server->host, $server->port); + } + + return $worker; + } +); + + +/********************************** + * DI initialize for email + ***********************************/ +$di->setShared( + 'mailer', + function () use ($self) { + return $self->diMailer(); + } +); + +$di->set('mailMessage', 'Eva\EvaEngine\MailMessage'); + +$di->setShared( + 'smsSender', + function () use ($self) { + return $self->diSmsSender(); + } +); +/********************************** + * DI initialize for helpers + ***********************************/ +$di->setShared( + 'url', + function () use ($di) { + $config = $di->getConfig(); + $url = new Eva\EvaEngine\Mvc\Url(); + $url->setVersionFile($config->staticBaseUriVersionFile); + $url->setBaseUri($config->baseUri); + $url->setStaticBaseUri($config->staticBaseUri); + + return $url; + } +); + +$di->set('escaper', 'Phalcon\Escaper'); + +$di->setShared( + 'tag', + function () use ($di, $self) { + \Eva\EvaEngine\Tag::setDi($di); + $self->registerViewHelpers(); + + return new Tag(); + } +); + +$di->setShared('flash', 'Phalcon\Flash\Session'); + +$di->set('placeholder', 'Eva\EvaEngine\View\Helper\Placeholder'); + +$di->setShared( + 'cookies', + function () { + $cookies = new \Phalcon\Http\Response\Cookies(); + $cookies->useEncryption(false); + + return $cookies; + } +); + +$di->setShared( + 'translate', + function () use ($di) { + $config = $di->getConfig(); + $file = $config->translate->path . $config->translate->forceLang . '.csv'; + if (false === file_exists($file)) { + //empty translator + return new \Phalcon\Translate\Adapter\NativeArray( + array( + 'content' => array() + ) + ); + } + $translate = new \Phalcon\Translate\Adapter\Csv( + array( + 'file' => $file, + 'delimiter' => ',', + ) + ); + + return $translate; + } +); + +$di->set( + 'fileSystem', + function () use ($di) { + return $di->diFileSystem(); + } +); +/* +|-------------------------------------------------------------------------- +| error 日志服务 +|-------------------------------------------------------------------------- +| 主要用于记录各种 error 日志信息,EvaEngine 默认的 ErrorHandler 依赖 errorLogger +| 服务,用于记录未捕捉的异常。 +| +*/ +$di->set( + 'errorLogger', + function () use ($di) { + $config = $di->getConfig(); + + return new Phalcon\Logger\Adapter\File(rtrim($config->logger->path, '/') . '/error.' . date('Y-m-d') . '.log'); + } +); +/* +|-------------------------------------------------------------------------- +| debug 日志服务 +|-------------------------------------------------------------------------- +| 主要用于记录各种 debug 日志信息 +| +*/ +$di->set( + 'debugLogger', + function () use ($di) { + $config = $di->getConfig(); + + return new Phalcon\Logger\Adapter\File(rtrim($config->logger->path, '/') . '/debug.' . date('Y-m-d') . '.log'); + } +); +/* +|-------------------------------------------------------------------------- +| 数据库查询日志记录服务 +|-------------------------------------------------------------------------- +| 默认地,EvaEngine 会监听 db:beforeQuery 事件,并通过 +| $dbAdapter->getSQLStatement() 获取每条执行的 SQL 语句,然后产生一条 INFO 级别 +| 的日志,通过 dbQueryLogger 来记录 SQL 查询记录。 +*/ +$di->setShared('dbQueryLogger', function () use ($di) { + $config = $di->getConfig(); + + return new Phalcon\Logger\Adapter\File($config->logger->path . 'db_query.' . date('Y-m-d') . '.log'); +}); +/* +|-------------------------------------------------------------------------- +| FirePHP 调试服务 +|-------------------------------------------------------------------------- +| firephp 服务是一个标准的日志实现,可以通过浏览器 firephp 插件获取到服务器上产生的 +| firephp 日志 +| +*/ +$di->setShared('firephp', function () { + return new \Phalcon\Logger\Adapter\Firephp(); +}); \ No newline at end of file diff --git a/composer.json b/composer.json index 6312ab1..1f16874 100644 --- a/composer.json +++ b/composer.json @@ -15,10 +15,14 @@ "phalcon/incubator": "~1.3", "swiftmailer/swiftmailer": "v5.3.0", "knplabs/gaufrette": "v0.1.8", - "evaengine/eva-sms": "0.1.*" + "evaengine/eva-sms": "0.1.*", + "symfony/console": "^2.6", + "phpdocumentor/reflection-docblock": "^2.0", + "doctrine/annotations": "^1.2" }, "require-dev": { - "satooshi/php-coveralls": "dev-master" + "satooshi/php-coveralls": "dev-master", + "mockery/mockery": "^0.9.4" }, "suggest": { "ext-redis": "*", diff --git a/config/config.cache.php b/config/config.cache.php new file mode 100644 index 0000000..344604a --- /dev/null +++ b/config/config.cache.php @@ -0,0 +1,96 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午4:17 +// +---------------------------------------------------------------------- +// + config.cache.php +// +---------------------------------------------------------------------- + +return array( + 'enable' => false, + // 对应到 systemCache service + 'systemCache' => array( + 'enable' => true, + 'frontend' => array( + 'adapter' => 'Data', + 'options' => array(), + ), + 'backend' => array( + 'adapter' => 'File', + 'options' => array( + 'cacheDir' => __DIR__ . '/../cache/system/', + 'prefix' => 'system/' + ), + ), + ), + // 对应到 fastCache service + 'fastCache' => array( + 'enable' => true, + 'host' => '127.0.0.1', + 'port' => 6379, + 'timeout' => 1, + ), + // 对应到 globalCache service + 'globalCache' => array( + 'enable' => true, + 'frontend' => array( + 'adapter' => 'Data', + 'options' => array(), + ), + 'backend' => array( + 'adapter' => 'File', + 'options' => array( + 'cacheDir' => __DIR__ . '/../cache/global/', + 'prefix' => 'global:' + ), + ), + ), + // 对应到 viewCache service + 'viewCache' => array( + 'enable' => true, + 'frontend' => array( + 'adapter' => 'Output', + 'options' => array(), + ), + 'backend' => array( + 'adapter' => 'File', + 'options' => array( + 'cacheDir' => __DIR__ . '/../cache/view/', + 'prefix' => 'view:' + ), + ), + ), + // 对应到 modelsCache service + 'modelsCache' => array( + 'enable' => true, + 'frontend' => array( + 'adapter' => 'Data', + 'options' => array(), + ), + 'backend' => array( + 'adapter' => 'File', + 'options' => array( + 'cacheDir' => __DIR__ . '/../cache/model/', + 'prefix' => 'models:' + ), + ), + ), + // 对应到 apiCache service + 'apiCache' => array( + 'enable' => true, + 'frontend' => array( + 'adapter' => 'Json', + 'options' => array(), + ), + 'backend' => array( + 'adapter' => 'File', + 'options' => array( + 'cacheDir' => __DIR__ . '/../cache/api/', + 'prefix' => 'api:' + ), + ), + ), +); \ No newline at end of file diff --git a/config/config.database.php b/config/config.database.php new file mode 100644 index 0000000..e3386d4 --- /dev/null +++ b/config/config.database.php @@ -0,0 +1,45 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午4:16 +// +---------------------------------------------------------------------- +// + config.database.php +// +---------------------------------------------------------------------- + +return array( + 'adapter' => array( + 'prefix' => 'eva_', + 'master' => array(/* + 'adapter' => 'mysql', + 'host' => '192.168.1.228', + 'dbname' => 'eva', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'prefix' => 'eva_', + */ + ), + 'slave' => array(/* + 'slave1' => array( + 'adapter' => 'mysql', + 'host' => '192.168.1.233', + 'dbname' => 'eva', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'prefix' => 'eva_', + ), + */ + ) + ), + 'modelsMetadata' => array( + 'enable' => true, + 'adapter' => 'files', + 'options' => array( + 'metaDataDir' => __DIR__ . '/../cache/schema/' + ), + ), +); \ No newline at end of file diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..c2725ca --- /dev/null +++ b/config/config.php @@ -0,0 +1,47 @@ + true, + 'baseUri' => '/', + 'staticBaseUri' => '/', + 'staticBaseUriVersionFile' => __DIR__ . '/../.git/FETCH_HEAD', + 'session' => array( + 'adapter' => 'files', + 'options' => array( + 'lifetime' => 3600 * 3, //3 hours + ), + ), + 'alias' => array( + 'Annotations' => \Eva\EvaEngine\Facades\Annotations::class, + 'Config' => \Eva\EvaEngine\Facades\Config::class, + 'Cookies' => \Eva\EvaEngine\Facades\Cookies::class, + 'Crypt' => \Eva\EvaEngine\Facades\Crypt::class, +// \Eva\EvaEngine\Facades\Db::class, + 'Dispatcher' => \Eva\EvaEngine\Facades\Dispatcher::class, + 'Escaper' => \Eva\EvaEngine\Facades\Escaper::class, + 'Event' => \Eva\EvaEngine\Facades\Event::class, + 'FlashSession' => \Eva\EvaEngine\Facades\FlashSession::class, + 'GlobalCache' => \Eva\EvaEngine\Facades\GlobalCache::class, + 'ModelsCache' => \Eva\EvaEngine\Facades\ModelsCache::class, + 'ModelsManager' => \Eva\EvaEngine\Facades\ModelsManager::class, + 'ModelsMetadata' => \Eva\EvaEngine\Facades\ModelsMetadata::class, + 'Request' => \Eva\EvaEngine\Facades\Request::class, + 'Response' => \Eva\EvaEngine\Facades\Response::class, + 'Router' => \Eva\EvaEngine\Facades\Router::class, + 'Security' => \Eva\EvaEngine\Facades\Security::class, + 'Session' => \Eva\EvaEngine\Facades\Session::class, + 'Tag' => \Eva\EvaEngine\Facades\Tag::class, + 'TransactionManager' => \Eva\EvaEngine\Facades\TransactionManager::class, + 'Url' => \Eva\EvaEngine\Facades\Url::class, + 'ViewCache' => \Eva\EvaEngine\Facades\ViewCache::class, + 'View' => \Eva\EvaEngine\Facades\View::class, + ), + +); \ No newline at end of file diff --git a/config/di.php b/config/di.php new file mode 100644 index 0000000..0ea7a79 --- /dev/null +++ b/config/di.php @@ -0,0 +1,376 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午3:50 +// +---------------------------------------------------------------------- +// + config.di.php +// +---------------------------------------------------------------------- + + +use Phalcon\Mvc\Router; + +return array( + /** + * disposable DI 在每次 get 的时候都会重新构造一个对象 + */ + 'disposables' => array( + 'mailMessage' => 'Eva\EvaEngine\MailMessage', + 'placeholder' => 'Eva\EvaEngine\View\Helper\Placeholder', + ), + /** + * shares DI 是共享的服务,每次获取的对象都是同一个,类似单例。 + */ + 'shares' => array( + 'eventsManager' => function () { + $eventsManager = new \Phalcon\Events\Manager(); + $eventsManager->enablePriorities(true); + + return $eventsManager; + }, + 'fastCache' => function () { + $config = eva_get('config'); + if (!($config->cache->fastCache->enable)) { + return new \Eva\EvaEngine\Cache\RedisDisabled(); + } + + $redis = new \Redis(); + $redis->connect( + $config->cache->fastCache->host, + $config->cache->fastCache->port, + $config->cache->fastCache->timeout + ); + + return $redis; + }, + /* + |-------------------------------------------------------------------------- + | 全局的 Redis 服务 + |-------------------------------------------------------------------------- + | fastCache 将会修改为普通的 cache + | + */ + 'redis' => function () { + $config = eva_get('config'); + + $redis = new \Redis(); + $redis->connect( + $config->redis->host, + $config->redis->port, + $config->redis->timeout + ); + if (isset($config->redis->db)) { + $redis->select($config->redis->db); + } + + return $redis; + }, + /* + |-------------------------------------------------------------------------- + | 视图缓存 + |-------------------------------------------------------------------------- + | 主要用于存储页面级别的缓存,默认地,EvaEngine 内置的 RouterCache 中间件使用的是 ViewCache + | + */ + 'viewCache' => function () { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig(eva_get('config'), 'viewCache'); + + }, + /* + |-------------------------------------------------------------------------- + | 模型缓存 + |-------------------------------------------------------------------------- + | 主要用于存储数据库结果集 + | + */ + 'modelsCache' => function () { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig(eva_get('config'), 'modelsCache'); + }, + /* + |-------------------------------------------------------------------------- + | API 缓存 + |-------------------------------------------------------------------------- + | 主要缓存 API 结果 + | + */ + 'apiCache' => function () { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig(eva_get('config'), 'apiCache'); + }, + /* + |-------------------------------------------------------------------------- + | 通用缓存 + |-------------------------------------------------------------------------- + | 当上面定义的缓存服务不适用当前语境时,可以使用通用缓存,或者通过 + | `\Eva\EvaEngine\Cache\CacheAdapterCreator` 创建一个自己的缓存服务 + | + */ + 'globalCache' => function () { + return (new \Eva\EvaEngine\Cache\CacheAdapterCreator)->createFromConfig(eva_get('config'), 'globalCache'); + }, + /* + |-------------------------------------------------------------------------- + | 配置服务 + |-------------------------------------------------------------------------- + | //TODO 完善「配置」的文档 + | + | + */ + + 'config' => function () { + /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ + $moduleManager = eva_get('moduleManager'); + + return $moduleManager->getAllConfig(); + }, + /* + |-------------------------------------------------------------------------- + | 默认的路由服务 + |-------------------------------------------------------------------------- + | //TODO 完善「路由」的文档 + | + | + */ + 'router' => function () { + //Disable default router + $router = new Router(false); + //Last extra slash + $router->removeExtraSlashes(true); + //Set last module as default module + /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ + $moduleManager = eva_get('moduleManager'); + $router->setDefaultModule($moduleManager->getDefaultModule()->getName()); + //NOTICE: Set a strange controller here to make router not match default index/index + $router->setDefaultController('EvaEngineDefaultController'); + + //NOTICE: EvaEngine Load front-end router at last + $routes = $moduleManager->getAllRoutesBackend(); + $routes->merge($moduleManager->getAllRoutesFrontend()); + + foreach ($routes->toArray() as $url => $route) { + if (count($route) !== count($route, COUNT_RECURSIVE)) { + if (isset($route['pattern']) && isset($route['paths'])) { + $method = isset($route['httpMethods']) ? $route['httpMethods'] : null; + $router->add($route['pattern'], $route['paths'], $method); + } else { + throw new RuntimeException( + sprintf('No route pattern and paths found by route %s', $url) + ); + } + } else { + $router->add($url, $route); + } + } + }, + /* + |-------------------------------------------------------------------------- + | 默认的分发器服务 + |-------------------------------------------------------------------------- + | //TODO 完善「分发器」的文档 + | + | + */ + 'dispatcher' => + function () { + $dispatcher = new \Phalcon\Mvc\Dispatcher(); + $dispatcher->setEventsManager(eva_get('eventsManager')); + + return $dispatcher; + }, + 'modelsMetadata' => function () { + $config = eva_get('config'); + $metadataConfig = $config->modelsMetadata; + + return (new \Eva\EvaEngine\Mvc\Model\ModelsMetadataAdapterCreator()) + ->create($metadataConfig->adapter, $metadataConfig->options->toArray()); + }, + 'modelsManager' => function () { + $config = eva_get('config'); + //for solving db master/slave under static find method + \Eva\EvaEngine\Mvc\Model\Manager::setDefaultPrefix($config->dbAdapter->prefix); + $modelsManager = new \Eva\EvaEngine\Mvc\Model\Manager(); + + return $modelsManager; + }, + 'view' => function () { + $view = new \Eva\EvaEngine\Mvc\View(); + $view->setViewsDir(__DIR__ . '/views/'); + $view->setEventsManager(eva_get('eventsManager')); + + return $view; + }, + 'session' => function () { + $config = eva_get('config'); + $sessionConfig = $config->session; + + return (new \Eva\EvaEngine\Session\SessionAdapterCreator())->create( + $sessionConfig->adapter, + $sessionConfig->options->toArray(), + $sessionConfig->session_name, + $sessionConfig->cookie_params + ); + }, + 'tokenStorage' => function () use ($self) { + return $self->diTokenStorage(); + }, + 'dbMaster' => function () { + $config = eva_get('config'); + if (!isset($config->dbAdapter->master->adapter) || !$config->dbAdapter->master) { + throw new \Eva\EvaEngine\Exception\RuntimeException(sprintf('No DB Master options found')); + } + + return (new \Eva\EvaEngine\Db\DbAdapterCreator())->create( + $config->dbAdapter->master->adapter, + $config->dbAdapter->master->options->toArray(), + eva_get('eventsManager') + ); + }, + 'dbSlave' => function () { + $config = eva_get('config'); + $slaves = $config->dbAdapter->slave; + $slaveKey = array_rand($slaves->toArray()); + if (!isset($slaves->$slaveKey) || count($slaves) < 1) { + throw new RuntimeException(sprintf('No DB slave options found')); + } + + + return (new \Eva\EvaEngine\Db\DbAdapterCreator())->create( + $config->dbAdapter->slave->$slaveKey->adapter, + $config->dbAdapter->slave->$slaveKey->options->toArray(), + eva_get('eventsManager') + ); + }, + 'transactions' => function () { + $transactions = new \Phalcon\Mvc\Model\Transaction\Manager(); + $transactions->setDbService('dbMaster'); + + return $transactions; + }, + 'queue' => function () { + $config = eva_get('config'); + $client = new \GearmanClient(); + $client->setTimeout(1000); + foreach ($config->queue->servers as $key => $server) { + $client->addServer($server->host, $server->port); + } + + return $client; + }, + 'worker' => function () { + $config = eva_get('config'); + $worker = new \GearmanWorker(); + foreach ($config->queue->servers as $key => $server) { + $worker->addServer($server->host, $server->port); + } + + return $worker; + }, + 'mailer' => function () use ($self) { + return $self->diMailer(); + }, + 'smsSender' => function () use ($self) { + return $self->diSmsSender(); + }, + 'url' => function () { + $config = eva_get('config'); + $url = new Eva\EvaEngine\Mvc\Url(); + $url->setVersionFile($config->staticBaseUriVersionFile); + $url->setBaseUri($config->baseUri); + $url->setStaticBaseUri($config->staticBaseUri); + + return $url; + }, + 'escaper' => 'Phalcon\Escaper', + 'tag' => + function () { +// \Eva\EvaEngine\Tag::setDi($di); +// $self->registerViewHelpers(); +// +// return new Tag(); + }, + 'flash' => 'Phalcon\Flash\Session', + 'cookies' => function () { + $cookies = new \Phalcon\Http\Response\Cookies(); + $cookies->useEncryption(false); + + return $cookies; + }, + 'translate' => + function () { + $config = eva_get('config'); + $file = $config->translate->path . $config->translate->forceLang . '.csv'; + if (false === file_exists($file)) { + //empty translator + return new \Phalcon\Translate\Adapter\NativeArray( + array( + 'content' => array() + ) + ); + } + $translate = new \Phalcon\Translate\Adapter\Csv( + array( + 'file' => $file, + 'delimiter' => ',', + ) + ); + + return $translate; + }, + 'fileSystem' => + function () { +// return $di->diFileSystem(); + }, + /* + |-------------------------------------------------------------------------- + | error 日志服务 + |-------------------------------------------------------------------------- + | 主要用于记录各种 error 日志信息,EvaEngine 默认的 ErrorHandler 依赖 errorLogger + | 服务,用于记录未捕捉的异常。 + | + */ + 'errorLogger' => function () { + $config = eva_get('config'); + + return new Phalcon\Logger\Adapter\File(rtrim($config->logger->path, + '/') . '/error.' . date('Y-m-d') . '.log'); + }, + /* + |-------------------------------------------------------------------------- + | debug 日志服务 + |-------------------------------------------------------------------------- + | 主要用于记录各种 debug 日志信息 + | + */ + 'debugLogger' => function () { + $config = eva_get('config'); + + return new Phalcon\Logger\Adapter\File(rtrim($config->logger->path, + '/') . '/debug.' . date('Y-m-d') . '.log'); + }, + /* + |-------------------------------------------------------------------------- + | 数据库查询日志记录服务 + |-------------------------------------------------------------------------- + | 默认地,EvaEngine 会监听 db:beforeQuery 事件,并通过 + | $dbAdapter->getSQLStatement() 获取每条执行的 SQL 语句,然后产生一条 INFO 级别 + | 的日志,通过 dbQueryLogger 来记录 SQL 查询记录。 + */ + 'dbQueryLogger' => function () { + $config = eva_get('config'); + + return new Phalcon\Logger\Adapter\File($config->logger->path . 'db_query.' . date('Y-m-d') . '.log'); + }, + /* + |-------------------------------------------------------------------------- + | FirePHP 调试服务 + |-------------------------------------------------------------------------- + | firephp 服务是一个标准的日志实现,可以通过浏览器 firephp 插件获取到服务器上产生的 + | firephp 日志 + | + */ + 'firephp' => function () { + return new \Phalcon\Logger\Adapter\Firephp(); + }, + ), +); diff --git a/config/routes.backend.php b/config/routes.backend.php new file mode 100644 index 0000000..90c57bf --- /dev/null +++ b/config/routes.backend.php @@ -0,0 +1,10 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/16 下午2:08 +// +---------------------------------------------------------------------- +// + routes.cli.php +// +---------------------------------------------------------------------- +return array(); \ No newline at end of file diff --git a/config/routes.frontend.php b/config/routes.frontend.php new file mode 100644 index 0000000..16eba40 --- /dev/null +++ b/config/routes.frontend.php @@ -0,0 +1,9 @@ +bootstrap(); +View::setViewsDir(__DIR__ . '/views/'); +$methods = []; +$method = [ + 'name' => 'eva_get' +]; +$namespaces = []; +foreach (\Eva\EvaEngine\IoC::getDI()->getServices() as $name => $service) { + $method['bindings'][$name] = get_class(\Eva\EvaEngine\IoC::get($name)); +}; +$methods[] = $method; +$method['name'] = '\Eva\EvaEngine\IoC::get'; +$methods[] = $method; +foreach (Config::get('alias') as $aliasName => $facade) { + /** @var \Eva\EvaEngine\Facades\FacadeBase $facade */ + $name = $facade::getFacadeAccessor(); + $binding = $facade::getFacadeRoot(); + // 生成 facade 帮助文件 + $alias = new \Eva\EvaEngine\DevTool\IDE\Facade\Alias($aliasName, $facade); + $namespace = $alias->getNamespace(); + + if (!isset($namespaces[$namespace])) { + $namespaces[$namespace] = array(); + } + + $namespaces[$namespace][] = $alias; +} + +file_put_contents(__DIR__ . '/.phpstorm.meta.php', View::getRender('ide', 'meta', ['methods' => $methods])); +file_put_contents( + __DIR__ . '/.eve-engine-ide-helper.php', + View::getRender('ide', 'helper', [ + 'version' => '1.2', + 'namespaces' => $namespaces, + 'helpers' => '', + ]) +); diff --git a/src/EvaEngine/Application.php b/src/EvaEngine/Application.php new file mode 100644 index 0000000..85d75ec --- /dev/null +++ b/src/EvaEngine/Application.php @@ -0,0 +1,133 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午4:44 +// +---------------------------------------------------------------------- +// + Application.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine; + + +use Eva\EvaEngine\Exception\RuntimeException; +use Eva\EvaEngine\Module\Manager; +use Phalcon\Cache\Backend\File; +use Phalcon\Cache\Frontend\Data; +use Phalcon\DI\FactoryDefault; + +class Application +{ + protected $project_root = __DIR__; + protected $mode = 'web'; + protected $di; + protected static $instance = null; + protected $appName; + /** + * @var \Phalcon\Mvc\Application + */ + protected $application; + + protected function __construct($project_root = __DIR__, $mode = 'web', $appName = '', $systemCache = null) + { + $this->project_root = $project_root; + $this->appName = empty($_SERVER['APPLICATION_NAME']) ? $appName : $_SERVER['APPLICATION_NAME']; + $this->mode = $mode; + $this->di = new FactoryDefault(); + $this->di->setShared('evaengine', $this); + if ($systemCache == null) { + $systemCache = function () { + return new File(new Data(), array( + 'cacheDir' => $this->getProjectRoot() . '/cache/system/', + 'prefix' => $this->getAppName() . '/' + )); + }; + } + $this->di->setShared('systemCache', $systemCache); + } + + public function loadModules(array $modulesConfig) + { + $moduleManager = new Manager($this->getProjectRoot() . '/modules', $modulesConfig); + $moduleManager->registerModules($modulesConfig); + $this->di->set('moduleManager', $moduleManager); + $this->application->registerModules($moduleManager->getModulesForPhalcon()); + } + + public function initialize() + { + // 注册 DI + $config = eva_get('config'); + foreach ($config->di as $name => $diDefinition) { + $this->di->set($name, $diDefinition); + } + $this->registerServices(); + $this->initializeEvents(); + $this->application->setDI($this->di); + $this->application->setEventsManager(eva_get('eventsManager')); + } + + protected function registerServices() + { + /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ + $moduleManager = eva_get('moduleManager'); + foreach ($moduleManager->getAllDIDefinition() as $name => $definition) { + $this->di->set($name, $definition['definition'], $definition['share']); + } + } + + protected function initializeEvents() + { + $eventsManager = eva_get('eventsManager'); + /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ + $moduleManager = eva_get('moduleManager'); + foreach ($moduleManager->getAllListeners() as $moduleListeners) { + foreach ($moduleListeners as $eventType => $listener) { + $priority = 0; + if (is_string($listener)) { + $listenerClass = $listener; + } elseif (true === is_array($listener) && count($listener) > 1) { + $listenerClass = $listener[0]; + $priority = (int)$listener[1]; + } else { + throw new \Exception(sprintf( + "Module listener format not correct: %s", + var_export($listener, true) + )); + } + if (false === class_exists($listenerClass)) { + throw new \Exception(sprintf("Module listener %s not exist", $listenerClass)); + } + $eventsManager->attach($eventType, new $listenerClass, $priority); + } + } + } + + public function getProjectRoot() + { + return $this->project_root; + } + + public function getAppName() + { + return $this->appName; + } + + public static function getInstance($app_root = __DIR__, $mode = 'web') + { + if (static::$instance == null) { + static::$instance = new static($app_root, $mode); + } + + return static::$instance; + } + + + public function getDI() + { + return $this->di; + } + +} \ No newline at end of file diff --git a/src/EvaEngine/CLI/Formatter/OutputFormatter.php b/src/EvaEngine/CLI/Formatter/OutputFormatter.php deleted file mode 100755 index 163f158..0000000 --- a/src/EvaEngine/CLI/Formatter/OutputFormatter.php +++ /dev/null @@ -1,239 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Formatter; - -/** - * Formatter class for console output. - * - * @author Konstantin Kudryashov - * - * @api - */ -class OutputFormatter implements OutputFormatterInterface -{ - private $decorated; - private $styles = array(); - private $styleStack; - - /** - * Escapes "<" special char in given text. - * - * @param string $text Text to escape - * - * @return string Escaped text - */ - public static function escape($text) - { - return preg_replace('/([^\\\\]?) FormatterStyle" instances - * - * @api - */ - public function __construct($decorated = false, array $styles = array()) - { - $this->decorated = (bool) $decorated; - - $this->setStyle('error', new OutputFormatterStyle('white', 'red')); - $this->setStyle('info', new OutputFormatterStyle('green')); - $this->setStyle('comment', new OutputFormatterStyle('yellow')); - $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); - $this->setStyle('success', new OutputFormatterStyle('black', 'green')); - $this->setStyle('warning', new OutputFormatterStyle('black', 'yellow')); - - - foreach ($styles as $name => $style) { - $this->setStyle($name, $style); - } - - $this->styleStack = new OutputFormatterStyleStack(); - } - - /** - * Sets the decorated flag. - * - * @param bool $decorated Whether to decorate the messages or not - * - * @api - */ - public function setDecorated($decorated) - { - $this->decorated = (bool) $decorated; - } - - /** - * Gets the decorated flag. - * - * @return bool true if the output will decorate messages, false otherwise - * - * @api - */ - public function isDecorated() - { - return $this->decorated; - } - - /** - * Sets a new style. - * - * @param string $name The style name - * @param OutputFormatterStyleInterface $style The style instance - * - * @api - */ - public function setStyle($name, OutputFormatterStyleInterface $style) - { - $this->styles[strtolower($name)] = $style; - } - - /** - * Checks if output formatter has style with specified name. - * - * @param string $name - * - * @return bool - * - * @api - */ - public function hasStyle($name) - { - return isset($this->styles[strtolower($name)]); - } - - /** - * Gets style options from style with specified name. - * - * @param string $name - * - * @return OutputFormatterStyleInterface - * - * @throws \InvalidArgumentException When style isn't defined - * - * @api - */ - public function getStyle($name) - { - if (!$this->hasStyle($name)) { - throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name)); - } - - return $this->styles[strtolower($name)]; - } - - /** - * Formats a message according to the given styles. - * - * @param string $message The message to style - * - * @return string The styled message - * - * @api - */ - public function format($message) - { - $offset = 0; - $output = ''; - $tagRegex = '[a-z][a-z0-9_=;-]*'; - preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE); - foreach ($matches[0] as $i => $match) { - $pos = $match[1]; - $text = $match[0]; - - // add the text up to the next tag - $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); - $offset = $pos + strlen($text); - - // opening tag? - if ($open = '/' != $text[1]) { - $tag = $matches[1][$i][0]; - } else { - $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; - } - - if (!$open && !$tag) { - // - $this->styleStack->pop(); - } elseif ($pos && '\\' == $message[$pos - 1]) { - // escaped tag - $output .= $this->applyCurrentStyle($text); - } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { - $output .= $this->applyCurrentStyle($text); - } elseif ($open) { - $this->styleStack->push($style); - } else { - $this->styleStack->pop($style); - } - } - - $output .= $this->applyCurrentStyle(substr($message, $offset)); - - return str_replace('\\<', '<', $output); - } - - /** - * @return OutputFormatterStyleStack - */ - public function getStyleStack() - { - return $this->styleStack; - } - - /** - * Tries to create new style instance from string. - * - * @param string $string - * - * @return OutputFormatterStyle|bool false if string is not format string - */ - private function createStyleFromString($string) - { - if (isset($this->styles[$string])) { - return $this->styles[$string]; - } - - if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { - return false; - } - - $style = new OutputFormatterStyle(); - foreach ($matches as $match) { - array_shift($match); - - if ('fg' == $match[0]) { - $style->setForeground($match[1]); - } elseif ('bg' == $match[0]) { - $style->setBackground($match[1]); - } else { - $style->setOption($match[1]); - } - } - - return $style; - } - - /** - * Applies current style from stack to text, if must be applied. - * - * @param string $text Input text - * - * @return string Styled text - */ - private function applyCurrentStyle($text) - { - return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; - } -} diff --git a/src/EvaEngine/CLI/Formatter/OutputFormatterInterface.php b/src/EvaEngine/CLI/Formatter/OutputFormatterInterface.php deleted file mode 100755 index 2993dd8..0000000 --- a/src/EvaEngine/CLI/Formatter/OutputFormatterInterface.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Formatter; - -/** - * Formatter interface for console output. - * - * @author Konstantin Kudryashov - * - * @api - */ -interface OutputFormatterInterface -{ - /** - * Sets the decorated flag. - * - * @param bool $decorated Whether to decorate the messages or not - * - * @api - */ - public function setDecorated($decorated); - - /** - * Gets the decorated flag. - * - * @return bool true if the output will decorate messages, false otherwise - * - * @api - */ - public function isDecorated(); - - /** - * Sets a new style. - * - * @param string $name The style name - * @param OutputFormatterStyleInterface $style The style instance - * - * @api - */ - public function setStyle($name, OutputFormatterStyleInterface $style); - - /** - * Checks if output formatter has style with specified name. - * - * @param string $name - * - * @return bool - * - * @api - */ - public function hasStyle($name); - - /** - * Gets style options from style with specified name. - * - * @param string $name - * - * @return OutputFormatterStyleInterface - * - * @api - */ - public function getStyle($name); - - /** - * Formats a message according to the given styles. - * - * @param string $message The message to style - * - * @return string The styled message - * - * @api - */ - public function format($message); -} diff --git a/src/EvaEngine/CLI/Formatter/OutputFormatterStyle.php b/src/EvaEngine/CLI/Formatter/OutputFormatterStyle.php deleted file mode 100755 index 971f3da..0000000 --- a/src/EvaEngine/CLI/Formatter/OutputFormatterStyle.php +++ /dev/null @@ -1,235 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Formatter; - -/** - * Formatter style class for defining styles. - * - * @author Konstantin Kudryashov - * - * @api - */ -class OutputFormatterStyle implements OutputFormatterStyleInterface -{ - private static $availableForegroundColors = array( - 'black' => array('set' => 30, 'unset' => 39), - 'red' => array('set' => 31, 'unset' => 39), - 'green' => array('set' => 32, 'unset' => 39), - 'yellow' => array('set' => 33, 'unset' => 39), - 'blue' => array('set' => 34, 'unset' => 39), - 'magenta' => array('set' => 35, 'unset' => 39), - 'cyan' => array('set' => 36, 'unset' => 39), - 'white' => array('set' => 37, 'unset' => 39) - ); - private static $availableBackgroundColors = array( - 'black' => array('set' => 40, 'unset' => 49), - 'red' => array('set' => 41, 'unset' => 49), - 'green' => array('set' => 42, 'unset' => 49), - 'yellow' => array('set' => 43, 'unset' => 49), - 'blue' => array('set' => 44, 'unset' => 49), - 'magenta' => array('set' => 45, 'unset' => 49), - 'cyan' => array('set' => 46, 'unset' => 49), - 'white' => array('set' => 47, 'unset' => 49) - ); - private static $availableOptions = array( - 'bold' => array('set' => 1, 'unset' => 22), - 'underscore' => array('set' => 4, 'unset' => 24), - 'blink' => array('set' => 5, 'unset' => 25), - 'reverse' => array('set' => 7, 'unset' => 27), - 'conceal' => array('set' => 8, 'unset' => 28) - ); - - private $foreground; - private $background; - private $options = array(); - - /** - * Initializes output formatter style. - * - * @param string|null $foreground The style foreground color name - * @param string|null $background The style background color name - * @param array $options The style options - * - * @api - */ - public function __construct($foreground = null, $background = null, array $options = array()) - { - if (null !== $foreground) { - $this->setForeground($foreground); - } - if (null !== $background) { - $this->setBackground($background); - } - if (count($options)) { - $this->setOptions($options); - } - } - - /** - * Sets style foreground color. - * - * @param string|null $color The color name - * - * @throws \InvalidArgumentException When the color name isn't defined - * - * @api - */ - public function setForeground($color = null) - { - if (null === $color) { - $this->foreground = null; - - return; - } - - if (!isset(static::$availableForegroundColors[$color])) { - throw new \InvalidArgumentException( - sprintf( - 'Invalid foreground color specified: "%s". Expected one of (%s)', - $color, - implode(', ', array_keys(static::$availableForegroundColors)) - ) - ); - } - - $this->foreground = static::$availableForegroundColors[$color]; - } - - /** - * Sets style background color. - * - * @param string|null $color The color name - * - * @throws \InvalidArgumentException When the color name isn't defined - * - * @api - */ - public function setBackground($color = null) - { - if (null === $color) { - $this->background = null; - - return; - } - - if (!isset(static::$availableBackgroundColors[$color])) { - throw new \InvalidArgumentException( - sprintf( - 'Invalid background color specified: "%s". Expected one of (%s)', - $color, - implode(', ', array_keys(static::$availableBackgroundColors)) - ) - ); - } - - $this->background = static::$availableBackgroundColors[$color]; - } - - /** - * Sets some specific style option. - * - * @param string $option The option name - * - * @throws \InvalidArgumentException When the option name isn't defined - * - * @api - */ - public function setOption($option) - { - if (!isset(static::$availableOptions[$option])) { - throw new \InvalidArgumentException( - sprintf( - 'Invalid option specified: "%s". Expected one of (%s)', - $option, - implode(', ', array_keys(static::$availableOptions)) - ) - ); - } - - if (false === array_search(static::$availableOptions[$option], $this->options)) { - $this->options[] = static::$availableOptions[$option]; - } - } - - /** - * Unsets some specific style option. - * - * @param string $option The option name - * - * @throws \InvalidArgumentException When the option name isn't defined - */ - public function unsetOption($option) - { - if (!isset(static::$availableOptions[$option])) { - throw new \InvalidArgumentException( - sprintf( - 'Invalid option specified: "%s". Expected one of (%s)', - $option, - implode(', ', array_keys(static::$availableOptions)) - ) - ); - } - - $pos = array_search(static::$availableOptions[$option], $this->options); - if (false !== $pos) { - unset($this->options[$pos]); - } - } - - /** - * Sets multiple style options at once. - * - * @param array $options - */ - public function setOptions(array $options) - { - $this->options = array(); - - foreach ($options as $option) { - $this->setOption($option); - } - } - - /** - * Applies the style to a given text. - * - * @param string $text The text to style - * - * @return string - */ - public function apply($text) - { - $setCodes = array(); - $unsetCodes = array(); - - if (null !== $this->foreground) { - $setCodes[] = $this->foreground['set']; - $unsetCodes[] = $this->foreground['unset']; - } - if (null !== $this->background) { - $setCodes[] = $this->background['set']; - $unsetCodes[] = $this->background['unset']; - } - if (count($this->options)) { - foreach ($this->options as $option) { - $setCodes[] = $option['set']; - $unsetCodes[] = $option['unset']; - } - } - - if (0 === count($setCodes)) { - return $text; - } - - return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); - } -} diff --git a/src/EvaEngine/CLI/Formatter/OutputFormatterStyleInterface.php b/src/EvaEngine/CLI/Formatter/OutputFormatterStyleInterface.php deleted file mode 100755 index 8deb0a4..0000000 --- a/src/EvaEngine/CLI/Formatter/OutputFormatterStyleInterface.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Formatter; - -/** - * Formatter style interface for defining styles. - * - * @author Konstantin Kudryashov - * - * @api - */ -interface OutputFormatterStyleInterface -{ - /** - * Sets style foreground color. - * - * @param string $color The color name - * - * @api - */ - public function setForeground($color = null); - - /** - * Sets style background color. - * - * @param string $color The color name - * - * @api - */ - public function setBackground($color = null); - - /** - * Sets some specific style option. - * - * @param string $option The option name - * - * @api - */ - public function setOption($option); - - /** - * Unsets some specific style option. - * - * @param string $option The option name - */ - public function unsetOption($option); - - /** - * Sets multiple style options at once. - * - * @param array $options - */ - public function setOptions(array $options); - - /** - * Applies the style to a given text. - * - * @param string $text The text to style - * - * @return string - */ - public function apply($text); -} diff --git a/src/EvaEngine/CLI/Formatter/OutputFormatterStyleStack.php b/src/EvaEngine/CLI/Formatter/OutputFormatterStyleStack.php deleted file mode 100755 index 8f14673..0000000 --- a/src/EvaEngine/CLI/Formatter/OutputFormatterStyleStack.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Formatter; - -/** - * @author Jean-François Simon - */ -class OutputFormatterStyleStack -{ - /** - * @var OutputFormatterStyleInterface[] - */ - private $styles; - - /** - * @var OutputFormatterStyleInterface - */ - private $emptyStyle; - - /** - * Constructor. - * - * @param OutputFormatterStyleInterface|null $emptyStyle - */ - public function __construct(OutputFormatterStyleInterface $emptyStyle = null) - { - $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); - $this->reset(); - } - - /** - * Resets stack (ie. empty internal arrays). - */ - public function reset() - { - $this->styles = array(); - } - - /** - * Pushes a style in the stack. - * - * @param OutputFormatterStyleInterface $style - */ - public function push(OutputFormatterStyleInterface $style) - { - $this->styles[] = $style; - } - - /** - * Pops a style from the stack. - * - * @param OutputFormatterStyleInterface|null $style - * - * @return OutputFormatterStyleInterface - * - * @throws \InvalidArgumentException When style tags incorrectly nested - */ - public function pop(OutputFormatterStyleInterface $style = null) - { - if (empty($this->styles)) { - return $this->emptyStyle; - } - - if (null === $style) { - return array_pop($this->styles); - } - - foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { - if ($style->apply('') === $stackedStyle->apply('')) { - $this->styles = array_slice($this->styles, 0, $index); - - return $stackedStyle; - } - } - - throw new \InvalidArgumentException('Incorrectly nested style tag found.'); - } - - /** - * Computes current style with stacks top codes. - * - * @return OutputFormatterStyle - */ - public function getCurrent() - { - if (empty($this->styles)) { - return $this->emptyStyle; - } - - return $this->styles[count($this->styles)-1]; - } - - /** - * @param OutputFormatterStyleInterface $emptyStyle - * - * @return OutputFormatterStyleStack - */ - public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) - { - $this->emptyStyle = $emptyStyle; - - return $this; - } - - /** - * @return OutputFormatterStyleInterface - */ - public function getEmptyStyle() - { - return $this->emptyStyle; - } -} diff --git a/src/EvaEngine/CLI/Output/BufferedOutput.php b/src/EvaEngine/CLI/Output/BufferedOutput.php deleted file mode 100755 index e47be1a..0000000 --- a/src/EvaEngine/CLI/Output/BufferedOutput.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Output; - -/** - * @author Jean-François Simon - */ -class BufferedOutput extends Output -{ - /** - * @var string - */ - private $buffer = ''; - - /** - * Empties buffer and returns its content. - * - * @return string - */ - public function fetch() - { - $content = $this->buffer; - $this->buffer = ''; - - return $content; - } - - /** - * {@inheritdoc} - */ - protected function doWrite($message, $newline) - { - $this->buffer .= $message; - - if ($newline) { - $this->buffer .= "\n"; - } - } -} diff --git a/src/EvaEngine/CLI/Output/ConsoleOutput.php b/src/EvaEngine/CLI/Output/ConsoleOutput.php deleted file mode 100755 index 8fa3dc8..0000000 --- a/src/EvaEngine/CLI/Output/ConsoleOutput.php +++ /dev/null @@ -1,170 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Output; - -use Eva\EvaEngine\CLI\Formatter\OutputFormatterInterface; - -/** - * ConsoleOutput is the default class for all CLI output. It uses STDOUT. - * - * This class is a convenient wrapper around `StreamOutput`. - * - * $output = new ConsoleOutput(); - * - * This is equivalent to: - * - * $output = new StreamOutput(fopen('php://stdout', 'w')); - * - * @author Fabien Potencier - * - * @api - */ -class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface -{ - private $stderr; - - /** - * Constructor. - * - * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) - * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) - * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) - * - * @api - */ - public function __construct( - $verbosity = self::VERBOSITY_NORMAL, - $decorated = null, - OutputFormatterInterface $formatter = null - ) { - $outputStream = 'php://stdout'; - if (!$this->hasStdoutSupport()) { - $outputStream = 'php://output'; - } - - parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter); - - $this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $this->getFormatter()); - } - - /** - * {@inheritdoc} - */ - public function setDecorated($decorated) - { - parent::setDecorated($decorated); - $this->stderr->setDecorated($decorated); - } - - /** - * {@inheritdoc} - */ - public function setFormatter(OutputFormatterInterface $formatter) - { - parent::setFormatter($formatter); - $this->stderr->setFormatter($formatter); - } - - /** - * {@inheritdoc} - */ - public function setVerbosity($level) - { - parent::setVerbosity($level); - $this->stderr->setVerbosity($level); - } - - /** - * {@inheritdoc} - */ - public function getErrorOutput() - { - return $this->stderr; - } - - /** - * {@inheritdoc} - */ - public function setErrorOutput(OutputInterface $error) - { - $this->stderr = $error; - } - - /** - * Returns true if current environment supports writing console output to - * STDOUT. - * - * IBM iSeries (OS400) exhibits character-encoding issues when writing to - * STDOUT and doesn't properly convert ASCII to EBCDIC, resulting in garbage - * output. - * - * @return bool - */ - protected function hasStdoutSupport() - { - return ('OS400' != php_uname('s')); - } - - public function writelnInfo($message, $type = self::OUTPUT_NORMAL) - { - $this->writeln("{$message} ", $type); - } - - public function writelnError($message, $type = self::OUTPUT_NORMAL) - { - $this->writeln("{$message} ", $type); - } - - public function writelnWarning($message, $type = self::OUTPUT_NORMAL) - { - $this->writeln("{$message} ", $type); - } - - public function writelnComment($message, $type = self::OUTPUT_NORMAL) - { - $this->writeln("{$message} ", $type); - } - - public function writelnSuccess($message, $type = self::OUTPUT_NORMAL) - { - $this->writeln("{$message} ", $type); - } - - public function writeList(array $list) - { - $maxLength = 0; - foreach ($list as $k => $v) { - $k = trim($k); - if (strlen($k) > $maxLength) { - $maxLength = strlen($k); - } - } - foreach ($list as $k => $v) { - $k = trim($k); - $kLen = strlen($k); - $this->write(' ' . $k . $this->getSpace($maxLength - $kLen) . ' '); - $this->writeln($v); - } - $this->writeln(""); - - } - - private function getSpace($length) - { - $str = ''; - for ($i = 0; $i < $length; $i++) { - $str .= ' '; - } - - return $str; - } -} diff --git a/src/EvaEngine/CLI/Output/ConsoleOutputInterface.php b/src/EvaEngine/CLI/Output/ConsoleOutputInterface.php deleted file mode 100755 index ea842f1..0000000 --- a/src/EvaEngine/CLI/Output/ConsoleOutputInterface.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Output; - -/** - * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. - * This adds information about stderr output stream. - * - * @author Dariusz Górecki - */ -interface ConsoleOutputInterface extends OutputInterface -{ - /** - * Gets the OutputInterface for errors. - * - * @return OutputInterface - */ - public function getErrorOutput(); - - /** - * Sets the OutputInterface used for errors. - * - * @param OutputInterface $error - */ - public function setErrorOutput(OutputInterface $error); -} diff --git a/src/EvaEngine/CLI/Output/NullOutput.php b/src/EvaEngine/CLI/Output/NullOutput.php deleted file mode 100755 index 25bbaf9..0000000 --- a/src/EvaEngine/CLI/Output/NullOutput.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Output; - -use Eva\EvaEngine\CLI\Formatter\OutputFormatterInterface; -use Eva\EvaEngine\CLI\Formatter\OutputFormatter; - -/** - * NullOutput suppresses all output. - * - * $output = new NullOutput(); - * - * @author Fabien Potencier - * @author Tobias Schultze - * - * @api - */ -class NullOutput implements OutputInterface -{ - /** - * {@inheritdoc} - */ - public function setFormatter(OutputFormatterInterface $formatter) - { - // do nothing - } - - /** - * {@inheritdoc} - */ - public function getFormatter() - { - // to comply with the interface we must return a OutputFormatterInterface - return new OutputFormatter(); - } - - /** - * {@inheritdoc} - */ - public function setDecorated($decorated) - { - // do nothing - } - - /** - * {@inheritdoc} - */ - public function isDecorated() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function setVerbosity($level) - { - // do nothing - } - - /** - * {@inheritdoc} - */ - public function getVerbosity() - { - return self::VERBOSITY_QUIET; - } - - public function isQuiet() - { - return true; - } - - public function isVerbose() - { - return false; - } - - public function isVeryVerbose() - { - return false; - } - - public function isDebug() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function writeln($messages, $type = self::OUTPUT_NORMAL) - { - // do nothing - } - - /** - * {@inheritdoc} - */ - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) - { - // do nothing - } -} diff --git a/src/EvaEngine/CLI/Output/Output.php b/src/EvaEngine/CLI/Output/Output.php deleted file mode 100755 index e837197..0000000 --- a/src/EvaEngine/CLI/Output/Output.php +++ /dev/null @@ -1,168 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Output; - -use Eva\EvaEngine\CLI\Formatter\OutputFormatterInterface; -use Eva\EvaEngine\CLI\Formatter\OutputFormatter; - -/** - * Base class for output classes. - * - * There are five levels of verbosity: - * - * * normal: no option passed (normal output) - * * verbose: -v (more output) - * * very verbose: -vv (highly extended output) - * * debug: -vvv (all debug output) - * * quiet: -q (no output) - * - * @author Fabien Potencier - * - * @api - */ -abstract class Output implements OutputInterface -{ - private $verbosity; - private $formatter; - - /** - * Constructor. - * - * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) - * @param bool $decorated Whether to decorate messages - * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) - * - * @api - */ - public function __construct( - $verbosity = self::VERBOSITY_NORMAL, - $decorated = false, - OutputFormatterInterface $formatter = null - ) { - $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; - $this->formatter = $formatter ?: new OutputFormatter(); - $this->formatter->setDecorated($decorated); - } - - /** - * {@inheritdoc} - */ - public function setFormatter(OutputFormatterInterface $formatter) - { - $this->formatter = $formatter; - } - - /** - * {@inheritdoc} - */ - public function getFormatter() - { - return $this->formatter; - } - - /** - * {@inheritdoc} - */ - public function setDecorated($decorated) - { - $this->formatter->setDecorated($decorated); - } - - /** - * {@inheritdoc} - */ - public function isDecorated() - { - return $this->formatter->isDecorated(); - } - - /** - * {@inheritdoc} - */ - public function setVerbosity($level) - { - $this->verbosity = (int)$level; - } - - /** - * {@inheritdoc} - */ - public function getVerbosity() - { - return $this->verbosity; - } - - public function isQuiet() - { - return self::VERBOSITY_QUIET === $this->verbosity; - } - - public function isVerbose() - { - return self::VERBOSITY_VERBOSE <= $this->verbosity; - } - - public function isVeryVerbose() - { - return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; - } - - public function isDebug() - { - return self::VERBOSITY_DEBUG <= $this->verbosity; - } - - /** - * {@inheritdoc} - */ - public function writeln($messages, $type = self::OUTPUT_NORMAL) - { - $this->write($messages, true, $type); - } - - /** - * {@inheritdoc} - */ - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) - { - if (self::VERBOSITY_QUIET === $this->verbosity) { - return; - } - - $messages = (array)$messages; - - foreach ($messages as $message) { - switch ($type) { - case OutputInterface::OUTPUT_NORMAL: - $message = $this->formatter->format($message); - break; - case OutputInterface::OUTPUT_RAW: - break; - case OutputInterface::OUTPUT_PLAIN: - $message = strip_tags($this->formatter->format($message)); - break; - default: - throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); - } - - $this->doWrite($message, $newline); - } - } - - /** - * Writes a message to the output. - * - * @param string $message A message to write to the output - * @param bool $newline Whether to add a newline or not - */ - abstract protected function doWrite($message, $newline); -} diff --git a/src/EvaEngine/CLI/Output/OutputInterface.php b/src/EvaEngine/CLI/Output/OutputInterface.php deleted file mode 100755 index 795dc73..0000000 --- a/src/EvaEngine/CLI/Output/OutputInterface.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Output; - -use Eva\EvaEngine\CLI\Formatter\OutputFormatterInterface; - -/** - * OutputInterface is the interface implemented by all Output classes. - * - * @author Fabien Potencier - * - * @api - */ -interface OutputInterface -{ - const VERBOSITY_QUIET = 0; - const VERBOSITY_NORMAL = 1; - const VERBOSITY_VERBOSE = 2; - const VERBOSITY_VERY_VERBOSE = 3; - const VERBOSITY_DEBUG = 4; - - const OUTPUT_NORMAL = 0; - const OUTPUT_RAW = 1; - const OUTPUT_PLAIN = 2; - - /** - * Writes a message to the output. - * - * @param string|array $messages The message as an array of lines or a single string - * @param bool $newline Whether to add a newline - * @param int $type The type of output (one of the OUTPUT constants) - * - * @throws \InvalidArgumentException When unknown output type is given - * - * @api - */ - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL); - - /** - * Writes a message to the output and adds a newline at the end. - * - * @param string|array $messages The message as an array of lines of a single string - * @param int $type The type of output (one of the OUTPUT constants) - * - * @throws \InvalidArgumentException When unknown output type is given - * - * @api - */ - public function writeln($messages, $type = self::OUTPUT_NORMAL); - - /** - * Sets the verbosity of the output. - * - * @param int $level The level of verbosity (one of the VERBOSITY constants) - * - * @api - */ - public function setVerbosity($level); - - /** - * Gets the current verbosity of the output. - * - * @return int The current level of verbosity (one of the VERBOSITY constants) - * - * @api - */ - public function getVerbosity(); - - /** - * Sets the decorated flag. - * - * @param bool $decorated Whether to decorate the messages - * - * @api - */ - public function setDecorated($decorated); - - /** - * Gets the decorated flag. - * - * @return bool true if the output will decorate messages, false otherwise - * - * @api - */ - public function isDecorated(); - - /** - * Sets output formatter. - * - * @param OutputFormatterInterface $formatter - * - * @api - */ - public function setFormatter(OutputFormatterInterface $formatter); - - /** - * Returns current output formatter instance. - * - * @return OutputFormatterInterface - * - * @api - */ - public function getFormatter(); -} diff --git a/src/EvaEngine/CLI/Output/StreamOutput.php b/src/EvaEngine/CLI/Output/StreamOutput.php deleted file mode 100755 index 80d1aff..0000000 --- a/src/EvaEngine/CLI/Output/StreamOutput.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Eva\EvaEngine\CLI\Output; - -use Eva\EvaEngine\CLI\Formatter\OutputFormatterInterface; - -/** - * StreamOutput writes the output to a given stream. - * - * Usage: - * - * $output = new StreamOutput(fopen('php://stdout', 'w')); - * - * As `StreamOutput` can use any stream, you can also use a file: - * - * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); - * - * @author Fabien Potencier - * - * @api - */ -class StreamOutput extends Output -{ - private $stream; - - /** - * Constructor. - * - * @param mixed $stream A stream resource - * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) - * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) - * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) - * - * @throws \InvalidArgumentException When first argument is not a real stream - * - * @api - */ - public function __construct( - $stream, - $verbosity = self::VERBOSITY_NORMAL, - $decorated = null, - OutputFormatterInterface $formatter = null - ) { - if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { - throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); - } - - $this->stream = $stream; - - if (null === $decorated) { - $decorated = $this->hasColorSupport(); - } - - parent::__construct($verbosity, $decorated, $formatter); - } - - /** - * Gets the stream attached to this StreamOutput instance. - * - * @return resource A stream resource - */ - public function getStream() - { - return $this->stream; - } - - /** - * {@inheritdoc} - */ - protected function doWrite($message, $newline) - { - if (false === @fwrite($this->stream, $message . ($newline ? PHP_EOL : ''))) { - // should never happen - throw new \RuntimeException('Unable to write output.'); - } - - fflush($this->stream); - } - - /** - * Returns true if the stream supports colorization. - * - * Colorization is disabled if not supported by the stream: - * - * - Windows without Ansicon and ConEmu - * - non tty consoles - * - * @return bool true if the stream supports colorization, false otherwise - */ - protected function hasColorSupport() - { - if (DIRECTORY_SEPARATOR == '\\') { - return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); - } - - return function_exists('posix_isatty') && @posix_isatty($this->stream); - } -} diff --git a/src/EvaEngine/Cache/CacheAdapterCreator.php b/src/EvaEngine/Cache/CacheAdapterCreator.php new file mode 100644 index 0000000..bf8aa9f --- /dev/null +++ b/src/EvaEngine/Cache/CacheAdapterCreator.php @@ -0,0 +1,87 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/11 下午3:40 +// +---------------------------------------------------------------------- +// + CacheAdapterCreator.php +// +---------------------------------------------------------------------- +namespace Eva\EvaEngine\Cache; + +use Eva\EvaEngine\Exception\RuntimeException; +use Eva\EvaEngine\Foundation\AdapterCreator; + +class CacheAdapterCreator extends AdapterCreator +{ + /** + * {@inheritdoc} + */ + protected function getAdaptersMapping($category = 'default') + { + return array( + 'frontend' => array( + 'base64' => 'Phalcon\Cache\Frontend\Base64', + 'data' => 'Phalcon\Cache\Frontend\Data', + 'igbinary' => 'Phalcon\Cache\Frontend\Igbinary', + 'json' => 'Phalcon\Cache\Frontend\Json', + 'none' => 'Phalcon\Cache\Frontend\None', + 'output' => 'Phalcon\Cache\Frontend\Output', + ), + 'backend' => array( + 'apc' => 'Phalcon\Cache\Backend\Apc', + 'disable' => 'Eva\EvaEngine\Cache\Backend\Disable', + 'file' => 'Phalcon\Cache\Backend\File', + 'libmemcached' => 'Phalcon\Cache\Backend\Libmemcached', + 'memcache' => 'Phalcon\Cache\Backend\Memcache', + 'memory' => 'Phalcon\Cache\Backend\Memory', + 'mongo' => 'Phalcon\Cache\Backend\Mongo', + 'xcache' => 'Phalcon\Cache\Backend\Xcache', + 'redis' => 'Phalcon\Cache\Backend\Redis', + 'wincache' => 'Phalcon\Cache\Backend\Wincache', + ) + ); + } + + /** + * create a cache adapter + * + * @param string $frontendAdapter frontend adapter name or class name + * @param array $frontendOptions frontend options + * @param string $backendAdapter backend adapter name or class name + * @param array $backendOptions backend options + * @return \Phalcon\Cache\BackendInterface + * @throws RuntimeException + */ + public function create($frontendAdapter, array $frontendOptions, $backendAdapter, array $backendOptions) + { + $frontendCacheClass = $this->getAdapterClass($frontendAdapter, 'frontend'); + + $backendCacheClass = $this->getAdapterClass($backendAdapter, 'backend'); + + $frontendCache = new $frontendCacheClass($frontendOptions); + $backendCache = new $backendCacheClass($frontendCache, $backendOptions); + + return $backendCache; + } + + public function createFromConfig($config, $configKey) + { + + $frontendAdapter = $config->cache->$configKey->frontend->adapter; + $frontendOptions = $config->cache->$configKey->frontend->options; + $frontendOptions = !empty($frontendOptions) ? $frontendOptions->toArray() : array(); + + $backendAdapter = $config->cache->$configKey->backend->adapter; + $backendOptions = $config->cache->$configKey->backend->options; + $backendOptions = !empty($backendOptions) ? $backendOptions->toArray() : array(); + + if (!$config->cache->enable || !$config->cache->$configKey->enable) { + $backendAdapter = 'disable'; + $backendOptions = array(); + } + + return $this->create($frontendAdapter, $frontendOptions, $backendAdapter, $backendOptions); + } +} \ No newline at end of file diff --git a/src/EvaEngine/Cache/RedisDisabled.php b/src/EvaEngine/Cache/RedisDisabled.php new file mode 100644 index 0000000..3cf1e29 --- /dev/null +++ b/src/EvaEngine/Cache/RedisDisabled.php @@ -0,0 +1,3001 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/15 下午4:39 +// +---------------------------------------------------------------------- +// + RedisDisabled.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Cache; + + +class RedisDisabled extends \Redis +{ + /** + * Creates a Redis client + * + * @example $redis = new Redis(); + */ + public function __construct( ) {} + + /** + * Connects to a Redis instance. + * + * @param string $host can be a host, or the path to a unix domain socket + * @param int $port optional + * @param float $timeout value in seconds (optional, default is 0.0 meaning unlimited) + * @return bool TRUE on success, FALSE on error. + *
+     * $redis->connect('127.0.0.1', 6379);
+     * $redis->connect('127.0.0.1');            // port 6379 by default
+     * $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
+     * $redis->connect('/tmp/redis.sock');      // unix domain socket.
+     * 
+ */ + public function connect( $host, $port = 6379, $timeout = 0.0 ) {} + + /** + * Set the string value in argument as value of the key, with a time to live. + * + * @param string $key + * @param int $ttl in milliseconds + * @param string $value + * @return bool: TRUE if the command is successful. + * @link http://redis.io/commands/setex + * $redis->psetex('key', 100, 'value'); // sets key → value, with 0.1 sec TTL. + */ + public function psetex($key, $ttl, $value) {} + + /** + * Scan a set for members. + * + * @see scan() + * @param string $key + * @param int $iterator + * @param string $pattern + * @param int $count + * @return array|bool + */ + public function sScan($key, $iterator, $pattern = '', $count = 0) {} + + /** + * Scan the keyspace for keys. + * + * @param int $iterator + * @param string $pattern + * @param int $count How many keys to return in a go (only a sugestion to Redis) + * @return array|bool an array of keys or FALSE if there are no more keys + * @link http://redis.io/commands/scan + *
+     * $it = NULL; // Initialize our iterator to NULL
+     * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); // retry when we get no keys back
+     * while($arr_keys = $redis->scan($it)) {
+     *   foreach($arr_keys as $str_key) {
+     *     echo "Here is a key: $str_key\n";
+     *   }
+     *   echo "No more keys to scan!\n";
+     * }
+     * 
+ */ + public function scan($iterator, $pattern = '', $count = 0) {} + + /** + * Scan a sorted set for members, with optional pattern and count. + * + * @see scan() + * @param string $key + * @param int $iterator + * @param string $pattern + * @param int $count + * @return array|bool + */ + public function zScan($key, $iterator, $pattern = '', $count = 0) {} + + /** + * Scan a HASH value for members, with an optional pattern and count. + * + * @see scan() + * @param string $key + * @param int $iterator + * @param string $pattern + * @param int $count + * @return array + */ + public function hScan($key, $iterator, $pattern = '', $count = 0) {} + + + + /** + * Issue the CLIENT command with various arguments. + * @param string $command list | getname | setname | kill + * @param string $arg + * @return mixed + * @link http://redis.io/commands/client-list + * @link http://redis.io/commands/client-getname + * @link http://redis.io/commands/client-setname + * @link http://redis.io/commands/client-kill + *
+     * $redis->client('list');
+     * $redis->client('getname');
+     * $redis->client('setname', 'somename');
+     * $redis->client('kill', );
+     * 
+ * + * + * CLIENT LIST will return an array of arrays with client information. + * CLIENT GETNAME will return the client name or false if none has been set + * CLIENT SETNAME will return true if it can be set and false if not + * CLIENT KILL will return true if the client can be killed, and false if not + */ + public function client($command, $arg = '') {} + + /** + * Access the Redis slow log. + * + * @param string $command get | len | reset + * @return mixed + * @link http://redis.io/commands/slowlog + *
+     * // Get ten slowlog entries
+     * $redis->slowlog('get', 10);
+     *
+     * // Get the default number of slowlog entries
+     * $redis->slowlog('get');
+     *
+     * // Reset our slowlog
+     * $redis->slowlog('reset');
+     *
+     * // Retrieve slowlog length
+     * $redis->slowlog('len');
+     * 
+ */ + public function slowlog($command) {} + + /** + * @see connect() + * @param string $host + * @param int $port + * @param float $timeout + */ + public function open( $host, $port = 6379, $timeout = 0.0 ) {} + + /** + * Connects to a Redis instance or reuse a connection already established with pconnect/popen. + * + * The connection will not be closed on close or end of request until the php process ends. + * So be patient on to many open FD's (specially on redis server side) when using persistent connections on + * many servers connecting to one redis server. + * + * Also more than one persistent connection can be made identified by either host + port + timeout + * or unix socket + timeout. + * + * This feature is not available in threaded versions. pconnect and popen then working like their non persistent + * equivalents. + * + * @param string $host can be a host, or the path to a unix domain socket + * @param int $port optional + * @param float $timeout value in seconds (optional, default is 0 meaning unlimited) + * @return bool TRUE on success, FALSE on ertcnror. + *
+     * $redis->connect('127.0.0.1', 6379);
+     * $redis->connect('127.0.0.1');            // port 6379 by default
+     * $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
+     * $redis->connect('/tmp/redis.sock');      // unix domain socket.
+     * 
+ */ + public function pconnect( $host, $port = 6379, $timeout = 0.0 ) {} + + /** + * @see pconnect() + * @param string $host + * @param int $port + * @param float $timeout + */ + public function popen( $host, $port = 6379, $timeout = 0.0 ) {} + + /** + * Disconnects from the Redis instance, except when pconnect is used. + */ + public function close( ) {} + + /** + * Set client option. + * + * @param string $name parameter name + * @param string $value parameter value + * @return bool: TRUE on success, FALSE on error. + * @example + *
+     * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);        // don't serialize data
+     * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);         // use built-in serialize/unserialize
+     * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);    // use igBinary serialize/unserialize
+     * $redis->setOption(Redis::OPT_PREFIX, 'myAppName:');                      // use custom prefix on all keys
+     * 
+ */ + public function setOption( $name, $value ) {} + + /** + * Get client option + * + * @param string $name parameter name + * @return int Parameter value. + * @example + * // return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, or Redis::SERIALIZER_IGBINARY. + * $redis->getOption(Redis::OPT_SERIALIZER); + */ + public function getOption( $name ) {} + + /** + * Check the current connection status + * + * @return string STRING: +PONG on success. Throws a RedisException object on connectivity error, as described above. + * @link http://redis.io/commands/ping + */ + public function ping( ) {} + + /** + * Get the value related to the specified key + * + * @param string $key + * @return string|bool: If key didn't exist, FALSE is returned. Otherwise, the value related to this key is returned. + * @link http://redis.io/commands/get + * @example $redis->get('key'); + */ + public function get( $key ) {} + + + /** + * Set the string value in argument as value of the key. + * + * @param string $key + * @param string $value + * @param int $timeout [optional] Calling setex() is preferred if you want a timeout. + * @return bool: TRUE if the command is successful. + * @link http://redis.io/commands/set + * @example $redis->set('key', 'value'); + */ + public function set( $key, $value, $timeout = 0 ) {} + + /** + * Set the string value in argument as value of the key, with a time to live. + * + * @param string $key + * @param int $ttl + * @param string $value + * @return bool: TRUE if the command is successful. + * @link http://redis.io/commands/setex + * @example $redis->setex('key', 3600, 'value'); // sets key → value, with 1h TTL. + */ + public function setex( $key, $ttl, $value ) {} + + /** + * Set the string value in argument as value of the key if the key doesn't already exist in the database. + * + * @param string $key + * @param string $value + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/setnx + * @example + *
+     * $redis->setnx('key', 'value');   // return TRUE
+     * $redis->setnx('key', 'value');   // return FALSE
+     * 
+ */ + public function setnx( $key, $value ) {} + + /** + * Remove specified keys. + * + * @param int|array $key1 An array of keys, or an undefined number of parameters, each a key: key1 key2 key3 ... keyN + * @param string $key2 ... + * @param string $key3 ... + * @return int Number of keys deleted. + * @link http://redis.io/commands/del + * @example + *
+     * $redis->set('key1', 'val1');
+     * $redis->set('key2', 'val2');
+     * $redis->set('key3', 'val3');
+     * $redis->set('key4', 'val4');
+     * $redis->delete('key1', 'key2');          // return 2
+     * $redis->delete(array('key3', 'key4'));   // return 2
+     * 
+ */ + public function del( $key1, $key2 = null, $key3 = null ) {} + + /** + * @see del() + * @param $key1 + * @param null $key2 + * @param null $key3 + */ + public function delete( $key1, $key2 = null, $key3 = null ) {} + + /** + * Enter and exit transactional mode. + * + * @internal param Redis::MULTI|Redis::PIPELINE + * Defaults to Redis::MULTI. + * A Redis::MULTI block of commands runs as a single transaction; + * a Redis::PIPELINE block is simply transmitted faster to the server, but without any guarantee of atomicity. + * discard cancels a transaction. + * @return Redis returns the Redis instance and enters multi-mode. + * Once in multi-mode, all subsequent method calls return the same object until exec() is called. + * @link http://redis.io/commands/multi + * @example + *
+     * $ret = $redis->multi()
+     *      ->set('key1', 'val1')
+     *      ->get('key1')
+     *      ->set('key2', 'val2')
+     *      ->get('key2')
+     *      ->exec();
+     *
+     * //$ret == array (
+     * //    0 => TRUE,
+     * //    1 => 'val1',
+     * //    2 => TRUE,
+     * //    3 => 'val2');
+     * 
+ */ + public function multi( ) {} + + /** + * @see multi() + * @link http://redis.io/commands/exec + */ + public function exec( ) {} + + /** + * @see multi() + * @link http://redis.io/commands/discard + */ + public function discard( ) {} + + /** + * Watches a key for modifications by another client. If the key is modified between WATCH and EXEC, + * the MULTI/EXEC transaction will fail (return FALSE). unwatch cancels all the watching of all keys by this client. + * @param string | array $key: a list of keys + * @return void + * @link http://redis.io/commands/watch + * @example + *
+     * $redis->watch('x');
+     * // long code here during the execution of which other clients could well modify `x`
+     * $ret = $redis->multi()
+     *          ->incr('x')
+     *          ->exec();
+     * // $ret = FALSE if x has been modified between the call to WATCH and the call to EXEC.
+     * 
+ */ + public function watch( $key ) {} + + /** + * @see watch() + * @link http://redis.io/commands/unwatch + */ + public function unwatch( ) {} + + /** + * Subscribe to channels. Warning: this function will probably change in the future. + * + * @param array $channels an array of channels to subscribe to + * @param string | array $callback either a string or an array($instance, 'method_name'). + * The callback function receives 3 parameters: the redis instance, the channel name, and the message. + * @link http://redis.io/commands/subscribe + * @example + *
+     * function f($redis, $chan, $msg) {
+     *  switch($chan) {
+     *      case 'chan-1':
+     *          ...
+     *          break;
+     *
+     *      case 'chan-2':
+     *                     ...
+     *          break;
+     *
+     *      case 'chan-2':
+     *          ...
+     *          break;
+     *      }
+     * }
+     *
+     * $redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
+     * 
+ */ + public function subscribe( $channels, $callback ) {} + + /** + * Subscribe to channels by pattern + * + * @param array $patterns The number of elements removed from the set. + * @param string|array $callback Either a string or an array with an object and method. + * The callback will get four arguments ($redis, $pattern, $channel, $message) + * @link http://redis.io/commands/psubscribe + * @example + *
+     * function psubscribe($redis, $pattern, $chan, $msg) {
+     *  echo "Pattern: $pattern\n";
+     *  echo "Channel: $chan\n";
+     *  echo "Payload: $msg\n";
+     * }
+     * 
+ */ + public function psubscribe( $patterns, $callback ) {} + + /** + * Publish messages to channels. Warning: this function will probably change in the future. + * + * @param string $channel a channel to publish to + * @param string $message string + * @link http://redis.io/commands/publish + * @return int Number of clients that received the message + * @example $redis->publish('chan-1', 'hello, world!'); // send message. + */ + public function publish( $channel, $message ) {} + + /** + * Verify if the specified key exists. + * + * @param string $key + * @return bool: If the key exists, return TRUE, otherwise return FALSE. + * @link http://redis.io/commands/exists + * @example + *
+     * $redis->set('key', 'value');
+     * $redis->exists('key');               //  TRUE
+     * $redis->exists('NonExistingKey');    // FALSE
+     * 
+ */ + public function exists( $key ) {} + + /** + * Increment the number stored at key by one. + * + * @param string $key + * @return int the new value + * @link http://redis.io/commands/incr + * @example + *
+     * $redis->incr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value 1
+     * $redis->incr('key1'); // 2
+     * $redis->incr('key1'); // 3
+     * $redis->incr('key1'); // 4
+     * 
+ */ + public function incr( $key ) {} + + /** + * Increment the float value of a key by the given amount + * + * @param string $key + * @param float $increment + * @return float + * @link http://redis.io/commands/incrbyfloat + * @example + *
+     * $redis = new Redis();
+     * $redis->connect('127.0.0.1');
+     * $redis->set('x', 3);
+     * var_dump( $redis->incrByFloat('x', 1.5) );   // float(4.5)
+     *
+     * // ! SIC
+     * var_dump( $redis->get('x') );                // string(3) "4.5"
+     * 
+ */ + public function incrByFloat( $key, $increment ) {} + + /** + * Increment the number stored at key by one. If the second argument is filled, it will be used as the integer + * value of the increment. + * + * @param string $key key + * @param int $value value that will be added to key (only for incrBy) + * @return int the new value + * @link http://redis.io/commands/incrby + * @example + *
+     * $redis->incr('key1');        // key1 didn't exists, set to 0 before the increment and now has the value 1
+     * $redis->incr('key1');        // 2
+     * $redis->incr('key1');        // 3
+     * $redis->incr('key1');        // 4
+     * $redis->incrBy('key1', 10);  // 14
+     * 
+ */ + public function incrBy( $key, $value ) {} + + /** + * Decrement the number stored at key by one. + * + * @param string $key + * @return int the new value + * @link http://redis.io/commands/decr + * @example + *
+     * $redis->decr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value -1
+     * $redis->decr('key1'); // -2
+     * $redis->decr('key1'); // -3
+     * 
+ */ + public function decr( $key ) {} + + /** + * Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer + * value of the decrement. + * + * @param string $key + * @param int $value that will be substracted to key (only for decrBy) + * @return int the new value + * @link http://redis.io/commands/decrby + * @example + *
+     * $redis->decr('key1');        // key1 didn't exists, set to 0 before the increment and now has the value -1
+     * $redis->decr('key1');        // -2
+     * $redis->decr('key1');        // -3
+     * $redis->decrBy('key1', 10);  // -13
+     * 
+ */ + public function decrBy( $key, $value ) {} + + /** + * Get the values of all the specified keys. If one or more keys dont exist, the array will contain FALSE at the + * position of the key. + * + * @param array $keys Array containing the list of the keys + * @return array Array containing the values related to keys in argument + * @example + *
+     * $redis->set('key1', 'value1');
+     * $redis->set('key2', 'value2');
+     * $redis->set('key3', 'value3');
+     * $redis->getMultiple(array('key1', 'key2', 'key3')); // array('value1', 'value2', 'value3');
+     * $redis->getMultiple(array('key0', 'key1', 'key5')); // array(`FALSE`, 'value2', `FALSE`);
+     * 
+ */ + public function getMultiple( array $keys ) {} + + /** + * Adds the string values to the head (left) of the list. Creates the list if the key didn't exist. + * If the key exists and is not a list, FALSE is returned. + * + * @param string $key + * @param string $value1 String, value to push in key + * @param string $value2 Optional + * @param string $valueN Optional + * @return int The new length of the list in case of success, FALSE in case of Failure. + * @link http://redis.io/commands/lpush + * @example + *
+     * $redis->lPush('l', 'v1', 'v2', 'v3', 'v4')   // int(4)
+     * var_dump( $redis->lRange('l', 0, -1) );
+     * //// Output:
+     * // array(4) {
+     * //   [0]=> string(2) "v4"
+     * //   [1]=> string(2) "v3"
+     * //   [2]=> string(2) "v2"
+     * //   [3]=> string(2) "v1"
+     * // }
+     * 
+ */ + public function lPush( $key, $value1, $value2 = null, $valueN = null ) {} + + /** + * Adds the string values to the tail (right) of the list. Creates the list if the key didn't exist. + * If the key exists and is not a list, FALSE is returned. + * + * @param string $key + * @param string $value1 String, value to push in key + * @param string $value2 Optional + * @param string $valueN Optional + * @return int The new length of the list in case of success, FALSE in case of Failure. + * @link http://redis.io/commands/rpush + * @example + *
+     * $redis->rPush('l', 'v1', 'v2', 'v3', 'v4');    // int(4)
+     * var_dump( $redis->lRange('l', 0, -1) );
+     * //// Output:
+     * // array(4) {
+     * //   [0]=> string(2) "v1"
+     * //   [1]=> string(2) "v2"
+     * //   [2]=> string(2) "v3"
+     * //   [3]=> string(2) "v4"
+     * // }
+     * 
+ */ + public function rPush( $key, $value1, $value2 = null, $valueN = null ) {} + + /** + * Adds the string value to the head (left) of the list if the list exists. + * + * @param string $key + * @param string $value String, value to push in key + * @return int The new length of the list in case of success, FALSE in case of Failure. + * @link http://redis.io/commands/lpushx + * @example + *
+     * $redis->delete('key1');
+     * $redis->lPushx('key1', 'A');     // returns 0
+     * $redis->lPush('key1', 'A');      // returns 1
+     * $redis->lPushx('key1', 'B');     // returns 2
+     * $redis->lPushx('key1', 'C');     // returns 3
+     * // key1 now points to the following list: [ 'A', 'B', 'C' ]
+     * 
+ */ + public function lPushx( $key, $value ) {} + + /** + * Adds the string value to the tail (right) of the list if the ist exists. FALSE in case of Failure. + * + * @param string $key + * @param string $value String, value to push in key + * @return int The new length of the list in case of success, FALSE in case of Failure. + * @link http://redis.io/commands/rpushx + * @example + *
+     * $redis->delete('key1');
+     * $redis->rPushx('key1', 'A'); // returns 0
+     * $redis->rPush('key1', 'A'); // returns 1
+     * $redis->rPushx('key1', 'B'); // returns 2
+     * $redis->rPushx('key1', 'C'); // returns 3
+     * // key1 now points to the following list: [ 'A', 'B', 'C' ]
+     * 
+ */ + public function rPushx( $key, $value ) {} + + /** + * Returns and removes the first element of the list. + * + * @param string $key + * @return string if command executed successfully BOOL FALSE in case of failure (empty list) + * @link http://redis.io/commands/lpop + * @example + *
+     * $redis->rPush('key1', 'A');
+     * $redis->rPush('key1', 'B');
+     * $redis->rPush('key1', 'C');  // key1 => [ 'A', 'B', 'C' ]
+     * $redis->lPop('key1');        // key1 => [ 'B', 'C' ]
+     * 
+ */ + public function lPop( $key ) {} + + /** + * Returns and removes the last element of the list. + * + * @param string $key + * @return string if command executed successfully BOOL FALSE in case of failure (empty list) + * @link http://redis.io/commands/rpop + * @example + *
+     * $redis->rPush('key1', 'A');
+     * $redis->rPush('key1', 'B');
+     * $redis->rPush('key1', 'C');  // key1 => [ 'A', 'B', 'C' ]
+     * $redis->rPop('key1');        // key1 => [ 'A', 'B' ]
+     * 
+ */ + public function rPop( $key ) {} + + /** + * Is a blocking lPop primitive. If at least one of the lists contains at least one element, + * the element will be popped from the head of the list and returned to the caller. + * Il all the list identified by the keys passed in arguments are empty, blPop will block + * during the specified timeout until an element is pushed to one of those lists. This element will be popped. + * + * @param array $keys Array containing the keys of the lists INTEGER Timeout + * Or STRING Key1 STRING Key2 STRING Key3 ... STRING Keyn INTEGER Timeout + * @return array array('listName', 'element') + * @link http://redis.io/commands/blpop + * @example + *
+     * // Non blocking feature
+     * $redis->lPush('key1', 'A');
+     * $redis->delete('key2');
+     *
+     * $redis->blPop('key1', 'key2', 10); // array('key1', 'A')
+     * // OR
+     * $redis->blPop(array('key1', 'key2'), 10); // array('key1', 'A')
+     *
+     * $redis->brPop('key1', 'key2', 10); // array('key1', 'A')
+     * // OR
+     * $redis->brPop(array('key1', 'key2'), 10); // array('key1', 'A')
+     *
+     * // Blocking feature
+     *
+     * // process 1
+     * $redis->delete('key1');
+     * $redis->blPop('key1', 10);
+     * // blocking for 10 seconds
+     *
+     * // process 2
+     * $redis->lPush('key1', 'A');
+     *
+     * // process 1
+     * // array('key1', 'A') is returned
+     * 
+ */ + public function blPop( array $keys ) {} + + /** + * Is a blocking rPop primitive. If at least one of the lists contains at least one element, + * the element will be popped from the head of the list and returned to the caller. + * Il all the list identified by the keys passed in arguments are empty, brPop will + * block during the specified timeout until an element is pushed to one of those lists. T + * his element will be popped. + * + * @param array $keys Array containing the keys of the lists INTEGER Timeout + * Or STRING Key1 STRING Key2 STRING Key3 ... STRING Keyn INTEGER Timeout + * @return array array('listName', 'element') + * @link http://redis.io/commands/brpop + * @example + *
+     * // Non blocking feature
+     * $redis->lPush('key1', 'A');
+     * $redis->delete('key2');
+     *
+     * $redis->blPop('key1', 'key2', 10); // array('key1', 'A')
+     * // OR
+     * $redis->blPop(array('key1', 'key2'), 10); // array('key1', 'A')
+     *
+     * $redis->brPop('key1', 'key2', 10); // array('key1', 'A')
+     * // OR
+     * $redis->brPop(array('key1', 'key2'), 10); // array('key1', 'A')
+     *
+     * // Blocking feature
+     *
+     * // process 1
+     * $redis->delete('key1');
+     * $redis->blPop('key1', 10);
+     * // blocking for 10 seconds
+     *
+     * // process 2
+     * $redis->lPush('key1', 'A');
+     *
+     * // process 1
+     * // array('key1', 'A') is returned
+     * 
+ */ + public function brPop( array $keys ) {} + + + /** + * Returns the size of a list identified by Key. If the list didn't exist or is empty, + * the command returns 0. If the data type identified by Key is not a list, the command return FALSE. + * + * @param string $key + * @return int The size of the list identified by Key exists. + * bool FALSE if the data type identified by Key is not list + * @link http://redis.io/commands/llen + * @example + *
+     * $redis->rPush('key1', 'A');
+     * $redis->rPush('key1', 'B');
+     * $redis->rPush('key1', 'C');  // key1 => [ 'A', 'B', 'C' ]
+     * $redis->lLen('key1');       // 3
+     * $redis->rPop('key1');
+     * $redis->lLen('key1');       // 2
+     * 
+ */ + public function lLen( $key ) {} + + /** + * @see lLen() + * @param string $key + * @param int $index + * @link http://redis.io/commands/llen + */ + public function lSize( $key ) {} + + + /** + * Return the specified element of the list stored at the specified key. + * 0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... + * Return FALSE in case of a bad index or a key that doesn't point to a list. + * @param string $key + * @param int $index + * @return String the element at this index + * Bool FALSE if the key identifies a non-string data type, or no value corresponds to this index in the list Key. + * @link http://redis.io/commands/lindex + * @example + *
+     * $redis->rPush('key1', 'A');
+     * $redis->rPush('key1', 'B');
+     * $redis->rPush('key1', 'C');  // key1 => [ 'A', 'B', 'C' ]
+     * $redis->lGet('key1', 0);     // 'A'
+     * $redis->lGet('key1', -1);    // 'C'
+     * $redis->lGet('key1', 10);    // `FALSE`
+     * 
+ */ + public function lIndex( $key, $index ) {} + + /** + * @see lIndex() + * @param string $key + * @param int $index + * @link http://redis.io/commands/lindex + */ + public function lGet( $key, $index ) {} + + + /** + * Set the list at index with the new value. + * + * @param string $key + * @param int $index + * @param string $value + * @return BOOL TRUE if the new value is setted. FALSE if the index is out of range, or data type identified by key + * is not a list. + * @link http://redis.io/commands/lset + * @example + *
+     * $redis->rPush('key1', 'A');
+     * $redis->rPush('key1', 'B');
+     * $redis->rPush('key1', 'C');  // key1 => [ 'A', 'B', 'C' ]
+     * $redis->lGet('key1', 0);     // 'A'
+     * $redis->lSet('key1', 0, 'X');
+     * $redis->lGet('key1', 0);     // 'X'
+     * 
+ */ + public function lSet( $key, $index, $value ) {} + + + /** + * Returns the specified elements of the list stored at the specified key in + * the range [start, end]. start and stop are interpretated as indices: 0 the first element, + * 1 the second ... -1 the last element, -2 the penultimate ... + * @param string $key + * @param int $start + * @param int $end + * @return array containing the values in specified range. + * @link http://redis.io/commands/lrange + * @example + *
+     * $redis->rPush('key1', 'A');
+     * $redis->rPush('key1', 'B');
+     * $redis->rPush('key1', 'C');
+     * $redis->lRange('key1', 0, -1); // array('A', 'B', 'C')
+     * 
+ */ + public function lRange( $key, $start, $end ) {} + + /** + * @see lRange() + * @link http://redis.io/commands/lrange + * @param string $key + * @param int $start + * @param int $end + */ + public function lGetRange( $key, $start, $end ) {} + + + /** + * Trims an existing list so that it will contain only a specified range of elements. + * + * @param string $key + * @param int $start + * @param int $stop + * @return array Bool return FALSE if the key identify a non-list value. + * @link http://redis.io/commands/ltrim + * @example + *
+     * $redis->rPush('key1', 'A');
+     * $redis->rPush('key1', 'B');
+     * $redis->rPush('key1', 'C');
+     * $redis->lRange('key1', 0, -1); // array('A', 'B', 'C')
+     * $redis->lTrim('key1', 0, 1);
+     * $redis->lRange('key1', 0, -1); // array('A', 'B')
+     * 
+ */ + public function lTrim( $key, $start, $stop ) {} + + /** + * @see lTrim() + * @link http://redis.io/commands/ltrim + * @param string $key + * @param int $start + * @param int $stop + */ + public function listTrim( $key, $start, $stop ) {} + + + /** + * Removes the first count occurences of the value element from the list. + * If count is zero, all the matching elements are removed. If count is negative, + * elements are removed from tail to head. + * + * @param string $key + * @param string $value + * @param int $count + * @return int the number of elements to remove + * bool FALSE if the value identified by key is not a list. + * @link http://redis.io/commands/lrem + * @example + *
+     * $redis->lPush('key1', 'A');
+     * $redis->lPush('key1', 'B');
+     * $redis->lPush('key1', 'C');
+     * $redis->lPush('key1', 'A');
+     * $redis->lPush('key1', 'A');
+     *
+     * $redis->lRange('key1', 0, -1);   // array('A', 'A', 'C', 'B', 'A')
+     * $redis->lRem('key1', 'A', 2);    // 2
+     * $redis->lRange('key1', 0, -1);   // array('C', 'B', 'A')
+     * 
+ */ + public function lRem( $key, $value, $count ) {} + + /** + * @see lRem + * @link http://redis.io/commands/lremove + * @param string $key + * @param string $value + * @param int $count + */ + public function lRemove( $key, $value, $count ) {} + + + /** + * Insert value in the list before or after the pivot value. the parameter options + * specify the position of the insert (before or after). If the list didn't exists, + * or the pivot didn't exists, the value is not inserted. + * + * @param string $key + * @param int $position Redis::BEFORE | Redis::AFTER + * @param string $pivot + * @param string $value + * @return int The number of the elements in the list, -1 if the pivot didn't exists. + * @link http://redis.io/commands/linsert + * @example + *
+     * $redis->delete('key1');
+     * $redis->lInsert('key1', Redis::AFTER, 'A', 'X');     // 0
+     *
+     * $redis->lPush('key1', 'A');
+     * $redis->lPush('key1', 'B');
+     * $redis->lPush('key1', 'C');
+     *
+     * $redis->lInsert('key1', Redis::BEFORE, 'C', 'X');    // 4
+     * $redis->lRange('key1', 0, -1);                       // array('A', 'B', 'X', 'C')
+     *
+     * $redis->lInsert('key1', Redis::AFTER, 'C', 'Y');     // 5
+     * $redis->lRange('key1', 0, -1);                       // array('A', 'B', 'X', 'C', 'Y')
+     *
+     * $redis->lInsert('key1', Redis::AFTER, 'W', 'value'); // -1
+     * 
+ */ + public function lInsert( $key, $position, $pivot, $value ) {} + + + /** + * Adds a values to the set value stored at key. + * If this value is already in the set, FALSE is returned. + * + * @param string $key Required key + * @param string $value1 Required value + * @param string $value2 Optional value + * @param string $valueN Optional value + * @return int The number of elements added to the set + * @link http://redis.io/commands/sadd + * @example + *
+     * $redis->sAdd('k', 'v1');                // int(1)
+     * $redis->sAdd('k', 'v1', 'v2', 'v3');    // int(2)
+     * 
+ */ + public function sAdd( $key, $value1, $value2 = null, $valueN = null ) {} + + + /** + * Removes the specified members from the set value stored at key. + * + * @param string $key + * @param string $member1 + * @param string $member2 + * @param string $memberN + * @return int The number of elements removed from the set. + * @link http://redis.io/commands/srem + * @example + *
+     * var_dump( $redis->sAdd('k', 'v1', 'v2', 'v3') );    // int(3)
+     * var_dump( $redis->sRem('k', 'v2', 'v3') );          // int(2)
+     * var_dump( $redis->sMembers('k') );
+     * //// Output:
+     * // array(1) {
+     * //   [0]=> string(2) "v1"
+     * // }
+     * 
+ */ + public function sRem( $key, $member1, $member2 = null, $memberN = null ) {} + + /** + * @see sRem() + * @link http://redis.io/commands/srem + * @param string $key + * @param string $member1 + * @param string $member2 + * @param string $memberN + */ + public function sRemove( $key, $member1, $member2 = null, $memberN = null ) {} + + + /** + * Moves the specified member from the set at srcKey to the set at dstKey. + * + * @param string $srcKey + * @param string $dstKey + * @param string $member + * @return bool If the operation is successful, return TRUE. + * If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, FALSE is returned. + * @link http://redis.io/commands/smove + * @example + *
+     * $redis->sAdd('key1' , 'set11');
+     * $redis->sAdd('key1' , 'set12');
+     * $redis->sAdd('key1' , 'set13');          // 'key1' => {'set11', 'set12', 'set13'}
+     * $redis->sAdd('key2' , 'set21');
+     * $redis->sAdd('key2' , 'set22');          // 'key2' => {'set21', 'set22'}
+     * $redis->sMove('key1', 'key2', 'set13');  // 'key1' =>  {'set11', 'set12'}
+     *                                          // 'key2' =>  {'set21', 'set22', 'set13'}
+     * 
+ */ + public function sMove( $srcKey, $dstKey, $member ) {} + + + /** + * Checks if value is a member of the set stored at the key key. + * + * @param string $key + * @param string $value + * @return bool TRUE if value is a member of the set at key key, FALSE otherwise. + * @link http://redis.io/commands/sismember + * @example + *
+     * $redis->sAdd('key1' , 'set1');
+     * $redis->sAdd('key1' , 'set2');
+     * $redis->sAdd('key1' , 'set3'); // 'key1' => {'set1', 'set2', 'set3'}
+     *
+     * $redis->sIsMember('key1', 'set1'); // TRUE
+     * $redis->sIsMember('key1', 'setX'); // FALSE
+     * 
+ */ + public function sIsMember( $key, $value ) {} + + /** + * @see sIsMember() + * @link http://redis.io/commands/sismember + * @param string $key + * @param string $value + */ + public function sContains( $key, $value ) {} + + /** + * Returns the cardinality of the set identified by key. + * + * @param string $key + * @return int the cardinality of the set identified by key, 0 if the set doesn't exist. + * @link http://redis.io/commands/scard + * @example + *
+     * $redis->sAdd('key1' , 'set1');
+     * $redis->sAdd('key1' , 'set2');
+     * $redis->sAdd('key1' , 'set3');   // 'key1' => {'set1', 'set2', 'set3'}
+     * $redis->sCard('key1');           // 3
+     * $redis->sCard('keyX');           // 0
+     * 
+ */ + public function sCard( $key ) {} + + + /** + * Removes and returns a random element from the set value at Key. + * + * @param string $key + * @return string "popped" value + * bool FALSE if set identified by key is empty or doesn't exist. + * @link http://redis.io/commands/spop + * @example + *
+     * $redis->sAdd('key1' , 'set1');
+     * $redis->sAdd('key1' , 'set2');
+     * $redis->sAdd('key1' , 'set3');   // 'key1' => {'set3', 'set1', 'set2'}
+     * $redis->sPop('key1');            // 'set1', 'key1' => {'set3', 'set2'}
+     * $redis->sPop('key1');            // 'set3', 'key1' => {'set2'}
+     * 
+ */ + public function sPop( $key ) {} + + + /** + * Returns a random element from the set value at Key, without removing it. + * + * @param string $key + * @return string value from the set + * bool FALSE if set identified by key is empty or doesn't exist. + * @link http://redis.io/commands/srandmember + * @example + *
+     * $redis->sAdd('key1' , 'set1');
+     * $redis->sAdd('key1' , 'set2');
+     * $redis->sAdd('key1' , 'set3');   // 'key1' => {'set3', 'set1', 'set2'}
+     * $redis->sRandMember('key1');     // 'set1', 'key1' => {'set3', 'set1', 'set2'}
+     * $redis->sRandMember('key1');     // 'set3', 'key1' => {'set3', 'set1', 'set2'}
+     * 
+ */ + public function sRandMember( $key ) {} + + /** + * Returns the members of a set resulting from the intersection of all the sets + * held at the specified keys. If just a single key is specified, then this command + * produces the members of this set. If one of the keys is missing, FALSE is returned. + * + * @param string $key1 keys identifying the different sets on which we will apply the intersection. + * @param string $key2 ... + * @param string $keyN ... + * @return array, contain the result of the intersection between those keys. + * If the intersection between the different sets is empty, the return value will be empty array. + * @link http://redis.io/commands/sinterstore + * @example + *
+     * $redis->sAdd('key1', 'val1');
+     * $redis->sAdd('key1', 'val2');
+     * $redis->sAdd('key1', 'val3');
+     * $redis->sAdd('key1', 'val4');
+     *
+     * $redis->sAdd('key2', 'val3');
+     * $redis->sAdd('key2', 'val4');
+     *
+     * $redis->sAdd('key3', 'val3');
+     * $redis->sAdd('key3', 'val4');
+     *
+     * var_dump($redis->sInter('key1', 'key2', 'key3'));
+     *
+     * //array(2) {
+     * //  [0]=>
+     * //  string(4) "val4"
+     * //  [1]=>
+     * //  string(4) "val3"
+     * //}
+     * 
+ */ + public function sInter( $key1, $key2, $keyN = null ) {} + + /** + * Performs a sInter command and stores the result in a new set. + * + * @param string $dstKey the key to store the diff into. + * @param string $key1 are intersected as in sInter. + * @param string $key2 ... + * @param string $keyN ... + * @return int: The cardinality of the resulting set, or FALSE in case of a missing key. + * @link http://redis.io/commands/sinterstore + * @example + *
+     * $redis->sAdd('key1', 'val1');
+     * $redis->sAdd('key1', 'val2');
+     * $redis->sAdd('key1', 'val3');
+     * $redis->sAdd('key1', 'val4');
+     *
+     * $redis->sAdd('key2', 'val3');
+     * $redis->sAdd('key2', 'val4');
+     *
+     * $redis->sAdd('key3', 'val3');
+     * $redis->sAdd('key3', 'val4');
+     *
+     * var_dump($redis->sInterStore('output', 'key1', 'key2', 'key3'));
+     * var_dump($redis->sMembers('output'));
+     *
+     * //int(2)
+     * //
+     * //array(2) {
+     * //  [0]=>
+     * //  string(4) "val4"
+     * //  [1]=>
+     * //  string(4) "val3"
+     * //}
+     * 
+ */ + public function sInterStore( $dstKey, $key1, $key2, $keyN = null ) {} + + /** + * Performs the union between N sets and returns it. + * + * @param string $key1 Any number of keys corresponding to sets in redis. + * @param string $key2 ... + * @param string $keyN ... + * @return array of strings: The union of all these sets. + * @link http://redis.io/commands/sunionstore + * @example + *
+     * $redis->delete('s0', 's1', 's2');
+     *
+     * $redis->sAdd('s0', '1');
+     * $redis->sAdd('s0', '2');
+     * $redis->sAdd('s1', '3');
+     * $redis->sAdd('s1', '1');
+     * $redis->sAdd('s2', '3');
+     * $redis->sAdd('s2', '4');
+     *
+     * var_dump($redis->sUnion('s0', 's1', 's2'));
+     *
+     * array(4) {
+     * //  [0]=>
+     * //  string(1) "3"
+     * //  [1]=>
+     * //  string(1) "4"
+     * //  [2]=>
+     * //  string(1) "1"
+     * //  [3]=>
+     * //  string(1) "2"
+     * //}
+     * 
+ */ + public function sUnion( $key1, $key2, $keyN = null ) {} + + /** + * Performs the same action as sUnion, but stores the result in the first key + * + * @param string $dstKey the key to store the diff into. + * @param string $key1 Any number of keys corresponding to sets in redis. + * @param string $key2 ... + * @param string $keyN ... + * @return int Any number of keys corresponding to sets in redis. + * @link http://redis.io/commands/sunionstore + * @example + *
+     * $redis->delete('s0', 's1', 's2');
+     *
+     * $redis->sAdd('s0', '1');
+     * $redis->sAdd('s0', '2');
+     * $redis->sAdd('s1', '3');
+     * $redis->sAdd('s1', '1');
+     * $redis->sAdd('s2', '3');
+     * $redis->sAdd('s2', '4');
+     *
+     * var_dump($redis->sUnionStore('dst', 's0', 's1', 's2'));
+     * var_dump($redis->sMembers('dst'));
+     *
+     * //int(4)
+     * //array(4) {
+     * //  [0]=>
+     * //  string(1) "3"
+     * //  [1]=>
+     * //  string(1) "4"
+     * //  [2]=>
+     * //  string(1) "1"
+     * //  [3]=>
+     * //  string(1) "2"
+     * //}
+     * 
+ */ + public function sUnionStore( $dstKey, $key1, $key2, $keyN = null ) {} + + /** + * Performs the difference between N sets and returns it. + * + * @param string $key1 Any number of keys corresponding to sets in redis. + * @param string $key2 ... + * @param string $keyN ... + * @return array of strings: The difference of the first set will all the others. + * @link http://redis.io/commands/sdiff + * @example + *
+     * $redis->delete('s0', 's1', 's2');
+     *
+     * $redis->sAdd('s0', '1');
+     * $redis->sAdd('s0', '2');
+     * $redis->sAdd('s0', '3');
+     * $redis->sAdd('s0', '4');
+     *
+     * $redis->sAdd('s1', '1');
+     * $redis->sAdd('s2', '3');
+     *
+     * var_dump($redis->sDiff('s0', 's1', 's2'));
+     *
+     * //array(2) {
+     * //  [0]=>
+     * //  string(1) "4"
+     * //  [1]=>
+     * //  string(1) "2"
+     * //}
+     * 
+ */ + public function sDiff( $key1, $key2, $keyN = null ) {} + + /** + * Performs the same action as sDiff, but stores the result in the first key + * + * @param string $dstKey the key to store the diff into. + * @param string $key1 Any number of keys corresponding to sets in redis + * @param string $key2 ... + * @param string $keyN ... + * @return int: The cardinality of the resulting set, or FALSE in case of a missing key. + * @link http://redis.io/commands/sdiffstore + * @example + *
+     * $redis->delete('s0', 's1', 's2');
+     *
+     * $redis->sAdd('s0', '1');
+     * $redis->sAdd('s0', '2');
+     * $redis->sAdd('s0', '3');
+     * $redis->sAdd('s0', '4');
+     *
+     * $redis->sAdd('s1', '1');
+     * $redis->sAdd('s2', '3');
+     *
+     * var_dump($redis->sDiffStore('dst', 's0', 's1', 's2'));
+     * var_dump($redis->sMembers('dst'));
+     *
+     * //int(2)
+     * //array(2) {
+     * //  [0]=>
+     * //  string(1) "4"
+     * //  [1]=>
+     * //  string(1) "2"
+     * //}
+     * 
+ */ + public function sDiffStore( $dstKey, $key1, $key2, $keyN = null ) {} + + /** + * Returns the contents of a set. + * + * @param string $key + * @return array An array of elements, the contents of the set. + * @link http://redis.io/commands/smembers + * @example + *
+     * $redis->delete('s');
+     * $redis->sAdd('s', 'a');
+     * $redis->sAdd('s', 'b');
+     * $redis->sAdd('s', 'a');
+     * $redis->sAdd('s', 'c');
+     * var_dump($redis->sMembers('s'));
+     *
+     * //array(3) {
+     * //  [0]=>
+     * //  string(1) "c"
+     * //  [1]=>
+     * //  string(1) "a"
+     * //  [2]=>
+     * //  string(1) "b"
+     * //}
+     * // The order is random and corresponds to redis' own internal representation of the set structure.
+     * 
+ */ + public function sMembers( $key ) {} + + /** + * @see sMembers() + * @param string $key + * @link http://redis.io/commands/smembers + */ + public function sGetMembers( $key ) {} + + /** + * Sets a value and returns the previous entry at that key. + * + * @param string $key + * @param string $value + * @return string A string, the previous value located at this key. + * @link http://redis.io/commands/getset + * @example + *
+     * $redis->set('x', '42');
+     * $exValue = $redis->getSet('x', 'lol');   // return '42', replaces x by 'lol'
+     * $newValue = $redis->get('x')'            // return 'lol'
+     * 
+ */ + public function getSet( $key, $value ) {} + + /** + * Returns a random key. + * + * @return string: an existing key in redis. + * @link http://redis.io/commands/randomkey + * @example + *
+     * $key = $redis->randomKey();
+     * $surprise = $redis->get($key);  // who knows what's in there.
+     * 
+ */ + public function randomKey( ) {} + + + /** + * Switches to a given database. + * + * @param int $dbindex + * @return bool TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/select + * @example + *
+     * $redis->select(0);       // switch to DB 0
+     * $redis->set('x', '42');  // write 42 to x
+     * $redis->move('x', 1);    // move to DB 1
+     * $redis->select(1);       // switch to DB 1
+     * $redis->get('x');        // will return 42
+     * 
+ */ + public function select( $dbindex ) {} + + /** + * Moves a key to a different database. + * + * @param string $key + * @param int $dbindex + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/move + * @example + *
+     * $redis->select(0);       // switch to DB 0
+     * $redis->set('x', '42');  // write 42 to x
+     * $redis->move('x', 1);    // move to DB 1
+     * $redis->select(1);       // switch to DB 1
+     * $redis->get('x');        // will return 42
+     * 
+ */ + public function move( $key, $dbindex ) {} + + /** + * Renames a key. + * + * @param string $srcKey + * @param string $dstKey + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/rename + * @example + *
+     * $redis->set('x', '42');
+     * $redis->rename('x', 'y');
+     * $redis->get('y');   // → 42
+     * $redis->get('x');   // → `FALSE`
+     * 
+ */ + public function rename( $srcKey, $dstKey ) {} + + /** + * @see rename() + * @link http://redis.io/commands/rename + * @param string $srcKey + * @param string $dstKey + */ + public function renameKey( $srcKey, $dstKey ) {} + + /** + * Renames a key. + * + * Same as rename, but will not replace a key if the destination already exists. + * This is the same behaviour as setNx. + * + * @param string $srcKey + * @param string $dstKey + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/renamenx + * @example + *
+     * $redis->set('x', '42');
+     * $redis->rename('x', 'y');
+     * $redis->get('y');   // → 42
+     * $redis->get('x');   // → `FALSE`
+     * 
+ */ + public function renameNx( $srcKey, $dstKey ) {} + + /** + * Sets an expiration date (a timeout) on an item. + * + * @param string $key The key that will disappear. + * @param int $ttl The key's remaining Time To Live, in seconds. + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/expire + * @example + *
+     * $redis->set('x', '42');
+     * $redis->setTimeout('x', 3);  // x will disappear in 3 seconds.
+     * sleep(5);                    // wait 5 seconds
+     * $redis->get('x');            // will return `FALSE`, as 'x' has expired.
+     * 
+ */ + public function expire( $key, $ttl ) {} + + /** + * Sets an expiration date (a timeout in milliseconds) on an item. + * + * @param string $key The key that will disappear. + * @param int $pttl The key's remaining Time To Live, in milliseconds. + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/pexpire + * @example + *
+     * $redis->set('x', '42');
+     * $redis->pExpire('x', 11500); // x will disappear in 11500 milliseconds.
+     * $redis->ttl('x');            // 12
+     * $redis->pttl('x');           // 11500
+     * 
+ */ + public function pExpire( $key, $ttl ) {} + + /** + * @see expire() + * @param string $key + * @param int $ttl + * @link http://redis.io/commands/expire + */ + public function setTimeout( $key, $ttl ) {} + + /** + * Sets an expiration date (a timestamp) on an item. + * + * @param string $key The key that will disappear. + * @param int $timestamp Unix timestamp. The key's date of death, in seconds from Epoch time. + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/expireat + * @example + *
+     * $redis->set('x', '42');
+     * $now = time(NULL);               // current timestamp
+     * $redis->expireAt('x', $now + 3); // x will disappear in 3 seconds.
+     * sleep(5);                        // wait 5 seconds
+     * $redis->get('x');                // will return `FALSE`, as 'x' has expired.
+     * 
+ */ + public function expireAt( $key, $timestamp ) {} + + /** + * Sets an expiration date (a timestamp) on an item. Requires a timestamp in milliseconds + * + * @param string $key The key that will disappear. + * @param int $timestamp Unix timestamp. The key's date of death, in seconds from Epoch time. + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/pexpireat + * @example + *
+     * $redis->set('x', '42');
+     * $redis->pExpireAt('x', 1555555555005);
+     * echo $redis->ttl('x');                       // 218270121
+     * echo $redis->pttl('x');                      // 218270120575
+     * 
+ */ + public function pExpireAt( $key, $timestamp ) {} + + /** + * Returns the keys that match a certain pattern. + * + * @param string $pattern pattern, using '*' as a wildcard. + * @return array of STRING: The keys that match a certain pattern. + * @link http://redis.io/commands/keys + * @example + *
+     * $allKeys = $redis->keys('*');   // all keys will match this.
+     * $keyWithUserPrefix = $redis->keys('user*');
+     * 
+ */ + public function keys( $pattern ) {} + + /** + * @see keys() + * @param string $pattern + * @link http://redis.io/commands/keys + */ + public function getKeys( $pattern ) {} + + /** + * Returns the current database's size. + * + * @return int: DB size, in number of keys. + * @link http://redis.io/commands/dbsize + * @example + *
+     * $count = $redis->dbSize();
+     * echo "Redis has $count keys\n";
+     * 
+ */ + public function dbSize( ) {} + + /** + * Authenticate the connection using a password. + * Warning: The password is sent in plain-text over the network. + * + * @param string $password + * @return bool: TRUE if the connection is authenticated, FALSE otherwise. + * @link http://redis.io/commands/auth + * @example $redis->auth('foobared'); + */ + public function auth( $password ) {} + + /** + * Starts the background rewrite of AOF (Append-Only File) + * + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/bgrewriteaof + * @example $redis->bgrewriteaof(); + */ + public function bgrewriteaof( ) {} + + /** + * Changes the slave status + * Either host and port, or no parameter to stop being a slave. + * + * @param string $host [optional] + * @param int $port [optional] + * @return bool: TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/slaveof + * @example + *
+     * $redis->slaveof('10.0.1.7', 6379);
+     * // ...
+     * $redis->slaveof();
+     * 
+ */ + public function slaveof( $host = '127.0.0.1', $port = 6379 ) {} + + /** + * Describes the object pointed to by a key. + * The information to retrieve (string) and the key (string). + * Info can be one of the following: + * - "encoding" + * - "refcount" + * - "idletime" + * + * @param string $string + * @param string $key + * @return string for "encoding", int for "refcount" and "idletime", FALSE if the key doesn't exist. + * @link http://redis.io/commands/object + * @example + *
+     * $redis->object("encoding", "l"); // → ziplist
+     * $redis->object("refcount", "l"); // → 1
+     * $redis->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds).
+     * 
+ */ + public function object( $string = '', $key = '' ) {} + + /** + * Performs a synchronous save. + * + * @return bool: TRUE in case of success, FALSE in case of failure. + * If a save is already running, this command will fail and return FALSE. + * @link http://redis.io/commands/save + * @example $redis->save(); + */ + public function save( ) {} + + /** + * Performs a background save. + * + * @return bool: TRUE in case of success, FALSE in case of failure. + * If a save is already running, this command will fail and return FALSE. + * @link http://redis.io/commands/bgsave + * @example $redis->bgSave(); + */ + public function bgsave( ) {} + + /** + * Returns the timestamp of the last disk save. + * + * @return int: timestamp. + * @link http://redis.io/commands/lastsave + * @example $redis->lastSave(); + */ + public function lastSave( ) {} + + + /** + * Returns the type of data pointed by a given key. + * + * @param string $key + * @return int + * + * Depending on the type of the data pointed by the key, + * this method will return the following value: + * - string: Redis::REDIS_STRING + * - set: Redis::REDIS_SET + * - list: Redis::REDIS_LIST + * - zset: Redis::REDIS_ZSET + * - hash: Redis::REDIS_HASH + * - other: Redis::REDIS_NOT_FOUND + * @link http://redis.io/commands/type + * @example $redis->type('key'); + */ + public function type( $key ) {} + + /** + * Append specified string to the string stored in specified key. + * + * @param string $key + * @param string $value + * @return int: Size of the value after the append + * @link http://redis.io/commands/append + * @example + *
+     * $redis->set('key', 'value1');
+     * $redis->append('key', 'value2'); // 12
+     * $redis->get('key');              // 'value1value2'
+     * 
+ */ + public function append( $key, $value ) {} + + + /** + * Return a substring of a larger string + * + * @param string $key + * @param int $start + * @param int $end + * @return string: the substring + * @link http://redis.io/commands/getrange + * @example + *
+     * $redis->set('key', 'string value');
+     * $redis->getRange('key', 0, 5);   // 'string'
+     * $redis->getRange('key', -5, -1); // 'value'
+     * 
+ */ + public function getRange( $key, $start, $end ) {} + + /** + * Return a substring of a larger string + * + * @deprecated + * @param string $key + * @param int $start + * @param int $end + */ + public function substr( $key, $start, $end ) {} + + + /** + * Changes a substring of a larger string. + * + * @param string $key + * @param int $offset + * @param string $value + * @return string: the length of the string after it was modified. + * @link http://redis.io/commands/setrange + * @example + *
+     * $redis->set('key', 'Hello world');
+     * $redis->setRange('key', 6, "redis"); // returns 11
+     * $redis->get('key');                  // "Hello redis"
+     * 
+ */ + public function setRange( $key, $offset, $value ) {} + + /** + * Get the length of a string value. + * + * @param string $key + * @return int + * @link http://redis.io/commands/strlen + * @example + *
+     * $redis->set('key', 'value');
+     * $redis->strlen('key'); // 5
+     * 
+ */ + public function strlen( $key ) {} + + /** + * Return a single bit out of a larger string + * + * @param string $key + * @param int $offset + * @return int: the bit value (0 or 1) + * @link http://redis.io/commands/getbit + * @example + *
+     * $redis->set('key', "\x7f");  // this is 0111 1111
+     * $redis->getBit('key', 0);    // 0
+     * $redis->getBit('key', 1);    // 1
+     * 
+ */ + public function getBit( $key, $offset ) {} + + /** + * Changes a single bit of a string. + * + * @param string $key + * @param int $offset + * @param bool|int $value bool or int (1 or 0) + * @return int: 0 or 1, the value of the bit before it was set. + * @link http://redis.io/commands/setbit + * @example + *
+     * $redis->set('key', "*");     // ord("*") = 42 = 0x2f = "0010 1010"
+     * $redis->setBit('key', 5, 1); // returns 0
+     * $redis->setBit('key', 7, 1); // returns 0
+     * $redis->get('key');          // chr(0x2f) = "/" = b("0010 1111")
+     * 
+ */ + public function setBit( $key, $offset, $value ) {} + + /** + * Count bits in a string. + * + * @param string $key + * @return int The number of bits set to 1 in the value behind the input key. + * @link http://redis.io/commands/bitcount + * @example + *
+     * $redis->set('bit', '345'); // // 11 0011  0011 0100  0011 0101
+     * var_dump( $redis->bitCount('bit', 0, 0) ); // int(4)
+     * var_dump( $redis->bitCount('bit', 1, 1) ); // int(3)
+     * var_dump( $redis->bitCount('bit', 2, 2) ); // int(4)
+     * var_dump( $redis->bitCount('bit', 0, 2) ); // int(11)
+     * 
+ */ + public function bitCount( $key ) {} + + /** + * Bitwise operation on multiple keys. + * + * @param string $operation either "AND", "OR", "NOT", "XOR" + * @param string $retKey return key + * @param string $key1 + * @param string $key2 + * @return int The size of the string stored in the destination key. + * @link http://redis.io/commands/bitop + * @example + *
+     * $redis->set('bit1', '1'); // 11 0001
+     * $redis->set('bit2', '2'); // 11 0010
+     *
+     * $redis->bitOp('AND', 'bit', 'bit1', 'bit2'); // bit = 110000
+     * $redis->bitOp('OR',  'bit', 'bit1', 'bit2'); // bit = 110011
+     * $redis->bitOp('NOT', 'bit', 'bit1', 'bit2'); // bit = 110011
+     * $redis->bitOp('XOR', 'bit', 'bit1', 'bit2'); // bit = 11
+     * 
+ */ + public function bitOp( $operation, $retKey, $key1, $key2, $key3 = null ) {} + + /** + * Removes all entries from the current database. + * + * @return bool: Always TRUE. + * @link http://redis.io/commands/flushdb + * @example $redis->flushDB(); + */ + public function flushDB( ) {} + + /** + * Removes all entries from all databases. + * + * @return bool: Always TRUE. + * @link http://redis.io/commands/flushall + * @example $redis->flushAll(); + */ + public function flushAll( ) {} + + /** + * Sort + * + * @param string $key + * @param array $option array(key => value, ...) - optional, with the following keys and values: + * - 'by' => 'some_pattern_*', + * - 'limit' => array(0, 1), + * - 'get' => 'some_other_pattern_*' or an array of patterns, + * - 'sort' => 'asc' or 'desc', + * - 'alpha' => TRUE, + * - 'store' => 'external-key' + * @return array + * An array of values, or a number corresponding to the number of elements stored if that was used. + * @link http://redis.io/commands/sort + * @example + *
+     * $redis->delete('s');
+     * $redis->sadd('s', 5);
+     * $redis->sadd('s', 4);
+     * $redis->sadd('s', 2);
+     * $redis->sadd('s', 1);
+     * $redis->sadd('s', 3);
+     *
+     * var_dump($redis->sort('s')); // 1,2,3,4,5
+     * var_dump($redis->sort('s', array('sort' => 'desc'))); // 5,4,3,2,1
+     * var_dump($redis->sort('s', array('sort' => 'desc', 'store' => 'out'))); // (int)5
+     * 
+ */ + public function sort( $key, $option = null ) {} + + + /** + * Returns an associative array of strings and integers + * @param string $option Optional. The option to provide redis. + * SERVER | CLIENTS | MEMORY | PERSISTENCE | STATS | REPLICATION | CPU | CLASTER | KEYSPACE | COMANDSTATS + * + * Returns an associative array of strings and integers, with the following keys: + * - redis_version + * - redis_git_sha1 + * - redis_git_dirty + * - arch_bits + * - multiplexing_api + * - process_id + * - uptime_in_seconds + * - uptime_in_days + * - lru_clock + * - used_cpu_sys + * - used_cpu_user + * - used_cpu_sys_children + * - used_cpu_user_children + * - connected_clients + * - connected_slaves + * - client_longest_output_list + * - client_biggest_input_buf + * - blocked_clients + * - used_memory + * - used_memory_human + * - used_memory_peak + * - used_memory_peak_human + * - mem_fragmentation_ratio + * - mem_allocator + * - loading + * - aof_enabled + * - changes_since_last_save + * - bgsave_in_progress + * - last_save_time + * - total_connections_received + * - total_commands_processed + * - expired_keys + * - evicted_keys + * - keyspace_hits + * - keyspace_misses + * - hash_max_zipmap_entries + * - hash_max_zipmap_value + * - pubsub_channels + * - pubsub_patterns + * - latest_fork_usec + * - vm_enabled + * - role + * @link http://redis.io/commands/info + * @return string + * @example + *
+     * $redis->info();
+     *
+     * or
+     *
+     * $redis->info("COMMANDSTATS"); //Information on the commands that have been run (>=2.6 only)
+     * $redis->info("CPU"); // just CPU information from Redis INFO
+     * 
+ */ + public function info( $option = null ) {} + + /** + * Resets the statistics reported by Redis using the INFO command (`info()` function). + * These are the counters that are reset: + * - Keyspace hits + * - Keyspace misses + * - Number of commands processed + * - Number of connections received + * - Number of expired keys + * + * @return bool: `TRUE` in case of success, `FALSE` in case of failure. + * @example $redis->resetStat(); + * @link http://redis.io/commands/config-resetstat + */ + public function resetStat( ) {} + + /** + * Returns the time to live left for a given key, in seconds. If the key doesn't exist, FALSE is returned. + * + * @param string $key + * @return int, the time left to live in seconds. + * @link http://redis.io/commands/ttl + * @example $redis->ttl('key'); + */ + public function ttl( $key ) {} + + /** + * Returns a time to live left for a given key, in milliseconds. + * + * If the key doesn't exist, FALSE is returned. + * + * @param string $key + * @return int the time left to live in milliseconds. + * @link http://redis.io/commands/pttl + * @example $redis->pttl('key'); + */ + public function pttl( $key ) {} + + /** + * Remove the expiration timer from a key. + * + * @param string $key + * @return bool: TRUE if a timeout was removed, FALSE if the key didn’t exist or didn’t have an expiration timer. + * @link http://redis.io/commands/persist + * @example $redis->persist('key'); + */ + public function persist( $key ) {} + + /** + * Sets multiple key-value pairs in one atomic command. + * MSETNX only returns TRUE if all the keys were set (see SETNX). + * + * @param array(key => value) $array Pairs: array(key => value, ...) + * @return bool TRUE in case of success, FALSE in case of failure. + * @link http://redis.io/commands/mset + * @example + *
+     * $redis->mset(array('key0' => 'value0', 'key1' => 'value1'));
+     * var_dump($redis->get('key0'));
+     * var_dump($redis->get('key1'));
+     * // Output:
+     * // string(6) "value0"
+     * // string(6) "value1"
+     * 
+ */ + public function mset( array $array ) {} + + + /** + * Returns the values of all specified keys. + * + * For every key that does not hold a string value or does not exist, + * the special value false is returned. Because of this, the operation never fails. + * + * @param array $array + * @return array + * @link http://redis.io/commands/mget + * @example + *
+     * $redis->delete('x', 'y', 'z', 'h');	// remove x y z
+     * $redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'));
+     * $redis->hset('h', 'field', 'value');
+     * var_dump($redis->mget(array('x', 'y', 'z', 'h')));
+     * // Output:
+     * // array(3) {
+     * // [0]=>
+     * // string(1) "a"
+     * // [1]=>
+     * // string(1) "b"
+     * // [2]=>
+     * // string(1) "c"
+     * // [3]=>
+     * // bool(false)
+     * // }
+     * 
+ */ + public function mget( array $array ) {} + + /** + * @see mset() + * @param array $array + * @return int 1 (if the keys were set) or 0 (no key was set) + * @link http://redis.io/commands/msetnx + */ + public function msetnx( array $array ) {} + + /** + * Pops a value from the tail of a list, and pushes it to the front of another list. + * Also return this value. + * + * @since redis >= 1.1 + * @param string $srcKey + * @param string $dstKey + * @return string The element that was moved in case of success, FALSE in case of failure. + * @link http://redis.io/commands/rpoplpush + * @example + *
+     * $redis->delete('x', 'y');
+     *
+     * $redis->lPush('x', 'abc');
+     * $redis->lPush('x', 'def');
+     * $redis->lPush('y', '123');
+     * $redis->lPush('y', '456');
+     *
+     * // move the last of x to the front of y.
+     * var_dump($redis->rpoplpush('x', 'y'));
+     * var_dump($redis->lRange('x', 0, -1));
+     * var_dump($redis->lRange('y', 0, -1));
+     *
+     * //Output:
+     * //
+     * //string(3) "abc"
+     * //array(1) {
+     * //  [0]=>
+     * //  string(3) "def"
+     * //}
+     * //array(3) {
+     * //  [0]=>
+     * //  string(3) "abc"
+     * //  [1]=>
+     * //  string(3) "456"
+     * //  [2]=>
+     * //  string(3) "123"
+     * //}
+     * 
+ */ + public function rpoplpush( $srcKey, $dstKey ) {} + + /** + * A blocking version of rpoplpush, with an integral timeout in the third parameter. + * + * @param string $srcKey + * @param string $dstKey + * @param int $timeout + * @return string The element that was moved in case of success, FALSE in case of timeout. + * @link http://redis.io/commands/brpoplpush + */ + public function brpoplpush( $srcKey, $dstKey, $timeout ) {} + + /** + * Adds the specified member with a given score to the sorted set stored at key. + * + * @param string $key Required key + * @param float $score1 Required score + * @param string $value1 Required value + * @param float $score2 Optional score + * @param string $value2 Optional value + * @param float $scoreN Optional score + * @param string $valueN Optional value + * @return int Number of values added + * @link http://redis.io/commands/zadd + * @example + *
+     * 
+     * $redis->zAdd('z', 1, 'v2', 2, 'v2', 3, 'v3', 4, 'v4' );  // int(2)
+     * $redis->zRem('z', 'v2', 'v3');                           // int(2)
+     * var_dump( $redis->zRange('z', 0, -1) );
+     * //// Output:
+     * // array(2) {
+     * //   [0]=> string(2) "v1"
+     * //   [1]=> string(2) "v4"
+     * // }
+     * 
+ *
+ */ + public function zAdd( $key, $score1, $value1, $score2 = null, $value2 = null, $scoreN = null, $valueN = null ) {} + + /** + * Returns a range of elements from the ordered set stored at the specified key, + * with values in the range [start, end]. start and stop are interpreted as zero-based indices: + * 0 the first element, + * 1 the second ... + * -1 the last element, + * -2 the penultimate ... + * + * @param string $key + * @param int $start + * @param int $end + * @param bool $withscores + * @return array Array containing the values in specified range. + * @link http://redis.io/commands/zrange + * @example + *
+     * $redis->zAdd('key1', 0, 'val0');
+     * $redis->zAdd('key1', 2, 'val2');
+     * $redis->zAdd('key1', 10, 'val10');
+     * $redis->zRange('key1', 0, -1); // array('val0', 'val2', 'val10')
+     * // with scores
+     * $redis->zRange('key1', 0, -1, true); // array('val0' => 0, 'val2' => 2, 'val10' => 10)
+     * 
+ */ + public function zRange( $key, $start, $end, $withscores = null ) {} + + /** + * Deletes a specified member from the ordered set. + * + * @param string $key + * @param string $member1 + * @param string $member2 + * @param string $memberN + * @return int Number of deleted values + * @link http://redis.io/commands/zrem + * @example + *
+     * $redis->zAdd('z', 1, 'v2', 2, 'v2', 3, 'v3', 4, 'v4' );  // int(2)
+     * $redis->zRem('z', 'v2', 'v3');                           // int(2)
+     * var_dump( $redis->zRange('z', 0, -1) );
+     * //// Output:
+     * // array(2) {
+     * //   [0]=> string(2) "v1"
+     * //   [1]=> string(2) "v4"
+     * // }
+     * 
+ */ + public function zRem( $key, $member1, $member2 = null, $memberN = null ) {} + + /** + * @see zRem() + * @param string $key + * @param string $member1 + * @param string $member2 + * @param string $memberN + * @return int Number of deleted values + * @link http://redis.io/commands/zrem + */ + public function zDelete( $key, $member1, $member2 = null, $memberN = null ) {} + + /** + * Returns the elements of the sorted set stored at the specified key in the range [start, end] + * in reverse order. start and stop are interpretated as zero-based indices: + * 0 the first element, + * 1 the second ... + * -1 the last element, + * -2 the penultimate ... + * + * @param string $key + * @param int $start + * @param int $end + * @param bool $withscore + * @return array Array containing the values in specified range. + * @link http://redis.io/commands/zrevrange + * @example + *
+     * $redis->zAdd('key', 0, 'val0');
+     * $redis->zAdd('key', 2, 'val2');
+     * $redis->zAdd('key', 10, 'val10');
+     * $redis->zRevRange('key', 0, -1); // array('val10', 'val2', 'val0')
+     *
+     * // with scores
+     * $redis->zRevRange('key', 0, -1, true); // array('val10' => 10, 'val2' => 2, 'val0' => 0)
+     * 
+ */ + public function zRevRange( $key, $start, $end, $withscore = null ) {} + + /** + * Returns the elements of the sorted set stored at the specified key which have scores in the + * range [start,end]. Adding a parenthesis before start or end excludes it from the range. + * +inf and -inf are also valid limits. + * + * zRevRangeByScore returns the same items in reverse order, when the start and end parameters are swapped. + * + * @param string $key + * @param int $start + * @param int $end + * @param array $options Two options are available: + * - withscores => TRUE, + * - and limit => array($offset, $count) + * @return array Array containing the values in specified range. + * @link http://redis.io/commands/zrangebyscore + * @example + *
+     * $redis->zAdd('key', 0, 'val0');
+     * $redis->zAdd('key', 2, 'val2');
+     * $redis->zAdd('key', 10, 'val10');
+     * $redis->zRangeByScore('key', 0, 3);                                          // array('val0', 'val2')
+     * $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE);              // array('val0' => 0, 'val2' => 2)
+     * $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1));                        // array('val2' => 2)
+     * $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1));                        // array('val2')
+     * $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1));  // array('val2' => 2)
+     * 
+ */ + public function zRangeByScore( $key, $start, $end, array $options = array() ) {} + + /** + * @see zRangeByScore() + * @param string $key + * @param int $start + * @param int $end + * @param array $options + * + * @return array + */ + public function zRevRangeByScore( $key, $start, $end, array $options = array() ) {} + + /** + * Returns the number of elements of the sorted set stored at the specified key which have + * scores in the range [start,end]. Adding a parenthesis before start or end excludes it + * from the range. +inf and -inf are also valid limits. + * + * @param string $key + * @param string $start + * @param string $end + * @return int the size of a corresponding zRangeByScore. + * @link http://redis.io/commands/zcount + * @example + *
+     * $redis->zAdd('key', 0, 'val0');
+     * $redis->zAdd('key', 2, 'val2');
+     * $redis->zAdd('key', 10, 'val10');
+     * $redis->zCount('key', 0, 3); // 2, corresponding to array('val0', 'val2')
+     * 
+ */ + public function zCount( $key, $start, $end ) {} + + /** + * Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. + * + * @param string $key + * @param float|string $start double or "+inf" or "-inf" string + * @param float|string $end double or "+inf" or "-inf" string + * @return int The number of values deleted from the sorted set + * @link http://redis.io/commands/zremrangebyscore + * @example + *
+     * $redis->zAdd('key', 0, 'val0');
+     * $redis->zAdd('key', 2, 'val2');
+     * $redis->zAdd('key', 10, 'val10');
+     * $redis->zRemRangeByScore('key', 0, 3); // 2
+     * 
+ */ + public function zRemRangeByScore( $key, $start, $end ) {} + + /** + * @see zRemRangeByScore() + * @param string $key + * @param float $start + * @param float $end + */ + public function zDeleteRangeByScore( $key, $start, $end ) {} + + /** + * Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. + * + * @param string $key + * @param int $start + * @param int $end + * @return int The number of values deleted from the sorted set + * @link http://redis.io/commands/zremrangebyrank + * @example + *
+     * $redis->zAdd('key', 1, 'one');
+     * $redis->zAdd('key', 2, 'two');
+     * $redis->zAdd('key', 3, 'three');
+     * $redis->zRemRangeByRank('key', 0, 1); // 2
+     * $redis->zRange('key', 0, -1, array('withscores' => TRUE)); // array('three' => 3)
+     * 
+ */ + public function zRemRangeByRank( $key, $start, $end ) {} + + /** + * @see zRemRangeByRank() + * @param string $key + * @param int $start + * @param int $end + * @link http://redis.io/commands/zremrangebyscore + */ + public function zDeleteRangeByRank( $key, $start, $end ) {} + + /** + * Returns the cardinality of an ordered set. + * + * @param string $key + * @return int the set's cardinality + * @link http://redis.io/commands/zsize + * @example + *
+     * $redis->zAdd('key', 0, 'val0');
+     * $redis->zAdd('key', 2, 'val2');
+     * $redis->zAdd('key', 10, 'val10');
+     * $redis->zCard('key');            // 3
+     * 
+ */ + public function zCard( $key ) {} + + /** + * @see zCard() + * @param string $key + */ + public function zSize( $key ) {} + + /** + * Returns the score of a given member in the specified sorted set. + * + * @param string $key + * @param string $member + * @return float + * @link http://redis.io/commands/zscore + * @example + *
+     * $redis->zAdd('key', 2.5, 'val2');
+     * $redis->zScore('key', 'val2'); // 2.5
+     * 
+ */ + public function zScore( $key, $member ) {} + + /** + * Returns the rank of a given member in the specified sorted set, starting at 0 for the item + * with the smallest score. zRevRank starts at 0 for the item with the largest score. + * + * @param string $key + * @param string $member + * @return int the item's score. + * @link http://redis.io/commands/zrank + * @example + *
+     * $redis->delete('z');
+     * $redis->zAdd('key', 1, 'one');
+     * $redis->zAdd('key', 2, 'two');
+     * $redis->zRank('key', 'one');     // 0
+     * $redis->zRank('key', 'two');     // 1
+     * $redis->zRevRank('key', 'one');  // 1
+     * $redis->zRevRank('key', 'two');  // 0
+     * 
+ */ + public function zRank( $key, $member ) {} + + /** + * @see zRank() + * @param string $key + * @param string $member + * @return int the item's score + * @link http://redis.io/commands/zrevrank + */ + public function zRevRank( $key, $member ) {} + + /** + * Increments the score of a member from a sorted set by a given amount. + * + * @param string $key + * @param float $value (double) value that will be added to the member's score + * @param string $member + * @return float the new value + * @link http://redis.io/commands/zincrby + * @example + *
+     * $redis->delete('key');
+     * $redis->zIncrBy('key', 2.5, 'member1');  // key or member1 didn't exist, so member1's score is to 0
+     *                                          // before the increment and now has the value 2.5
+     * $redis->zIncrBy('key', 1, 'member1');    // 3.5
+     * 
+ */ + public function zIncrBy( $key, $value, $member ) {} + + /** + * Creates an union of sorted sets given in second argument. + * The result of the union will be stored in the sorted set defined by the first argument. + * The third optionnel argument defines weights to apply to the sorted sets in input. + * In this case, the weights will be multiplied by the score of each element in the sorted set + * before applying the aggregation. The forth argument defines the AGGREGATE option which + * specify how the results of the union are aggregated. + * + * @param string $Output + * @param array $ZSetKeys + * @param array $Weights + * @param string $aggregateFunction Either "SUM", "MIN", or "MAX": defines the behaviour to use on + * duplicate entries during the zUnion. + * @return int The number of values in the new sorted set. + * @link http://redis.io/commands/zunionstore + * @example + *
+     * $redis->delete('k1');
+     * $redis->delete('k2');
+     * $redis->delete('k3');
+     * $redis->delete('ko1');
+     * $redis->delete('ko2');
+     * $redis->delete('ko3');
+     *
+     * $redis->zAdd('k1', 0, 'val0');
+     * $redis->zAdd('k1', 1, 'val1');
+     *
+     * $redis->zAdd('k2', 2, 'val2');
+     * $redis->zAdd('k2', 3, 'val3');
+     *
+     * $redis->zUnion('ko1', array('k1', 'k2')); // 4, 'ko1' => array('val0', 'val1', 'val2', 'val3')
+     *
+     * // Weighted zUnion
+     * $redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); // 4, 'ko2' => array('val0', 'val1', 'val2', 'val3')
+     * $redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); // 4, 'ko3' => array('val0', 'val2', 'val3', 'val1')
+     * 
+ */ + public function zUnion($Output, $ZSetKeys, array $Weights = null, $aggregateFunction = 'SUM') {} + + /** + * Creates an intersection of sorted sets given in second argument. + * The result of the union will be stored in the sorted set defined by the first argument. + * The third optional argument defines weights to apply to the sorted sets in input. + * In this case, the weights will be multiplied by the score of each element in the sorted set + * before applying the aggregation. The forth argument defines the AGGREGATE option which + * specify how the results of the union are aggregated. + * + * @param string $Output + * @param array $ZSetKeys + * @param array $Weights + * @param string $aggregateFunction Either "SUM", "MIN", or "MAX": + * defines the behaviour to use on duplicate entries during the zInter. + * @return int The number of values in the new sorted set. + * @link http://redis.io/commands/zinterstore + * @example + *
+     * $redis->delete('k1');
+     * $redis->delete('k2');
+     * $redis->delete('k3');
+     *
+     * $redis->delete('ko1');
+     * $redis->delete('ko2');
+     * $redis->delete('ko3');
+     * $redis->delete('ko4');
+     *
+     * $redis->zAdd('k1', 0, 'val0');
+     * $redis->zAdd('k1', 1, 'val1');
+     * $redis->zAdd('k1', 3, 'val3');
+     *
+     * $redis->zAdd('k2', 2, 'val1');
+     * $redis->zAdd('k2', 3, 'val3');
+     *
+     * $redis->zInter('ko1', array('k1', 'k2'));               // 2, 'ko1' => array('val1', 'val3')
+     * $redis->zInter('ko2', array('k1', 'k2'), array(1, 1));  // 2, 'ko2' => array('val1', 'val3')
+     *
+     * // Weighted zInter
+     * $redis->zInter('ko3', array('k1', 'k2'), array(1, 5), 'min'); // 2, 'ko3' => array('val1', 'val3')
+     * $redis->zInter('ko4', array('k1', 'k2'), array(1, 5), 'max'); // 2, 'ko4' => array('val3', 'val1')
+     * 
+ */ + public function zInter($Output, $ZSetKeys, array $Weights = null, $aggregateFunction = 'SUM') {} + + /** + * Adds a value to the hash stored at key. If this value is already in the hash, FALSE is returned. + * + * @param string $key + * @param string $hashKey + * @param string $value + * @return int + * 1 if value didn't exist and was added successfully, + * 0 if the value was already present and was replaced, FALSE if there was an error. + * @link http://redis.io/commands/hset + * @example + *
+     * $redis->delete('h')
+     * $redis->hSet('h', 'key1', 'hello');  // 1, 'key1' => 'hello' in the hash at "h"
+     * $redis->hGet('h', 'key1');           // returns "hello"
+     *
+     * $redis->hSet('h', 'key1', 'plop');   // 0, value was replaced.
+     * $redis->hGet('h', 'key1');           // returns "plop"
+     * 
+ */ + public function hSet( $key, $hashKey, $value ) {} + + /** + * Adds a value to the hash stored at key only if this field isn't already in the hash. + * + * @param string $key + * @param string $hashKey + * @param string $value + * @return bool TRUE if the field was set, FALSE if it was already present. + * @link http://redis.io/commands/hsetnx + * @example + *
+     * $redis->delete('h')
+     * $redis->hSetNx('h', 'key1', 'hello'); // TRUE, 'key1' => 'hello' in the hash at "h"
+     * $redis->hSetNx('h', 'key1', 'world'); // FALSE, 'key1' => 'hello' in the hash at "h". No change since the field
+     * wasn't replaced.
+     * 
+ */ + public function hSetNx( $key, $hashKey, $value ) {} + + /** + * Gets a value from the hash stored at key. + * If the hash table doesn't exist, or the key doesn't exist, FALSE is returned. + * + * @param string $key + * @param string $hashKey + * @return string The value, if the command executed successfully BOOL FALSE in case of failure + * @link http://redis.io/commands/hget + */ + public function hGet($key, $hashKey) {} + + /** + * Returns the length of a hash, in number of items + * + * @param string $key + * @return int the number of items in a hash, FALSE if the key doesn't exist or isn't a hash. + * @link http://redis.io/commands/hlen + * @example + *
+     * $redis->delete('h')
+     * $redis->hSet('h', 'key1', 'hello');
+     * $redis->hSet('h', 'key2', 'plop');
+     * $redis->hLen('h'); // returns 2
+     * 
+ */ + public function hLen( $key ) {} + + /** + * Removes a values from the hash stored at key. + * If the hash table doesn't exist, or the key doesn't exist, FALSE is returned. + * + * @param string $key + * @param string $hashKey1 + * @param string $hashKey2 + * @param string $hashKeyN + * @return int Number of deleted fields + * @link http://redis.io/commands/hdel + * @example + *
+     * $redis->hMSet('h',
+     *               array(
+     *                    'f1' => 'v1',
+     *                    'f2' => 'v2',
+     *                    'f3' => 'v3',
+     *                    'f4' => 'v4',
+     *               ));
+     *
+     * var_dump( $redis->hDel('h', 'f1') );        // int(1)
+     * var_dump( $redis->hDel('h', 'f2', 'f3') );  // int(2)
+     * s
+     * var_dump( $redis->hGetAll('h') );
+     * //// Output:
+     * //  array(1) {
+     * //    ["f4"]=> string(2) "v4"
+     * //  }
+     * 
+ */ + public function hDel( $key, $hashKey1, $hashKey2 = null, $hashKeyN = null ) {} + + /** + * Returns the keys in a hash, as an array of strings. + * + * @param string $key + * @return array An array of elements, the keys of the hash. This works like PHP's array_keys(). + * @link http://redis.io/commands/hkeys + * @example + *
+     * $redis->delete('h');
+     * $redis->hSet('h', 'a', 'x');
+     * $redis->hSet('h', 'b', 'y');
+     * $redis->hSet('h', 'c', 'z');
+     * $redis->hSet('h', 'd', 't');
+     * var_dump($redis->hKeys('h'));
+     *
+     * // Output:
+     * // array(4) {
+     * // [0]=>
+     * // string(1) "a"
+     * // [1]=>
+     * // string(1) "b"
+     * // [2]=>
+     * // string(1) "c"
+     * // [3]=>
+     * // string(1) "d"
+     * // }
+     * // The order is random and corresponds to redis' own internal representation of the set structure.
+     * 
+ */ + public function hKeys( $key ) {} + + /** + * Returns the values in a hash, as an array of strings. + * + * @param string $key + * @return array An array of elements, the values of the hash. This works like PHP's array_values(). + * @link http://redis.io/commands/hvals + * @example + *
+     * $redis->delete('h');
+     * $redis->hSet('h', 'a', 'x');
+     * $redis->hSet('h', 'b', 'y');
+     * $redis->hSet('h', 'c', 'z');
+     * $redis->hSet('h', 'd', 't');
+     * var_dump($redis->hVals('h'));
+     *
+     * // Output
+     * // array(4) {
+     * //   [0]=>
+     * //   string(1) "x"
+     * //   [1]=>
+     * //   string(1) "y"
+     * //   [2]=>
+     * //   string(1) "z"
+     * //   [3]=>
+     * //   string(1) "t"
+     * // }
+     * // The order is random and corresponds to redis' own internal representation of the set structure.
+     * 
+ */ + public function hVals( $key ) {} + + /** + * Returns the whole hash, as an array of strings indexed by strings. + * + * @param string $key + * @return array An array of elements, the contents of the hash. + * @link http://redis.io/commands/hgetall + * @example + *
+     * $redis->delete('h');
+     * $redis->hSet('h', 'a', 'x');
+     * $redis->hSet('h', 'b', 'y');
+     * $redis->hSet('h', 'c', 'z');
+     * $redis->hSet('h', 'd', 't');
+     * var_dump($redis->hGetAll('h'));
+     *
+     * // Output:
+     * // array(4) {
+     * //   ["a"]=>
+     * //   string(1) "x"
+     * //   ["b"]=>
+     * //   string(1) "y"
+     * //   ["c"]=>
+     * //   string(1) "z"
+     * //   ["d"]=>
+     * //   string(1) "t"
+     * // }
+     * // The order is random and corresponds to redis' own internal representation of the set structure.
+     * 
+ */ + public function hGetAll( $key ) {} + + /** + * Verify if the specified member exists in a key. + * + * @param string $key + * @param string $hashKey + * @return bool: If the member exists in the hash table, return TRUE, otherwise return FALSE. + * @link http://redis.io/commands/hexists + * @example + *
+     * $redis->hSet('h', 'a', 'x');
+     * $redis->hExists('h', 'a');               //  TRUE
+     * $redis->hExists('h', 'NonExistingKey');  // FALSE
+     * 
+ */ + public function hExists( $key, $hashKey ) {} + + /** + * Increments the value of a member from a hash by a given amount. + * + * @param string $key + * @param string $hashKey + * @param int $value (integer) value that will be added to the member's value + * @return int the new value + * @link http://redis.io/commands/hincrby + * @example + *
+     * $redis->delete('h');
+     * $redis->hIncrBy('h', 'x', 2); // returns 2: h[x] = 2 now.
+     * $redis->hIncrBy('h', 'x', 1); // h[x] ← 2 + 1. Returns 3
+     * 
+ */ + public function hIncrBy( $key, $hashKey, $value ) {} + + /** + * Increment the float value of a hash field by the given amount + * @param string $key + * @param string $field + * @param float $increment + * @return float + * @link http://redis.io/commands/hincrbyfloat + * @example + *
+     * $redis = new Redis();
+     * $redis->connect('127.0.0.1');
+     * $redis->hset('h', 'float', 3);
+     * $redis->hset('h', 'int',   3);
+     * var_dump( $redis->hIncrByFloat('h', 'float', 1.5) ); // float(4.5)
+     *
+     * var_dump( $redis->hGetAll('h') );
+     *
+     * // Output
+     *  array(2) {
+     *    ["float"]=>
+     *    string(3) "4.5"
+     *    ["int"]=>
+     *    string(1) "3"
+     *  }
+     * 
+ */ + public function hIncrByFloat( $key, $field, $increment ) {} + + /** + * Fills in a whole hash. Non-string values are converted to string, using the standard (string) cast. + * NULL values are stored as empty strings + * + * @param string $key + * @param array $hashKeys key → value array + * @return bool + * @link http://redis.io/commands/hmset + * @example + *
+     * $redis->delete('user:1');
+     * $redis->hMset('user:1', array('name' => 'Joe', 'salary' => 2000));
+     * $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now.
+     * 
+ */ + public function hMset( $key, $hashKeys ) {} + + /** + * Retirieve the values associated to the specified fields in the hash. + * + * @param string $key + * @param array $hashKeys + * @return array Array An array of elements, the values of the specified fields in the hash, + * with the hash keys as array keys. + * @link http://redis.io/commands/hmget + * @example + *
+     * $redis->delete('h');
+     * $redis->hSet('h', 'field1', 'value1');
+     * $redis->hSet('h', 'field2', 'value2');
+     * $redis->hmGet('h', array('field1', 'field2')); // returns array('field1' => 'value1', 'field2' => 'value2')
+     * 
+ */ + public function hMGet( $key, $hashKeys ) {} + + /** + * Get or Set the redis config keys. + * + * @param string $operation either `GET` or `SET` + * @param string $key for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples. + * @param string $value optional string (only for `SET`) + * @return array Associative array for `GET`, key -> value + * @link http://redis.io/commands/config-get + * @link http://redis.io/commands/config-set + * @example + *
+     * $redis->config("GET", "*max-*-entries*");
+     * $redis->config("SET", "dir", "/var/run/redis/dumps/");
+     * 
+ */ + public function config( $operation, $key, $value ) {} + + /** + * @see eval() + * @param string $script + * @param array $args + * @param int $numKeys + */ + public function evaluate( $script, $args = array(), $numKeys = 0 ) {} + + /** + * Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself. + * In order to run this command Redis will have to have already loaded the script, either by running it or via + * the SCRIPT LOAD command. + * @param string $scriptSha + * @param array $args + * @param int $numKeys + * @return mixed. @see eval() + * @see eval() + * @link http://redis.io/commands/evalsha + * @example + *
+     * $script = 'return 1';
+     * $sha = $redis->script('load', $script);
+     * $redis->evalSha($sha); // Returns 1
+     * 
+ */ + public function evalSha( $scriptSha, $args = array(), $numKeys = 0 ) {} + + /** + * @see evalSha() + * @param string $scriptSha + * @param array $args + * @param int $numKeys + */ + public function evaluateSha( $scriptSha, $args = array(), $numKeys = 0 ) {} + + /** + * Execute the Redis SCRIPT command to perform various operations on the scripting subsystem. + * @param string $command load | flush | kill | exists + * @param string $script + * @return mixed + * @link http://redis.io/commands/script-load + * @link http://redis.io/commands/script-kill + * @link http://redis.io/commands/script-flush + * @link http://redis.io/commands/script-exists + * @example + *
+     * $redis->script('load', $script);
+     * $redis->script('flush');
+     * $redis->script('kill');
+     * $redis->script('exists', $script1, [$script2, $script3, ...]);
+     * 
+ * + * SCRIPT LOAD will return the SHA1 hash of the passed script on success, and FALSE on failure. + * SCRIPT FLUSH should always return TRUE + * SCRIPT KILL will return true if a script was able to be killed and false if not + * SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script + */ + public function script( $command, $script ) {} + + /** + * The last error message (if any) + * @return string A string with the last returned script based error message, or NULL if there is no error + * @example + *
+     * $redis->eval('this-is-not-lua');
+     * $err = $redis->getLastError();
+     * // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'"
+     * 
+ */ + public function getLastError() {} + + /** + * Clear the last error message + * + * @return bool true + * @example + *
+     * $redis->set('x', 'a');
+     * $redis->incr('x');
+     * $err = $redis->getLastError();
+     * // "ERR value is not an integer or out of range"
+     * $redis->clearLastError();
+     * $err = $redis->getLastError();
+     * // NULL
+     * 
+ */ + public function clearLastError() {} + + /** + * A utility method to prefix the value with the prefix setting for phpredis. + * @param $value The value you wish to prefix + * @return string If a prefix is set up, the value now prefixed. If there is no prefix, the value will be returned unchanged. + * @example + *
+     * $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:');
+     * $redis->_prefix('my-value'); // Will return 'my-prefix:my-value'
+     * 
+ */ + public function _prefix( $value ) {} + + /** + * A utility method to unserialize data with whatever serializer is set up. If there is no serializer set, the + * value will be returned unchanged. If there is a serializer set up, and the data passed in is malformed, an + * exception will be thrown. This can be useful if phpredis is serializing values, and you return something from + * redis in a LUA script that is serialized. + * @param string $value The value to be unserialized + * @return mixed + * @example + *
+     * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
+     * $redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3)
+     * 
+ */ + public function _unserialize( $value ) {} + + /** + * Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command. + * The data that comes out of DUMP is a binary representation of the key as Redis stores it. + * @param string $key + * @return string The Redis encoded value of the key, or FALSE if the key doesn't exist + * @link http://redis.io/commands/dump + * @example + *
+     * $redis->set('foo', 'bar');
+     * $val = $redis->dump('foo'); // $val will be the Redis encoded key value
+     * 
+ */ + public function dump( $key ) {} + + /** + * Restore a key from the result of a DUMP operation. + * + * @param string $key The key name + * @param int $ttl How long the key should live (if zero, no expire will be set on the key) + * @param string $value (binary). The Redis encoded key value (from DUMP) + * @return bool + * @link http://redis.io/commands/restore + * @example + *
+     * $redis->set('foo', 'bar');
+     * $val = $redis->dump('foo');
+     * $redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo'
+     * 
+ */ + public function restore( $key, $ttl, $value ) {} + + /** + * Migrates a key to a different Redis instance. + * + * @param string $host The destination host + * @param int $port The TCP port to connect to. + * @param string $key The key to migrate. + * @param int $db The target DB. + * @param int $timeout The maximum amount of time given to this transfer. + * @return bool + * @link http://redis.io/commands/migrate + * @example + *
+     * $redis->migrate('backup', 6379, 'foo', 0, 3600);
+     * 
+ */ + public function migrate( $host, $port, $key, $db, $timeout ) {} + + /** + * Return the current Redis server time. + * @return array If successfull, the time will come back as an associative array with element zero being the + * unix timestamp, and element one being microseconds. + * @link http://redis.io/commands/time + *
+     * var_dump( $redis->time() );
+     * // array(2) {
+     * //   [0] => string(10) "1342364352"
+     * //   [1] => string(6) "253002"
+     * // }
+     * 
+ */ + public function time() {} +} + diff --git a/src/EvaEngine/Db/DbAdapterCreator.php b/src/EvaEngine/Db/DbAdapterCreator.php new file mode 100644 index 0000000..8b65e59 --- /dev/null +++ b/src/EvaEngine/Db/DbAdapterCreator.php @@ -0,0 +1,50 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/11 下午4:38 +// +---------------------------------------------------------------------- +// + DbAdapterCreator.php +// +---------------------------------------------------------------------- +namespace Eva\EvaEngine\Db; + +use Eva\EvaEngine\Foundation\AdapterCreator; +use Eva\EvaEngine\IoC; + +class DbAdapterCreator extends AdapterCreator +{ + protected static $adapterMapping = array( + 'mysql' => 'Phalcon\Db\Adapter\Pdo\Mysql', + 'oracle' => 'Phalcon\Db\Adapter\Pdo\Oracle', + 'postgresql' => 'Phalcon\Db\Adapter\Pdo\Postgresql', + 'sqlite' => 'Phalcon\Db\Adapter\Pdo\Sqlite', + ); + + public function create($adapter, array $options, $eventsManager = null) + { + $adapterClass = $this->getAdapterClass($adapter); + $options['charset'] = isset($options['charset']) && $options['charset'] ? $options['charset'] : 'utf8'; + /** @var \Phalcon\Db\Adapter\Pdo $dbAdapter */ + $dbAdapter = new $adapterClass($options); + $dbAdapter->setEventsManager($eventsManager ? $eventsManager : eva_get('eventsManager')); + + return $dbAdapter; + } + + /** + * {@inheritdoc} + */ + protected function getAdaptersMapping() + { + return array( + 'default' => array( + 'mysql' => 'Phalcon\Db\Adapter\Pdo\Mysql', + 'oracle' => 'Phalcon\Db\Adapter\Pdo\Oracle', + 'postgresql' => 'Phalcon\Db\Adapter\Pdo\Postgresql', + 'sqlite' => 'Phalcon\Db\Adapter\Pdo\Sqlite', + ) + ); + } +} \ No newline at end of file diff --git a/src/EvaEngine/DevTool/IDE/Facade/Alias.php b/src/EvaEngine/DevTool/IDE/Facade/Alias.php new file mode 100644 index 0000000..487e71d --- /dev/null +++ b/src/EvaEngine/DevTool/IDE/Facade/Alias.php @@ -0,0 +1,277 @@ + + * @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link https://github.com/barryvdh/laravel-ide-helper + */ +namespace Eva\EvaEngine\DevTool\IDE\Facade; + +class Alias +{ + protected $alias; + protected $facade; + protected $extends = null; + protected $classType = 'class'; + protected $short; + protected $namespace = '__root'; + protected $root = null; + protected $classes = array(); + protected $methods = array(); + protected $usedMethods = array(); + protected $valid = false; + protected $magicMethods = array(); + protected $interfaces = array(); + + /** + * @param string $alias + * @param string $facade + * @param array $magicMethods + * @param array $interfaces + */ + public function __construct($alias, $facade, $magicMethods = array(), $interfaces = array()) + { + $this->alias = $alias; + $this->magicMethods = $magicMethods; + $this->interfaces = $interfaces; + // Make the class absolute + $facade = '\\' . ltrim($facade, '\\'); + $this->facade = $facade; + $this->detectRoot(); + if ((!$this->isTrait() && $this->root)) { + $this->valid = true; + } else { + return; + } + $this->addClass($this->root); + $this->detectNamespace(); + $this->detectClassType(); + + if ($facade === '\Illuminate\Database\Eloquent\Model') { + $this->usedMethods = array('decrement', 'increment'); + } + } + + /** + * Add one or more classes to analyze + * + * @param array|string $classes + */ + public function addClass($classes) + { + $classes = (array)$classes; + foreach ($classes as $class) { + if (class_exists($class) || interface_exists($class)) { + $this->classes[] = $class; + } else { + echo "Class not exists: $class\r\n"; + } + } + } + + /** + * Check if this class is valid to process. + * @return bool + */ + public function isValid() + { + return $this->valid; + } + + /** + * Get the classtype, 'interface' or 'class' + * + * @return string + */ + public function getClasstype() + { + return $this->classType; + } + + /** + * Get the class which this alias extends + * + * @return null|string + */ + public function getExtends() + { + return $this->extends; + } + + /** + * Get the Alias by which this class is called + * + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * Return the short name (without namespace) + */ + public function getShortName() + { + return $this->short; + } + + /** + * Get the namespace from the alias + * + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * Get the methods found by this Alias + * + * @return array + */ + public function getMethods() + { + $this->addMagicMethods(); + $this->detectMethods(); + + return $this->methods; + } + + /** + * Detect the namespace + */ + protected function detectNamespace() + { + if (strpos($this->alias, '\\')) { + $nsParts = explode('\\', $this->alias); + $this->short = array_pop($nsParts); + $this->namespace = implode('\\', $nsParts); + } else { + $this->short = $this->alias; + } + } + + /** + * Detect the class type + */ + protected function detectClassType() + { + //Some classes extend the facade + if (interface_exists($this->facade)) { + $this->classType = 'interface'; + $this->extends = $this->facade; + } else { + $this->classType = 'class'; + if (class_exists($this->facade)) { + $this->extends = $this->facade; + } + } + } + + /** + * Get the real root of a facade + * + * @return bool|string + */ + protected function detectRoot() + { + $facade = $this->facade; + try { + //If possible, get the facade root + if (method_exists($facade, 'getFacadeRoot')) { + $root = get_class($facade::getFacadeRoot()); + } else { + $root = $facade; + } + //If it doesn't exist, skip it + if (!class_exists($root) && !interface_exists($root)) { + return; + } + $this->root = $root; + //When the database connection is not set, some classes will be skipped + } catch (\PDOException $e) { + $this->error( + "PDOException: " . $e->getMessage() . "\nPlease configure your database connection correctly, or use the sqlite memory driver (-M). Skipping $facade." + ); + } catch (\Exception $e) { + $this->error("Exception: " . $e->getMessage() . "\nSkipping $facade."); + } + } + + /** + * Detect if this class is a trait or not. + * + * @return bool + */ + protected function isTrait() + { + // Check if the facade is not a Trait + if (function_exists('trait_exists') && trait_exists($this->facade)) { + return true; + } + + return false; + } + + /** + * Add magic methods, as defined in the configuration files + */ + protected function addMagicMethods() + { + foreach ($this->magicMethods as $magic => $real) { + list($className, $name) = explode('::', $real); + if (!class_exists($className) && !interface_exists($className)) { + continue; + } + $method = new \ReflectionMethod($className, $name); + $class = new \ReflectionClass($className); + if (!in_array($method->name, $this->usedMethods)) { + if ($class !== $this->root) { + $this->methods[] = new Method($method, $this->alias, $class, $magic, $this->interfaces); + } + $this->usedMethods[] = $magic; + } + } + } + + /** + * Get the methods for one or multiple classes. + * + * @return string + */ + protected function detectMethods() + { + foreach ($this->classes as $class) { + $reflection = new \ReflectionClass($class); + $methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC); + if ($methods) { + foreach ($methods as $method) { + if (!in_array($method->name, $this->usedMethods)) { + // Only add the methods to the output when the root is not the same as the class. + // And don't add the __*() methods + if ($this->extends !== $class && substr($method->name, 0, 2) !== '__') { + $this->methods[] = new Method($method, $this->alias, $reflection, $method->name, + $this->interfaces); + } + $this->usedMethods[] = $method->name; + } + } + } + } + } + + /** + * Output an error. + * + * @param string $string + * @return void + */ + protected function error($string) + { + echo $string . "\r\n"; + } +} \ No newline at end of file diff --git a/src/EvaEngine/DevTool/IDE/Facade/Generator.php b/src/EvaEngine/DevTool/IDE/Facade/Generator.php new file mode 100644 index 0000000..0a3c718 --- /dev/null +++ b/src/EvaEngine/DevTool/IDE/Facade/Generator.php @@ -0,0 +1,209 @@ +config = $config; + $this->view = $view; + // Find the drivers to add to the extra/interfaces + $this->detectDrivers(); + $this->extra = array_merge($this->extra, $this->config->get('ide-helper.extra')); + $this->magic = array_merge($this->magic, $this->config->get('ide-helper.magic')); + $this->interfaces = array_merge($this->interfaces, $this->config->get('ide-helper.interfaces')); + // Make all interface classes absolute + foreach ($this->interfaces as &$interface) { + $interface = '\\' . ltrim($interface, '\\'); + } + $this->helpers = $helpers; + } + /** + * Generate the helper file contents; + * + * @param string $format The format to generate the helper in (php/json) + * @return string; + */ + public function generate($format = 'php') + { + // Check if the generator for this format exists + $method = 'generate'.ucfirst($format).'Helper'; + if (method_exists($this, $method)) { + return $this->$method(); + } + return $this->generatePhpHelper(); + } + public function generatePhpHelper() + { + $app = app(); + return $this->view->make('ide-helper::helper') + ->with('namespaces', $this->getNamespaces()) + ->with('helpers', $this->helpers) + ->with('version', $app::VERSION) + ->render(); + } + public function generateJsonHelper() + { + $classes = array(); + foreach ($this->getNamespaces() as $aliases) { + foreach($aliases as $alias) { + $functions = array(); + foreach ($alias->getMethods() as $method) { + $functions[$method->getName()] = '('. $method->getParamsWithDefault().')'; + } + $classes[$alias->getAlias()] = array( + 'functions' => $functions, + ); + } + } + $flags = JSON_FORCE_OBJECT; + if (defined('JSON_PRETTY_PRINT')) { + $flags |= JSON_PRETTY_PRINT; + } + return json_encode(array( + 'php' => array( + 'classes' => $classes, + ), + ), $flags); + } + protected function detectDrivers() + { + try{ + if (class_exists('Auth') && is_a('Auth', '\Illuminate\Support\Facades\Auth', true)) { + $class = get_class(\Auth::driver()); + $this->extra['Auth'] = array($class); + $this->interfaces['\Illuminate\Auth\UserProviderInterface'] = $class; + } + }catch (\Exception $e) {} + try{ + if (class_exists('DB') && is_a('DB', '\Illuminate\Support\Facades\DB', true)) { + $class = get_class(\DB::connection()); + $this->extra['DB'] = array($class); + $this->interfaces['\Illuminate\Database\ConnectionInterface'] = $class; + } + }catch (\Exception $e) {} + try{ + if (class_exists('Cache') && is_a('Cache', '\Illuminate\Support\Facades\Cache', true)) { + $driver = get_class(\Cache::driver()); + $store = get_class(\Cache::getStore()); + $this->extra['Cache'] = array($driver, $store); + $this->interfaces['\Illuminate\Cache\StoreInterface'] = $store; + } + }catch (\Exception $e) {} + try{ + if (class_exists('Queue') && is_a('Queue', '\Illuminate\Support\Facades\Queue', true)) { + $class = get_class(\Queue::connection()); + $this->extra['Queue'] = array($class); + $this->interfaces['\Illuminate\Queue\QueueInterface'] = $class; + } + }catch (\Exception $e) {} + try{ + if (class_exists('SSH') && is_a('SSH', '\Illuminate\Support\Facades\SSH', true)){ + $class = get_class(\SSH::connection()); + $this->extra['SSH'] = array($class); + $this->interfaces['\Illuminate\Remote\ConnectionInterface'] = $class; + } + }catch (\Exception $e) {} + } + /** + * Find all namespaces/aliases that are valid for us to render + * + * @return array + */ + protected function getNamespaces() + { + $namespaces = array(); + // Get all aliases + foreach (AliasLoader::getInstance()->getAliases() as $name => $facade) { + + // Skip the Redis facade, if not available (otherwise Fatal PHP Error) + if ($facade == 'Illuminate\Support\Facades\Redis' && !class_exists('Predis\Client')) { + continue; + } + + $magicMethods = array_key_exists($name, $this->magic) ? $this->magic[$name] : array(); + $alias = new Alias($name, $facade, $magicMethods, $this->interfaces); + if ($alias->isValid()) { + //Add extra methods, from other classes (magic static calls) + if (array_key_exists($name, $this->extra)) { + $alias->addClass($this->extra[$name]); + } + $namespace = $alias->getNamespace(); + if (!isset($namespaces[$namespace])) { + $namespaces[$namespace] = array(); + } + $namespaces[$namespace][] = $alias; + } + } + return $namespaces; + } + /** + * Get the driver/connection/store from the managers + * + * @param $alias + * @return array|bool|string + */ + public function getDriver($alias) + { + try { + if ($alias == "Auth") { + $driver = \Auth::driver(); + } elseif ($alias == "DB") { + $driver = \DB::connection(); + } elseif ($alias == "Cache") { + $driver = get_class(\Cache::driver()); + $store = get_class(\Cache::getStore()); + return array($driver, $store); + } elseif ($alias == "Queue") { + $driver = \Queue::connection(); + } else { + return false; + } + return get_class($driver); + } catch (\Exception $e) { + $this->error("Could not determine driver/connection for $alias."); + return false; + } + } + /** + * Write a string as error output. + * + * @param string $string + * @return void + */ + protected function error($string) + { + if($this->output){ + $this->output->writeln("$string"); + }else{ + echo $string . "\r\n"; + } + } +} \ No newline at end of file diff --git a/src/EvaEngine/DevTool/IDE/Facade/Method.php b/src/EvaEngine/DevTool/IDE/Facade/Method.php new file mode 100644 index 0000000..c0ea50a --- /dev/null +++ b/src/EvaEngine/DevTool/IDE/Facade/Method.php @@ -0,0 +1,277 @@ +method = $method; + $this->interfaces = $interfaces; + $this->name = $methodName ?: $method->name; + $this->namespace = $method->getDeclaringClass()->getNamespaceName(); + //Create a DocBlock and serializer instance + $this->phpdoc = new DocBlock($method, new Context($this->namespace)); + //Normalize the description and inherit the docs from parents/interfaces + try { + $this->normalizeParams($this->phpdoc); + $this->normalizeReturn($this->phpdoc); + $this->normalizeDescription($this->phpdoc); + } catch (\Exception $e) {} + //Get the parameters, including formatted default values + $this->getParameters($method); + //Make the method static + $this->phpdoc->appendTag(Tag::createInstance('@static', $this->phpdoc)); + //Reference the 'real' function in the declaringclass + $declaringClass = $method->getDeclaringClass(); + $this->declaringClassName = '\\' . ltrim($declaringClass->name, '\\'); + $this->root = '\\' . ltrim($class->getName(), '\\'); + } + /** + * Get the class wherein the function resides + * + * @return string + */ + public function getDeclaringClass() + { + return $this->declaringClassName; + } + /** + * Return the class from which this function would be called + * + * @return string + */ + public function getRoot() + { + return $this->root; + } + /** + * Get the docblock for this method + * + * @param string $prefix + * @return mixed + */ + public function getDocComment($prefix = "\t\t") + { + $serializer = new DocBlockSerializer(1, $prefix); + return $serializer->getDocComment($this->phpdoc); + } + /** + * Get the method name + * + * @return string + */ + public function getName() + { + return $this->name; + } + /** + * Get the parameters for this method + * + * @param bool $implode Wether to implode the array or not + * @return string + */ + public function getParams($implode = true) + { + return $implode ? implode(', ', $this->params) : $this->params; + } + /** + * Get the parameters for this method including default values + * + * @param bool $implode Wether to implode the array or not + * @return string + */ + public function getParamsWithDefault($implode = true) + { + return $implode ? implode(', ', $this->params_with_default) : $this->params_with_default; + } + /** + * Get the description and get the inherited docs. + * + * @param DocBlock $phpdoc + */ + protected function normalizeDescription(DocBlock $phpdoc) + { + //Get the short + long description from the DocBlock + $description = $phpdoc->getText(); + //Loop through parents/interfaces, to fill in {@inheritdoc} + if (strpos($description, '{@inheritdoc}') !== false) { + $inheritdoc = $this->getInheritDoc($this->method); + $inheritDescription = $inheritdoc->getText(); + $description = str_replace('{@inheritdoc}', $inheritDescription, $description); + $phpdoc->setText($description); + $this->normalizeParams($inheritdoc); + $this->normalizeReturn($inheritdoc); + //Add the tags that are inherited + $inheritTags = $inheritdoc->getTags(); + if ($inheritTags) { + /** @var Tag $tag */ + foreach ($inheritTags as $tag) { + $tag->setDocBlock(); + $phpdoc->appendTag($tag); + } + } + } + } + /** + * Normalize the parameters + * + * @param DocBlock $phpdoc + */ + protected function normalizeParams(DocBlock $phpdoc) + { + //Get the return type and adjust them for beter autocomplete + $paramTags = $phpdoc->getTagsByName('param'); + if ($paramTags) { + /** @var ParamTag $tag */ + foreach($paramTags as $tag){ + // Convert the keywords + $content = $this->convertKeywords($tag->getContent()); + $tag->setContent($content); + // Get the expanded type and re-set the content + $content = $tag->getType() . ' ' . $tag->getVariableName() . ' ' . $tag->getDescription(); + $tag->setContent(trim($content)); + } + } + } + /** + * Normalize the return tag (make full namespace, replace interfaces) + * + * @param DocBlock $phpdoc + */ + protected function normalizeReturn(DocBlock $phpdoc) + { + //Get the return type and adjust them for beter autocomplete + $returnTags = $phpdoc->getTagsByName('return'); + if ($returnTags) { + /** @var ReturnTag $tag */ + $tag = reset($returnTags); + // Get the expanded type + $returnValue = $tag->getType(); + // Replace the interfaces + foreach($this->interfaces as $interface => $real){ + $returnValue = str_replace($interface, $real, $returnValue); + } + // Set the changed content + $tag->setContent($returnValue . ' ' . $tag->getDescription()); + $this->return = $returnValue; + }else{ + $this->return = null; + } + } + /** + * Convert keywwords that are incorrect. + * + * @param string $string + * @return string + */ + protected function convertKeywords($string) + { + $string = str_replace('\Closure', 'Closure', $string); + $string = str_replace('Closure', '\Closure', $string); + $string = str_replace('dynamic', 'mixed', $string); + return $string; + } + /** + * Should the function return a value? + * + * @return bool + */ + public function shouldReturn() + { + if($this->return !== "void" && $this->method->name !== "__construct"){ + return true; + } + return false; + } + /** + * Get the parameters and format them correctly + * + * @param $method + * @return array + */ + public function getParameters($method) + { + //Loop through the default values for paremeters, and make the correct output string + $params = array(); + $paramsWithDefault = array(); + foreach ($method->getParameters() as $param) { + $paramStr = '$' . $param->getName(); + $params[] = $paramStr; + if ($param->isOptional()) { + $default = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null; + if (is_bool($default)) { + $default = $default ? 'true' : 'false'; + } elseif (is_array($default)) { + $default = 'array()'; + } elseif (is_null($default)) { + $default = 'null'; + } elseif (is_int($default)) { + //$default = $default; + } else { + $default = "'" . trim($default) . "'"; + } + $paramStr .= " = $default"; + } + $paramsWithDefault[] = $paramStr; + } + $this->params = $params; + $this->params_with_default = $paramsWithDefault; + } + /** + * @param \ReflectionMethod $reflectionMethod + * @return DocBlock + */ + protected function getInheritDoc($reflectionMethod) + { + $parentClass = $reflectionMethod->getDeclaringClass()->getParentClass(); + //Get either a parent or the interface + if ($parentClass) { + $method = $parentClass->getMethod($reflectionMethod->getName()); + } else { + $method = $reflectionMethod->getPrototype(); + } + if ($method) { + $namespace = $method->getDeclaringClass()->getNamespaceName(); + $phpdoc = new DocBlock($method, new Context($namespace)); + + if (strpos($phpdoc->getText(), '{@inheritdoc}') !== false) { + //Not at the end yet, try another parent/interface.. + return $this->getInheritDoc($method); + } else { + return $phpdoc; + } + } + } +} \ No newline at end of file diff --git a/src/EvaEngine/Engine.php b/src/EvaEngine/Engine.php index 2dddbd6..795dffe 100644 --- a/src/EvaEngine/Engine.php +++ b/src/EvaEngine/Engine.php @@ -9,7 +9,6 @@ namespace Eva\EvaEngine; -use Eva\EvaEngine\CLI\Output\ConsoleOutput; //use Eva\EvaEngine\Events\DispatchCacheListener; use Eva\EvaEngine\Interceptor\Dispatch as DispatchInterceptor; use Eva\EvaEngine\SDK\SendCloudMailer; @@ -33,6 +32,7 @@ use Phalcon\DI\FactoryDefault\CLI; use Eva\EvaEngine\Service\TokenStorage; use Phalcon\DiInterface; +use Symfony\Component\Console\Output\ConsoleOutput; /** * Core application configuration / bootstrap @@ -214,7 +214,7 @@ public function getModulesPath() /** * * @param $cacheFile cache file path - * @param bool $serialize + * @param bool $serialize * @return mixed|null */ public function readCache($cacheFile, $serialize = false) @@ -229,7 +229,7 @@ public function readCache($cacheFile, $serialize = false) /** * @param $cacheFile * @param $content - * @param bool $serialize + * @param bool $serialize * @return bool */ public function writeCache($cacheFile, $content, $serialize = false) @@ -751,6 +751,7 @@ function () use ($di) { IoC::setDI($di); + return $this->di = $di; } @@ -821,10 +822,13 @@ public function diConfig() return new Config($cache); } - $config = new Config(); + $config = new Config(include __DIR__ . '/../../config/config.php'); + + //merge all loaded module configs $moduleManager = $di->getModuleManager(); if (!$moduleManager || !$modules = $moduleManager->getModules()) { + return $config; throw new Exception\RuntimeException(sprintf('Config need at least one module loaded')); } @@ -836,7 +840,6 @@ public function diConfig() $config->merge(new Config($moduleConfig)); } } - //merge config default $config->merge(new Config(include $this->getConfigPath() . "/config.default.php")); @@ -1211,7 +1214,7 @@ public function diTokenStorage() return new TokenStorage( array_merge( array( - 'uniqueId' => $this->getAppName(), + 'uniqueId' => $this->getAppName(), ), $config->tokenStorage->toArray() ) @@ -1226,14 +1229,14 @@ public function diTranslate() //empty translator return new \Phalcon\Translate\Adapter\NativeArray( array( - 'content' => array() + 'content' => array() ) ); } $translate = new \Phalcon\Translate\Adapter\Csv( array( - 'file' => $file, - 'delimiter' => ',', + 'file' => $file, + 'delimiter' => ',', ) ); @@ -1257,6 +1260,10 @@ public function diFileSystem() */ public function bootstrap() { + // 注册 facade 别名 + foreach ($this->getDI()->getConfig()->alias as $aliasName => $facade) { + class_alias($facade, $aliasName); + } if ($this->getDI()->getConfig()->debug) { $debugger = $this->getDebugger(); $debugger->debugVar($this->getDI()->getModuleManager()->getModules(), 'modules'); diff --git a/src/EvaEngine/Error/CLIErrorHandler.php b/src/EvaEngine/Error/CLIErrorHandler.php index 9ba5b50..62cd5c7 100644 --- a/src/EvaEngine/Error/CLIErrorHandler.php +++ b/src/EvaEngine/Error/CLIErrorHandler.php @@ -9,12 +9,11 @@ namespace Eva\EvaEngine\Error; -use Eva\EvaEngine\CLI\Output\ConsoleOutput; -use Eva\EvaEngine\CLI\Output\StreamOutput; -use Eva\EvaEngine\CLI\Formatter\OutputFormatterInterface; + use Phalcon\DI; use Phalcon\Logger\Adapter\File as FileLogger; use Phalcon\Logger\AdapterInterface as LoggerInterface; +use Symfony\Component\Console\Output\ConsoleOutput; /** * Error handler for CLI mode @@ -41,7 +40,7 @@ public static function errorHandler($errno, $errstr, $errfile, $errline) } $output = new ConsoleOutput(); $output->writeln(""); - $output->writelnWarning(' [WARNING]: '. $errstr.' in file '. $errfile .' at line '.$errline); + $output->writelnWarning(' [WARNING]: ' . $errstr . ' in file ' . $errfile . ' at line ' . $errline); $output->writeln(""); } @@ -96,6 +95,7 @@ public static function getLogger() public static function setLogger(LoggerInterface $logger) { static::$logger = $logger; + return self; } diff --git a/src/EvaEngine/Error/DispatchListener.php b/src/EvaEngine/Error/DispatchListener.php new file mode 100644 index 0000000..f1b5aaa --- /dev/null +++ b/src/EvaEngine/Error/DispatchListener.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/5/25 下午5:26 +// +---------------------------------------------------------------------- +// + DispatchListener.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Error; + + +class DispatchListener +{ + public function beforeException($event, $dispatcher, $exception) + { + //For fixing phalcon weird behavior https://github.com/phalcon/cphalcon/issues/2558 + throw $exception; + } +} \ No newline at end of file diff --git a/src/EvaEngine/Facades/Annotations.php b/src/EvaEngine/Facades/Annotations.php new file mode 100644 index 0000000..c539bf4 --- /dev/null +++ b/src/EvaEngine/Facades/Annotations.php @@ -0,0 +1,18 @@ +instance(static::getFacadeAccessor(), $instance); + } + + /** + * Initiate a mock expectation on the facade. + * + * @param mixed + * @return \Mockery\Expectation + */ + public static function shouldReceive() + { + $name = static::getFacadeAccessor(); + if (static::isMock()) { + $mock = static::$resolvedInstance[$name]; + } else { + $mock = static::createFreshMockInstance($name); + } + + return call_user_func_array(array($mock, 'shouldReceive'), func_get_args()); + } + + /** + * Create a fresh mock instance for the given class. + * + * @param string $name + * @return \Mockery\Expectation + */ + protected static function createFreshMockInstance($name) + { + static::$resolvedInstance[$name] = $mock = static::createMockByName($name); + if (isset(static::$app)) { + static::$app->instance($name, $mock); + } + + return $mock; + } + + /** + * Create a fresh mock instance for the given class. + * + * @param string $name + * @return \Mockery\Expectation + */ + protected static function createMockByName($name) + { + $class = static::getMockableClass($name); + + return $class ? Mockery::mock($class) : Mockery::mock(); + } + + /** + * Determines whether a mock is set as the instance of the facade. + * + * @return bool + */ + protected static function isMock() + { + $name = static::getFacadeAccessor(); + + return isset(static::$resolvedInstance[$name]) && static::$resolvedInstance[$name] instanceof MockInterface; + } + + /** + * Get the mockable class for the bound instance. + * + * @return string + */ + protected static function getMockableClass() + { + if ($root = static::getFacadeRoot()) { + return get_class($root); + } + } + + /** + * Get the root object behind the facade. + * + * @return mixed + */ + public static function getFacadeRoot() + { + return static::resolveFacadeInstance(static::getFacadeAccessor()); + } + + /** + * Get the registered name of the component. + * + * @return string + * + * @throws \RuntimeException + */ + protected static function getFacadeAccessor() + { + throw new RuntimeException("Facade does not implement getFacadeAccessor method."); + } + + /** + * Resolve the facade root instance from the container. + * + * @param string $name + * @return mixed + */ + protected static function resolveFacadeInstance($name) + { + if (is_object($name)) { + return $name; + } + if (isset(static::$resolvedInstance[$name])) { + return static::$resolvedInstance[$name]; + } + + return static::$resolvedInstance[$name] = eva_get($name); + } + + /** + * Clear a resolved facade instance. + * + * @param string $name + * @return void + */ + public static function clearResolvedInstance($name) + { + unset(static::$resolvedInstance[$name]); + } + + /** + * Clear all of the resolved instances. + * + * @return void + */ + public static function clearResolvedInstances() + { + static::$resolvedInstance = array(); + } + +// /** +// * Get the application instance behind the facade. +// * +// * @return \Illuminate\Contracts\Foundation\Application +// */ +// public static function getFacadeApplication() +// { +// return static::$app; +// } +// +// /** +// * Set the application instance. +// * +// * @param \Illuminate\Contracts\Foundation\Application $app +// * @return void +// */ +// public static function setFacadeApplication($app) +// { +// static::$app = $app; +// } + + /** + * Handle dynamic, static calls to the object. + * + * @param string $method + * @param array $args + * @return mixed + */ + public static function __callStatic($method, $args) + { + $instance = static::getFacadeRoot(); + switch (count($args)) { + case 0: + return $instance->$method(); + case 1: + return $instance->$method($args[0]); + case 2: + return $instance->$method($args[0], $args[1]); + case 3: + return $instance->$method($args[0], $args[1], $args[2]); + case 4: + return $instance->$method($args[0], $args[1], $args[2], $args[3]); + default: + return call_user_func_array(array($instance, $method), $args); + } + } +} \ No newline at end of file diff --git a/src/EvaEngine/Facades/FlashSession.php b/src/EvaEngine/Facades/FlashSession.php new file mode 100644 index 0000000..dcb7a87 --- /dev/null +++ b/src/EvaEngine/Facades/FlashSession.php @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/11 下午5:20 +// +---------------------------------------------------------------------- +// + AdapterCreator.php +// +---------------------------------------------------------------------- +namespace Eva\EvaEngine\Foundation; + +use Eva\EvaEngine\Exception\RuntimeException; + +/** + * 适配器创建器抽象类 + * @package Eva\EvaEngine\Foundation + */ +abstract class AdapterCreator +{ + /** + * 获取 adapter 和类的映射关系 + * 参考: + * ```php + * protected function getAdaptersMapping() + * { + * return array( + * // `default` 对应 `getAdapterClass` 方法的 category 参数 + * 'default' => array( + * 'adapter1' => 'Some\Adapter\XXX1', + * 'adapter2' => 'Some\Adapter\XXX2' + * ) + * ); + * } + * ``` + * @return array + */ + abstract protected function getAdaptersMapping(); + /** + * @param string $adapter 适配器名称,可以为完整的类名 + * @param string $category 分类,可选 + * @return string 适配器类名 + * @throws RuntimeException + */ + protected function getAdapterClass($adapter, $category = 'default') + { + $adaptersMapping = $this->getAdaptersMapping($category); + $adapterClass = $adapter; + if (isset($adaptersMapping[$category]) && in_array($adapter, $adaptersMapping[$category])) { + $adapterClass = $adaptersMapping[$category][$adapter]; + } + if (!class_exists($adapterClass)) { + throw new RuntimeException( + sprintf( + 'Adapter class "%s" not found, adapter name: "%s"', + $adapterClass, + $adapter + ) + ); + } + + return $adapterClass; + } +} \ No newline at end of file diff --git a/src/EvaEngine/IoC.php b/src/EvaEngine/IoC.php index 7fbe60c..ede9767 100644 --- a/src/EvaEngine/IoC.php +++ b/src/EvaEngine/IoC.php @@ -53,7 +53,7 @@ public static function get($name, $parameters = null) if (self::$di == null) { throw new RuntimeException('IoC container is null!'); } - return self::$di->get($name, $parameters); + return self::$di->get($name); } /** diff --git a/src/EvaEngine/Listeners/DbQueryLoggingListener.php b/src/EvaEngine/Listeners/DbQueryLoggingListener.php new file mode 100644 index 0000000..a5cbe6d --- /dev/null +++ b/src/EvaEngine/Listeners/DbQueryLoggingListener.php @@ -0,0 +1,37 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/11 下午4:53 +// +---------------------------------------------------------------------- +// + DbListener.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Listeners; + +use Eva\EvaEngine\IoC; +use Phalcon\Logger; + +class DbQueryLoggingListener +{ + public function beforeQuery($event, $dbAdapter) + { + $config = IoC::get('config'); + // 数据查询日志记录仅在 debug 模式下有效 + if (!$config->debug) { + return; + } + $sqlVariables = $dbAdapter->getSQLVariables(); + $logger = IoC::get('dbQueryLogger'); + if (count($sqlVariables)) { + $query = str_replace(array('%', '?'), array('%%', "'%s'"), $dbAdapter->getSQLStatement()); + $query = vsprintf($query, $sqlVariables); + // + $logger->log($query, Logger::INFO); + } else { + $logger->log($dbAdapter->getSQLStatement(), Logger::INFO); + } + } +} \ No newline at end of file diff --git a/src/EvaEngine/Listeners/DispatchListener.php b/src/EvaEngine/Listeners/DispatchListener.php new file mode 100644 index 0000000..72aa332 --- /dev/null +++ b/src/EvaEngine/Listeners/DispatchListener.php @@ -0,0 +1,20 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/5/25 下午5:30 +// +---------------------------------------------------------------------- +// + DispatchListener.php +// +---------------------------------------------------------------------- +namespace Eva\EvaEngine\Listeners; + +class DispatchListener +{ + public function beforeException($event, $dispatcher, $exception) + { + //For fixing phalcon weird behavior https://github.com/phalcon/cphalcon/issues/2558 + throw $exception; + } +} \ No newline at end of file diff --git a/src/EvaEngine/Logger/Multiple.php b/src/EvaEngine/Logger/Multiple.php new file mode 100644 index 0000000..7a59bee --- /dev/null +++ b/src/EvaEngine/Logger/Multiple.php @@ -0,0 +1,47 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/17 下午1:07 +// +---------------------------------------------------------------------- +// + Multiple.php +// +---------------------------------------------------------------------- +namespace Eva\EvaEngine\Logger; + +use Phalcon\Logger; +use Phalcon\Logger\AdapterInterface; +use Phalcon\Logger\Multiple as PhalconMultiple; + +class Multiple extends PhalconMultiple +{ + protected $_loggers = array(); + + public function push(AdapterInterface $logger, $type = Logger::SPECIAL) + { + $this->_loggers[$type][] = $logger; + } + + public function log($type, $message = null, $context = null) + { + foreach ($this->_loggers as $handlerType => $loggers) { + if ($type <= $handlerType) { + /** @var AdapterInterface $logger */ + foreach ($loggers as $logger) { + $logger->log($type, $message, $context); + } + } + } + } + + public function setFormatter(Logger\FormatterInterface $formatter) + { + foreach ($this->_loggers as $handlerType => $loggers) { + /** @var AdapterInterface $logger */ + foreach ($loggers as $logger) { + $logger->setFormatter($formatter); + } + } + } +} \ No newline at end of file diff --git a/src/EvaEngine/Module/AbstractModule.php b/src/EvaEngine/Module/AbstractModule.php index 497c766..f728056 100644 --- a/src/EvaEngine/Module/AbstractModule.php +++ b/src/EvaEngine/Module/AbstractModule.php @@ -49,6 +49,14 @@ public static function registerGlobalRelations() return array(); } + /** + * @return array + */ + public static function registerErrorHandlers() + { + return array(); + } + /** * Registers the module auto-loader */ @@ -64,4 +72,14 @@ public function registerAutoloaders() public function registerServices($di) { } + + /** + * Registers global services + * + * @param \Phalcon\DiInterface $di + */ + public function registerGlobalServices($di) + { + + } } diff --git a/src/EvaEngine/Module/Manager.bak.php b/src/EvaEngine/Module/Manager.bak.php new file mode 100644 index 0000000..b202b87 --- /dev/null +++ b/src/EvaEngine/Module/Manager.bak.php @@ -0,0 +1,615 @@ +loader) { + return $this->loader; + } + + return $this->loader = new Loader(); + } + + /** + * @param Loader $loader + * @return $this + */ + public function setLoader(Loader $loader) + { + $this->loader = $loader; + + return $this; + } + + /** + * @return null|string + */ + public function getDefaultPath() + { + return $this->defaultPath; + } + + /** + * @param $defaultPath + * @return $this + */ + public function setDefaultPath($defaultPath) + { + $this->defaultPath = $defaultPath; + + return $this; + } + + /** + * @param $cacheFile + * @return $this + */ + public function setCacheFile($cacheFile) + { + $this->cacheFile = $cacheFile; + + return $this; + } + + /** + * @return string + */ + public function getCacheFile() + { + return $this->cacheFile; + } + + /** + * @return EventsManager + */ + public function getEventsManager() + { + if ($this->eventsManager) { + return $this->eventsManager; + } + + return $this->eventsManager = new EventsManager(); + } + + /** + * @param ManagerInterface $eventsManager + * @return ManagerInterface + */ + public function setEventsManager($eventsManager) + { + return $this->eventsManager = $eventsManager; + } + + /** + * @param $cacheFile + * @return mixed|null + */ + public function readCache($cacheFile) + { + if (file_exists($cacheFile) && $cache = include $cacheFile) { + return $cache; + } + + return null; + } + + /** + * @param $cacheFile + * @param array $content + * @return bool + */ + public function writeCache($cacheFile, array $content) + { + if ($cacheFile && $fh = fopen($cacheFile, 'w')) { + fwrite($fh, 'modules[$moduleName]) ? true : false; + } + + /** + * @return array + */ + public function getModules() + { + return $this->modules; + } + + /** + * @param $moduleName + * @return array + */ + public function getModule($moduleName) + { + return empty($this->modules[$moduleName]) ? array() : $this->modules[$moduleName]; + } + + /** + * Get full module settings by module name or module setting array + * Module setting includes: + * - className: Required | Module bootstrap class full name, e.g. Eva\EvaCommon\Module + * - path : Required | Module.php file path, e.g. /www/eva/modules/EvaCommon/Module.php + * - moduleConfig: Optional | Module config file path, e.g. /www/eva/modules/EvaCommon/config/config.php , + * default is module_dir/config/config.php + * - routesFrontend Optional | Module front-end router config file path, + * e.g. /www/eva/modules/EvaCommon/config/routes.frontend.php + * - routesBackend Optional | Module back-end router config file path, + * e.g. /www/eva/modules/EvaCommon/config/routes.backend.php + * - routesCommand Optional | Module cli router config file path, + * e.g. /www/eva/modules/EvaCommon/config/routes.command.php + * - adminMenu Optional | Admin sidebar menu + * - di : Optional | Module global DI + * All optional setting could set as false to disable. + * + * @param $moduleName MUST as same as module folder, keep moduleName unique + * @param $moduleSetting mixed Support 3 types: + * - null, EvaEngine official module, module namespace MUST start with Eva\\, + * and source path MUST under application/modules + * - string, Full module class, which is already loaded by composer + * - array, Module setting array, require className and path at least, other options will be auto filled + * @return array Module + * @throws \Exception + */ + public function getModuleInfo($moduleName, $moduleSetting = null) + { + $moduleName = ucfirst($moduleName); + $modulesPath = $this->getDefaultPath(); + $ds = DIRECTORY_SEPARATOR; + + //Get basic module info only contains className and path + if (true === is_null($moduleSetting)) { + $module = array( + 'className' => "Eva\\$moduleName\\Module", + 'path' => "$modulesPath{$ds}$moduleName{$ds}Module.php", + ); + } elseif (true === is_string($moduleSetting) && strpos($moduleSetting, '\\') !== false) { + //Composer module + $moduleClass = $moduleSetting; + if (false === class_exists($moduleClass)) { + throw new \Exception(sprintf('Module %s load failed by not exist class', $moduleClass)); + } + + $ref = new \ReflectionClass($moduleClass); + $module = array( + 'className' => $moduleClass, + 'path' => $ref->getFileName(), + ); + } elseif (true === is_array($moduleSetting)) { + $module = array_merge( + array( + 'className' => '', + 'path' => '', + ), + $moduleSetting + ); + $module['className'] = $module['className'] ?: "Eva\\$moduleName\\Module"; + $module['path'] = $module['path'] ?: "$modulesPath{$ds}$moduleName{$ds}Module.php"; + } else { + throw new \Exception(sprintf('Module %s load failed by incorrect format', $moduleName)); + } + + /** + * @var StandardInterface $moduleClass + */ + $moduleClass = $module['className']; + $this->getLoader()->registerClasses( + array( + $moduleClass => $module['path'] + ) + )->register(); + + if (false === class_exists($moduleClass)) { + throw new \Exception(sprintf('Module %s load failed by not exist class', $moduleClass)); + } + + if (count( + array_intersect( + array( + 'Phalcon\Mvc\ModuleDefinitionInterface', + 'Eva\EvaEngine\Module\StandardInterface' + ), + class_implements($moduleClass) + ) + ) !== 2 + ) { + throw new \Exception(sprintf('Module %s interfaces not correct', $moduleClass)); + } + + $module['dir'] = $moduleDir = dirname($module['path']); + $module = array_merge( + array( + 'moduleConfig' => "$moduleDir{$ds}config{$ds}config.php", //module config file path + 'routesFrontend' => "$moduleDir{$ds}config{$ds}routes.frontend.php", //module router frontend path + 'routesBackend' => "$moduleDir{$ds}config{$ds}routes.backend.php", //module router backend path + 'routesCommand' => "$moduleDir{$ds}config{$ds}routes.command.php", // module router in CLI mode + 'adminMenu' => "$moduleDir{$ds}config{$ds}admin.menu.php", //admin menu + 'autoloaders' => $moduleClass::registerGlobalAutoloaders(), //autoloaders + 'relations' => $moduleClass::registerGlobalRelations(), //entity relations for injection + 'listeners' => $moduleClass::registerGlobalEventListeners(), //module listeners list array + 'viewHelpers' => $moduleClass::registerGlobalViewHelpers(), //module view helpers + //'translatePath' => false, + ), + $module + ); + + return $module; + } + + /** + * @param array $moduleSettings + * @return $this + * @throws \Exception + */ + public function loadModules(array $moduleSettings) + { + //Trigger Event + $this->getEventsManager()->fire('module:beforeLoadModule', $this); + + $cacheFile = $this->getCacheFile(); + $loader = $this->getLoader(); + + if ($cacheFile && $cache = $this->readCache($cacheFile)) { + $loader->registerNamespaces($cache['namespaces'])->register(); + $loader->registerClasses($cache['classes'])->register(); + $this->modules = $cache['modules']; + + //Trigger Event + $this->getEventsManager()->fire('module:afterLoadModule', $this); + + return $this; + } + + $modules = array(); + //All Module.php map array for cache + $classes = array(); + foreach ($moduleSettings as $moduleName => $moduleSetting) { + if (true === is_int($moduleName)) { + $moduleName = $moduleSetting; + $moduleSetting = null; + } + $module = $this->getModuleInfo($moduleName, $moduleSetting); + $modules[$moduleName] = $module; + $classes[$module['className']] = $module['path']; + } + $this->modules = $modules; + + $namespaces = $this->getMergedAutoloaders(); + if ($namespaces) { + $loader->registerNamespaces($namespaces)->register(); + } + + if ($cacheFile) { + $this->writeCache( + $cacheFile, + array( + 'classes' => $classes, + 'namespaces' => $namespaces, + 'modules' => $modules, + ) + ); + } + //Trigger Event + $this->getEventsManager()->fire('module:afterLoadModule', $this); + + return $this; + } + + /** + * @param $key + * @return array + */ + private function getMergedArray($key) + { + if (!($modules = $this->modules)) { + return array(); + } + $mergedArray = array(); + foreach ($modules as $moduleName => $module) { + if (false === is_array($module[$key])) { + continue; + } + $mergedArray = array_merge($mergedArray, $module[$key]); + } + + return $mergedArray; + } + + /** + * @return array + */ + public function getMergedAutoloaders() + { + return $this->getMergedArray('autoloaders'); + } + + /** + * @return array + */ + public function getMergedViewHelpers() + { + return $this->getMergedArray('viewHelpers'); + } + + /** + * @return array + */ + public function getMergedRelations() + { + return $this->getMergedArray('relations'); + } + + + /** + * Module Events could by key => value pairs like: + * 'module' => 'Eva\RealModule\Events\ModuleListener', + * Or could be key => array pairs like + * 'module' => array('Eva\RealModule\Events\ModuleListener', 100) + * Array[1] is listener priority + * + * @param array $listeners + * @return $this + * @throws \Exception + */ + public function attachEvents(array $listeners = array()) + { + if (!$listeners) { + $modules = $this->getModules(); + if (!$modules) { + return $this; + } + + $listeners = array(); + foreach ($modules as $moduleName => $module) { + $listeners[$moduleName] = $this->getModuleListeners($moduleName); + } + } + + if (!$listeners) { + return $this; + } + + $eventsManager = $this->getEventsManager(); + foreach ($listeners as $moduleName => $moduleListeners) { + foreach ($moduleListeners as $eventType => $listener) { + $priority = 0; + if (is_string($listener)) { + $listenerClass = $listener; + } elseif (true === is_array($listener) && count($listener) > 1) { + $listenerClass = $listener[0]; + $priority = (int)$listener[1]; + } else { + throw new \Exception(sprintf("Module %s listener format not correct", $moduleName)); + } + if (false === class_exists($listenerClass)) { + throw new \Exception(sprintf("Module listener %s not exist", $listenerClass)); + } + $eventsManager->attach($eventType, new $listenerClass, $priority); + } + } + + return $this; + } + + /** + * @param $moduleName + * @return string + */ + public function getModulePath($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['dir']) && file_exists($modules[$moduleName]['dir'])) { + return $modules[$moduleName]['dir']; + } + + return ''; + } + + /** + * @param $moduleName + * @return array|mixed + */ + public function getModuleConfig($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['moduleConfig']) && file_exists($modules[$moduleName]['moduleConfig'])) { + return include $modules[$moduleName]['moduleConfig']; + } + + return array(); + } + + /** + * @param $moduleName + * @return array|mixed + */ + public function getModuleRoutesFrontend($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['routesFrontend']) && file_exists($modules[$moduleName]['routesFrontend'])) { + return include $modules[$moduleName]['routesFrontend']; + } + + return array(); + } + + /** + * @param $moduleName + * @return array|mixed + */ + public function getModuleRoutesCommand($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['routesCommand']) && file_exists($modules[$moduleName]['routesCommand'])) { + return include $modules[$moduleName]['routesCommand']; + } + + return array(); + } + + /** + * @param $moduleName + * @return array|mixed + */ + public function getModuleRoutesBackend($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['routesBackend']) && file_exists($modules[$moduleName]['routesBackend'])) { + return include $modules[$moduleName]['routesBackend']; + } + + return array(); + } + + /** + * @param $moduleName + * @return array + */ + public function getModuleListeners($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['listeners'])) { + return $modules[$moduleName]['listeners']; + } + + return array(); + } + + /** + * @param $moduleName + * @return mixed|string + */ + public function getModuleAdminMenu($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['adminMenu']) && file_exists($modules[$moduleName]['adminMenu'])) { + return include $modules[$moduleName]['adminMenu']; + } + + return ''; + } + + /** + * @param $moduleName + * @return array + */ + public function getModuleViewHelpers($moduleName) + { + $modules = $this->getModules(); + if (!empty($modules[$moduleName]['viewHelpers'])) { + return $modules[$moduleName]['viewHelpers']; + } + + return array(); + } + + /** + * @param Model $entity + * @return array + */ + public function getInjectRelations(Model $entity) + { + $relations = $this->injectRelations; + + if ($relations === false) { + $relations = $this->getMergedRelations(); + $this->injectRelations = $relations; + } + + if (!$relations) { + return array(); + } + + $entityRalations = array(); + foreach ($relations as $relation) { + if ($entity instanceof $relation['entity']) { + $entityRalations[] = $relation; + } + } + + return $entityRalations; + } + + /** + * @param null $defaultPath + */ + public function __construct($defaultPath = null) + { + if ($defaultPath) { + $this->defaultPath = $defaultPath; + } + } +} diff --git a/src/EvaEngine/Module/Manager.php b/src/EvaEngine/Module/Manager.php index 2ff7697..8497f51 100644 --- a/src/EvaEngine/Module/Manager.php +++ b/src/EvaEngine/Module/Manager.php @@ -9,11 +9,12 @@ namespace Eva\EvaEngine\Module; +use Phalcon\Config; +use Phalcon\DiInterface; use Phalcon\Loader; -use Phalcon\Events\EventsAwareInterface; -use Phalcon\Events\ManagerInterface; -use Phalcon\Events\Manager as EventsManager; + use Eva\EvaEngine\Mvc\Model; +use Phalcon\Mvc\Application; /** * Module Manager for module register / load @@ -26,568 +27,339 @@ * * @package Eva\EvaEngine\Module */ -class Manager implements EventsAwareInterface +class Manager { - /** - * Loaded modules - * @var array + * @var string 模块的默认所在目录 + */ + protected $defaultModulesDir = ''; + /** + * @var array */ protected $modules = array(); - /** - * @var string + * @var array */ - protected $defaultPath; - + protected $phalconModules = array(); /** - * @var Loader + * @var DIInterface */ - protected $loader; + protected $di; + + protected $allAutoLoaders; /** - * @var string + * @var array */ - protected $cacheFile; - + protected $allRoutesFrontend; /** - * @var EventsManager + * @var Config */ - protected $eventsManager; - + protected $allRoutesBackend; /** - * @var bool + * @var Config */ - protected $injectRelations = false; + protected $allRoutesCLI; + + protected $allListeners = array(); + protected $allRelations = array(); /** - * @return Loader + * @var array() */ - public function getLoader() - { - if ($this->loader) { - return $this->loader; - } - return $this->loader = new Loader(); - } - + protected $allAdminMenuFiles = array(); /** - * @param Loader $loader - * @return $this + * @var array 所有的 viewHelper */ - public function setLoader(Loader $loader) - { - $this->loader = $loader; - return $this; - } + protected $allViewHelpers = array(); + /** + * @var Config + */ + protected $allConfig; + /** + * @var array + */ + protected $allDIDefinition = array(); + /** + * @var array + */ + protected $allErrorHandlers = array(); /** - * @return null|string + * @param string $defaultModulesDir 模块默认路径,当没有指定模块的路径时,从这里加载模块 */ - public function getDefaultPath() + public function __construct($defaultModulesDir) { - return $this->defaultPath; + $this->defaultModulesDir = $defaultModulesDir; + $this->moduleParser = new ModuleParser($this->defaultModulesDir); + $this->allConfig = new Config(); + $this->allRoutesBackend = new Config(); + $this->allRoutesFrontend = new Config(); + $this->allRoutesCLI = new Config(); } /** - * @param $defaultPath - * @return $this + * 通过模块配置数组来注册多个模块 + * + * @param array $modulesConfig */ - public function setDefaultPath($defaultPath) + public function registerModules(array $modulesConfig) { - $this->defaultPath = $defaultPath; - return $this; + foreach ($modulesConfig as $name => $moduleOptions) { + // 当数组键为数字时,说明没有设置模块的 options,则模块名称取数组的「值」 + if (is_numeric($name)) { + $name = $moduleOptions; + $moduleOptions = array(); + } + $module = $this->moduleParser->parse($name, $moduleOptions); + $this->register($module); + } } /** - * @param $cacheFile - * @return $this + * 获取默认的模块 + * + * @return Module */ - public function setCacheFile($cacheFile) + public function getDefaultModule() { - $this->cacheFile = $cacheFile; - return $this; + // 最后注册的模块作为默认模块 + return end($this->modules); } /** - * @return string + * 通过模块名来获取模块 + * + * @param $name + * @return Module */ - public function getCacheFile() + public function getModule($name) { - return $this->cacheFile; + return $this->modules[$name]; } /** - * @return EventsManager + * 获取所有的模块 + * + * @return array */ - public function getEventsManager() + public function getModules() { - if ($this->eventsManager) { - return $this->eventsManager; - } - return $this->eventsManager = new EventsManager(); + return $this->modules; } /** - * @param ManagerInterface $eventsManager - * @return ManagerInterface + * 获取注册到给定的 Entity 上的 ORM 关系 + * + * @param \Phalcon\Mvc\Model $entity + * @return array */ - public function setEventsManager($eventsManager) + public function getInjectedRelations(\Phalcon\Mvc\Model $entity) { - return $this->eventsManager = $eventsManager; + $relations = array(); + foreach ($this->allRelations as $relationDefinition) { + if ($entity instanceof $relationDefinition['entity']) { + $relations[] = $relationDefinition; + } + } + + return $relations; } /** - * @param $cacheFile - * @return mixed|null + * 注册一个模块到模块管理器 + * + * @param Module $module */ - public function readCache($cacheFile) + public function register(Module $module) { - if (file_exists($cacheFile) && $cache = include $cacheFile) { - return $cache; + $this->modules[$module->getName()] = $module; + $this->phalconModules[$module->getName()] = array( + 'path' => $module->getPath(), + 'className' => $module->getClassName() + ); + $this->allAdminMenuFiles[] = $module->getAdminMenusFile(); + $this->allConfig->merge(new Config($module->getConfig())); + $this->allRoutesFrontend->merge(new Config($module->getRoutesFrontend())); + $this->allRoutesBackend->merge(new Config($module->getRoutesBackend())); + $this->allRoutesCLI->merge(new Config($module->getRoutesCLI())); + $this->allListeners[] = $module->getListeners(); + $this->allViewHelpers = array_merge($this->allViewHelpers, $module->getViewHelpers()); + $this->allErrorHandlers = array_merge($this->allErrorHandlers, $module->getErrorHandlers()); + if (is_array($module->getRelations()) && !empty($module->getRelations())) { + foreach ($module->getRelations() as $entity => $relationDefinition) { + $this->allRelations[$entity][] = $relationDefinition; + } } - return null; } /** - * @param $cacheFile - * @param array $content - * @return bool + * 获取用于注册到 Phalcon Application 的 module 数组 + * + * @return array */ - public function writeCache($cacheFile, array $content) + public function getModulesForPhalcon() { - if ($cacheFile && $fh = fopen($cacheFile, 'w')) { - fwrite($fh, 'getModulesForPhalcon(); } /** - * @param $moduleName - * @return bool + * @return mixed */ - public function hasModule($moduleName) + public function getAllAutoLoaders() { - return isset($this->modules[$moduleName]) ? true : false; + return $this->allAutoLoaders; } /** - * @return array + * @param mixed $allAutoLoaders */ - public function getModules() + public function setAllAutoLoaders($allAutoLoaders) { - return $this->modules; + $this->allAutoLoaders = $allAutoLoaders; } /** - * @param $moduleName - * @return array + * @return Config */ - public function getModule($moduleName) + public function getAllRoutesFrontend() { - return empty($this->modules[$moduleName]) ? array() : $this->modules[$moduleName]; + return $this->allRoutesFrontend; } /** - * Get full module settings by module name or module setting array - * Module setting includes: - * - className: Required | Module bootstrap class full name, e.g. Eva\EvaCommon\Module - * - path : Required | Module.php file path, e.g. /www/eva/modules/EvaCommon/Module.php - * - moduleConfig: Optional | Module config file path, e.g. /www/eva/modules/EvaCommon/config/config.php , - * default is module_dir/config/config.php - * - routesFrontend Optional | Module front-end router config file path, - * e.g. /www/eva/modules/EvaCommon/config/routes.frontend.php - * - routesBackend Optional | Module back-end router config file path, - * e.g. /www/eva/modules/EvaCommon/config/routes.backend.php - * - routesCommand Optional | Module cli router config file path, - * e.g. /www/eva/modules/EvaCommon/config/routes.command.php - * - adminMenu Optional | Admin sidebar menu - * - di : Optional | Module global DI - * All optional setting could set as false to disable. - * - * @param $moduleName MUST as same as module folder, keep moduleName unique - * @param $moduleSetting mixed Support 3 types: - * - null, EvaEngine official module, module namespace MUST start with Eva\\, - * and source path MUST under application/modules - * - string, Full module class, which is already loaded by composer - * - array, Module setting array, require className and path at least, other options will be auto filled - * @return array Module - * @throws \Exception - */ - public function getModuleInfo($moduleName, $moduleSetting = null) + * @param Config $allRoutesFrontend + */ + public function setAllRoutesFrontend($allRoutesFrontend) { - $moduleName = ucfirst($moduleName); - $modulesPath = $this->getDefaultPath(); - $ds = DIRECTORY_SEPARATOR; - - //Get basic module info only contains className and path - if (true === is_null($moduleSetting)) { - $module = array( - 'className' => "Eva\\$moduleName\\Module", - 'path' => "$modulesPath{$ds}$moduleName{$ds}Module.php", - ); - } elseif (true === is_string($moduleSetting) && strpos($moduleSetting, '\\') !== false) { - //Composer module - $moduleClass = $moduleSetting; - if (false === class_exists($moduleClass)) { - throw new \Exception(sprintf('Module %s load failed by not exist class', $moduleClass)); - } - - $ref = new \ReflectionClass($moduleClass); - $module = array( - 'className' => $moduleClass, - 'path' => $ref->getFileName(), - ); - } elseif (true === is_array($moduleSetting)) { - $module = array_merge( - array( - 'className' => '', - 'path' => '', - ), - $moduleSetting - ); - $module['className'] = $module['className'] ?: "Eva\\$moduleName\\Module"; - $module['path'] = $module['path'] ?: "$modulesPath{$ds}$moduleName{$ds}Module.php"; - } else { - throw new \Exception(sprintf('Module %s load failed by incorrect format', $moduleName)); - } - - /** - * @var StandardInterface $moduleClass -*/ - $moduleClass = $module['className']; - $this->getLoader()->registerClasses( - array( - $moduleClass => $module['path'] - ) - )->register(); - - if (false === class_exists($moduleClass)) { - throw new \Exception(sprintf('Module %s load failed by not exist class', $moduleClass)); - } - - if (count( - array_intersect( - array( - 'Phalcon\Mvc\ModuleDefinitionInterface', - 'Eva\EvaEngine\Module\StandardInterface' - ), - class_implements($moduleClass) - ) - ) !== 2) { - throw new \Exception(sprintf('Module %s interfaces not correct', $moduleClass)); - } - - $module['dir'] = $moduleDir = dirname($module['path']); - $module = array_merge( - array( - 'moduleConfig' => "$moduleDir{$ds}config{$ds}config.php", //module config file path - 'routesFrontend' => "$moduleDir{$ds}config{$ds}routes.frontend.php", //module router frontend path - 'routesBackend' => "$moduleDir{$ds}config{$ds}routes.backend.php", //module router backend path - 'routesCommand' => "$moduleDir{$ds}config{$ds}routes.command.php", // module router in CLI mode - 'adminMenu' => "$moduleDir{$ds}config{$ds}admin.menu.php", //admin menu - 'autoloaders' => $moduleClass::registerGlobalAutoloaders(), //autoloaders - 'relations' => $moduleClass::registerGlobalRelations(), //entity relations for injection - 'listeners' => $moduleClass::registerGlobalEventListeners(), //module listeners list array - 'viewHelpers' => $moduleClass::registerGlobalViewHelpers(), //module view helpers - //'translatePath' => false, - ), - $module - ); - - return $module; + $this->allRoutesFrontend = $allRoutesFrontend; } /** - * @param array $moduleSettings - * @return $this - * @throws \Exception + * @return Config */ - public function loadModules(array $moduleSettings) + public function getAllRoutesBackend() { - //Trigger Event - $this->getEventsManager()->fire('module:beforeLoadModule', $this); - - $cacheFile = $this->getCacheFile(); - $loader = $this->getLoader(); - - if ($cacheFile && $cache = $this->readCache($cacheFile)) { - $loader->registerNamespaces($cache['namespaces'])->register(); - $loader->registerClasses($cache['classes'])->register(); - $this->modules = $cache['modules']; - - //Trigger Event - $this->getEventsManager()->fire('module:afterLoadModule', $this); - return $this; - } - - $modules = array(); - //All Module.php map array for cache - $classes = array(); - foreach ($moduleSettings as $moduleName => $moduleSetting) { - if (true === is_int($moduleName)) { - $moduleName = $moduleSetting; - $moduleSetting = null; - } - $module = $this->getModuleInfo($moduleName, $moduleSetting); - $modules[$moduleName] = $module; - $classes[$module['className']] = $module['path']; - } - $this->modules = $modules; - - $namespaces = $this->getMergedAutoloaders(); - if ($namespaces) { - $loader->registerNamespaces($namespaces)->register(); - } - - if ($cacheFile) { - $this->writeCache( - $cacheFile, - array( - 'classes' => $classes, - 'namespaces' => $namespaces, - 'modules' => $modules, - ) - ); - } - //Trigger Event - $this->getEventsManager()->fire('module:afterLoadModule', $this); - return $this; + return $this->allRoutesBackend; } /** - * @param $key - * @return array + * @param Config $allRoutesBackend */ - private function getMergedArray($key) + public function setAllRoutesBackend($allRoutesBackend) { - if (!($modules = $this->modules)) { - return array(); - } - $mergedArray = array(); - foreach ($modules as $moduleName => $module) { - if (false === is_array($module[$key])) { - continue; - } - $mergedArray = array_merge($mergedArray, $module[$key]); - } - return $mergedArray; + $this->allRoutesBackend = $allRoutesBackend; } /** - * @return array + * @return Config */ - public function getMergedAutoloaders() + public function getAllRoutesCLI() { - return $this->getMergedArray('autoloaders'); + return $this->allRoutesCLI; } /** - * @return array + * @param Config $allRoutesCLI */ - public function getMergedViewHelpers() + public function setAllRoutesCLI($allRoutesCLI) { - return $this->getMergedArray('viewHelpers'); + $this->allRoutesCLI = $allRoutesCLI; } /** * @return array */ - public function getMergedRelations() + public function getAllListeners() { - return $this->getMergedArray('relations'); + return $this->allListeners; } - /** - * Module Events could by key => value pairs like: - * 'module' => 'Eva\RealModule\Events\ModuleListener', - * Or could be key => array pairs like - * 'module' => array('Eva\RealModule\Events\ModuleListener', 100) - * Array[1] is listener priority - * - * @param array $listeners - * @return $this - * @throws \Exception + * @param array $allListeners */ - public function attachEvents(array $listeners = array()) + public function setAllListeners($allListeners) { - if (!$listeners) { - $modules = $this->getModules(); - if (!$modules) { - return $this; - } - - $listeners = array(); - foreach ($modules as $moduleName => $module) { - $listeners[$moduleName] = $this->getModuleListeners($moduleName); - } - } - - if (!$listeners) { - return $this; - } - - $eventsManager = $this->getEventsManager(); - foreach ($listeners as $moduleName => $moduleListeners) { - foreach ($moduleListeners as $eventType => $listener) { - $priority = 0; - if (is_string($listener)) { - $listenerClass = $listener; - } elseif (true === is_array($listener) && count($listener) > 1) { - $listenerClass = $listener[0]; - $priority = (int) $listener[1]; - } else { - throw new \Exception(sprintf("Module %s listener format not correct", $moduleName)); - } - if (false === class_exists($listenerClass)) { - throw new \Exception(sprintf("Module listener %s not exist", $listenerClass)); - } - $eventsManager->attach($eventType, new $listenerClass, $priority); - } - } - return $this; + $this->allListeners = $allListeners; } /** - * @param $moduleName - * @return string + * @return array */ - public function getModulePath($moduleName) + public function getAllRelations() { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['dir']) && file_exists($modules[$moduleName]['dir'])) { - return $modules[$moduleName]['dir']; - } - return ''; + return $this->allRelations; } /** - * @param $moduleName - * @return array|mixed + * @param array $allRelations */ - public function getModuleConfig($moduleName) + public function setAllRelations($allRelations) { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['moduleConfig']) && file_exists($modules[$moduleName]['moduleConfig'])) { - return include $modules[$moduleName]['moduleConfig']; - } - return array(); + $this->allRelations = $allRelations; } /** - * @param $moduleName - * @return array|mixed + * @return array */ - public function getModuleRoutesFrontend($moduleName) + public function getAllAdminMenuFiles() { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['routesFrontend']) && file_exists($modules[$moduleName]['routesFrontend'])) { - return include $modules[$moduleName]['routesFrontend']; - } - return array(); + return $this->allAdminMenuFiles; } /** - * @param $moduleName - * @return array|mixed + * @param array $allAdminMenuFiles */ - public function getModuleRoutesCommand($moduleName) + public function setAllAdminMenuFiles($allAdminMenuFiles) { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['routesCommand']) && file_exists($modules[$moduleName]['routesCommand'])) { - return include $modules[$moduleName]['routesCommand']; - } - return array(); + $this->allAdminMenuFiles = $allAdminMenuFiles; } /** - * @param $moduleName - * @return array|mixed + * @return array */ - public function getModuleRoutesBackend($moduleName) + public function getAllViewHelpers() { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['routesBackend']) && file_exists($modules[$moduleName]['routesBackend'])) { - return include $modules[$moduleName]['routesBackend']; - } - return array(); + return $this->allViewHelpers; } /** - * @param $moduleName - * @return array + * @param array $allViewHelpers */ - public function getModuleListeners($moduleName) + public function setAllViewHelpers($allViewHelpers) { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['listeners'])) { - return $modules[$moduleName]['listeners']; - } - return array(); + $this->allViewHelpers = $allViewHelpers; } /** - * @param $moduleName - * @return mixed|string + * @return Config */ - public function getModuleAdminMenu($moduleName) + public function getAllConfig() { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['adminMenu']) && file_exists($modules[$moduleName]['adminMenu'])) { - return include $modules[$moduleName]['adminMenu']; - } - return ''; + return $this->allConfig; } /** - * @param $moduleName - * @return array + * @param Config $allConfig */ - public function getModuleViewHelpers($moduleName) + public function setAllConfig($allConfig) { - $modules = $this->getModules(); - if (!empty($modules[$moduleName]['viewHelpers'])) { - return $modules[$moduleName]['viewHelpers']; - } - return array(); + $this->allConfig = $allConfig; } /** - * @param Model $entity * @return array */ - public function getInjectRelations(Model $entity) + public function getAllDIDefinition() { - $relations = $this->injectRelations; - - if ($relations === false) { - $relations = $this->getMergedRelations(); - $this->injectRelations = $relations; - } - - if (!$relations) { - return array(); - } - - $entityRalations = array(); - foreach ($relations as $relation) { - if ($entity instanceof $relation['entity']) { - $entityRalations[] = $relation; - } - } - return $entityRalations; + return $this->allDIDefinition; } /** - * @param null $defaultPath + * @param array $allDIDefinition */ - public function __construct($defaultPath = null) + public function setAllDIDefinition($allDIDefinition) { - if ($defaultPath) { - $this->defaultPath = $defaultPath; - } + $this->allDIDefinition = $allDIDefinition; } } diff --git a/src/EvaEngine/Module/Module.bak.php b/src/EvaEngine/Module/Module.bak.php new file mode 100644 index 0000000..99d8c0f --- /dev/null +++ b/src/EvaEngine/Module/Module.bak.php @@ -0,0 +1,104 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/5/22 下午5:54 +// +---------------------------------------------------------------------- +// + Module.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Module; + + +class ModuleBak +{ + protected $name; + protected $settings = array(); + + public function getInfo() + { + $moduleName = ucfirst($this->name); + $modulesPath = $this->getDefaultPath(); + $ds = DIRECTORY_SEPARATOR; + + //Get basic module info only contains className and path + if (true === is_null($this->settings)) { + $module = array( + 'className' => "Eva\\$moduleName\\Module", + 'path' => "$modulesPath{$ds}$moduleName{$ds}Module.php", + ); + } elseif (true === is_string($this->settings) && strpos($this->settings, '\\') !== false) { + //Composer module + $moduleClass = $this->settings; + if (false === class_exists($moduleClass)) { + throw new \Exception(sprintf('Module %s load failed by not exist class', $moduleClass)); + } + + $ref = new \ReflectionClass($moduleClass); + $module = array( + 'className' => $moduleClass, + 'path' => $ref->getFileName(), + ); + } elseif (true === is_array($this->settings)) { + $module = array_merge( + array( + 'className' => '', + 'path' => '', + ), + $this->settings + ); + $module['className'] = $module['className'] ?: "Eva\\$moduleName\\Module"; + $module['path'] = $module['path'] ?: "$modulesPath{$ds}$moduleName{$ds}Module.php"; + } else { + throw new \Exception(sprintf('Module %s load failed by incorrect format', $moduleName)); + } + + /** + * @var StandardInterface $moduleClass + */ + $moduleClass = $module['className']; + $this->getLoader()->registerClasses( + array( + $moduleClass => $module['path'] + ) + )->register(); + + if (false === class_exists($moduleClass)) { + throw new \Exception(sprintf('Module %s load failed by not exist class', $moduleClass)); + } + + if (count( + array_intersect( + array( + \Phalcon\Mvc\ModuleDefinitionInterface::class, + \Eva\EvaEngine\Module\StandardInterface::class + ), + class_implements($moduleClass) + ) + ) !== 2 + ) { + throw new \Exception(sprintf('Module %s interfaces not correct', $moduleClass)); + } + + $module['dir'] = $moduleDir = dirname($module['path']); + $module = array_merge( + array( + 'moduleConfig' => "$moduleDir{$ds}config{$ds}config.php", //module config file path + 'routesFrontend' => "$moduleDir{$ds}config{$ds}routes.frontend.php", //module router frontend path + 'routesBackend' => "$moduleDir{$ds}config{$ds}routes.backend.php", //module router backend path + 'routesCommand' => "$moduleDir{$ds}config{$ds}routes.command.php", // module router in CLI mode + 'adminMenu' => "$moduleDir{$ds}config{$ds}admin.menu.php", //admin menu + 'autoloaders' => $moduleClass::registerGlobalAutoloaders(), //autoloaders + 'relations' => $moduleClass::registerGlobalRelations(), //entity relations for injection + 'listeners' => $moduleClass::registerGlobalEventListeners(), //module listeners list array + 'viewHelpers' => $moduleClass::registerGlobalViewHelpers(), //module view helpers + //'translatePath' => false, + ), + $module + ); + + return $module; + } +} \ No newline at end of file diff --git a/src/EvaEngine/Module/Module.php b/src/EvaEngine/Module/Module.php new file mode 100644 index 0000000..fafb416 --- /dev/null +++ b/src/EvaEngine/Module/Module.php @@ -0,0 +1,345 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/15 下午4:50 +// +---------------------------------------------------------------------- +// + Module.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Module; + + +class Module +{ + /** + * @var string name of module + */ + protected $name; + /** + * @var string root path of module + */ + protected $dir; + /** + * @var string file path of module definition + */ + protected $path; + /** + * @var string class name of module definition + */ + protected $className; + + /** + * @var array routes of frontend + */ + protected $routesFrontend = array(); + /** + * @var array routes of backend + */ + protected $routesBackend = array(); + /** + * @var array routes in CLI + */ + protected $routesCLI = array(); + /** + * @var array 监听器 + */ + protected $listeners = array(); + /** + * @var string 管理后台菜单文件 + */ + protected $adminMenusFile = ''; + /** + * @var array 模块配置 + */ + protected $config = array(); + /** + * @var array 模块注册的全局 + */ + protected $relations = array(); + /** + * @var array 视图助手 + */ + protected $viewHelpers = array(); + /** + * @var array 模块注册的 autoLoaders + */ + protected $autoLoaders = array(); + /** + * @var array + */ + protected $errorHandlers = array(); + /** + * @var array + */ + protected $diDefinition = array(); + /** + * @var array 模块选项 + */ + protected $options = array(); + + + public function __construct($name) + { + $this->setName($name); + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getDir() + { + return $this->dir; + } + + /** + * @param string $dir + */ + public function setDir($dir) + { + $this->dir = $dir; + } + + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * @param string $path + */ + public function setPath($path) + { + $this->path = $path; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->className; + } + + /** + * @param string $className + */ + public function setClassName($className) + { + $this->className = $className; + } + + /** + * @return array + */ + public function getRoutesFrontend() + { + return $this->routesFrontend; + } + + /** + * @param array $routesFrontend + */ + public function setRoutesFrontend($routesFrontend) + { + $this->routesFrontend = $routesFrontend; + } + + /** + * @return array + */ + public function getRoutesBackend() + { + return $this->routesBackend; + } + + /** + * @param array $routesBackend + */ + public function setRoutesBackend($routesBackend) + { + $this->routesBackend = $routesBackend; + } + + /** + * @return array + */ + public function getRoutesCLI() + { + return $this->routesCLI; + } + + /** + * @param array $routesCLI + */ + public function setRoutesCLI($routesCLI) + { + $this->routesCLI = $routesCLI; + } + + /** + * @return array + */ + public function getListeners() + { + return $this->listeners; + } + + /** + * @param array $listeners + */ + public function setListeners($listeners) + { + $this->listeners = $listeners; + } + + /** + * @return string + */ + public function getAdminMenusFile() + { + return $this->adminMenusFile; + } + + /** + * @param string $adminMenusFile + */ + public function setAdminMenusFile($adminMenusFile) + { + $this->adminMenusFile = $adminMenusFile; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @param array $config + */ + public function setConfig($config) + { + $this->config = $config; + } + + /** + * @return array + */ + public function getRelations() + { + return $this->relations; + } + + /** + * @param array $relations + */ + public function setRelations($relations) + { + $this->relations = $relations; + } + + /** + * @return array + */ + public function getViewHelpers() + { + return $this->viewHelpers; + } + + /** + * @param array $viewHelpers + */ + public function setViewHelpers($viewHelpers) + { + $this->viewHelpers = $viewHelpers; + } + + /** + * @return array + */ + public function getAutoLoaders() + { + return $this->autoLoaders; + } + + /** + * @param array $autoLoaders + */ + public function setAutoLoaders($autoLoaders) + { + $this->autoLoaders = $autoLoaders; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param array $options + */ + public function setOptions($options) + { + $this->options = $options; + } + + /** + * @return array + */ + public function getErrorHandlers() + { + return $this->errorHandlers; + } + + /** + * @param array $errorHandlers + */ + public function setErrorHandlers($errorHandlers) + { + $this->errorHandlers = $errorHandlers; + } + + /** + * @return array + */ + public function getDiDefinition() + { + return $this->diDefinition; + } + + /** + * @param array $diDefinition + */ + public function setDiDefinition($diDefinition) + { + $this->diDefinition = $diDefinition; + } + +} \ No newline at end of file diff --git a/src/EvaEngine/Module/ModuleParser.php b/src/EvaEngine/Module/ModuleParser.php new file mode 100644 index 0000000..c7a6670 --- /dev/null +++ b/src/EvaEngine/Module/ModuleParser.php @@ -0,0 +1,267 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午4:51 +// +---------------------------------------------------------------------- +// + ModuleParser.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Module; + + +use Eva\EvaEngine\Exception\RuntimeException; +use Phalcon\Config; +use Phalcon\Loader; +use Phalcon\Mvc\ModuleDefinitionInterface; + +class ModuleParser +{ + protected $defaultModulesPath; + + public function __construct($defaultModulesPath) + { + $this->defaultModulesPath = $defaultModulesPath; + } + + protected function parseOptions(Module $module, $options) + { + // 模块定义所在的文件名 + $options['path'] = isset($options['path']) ? + $options['path'] : + rtrim($this->defaultModulesPath, '/') . '/Module.php'; + $options['path'] = realpath($options['path']); + // 模块根目录 + $options['dir'] = + isset($options['dir']) ? + $options['dir'] : + dirname($options['path']); + $options['dir'] = realpath(rtrim($options['dir'], '/')); + // 模块定义的类名 + $options['className'] = isset($options['className']) ? + $options['className'] : $module->getName() . '\\Module'; + + // 是否允许加载前台路由,默认是允许 + $options['routesFrontend'] = isset($options['routesFrontend']) ? + boolval($options['routesFrontend']) : true; + + // 是否允许加载后台路由,默认是允许 + $options['routesBackend'] = isset($options['routesBackend']) ? + boolval($options['routesBackend']) : true; + + // 是否允许加载模块的配置文件,默认为允许 + $options['configEnable'] = isset($options['configEnable']) ? + boolval($options['configEnable']) : true; + + // 是否允许注册模块的事件,默认为允许 + $options['eventsEnable'] = isset($options['eventsEnable']) ? + boolval($options['eventsEnable']) : true; + + // 是否允许注册模块的服务,默认为允许。 + $options['diEnable'] = isset($options['diEnable']) ? + boolval($options['diEnable']) : true; + // 是否允许显示管理后台菜单,默认为允许 + $options['adminMenusEnable'] = isset($options['adminMenusEnable']) ? + boolval($options['adminMenusEnable']) : true; + // 是否允许注册全局的 ORM 关联关系 + $options['relationsEnable'] = isset($options['relationsEnable']) ? + boolval($options['relationsEnable']) : true; + // 是否允许注册全局的视图助手 + $options['viewHelpersEnable'] = isset($options['viewHelpersEnable']) ? + boolval($options['viewHelpersEnable']) : true; + // 是否允许注册 ErrorHandler + $options['errorHandlerEnable'] = isset($options['errorHandlerEnable']) ? + boolval($options['errorHandlerEnable']) : true; + + return $options; + } + + + public function parse($name, $options) + { + + $module = new Module($name); + $options = $this->parseOptions($module, $options); + $module->setOptions($options); + $module->setClassName($options['className']); + $module->setPath($options['path']); + $module->setDir($options['dir']); + $this->registerDefinitionClass($module, $options) + ->parseConfig($module, $options) + ->parseRoutesFrontend($module, $options) + ->parseRoutesBackend($module, $options) + ->parseAdminMenus($module, $options) + ->parseListeners($module, $options) + ->parseRelations($module, $options) + ->parseViewHelpers($module, $options) + ->parseGlobalAutoLoaders($module, $options) + ->parseDI($module, $options); + + return $module; + } + + protected function parseErrorHandler(Module $module, $options) + { + if ($options['errorHandlerEnable']) { + $moduleClass = $module->getClassName(); + $module->setErrorHandlers($moduleClass::registerErrorHandlers()); + } + } + + protected function parseGlobalAutoLoaders(Module $module, $options) + { + $moduleClass = $module->getClassName(); + $module->setAutoLoaders($moduleClass::registerGlobalAutoloaders()); + + return $this; + } + + protected function parseViewHelpers(Module $module, $options) + { + $moduleClass = $module->getClassName(); + if ($options['viewHelpersEnable']) { + $module->setViewHelpers($moduleClass::registerGlobalViewHelpers()); + } + + return $this; + } + + protected function parseListeners(Module $module, $options) + { + $moduleClass = $module->getClassName(); + if ($options['eventsEnable']) { + $module->setListeners($moduleClass::registerGlobalEventListeners()); + } + + return $this; + } + + protected function parseRelations(Module $module, $options) + { + $moduleClass = $module->getClassName(); + if ($options['relationsEnable']) { + $module->setRelations($moduleClass::registerGlobalRelations()); + } + + return $this; + } + + protected function parseConfig(Module $module, $options) + { + $moduleConfig = new Config(array()); + + // 加载模块的配置 + if ($options['configEnable']) { + foreach (glob($module->getDir() . '/config/config.*.php') as $_sub_configFile) { + $matches = array(); + $fileName = substr($_sub_configFile, strrpos($_sub_configFile, '/') + 1); + preg_match('/config\.(.+)\.php/i', $fileName, $matches); + $configKey = $matches[1]; + $moduleConfig->merge( + new Config(array( + $configKey => include $_sub_configFile + )) + ); + } + if (is_file($module->getDir() . '/config/config.php')) { + $moduleConfig->merge(new Config(include $module->getDir() . '/config/config.php')); + } + } + + $module->setConfig($moduleConfig->toArray()); + + return $this; + } + + protected function parseDI(Module $module, $options) + { + if ($options['diEnable']) { + if (is_file($module->getDir() . '/config/di.php')) { + $diDefinitions = include $module->getDir() . '/config/di.php'; + $realDiDefinitions = array(); + foreach ($diDefinitions['disposables'] as $name => $definition) { + $realDiDefinitions[$name] = array( + 'definition' => $definition, + 'share' => false, + ); + } + // 在同一模块下,同名的共享 DI 会覆盖独享 DI + foreach ($diDefinitions['shares'] as $name => $definition) { + $realDiDefinitions[$name] = array( + 'definition' => $definition, + 'share' => true, + ); + } + $module->setDiDefinition($realDiDefinitions); + } + } + } + + protected function parseRoutesFrontend(Module $module, $options) + { + // 加载模块的前台路由 + if ($options['routesFrontend'] && is_file($module->getDir() . '/config/routes.frontend.php')) { + $module->setRoutesFrontend(include $module->getDir() . '/config/routes.frontend.php'); + } + + return $this; + } + + protected function parseRoutesBackend(Module $module, $options) + { + // 加载模块的后台路由 + if ($options['routesBackend'] && is_file($module->getDir() . '/config/routes.backend.php')) { + $module->setRoutesBackend(include $module->getDir() . '/config/routes.backend.php'); + } + + return $this; + } + + protected function parseAdminMenus(Module $module, $options) + { + // 加载模块的管理后台菜单 + if ($options['adminMenus'] && is_file($module->getDir() . '/config/admin.menu.php')) { + $module->setAdminMenusFile($module->getDir() . '/config/admin.menu.php'); + } + + return $this; + } + + /** + * 注册模块定义类 + * + * @param Module $module + * @param $options + * @return $this + * @throws RuntimeException + */ + protected function registerDefinitionClass(Module $module, $options) + { + $loader = new Loader(); + $loader->registerClasses(array( + $module->getClassName() => $module->getPath() + ))->register(); + + if (false === class_exists($module->getClassName())) { + throw new RuntimeException( + sprintf('Module %s load failed by not exist class', $module->getClassName()) + ); + } + $moduleImplements = array_intersect( + array( + ModuleDefinitionInterface::class, + StandardInterface::class + ), + class_implements($module->getClassName()) + ); + if (count($moduleImplements) !== 2) { + throw new RuntimeException( + sprintf('Module %s interfaces not correct', $module->getClassName()) + ); + } + + return $this; + } +} \ No newline at end of file diff --git a/src/EvaEngine/Mvc/Controller/ControllerBase.php b/src/EvaEngine/Mvc/Controller/ControllerBase.php index 6541a9b..ed270c0 100644 --- a/src/EvaEngine/Mvc/Controller/ControllerBase.php +++ b/src/EvaEngine/Mvc/Controller/ControllerBase.php @@ -189,7 +189,7 @@ public function showException($exception, $messages = null, $messageType = 'erro } } - $logger = $this->getDI()->get('logException'); + $logger = $this->getDI()->get('errorLogger'); $logger->log( implode('', $messageArray) . "\n" . get_class($exception) . ":" . $exception->getMessage() . "\n" . @@ -263,7 +263,7 @@ public function ignoreException($exception, $messages = null, $messageType = 'de } } - $logger = $this->getDI()->get('logException'); + $logger = $this->getDI()->get('errorLogger'); $logger->debug($exception); /* $logger->debug( diff --git a/src/EvaEngine/Mvc/Model/ModelsMetadataAdapterCreator.php b/src/EvaEngine/Mvc/Model/ModelsMetadataAdapterCreator.php new file mode 100644 index 0000000..19ac99a --- /dev/null +++ b/src/EvaEngine/Mvc/Model/ModelsMetadataAdapterCreator.php @@ -0,0 +1,44 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午2:54 +// +---------------------------------------------------------------------- +// + ModelsMetadataAdapterCreator.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Mvc\Model; + + +use Eva\EvaEngine\Foundation\AdapterCreator; + +class ModelsMetadataAdapterCreator extends AdapterCreator +{ + + /** + * {@inheritdoc} + */ + protected function getAdaptersMapping() + { + return array( + 'default' => array( + 'apc' => 'Phalcon\Mvc\Model\MetaData\Apc', + 'files' => 'Phalcon\Mvc\Model\MetaData\Files', + 'memory' => 'Phalcon\Mvc\Model\MetaData\Memory', + 'xcache' => 'Phalcon\Mvc\Model\MetaData\Xcache', + 'memcache' => 'Phalcon\Mvc\Model\MetaData\Memcache', + 'redis' => 'Phalcon\Mvc\Model\MetaData\Redis', + 'wincache' => 'Phalcon\Mvc\Model\MetaData\Wincache', + ) + ); + } + + public function create($adapter, array $options = array()) + { + $adapterClass = $this->getAdapterClass($adapter); + + return new $adapterClass($options); + } +} \ No newline at end of file diff --git a/src/EvaEngine/Session/SessionAdapterCreator.php b/src/EvaEngine/Session/SessionAdapterCreator.php new file mode 100644 index 0000000..9dccfad --- /dev/null +++ b/src/EvaEngine/Session/SessionAdapterCreator.php @@ -0,0 +1,77 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/11 下午5:05 +// +---------------------------------------------------------------------- +// + SessionAdapterCreator.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Session; + +use Eva\EvaEngine\Exception\RuntimeException; +use Eva\EvaEngine\Foundation\AdapterCreator; +use Eva\EvaEngine\IoC; + +class SessionAdapterCreator extends AdapterCreator +{ + protected static $adapterMapping = array( + 'files' => 'Phalcon\Session\Adapter\Files', + 'database' => 'Phalcon\Session\Adapter\Database', + 'memcache' => 'Phalcon\Session\Adapter\Memcache', + 'libmemcached' => 'Eva\EvaEngine\Session\Adapter\Libmemcached', + 'mongo' => 'Phalcon\Session\Adapter\Mongo', + 'redis' => 'Phalcon\Session\Adapter\Redis', + 'handlersocket' => 'Phalcon\Session\Adapter\HandlerSocket', + ); + + public function create( + $adapter, + array $options = array(), + $session_name = 'PHPSESSID', + array $cookie_params = array() + ) { + + $adapterClass = $this->getAdapterClass($adapter); + + session_name($session_name); + + if (!empty($cookie_params)) { + session_set_cookie_params( + isset($cookie_params['lifetime']) ? $cookie_params['lifetime'] : 0, + isset($cookie_params['path']) ? $cookie_params['path'] : null, + isset($cookie_params['domain']) ? $cookie_params['domain'] : null, + isset($cookie_params['secure']) ? $cookie_params['secure'] : false, + isset($cookie_params['httponly']) ? $cookie_params['httponly'] : false + ); + } + /** @var \Phalcon\Session\AdapterInterface $session */ + $session = new $adapterClass($options); + if (!$session->isStarted()) { + //NOTICE: Get php warning here, not found reason + @$session->start(); + } + + return $session; + } + + /** + * {@inheritdoc} + */ + protected function getAdaptersMapping() + { + return array( + 'default' => array( + 'files' => 'Phalcon\Session\Adapter\Files', + 'database' => 'Phalcon\Session\Adapter\Database', + 'memcache' => 'Phalcon\Session\Adapter\Memcache', + 'libmemcached' => 'Eva\EvaEngine\Session\Adapter\Libmemcached', + 'mongo' => 'Phalcon\Session\Adapter\Mongo', + 'redis' => 'Phalcon\Session\Adapter\Redis', + 'handlersocket' => 'Phalcon\Session\Adapter\HandlerSocket', + ) + ); + } +} \ No newline at end of file diff --git a/src/EvaEngine/Tasks/TaskBase.php b/src/EvaEngine/Tasks/TaskBase.php index d4c17be..e30a783 100644 --- a/src/EvaEngine/Tasks/TaskBase.php +++ b/src/EvaEngine/Tasks/TaskBase.php @@ -12,13 +12,13 @@ // + TaskBase.php // +---------------------------------------------------------------------- -use Eva\EvaEngine\CLI\Output\ConsoleOutput; use Phalcon\CLI\Task; +use Symfony\Component\Console\Output\ConsoleOutput; class TaskBase extends Task { /** - * @var \Eva\EvaEngine\CLI\Output\ConsoleOutput + * @var ConsoleOutput */ protected $output; diff --git a/src/EvaEngine/View/ViewHelper.php b/src/EvaEngine/View/ViewHelper.php new file mode 100644 index 0000000..6c75cc7 --- /dev/null +++ b/src/EvaEngine/View/ViewHelper.php @@ -0,0 +1,219 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/16 下午5:54 +// +---------------------------------------------------------------------- +// + ViewHelper.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\View; + + +use Eva\EvaEngine\Exception\RuntimeException; +use Eva\EvaEngine\Mvc\View; +use Phalcon\Text; + +class ViewHelper +{ + protected $helpers = array(); + + public function register($name, $definition) + { + if (!is_callable($definition)) { + throw new RuntimeException(sprintf('ViewHelper definition must be callable, viewHelper name: "%s"', $name)); + } + $this->helpers[$name] = $definition; + } + + public function __call($helperName, $args) + { + if (empty($this->helpers[$helperName])) { + throw new RuntimeException(sprintf('Request helper %s not registered', $helperName)); + } + call_user_func_array($this->helpers[$helperName], $args); + } + + public function config() + { + $config = eva_get('config'); + if (!$args = func_get_args()) { + return $config; + } + + $res = $config; + foreach ($args as $arg) { + if (!isset($res->$arg)) { + return ''; + } + $res = $res->$arg; + } + + return $res; + } + + public function component($componentName, array $params = array()) + { + return View::getComponent($componentName, $params); + } + + public function _($message = null, $replacement = null) + { + return $this->translate($message, $replacement); + } + + public function translate($message = null, $replacement = null) + { + $translate = eva_get('translate'); + if ($message) { + return $translate->_(trim($message), $replacement); + } + + return $translate; + } + + public function flashOutput() + { + $flash = eva_get('flash'); + if (!$flash) { + return ''; + } + $messages = $flash->getMessages(); + $classMapping = array( + 'error' => 'alert alert-danger', + 'warning' => 'alert alert-warning', + 'success' => 'alert alert-success', + 'notice' => 'alert alert-info', + ); + + $messageString = ''; + $escaper = self::getDI()->getEscaper(); + foreach ($messages as $type => $submessages) { + foreach ($submessages as $message) { + $messageString .= + '
' + . self::translate($message) + . '
'; + } + } + + return $messageString; + } + + public function uri($uri, array $query = null, array $baseQuery = null) + { + $url = eva_get('url'); + if ($query && $baseQuery) { + $query = array_merge($baseQuery, $query); + } + + return $url->get($uri, $query); + } + + public function thumb($uri, $query = null, $configKey = 'default') + { + + if ($query) { + if (true === is_array($query)) { + $query = implode(',', $query); + } + + if (false !== ($pos = strrpos($uri, '.'))) { + $uri = explode('/', $uri); + $fileName = array_pop($uri); + $nameArray = explode('.', $fileName); + $nameExt = array_pop($nameArray); + $nameFinal = array_pop($nameArray); + $nameFinal .= ',' . $query; + array_push($nameArray, $nameFinal, $nameExt); + $fileName = implode('.', $nameArray); + array_push($uri, $fileName); + $uri = implode('/', $uri); + } + } + + + if (Text::startsWith($uri, 'http://', false) || Text::startsWith($uri, 'https://', false)) { + return $uri; + } + + + $config = eva_get('config'); + if (isset($config->thumbnail->$configKey->baseUri) && $baseUrl = $config->thumbnail->$configKey->baseUri) { + return $baseUrl . $uri; + } + + return $uri; + } + + /** + * Get either a Gravatar URL or complete image tag for a specified email address. + * @param $email + * @param int $s + * @param string $d + * @param string $r + * @return string + */ + public function gravatar($email, $s = 80, $d = 'mm', $r = 'g') + { + $url = 'http://www.gravatar.com/avatar/'; + $url .= md5(strtolower(trim($email))); + $url .= "?s=$s&d=$d&r=$r"; + + return $url; + } + + + /** + * Transform input time to time string which can be parsed by javascript + * @param string $time + * @param null $timezone + * @return string javascript parse-able string + */ + public function jsTime($time = '', $timezone = null) + { + $time = $time ? $time : time(); + $timezone = $timezone ? $timezone : self::getDI()->getConfig()->datetime->defaultTimezone; + $time = $time + $timezone * 3600; + $prefix = $timezone < 0 ? '-' : '+'; + + $zone = str_pad(str_pad(abs($timezone), 2, 0, STR_PAD_LEFT), 4, 0); + + return gmdate('D M j H:i:s', $time) . ' UTC' . $prefix . $zone . ' ' . gmdate('Y', $time); + } + + /** + * Transform input time to iso time + * + * @param string $time + * @param int $timezone + * + * @access public + * + * @return string time string + */ + public function isoTime($time = null, $timezone = null) + { + //$timezone = $timezone ? $timezone : self::getDI()->getConfig()->datetime->defaultTimezone; + $time = $time ? $time : time(); + + return $time = gmdate('c', $time); + } + + public function datetime($time = '', $format = '', $timezone = null) + { + $timezone = $timezone ? $timezone : self::getDI()->getConfig()->datetime->defaultTimezone; + $format = $format ? $format : self::getDI()->getConfig()->datetime->defaultFormat; + $time = $time ? $time : time(); + $time = is_numeric($time) ? $time : strtotime($time); + $time = $time + $timezone * 3600; + + return gmdate($format, $time); + } +} \ No newline at end of file diff --git a/src/EvaEngine/helpers.php b/src/EvaEngine/helpers.php index ca7ca95..7eb0f16 100644 --- a/src/EvaEngine/helpers.php +++ b/src/EvaEngine/helpers.php @@ -38,7 +38,7 @@ function dd($r) } /** - * 方便链式调用,避免过多的中间变量,例如:with(new Post())->findPosts() + * 方便链式调用,避免过多的中间变量,例如:with(new Post())->findPosts(),较老的版本 * * @param $obj * @return mixed @@ -66,6 +66,13 @@ function array_pluck($array, $itemKey, $keepItemKey = true) } } if (!function_exists('starts_with')) { + /** + * 判断 $haystack 是否以 $needle 打头 + * + * @param $haystack + * @param $needle + * @return bool + */ function starts_with($haystack, $needle) { // search backwards starting from haystack length characters from the end @@ -73,19 +80,36 @@ function starts_with($haystack, $needle) } } if (!function_exists('ends_with')) { + /** + * 判断 $haystack 是否以 $needle 结尾 + * + * @param string $haystack + * @param string $needle + * @return bool + */ function ends_with($haystack, $needle) { // search forward starting from end minus needle length characters return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos( - $haystack, - $needle, - $temp - ) !== false); + $haystack, + $needle, + $temp + ) !== false); } } if (!function_exists('eva_url')) { + /** + * 根据域名名称(站点名称)生成 URL + * + * @param string $domainName 域名名称(站点名称) + * @param string $baseUri 基础 URI + * @param array $params query参数数组 + * @param bool $https 是否是https,留空则自动检测 + * @return string + * @throws \Eva\EvaEngine\Exception\RuntimeException + */ function eva_url($domainName, $baseUri = '/', $params = array(), $https = false) { $config = IoC::get('config'); @@ -110,6 +134,13 @@ function eva_url($domainName, $baseUri = '/', $params = array(), $https = false) } } if (!function_exists('eva_domain')) { + /** + * 根据域名名称(站点名称)获取域名 + * + * @param string $domainName 域名名称(站点名称) + * @return mixed + * @throws \Eva\EvaEngine\Exception\RuntimeException + */ function eva_domain($domainName) { $config = IoC::get('config'); @@ -117,3 +148,16 @@ function eva_domain($domainName) return @$config->domains->$domainName->domain; } } +if (!function_exists('eva_get')) { + /** + * 从 DI 容器中获取服务 + * + * @param string $serviceName 服务名 + * @return mixed + * @throws \Eva\EvaEngine\Exception\RuntimeException + */ + function eva_get($serviceName) + { + return IoC::get($serviceName); + } +} \ No newline at end of file diff --git a/test.php b/test.php new file mode 100644 index 0000000..12424e7 --- /dev/null +++ b/test.php @@ -0,0 +1,16 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午4:23 +// +---------------------------------------------------------------------- +// + test.php +// +---------------------------------------------------------------------- +foreach(glob(__DIR__ . '/config/config.*.php') as $file) { + $fileName = substr($file, strrpos($file, '/') + 1) . PHP_EOL; + $matches = array(); + preg_match('/config\.(.+)\.php/', $fileName, $matches); + var_dump($matches[1]); +} \ No newline at end of file diff --git a/tests/EvaEngineTest/Module/ModuleTest.php b/tests/EvaEngineTest/Module/ModuleTest.php new file mode 100644 index 0000000..9ca7d11 --- /dev/null +++ b/tests/EvaEngineTest/Module/ModuleTest.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/15 下午4:02 +// +---------------------------------------------------------------------- +// + ModuleTest.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\EvaEngineTest\Module; + + +class ModuleTest extends \PHPUnit_Framework_TestCase +{ + +} \ No newline at end of file diff --git a/views/ide/helper.phtml b/views/ide/helper.phtml new file mode 100644 index 0000000..e61ccd1 --- /dev/null +++ b/views/ide/helper.phtml @@ -0,0 +1,39 @@ + + + /** + * A helper file for EvaEngine 1.2, to provide autocomplete information to your IDE + * Generated for EvaEngine on . + * + * @author Barry vd. Heuvel + * @see https://github.com/barryvdh/laravel-ide-helper + */ + + $aliases): ?> + namespace { + + exit("This file should not be included, only analyzed by your IDE"); + + + + + getClassType() ?> getShortName() ?> getExtends() ? 'extends ' . $alias->getExtends() : '' ?>{ + getMethods() as $method): ?> + + getDocComment(' ')) ?> + + public static function getName() ?>(getParamsWithDefault() ?>){getDeclaringClass() !== $method->getRoot()): ?> + + //Method inherited from getDeclaringClass() ?> + + + shouldReturn() ? 'return ': '' ?>getRoot() ?>::getName() ?>(getParams() ?>); + } + + + } + + + + } + + \ No newline at end of file diff --git a/views/ide/meta.phtml b/views/ide/meta.phtml new file mode 100644 index 0000000..4f7abb5 --- /dev/null +++ b/views/ide/meta.phtml @@ -0,0 +1,22 @@ + namespace PHPSTORM_META { + /** + * PhpStorm Meta file, to provide autocomplete information for PhpStorm + * Generated on . + * + * @author EvaEngine + * @see https://github.com/evaengine/evaengine + */ + + /** @noinspection PhpIllegalArrayKeyTypeInspection */ + /** @noinspection PhpUnusedLocalVariableInspection */ + /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */ + $STATIC_METHOD_TYPES = [ + + ('') => [ + $class): ?> + '' instanceof \, + + ], + + ]; +} \ No newline at end of file From 2803be3be5698776493bb162528039ae15253ae4 Mon Sep 17 00:00:00 2001 From: mr5 Date: Thu, 25 Jun 2015 10:41:01 +0800 Subject: [PATCH 2/2] add cli mode --- .phpstorm.meta.php | 67 +- Module.php | 2 +- composer.json | 6 +- config/di.php | 128 +- config/{routes.cli.php => routes.console.php} | 4 +- console.php | 30 + examples/console.php | 22 + src/EvaEngine/Application.php | 133 -- src/EvaEngine/CLI.php | 64 + src/EvaEngine/Cache/CacheAdapterCreator.php | 4 +- .../Commands/Debug/Routes/ListCommand.php | 87 + src/EvaEngine/Console/CommandBase.php | 328 ++++ src/EvaEngine/Console/ConsoleApplication.php | 99 ++ src/EvaEngine/Db/DbAdapterCreator.php | 8 +- src/EvaEngine/Engine.bak..php | 1398 ++++++++++++++++ src/EvaEngine/Engine.php | 1432 ++--------------- src/EvaEngine/Foundation/AdapterCreator.php | 10 +- .../Foundation/ApplicationInterface.php | 33 + src/EvaEngine/IoC.php | 37 +- src/EvaEngine/Module/Manager.php | 95 +- src/EvaEngine/Module/Module.php | 14 +- src/EvaEngine/Module/ModuleParser.php | 50 +- src/EvaEngine/Mvc/Model.php | 2 +- src/EvaEngine/Mvc/View.php | 13 +- src/EvaEngine/Mvc/WebApplication.php | 88 + .../Session/SessionAdapterCreator.php | 9 - src/EvaEngine/helpers.php | 2 +- 27 files changed, 2505 insertions(+), 1660 deletions(-) rename config/{routes.cli.php => routes.console.php} (83%) create mode 100644 console.php create mode 100644 examples/console.php delete mode 100644 src/EvaEngine/Application.php create mode 100644 src/EvaEngine/CLI.php create mode 100644 src/EvaEngine/Commands/Debug/Routes/ListCommand.php create mode 100644 src/EvaEngine/Console/CommandBase.php create mode 100644 src/EvaEngine/Console/ConsoleApplication.php create mode 100644 src/EvaEngine/Engine.bak..php create mode 100644 src/EvaEngine/Foundation/ApplicationInterface.php create mode 100644 src/EvaEngine/Mvc/WebApplication.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index c2f4698..ff2c703 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -1,39 +1,44 @@ [ - 'annotations' instanceof \Phalcon\Annotations\Adapter\Memory, - 'config' instanceof \Phalcon\Config, - 'cookies' instanceof \Phalcon\Http\Response\Cookies, - 'crypt' instanceof \Phalcon\Crypt, - 'dispatcher' instanceof \Phalcon\Mvc\Dispatcher, - 'escaper' instanceof \Phalcon\Escaper, - 'eventsManager' instanceof \Phalcon\Events\Manager, - 'flashSession' instanceof \Phalcon\Flash\Session, - 'globalCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, - 'modelsCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, - 'modelsManager' instanceof \Eva\EvaEngine\Mvc\Model\Manager, - 'modelsMetadata' instanceof \Phalcon\Mvc\Model\MetaData\Files, - 'request' instanceof \Phalcon\Http\Request, - 'response' instanceof \Phalcon\Http\Response, - 'router' instanceof \Phalcon\Mvc\Router, - 'security' instanceof \Phalcon\Security, - 'session' instanceof \Phalcon\Session\Adapter\Files, - 'tag' instanceof \Eva\EvaEngine\Tag, - 'transactionManager' instanceof \Phalcon\Mvc\Model\Transaction\Manager, - 'url' instanceof \Eva\EvaEngine\Mvc\Url, - 'viewCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, - 'view' instanceof \Eva\EvaEngine\Mvc\View, - ], - ]; + eva_get('') => [ + + 'annotations' instanceof \Phalcon\Annotations\Adapter\Memory, + 'config' instanceof \Phalcon\Config, + 'cookies' instanceof \Phalcon\Http\Response\Cookies, + 'crypt' instanceof \Phalcon\Crypt, + 'dispatcher' instanceof \Phalcon\Mvc\Dispatcher, + 'evaengine' instanceof \Eva\EvaEngine\Engine, + 'escaper' instanceof \Phalcon\Escaper, + 'loader' instanceof \Phalcon\Loader, + 'whoops' instanceof \Whoops\Run, + 'eventsManager' instanceof \Phalcon\Events\Manager, + 'flashSession' instanceof \Phalcon\Flash\Session, + 'globalCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, + 'modelsCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, + 'moduleManager' instanceof \Eva\EvaEngine\Module\Manager, + 'modelsManager' instanceof \Eva\EvaEngine\Mvc\Model\Manager, + 'modelsMetadata' instanceof \Phalcon\Mvc\Model\MetaData\Files, + 'request' instanceof \Phalcon\Http\Request, + 'response' instanceof \Phalcon\Http\Response, + 'router' instanceof \Phalcon\Mvc\Router, + 'security' instanceof \Phalcon\Security, + 'session' instanceof \Phalcon\Session\Adapter\Files, + 'tag' instanceof \Eva\EvaEngine\Tag, + 'transactionManager' instanceof \Phalcon\Mvc\Model\Transaction\Manager, + 'url' instanceof \Eva\EvaEngine\Mvc\Url, + 'viewCache' instanceof \Eva\EvaEngine\Cache\Backend\Disable, + 'view' instanceof \Eva\EvaEngine\Mvc\View, + ], + ]; } \ No newline at end of file diff --git a/Module.php b/Module.php index 4933b6f..bcce5a9 100644 --- a/Module.php +++ b/Module.php @@ -16,7 +16,7 @@ use Eva\EvaEngine\Module\StandardInterface; use Phalcon\Events\Manager as EventsManager; -class Module implements StandardInterface +class Module extends AbstractModule { /** * Registers an autoloader related to the module diff --git a/composer.json b/composer.json index 1f16874..9455986 100644 --- a/composer.json +++ b/composer.json @@ -18,11 +18,13 @@ "evaengine/eva-sms": "0.1.*", "symfony/console": "^2.6", "phpdocumentor/reflection-docblock": "^2.0", - "doctrine/annotations": "^1.2" + "doctrine/annotations": "^1.2", + "jeremeamia/superclosure": "^2.1" }, "require-dev": { "satooshi/php-coveralls": "dev-master", - "mockery/mockery": "^0.9.4" + "mockery/mockery": "^0.9.4", + "filp/whoops": "^1.1" }, "suggest": { "ext-redis": "*", diff --git a/config/di.php b/config/di.php index 0ea7a79..d483cb4 100644 --- a/config/di.php +++ b/config/di.php @@ -11,7 +11,7 @@ use Phalcon\Mvc\Router; - +$a = 'aaa'; return array( /** * disposable DI 在每次 get 的时候都会重新构造一个对象 @@ -19,6 +19,35 @@ 'disposables' => array( 'mailMessage' => 'Eva\EvaEngine\MailMessage', 'placeholder' => 'Eva\EvaEngine\View\Helper\Placeholder', + 'flash' => 'Phalcon\Flash\Session', + 'cookies' => function () use ($a){ + $cookies = new \Phalcon\Http\Response\Cookies(); + $cookies->useEncryption(false); + + return $cookies; + }, + 'translate' => + function () { + $config = eva_get('config'); + $file = $config->translate->path . $config->translate->forceLang . '.csv'; + if (false === file_exists($file)) { + //empty translator + return new \Phalcon\Translate\Adapter\NativeArray( + array( + 'content' => array() + ) + ); + } + $translate = new \Phalcon\Translate\Adapter\Csv( + array( + 'file' => $file, + 'delimiter' => ',', + ) + ); + + return $translate; + }, + ), /** * shares DI 是共享的服务,每次获取的对象都是同一个,类似单例。 @@ -121,8 +150,18 @@ 'config' => function () { /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ $moduleManager = eva_get('moduleManager'); + $evaengine = eva_get('evaengine'); + + $config = $moduleManager->getAllConfig(); + $config->merge( + new \Phalcon\Config(include $evaengine->getProjectRoot() . '/config/config.default.php') + ); + + $config->merge( + new \Phalcon\Config(include $evaengine->getProjectRoot() . '/config/config.local.php') + ); - return $moduleManager->getAllConfig(); + return $config; }, /* |-------------------------------------------------------------------------- @@ -138,30 +177,12 @@ //Last extra slash $router->removeExtraSlashes(true); //Set last module as default module - /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ - $moduleManager = eva_get('moduleManager'); - $router->setDefaultModule($moduleManager->getDefaultModule()->getName()); + $router->setDefaultModule(eva_get('moduleManager')->getDefaultModule()->getName()); //NOTICE: Set a strange controller here to make router not match default index/index $router->setDefaultController('EvaEngineDefaultController'); - //NOTICE: EvaEngine Load front-end router at last - $routes = $moduleManager->getAllRoutesBackend(); - $routes->merge($moduleManager->getAllRoutesFrontend()); - - foreach ($routes->toArray() as $url => $route) { - if (count($route) !== count($route, COUNT_RECURSIVE)) { - if (isset($route['pattern']) && isset($route['paths'])) { - $method = isset($route['httpMethods']) ? $route['httpMethods'] : null; - $router->add($route['pattern'], $route['paths'], $method); - } else { - throw new RuntimeException( - sprintf('No route pattern and paths found by route %s', $url) - ); - } - } else { - $router->add($url, $route); - } - } + return $router; + }, /* |-------------------------------------------------------------------------- @@ -208,12 +229,12 @@ function () { $sessionConfig->adapter, $sessionConfig->options->toArray(), $sessionConfig->session_name, - $sessionConfig->cookie_params + $sessionConfig->cookie_params->toArray() ); }, - 'tokenStorage' => function () use ($self) { - return $self->diTokenStorage(); - }, +// 'tokenStorage' => function () use ($self) { +// return $self->diTokenStorage(); +// }, 'dbMaster' => function () { $config = eva_get('config'); if (!isset($config->dbAdapter->master->adapter) || !$config->dbAdapter->master) { @@ -222,7 +243,7 @@ function () { return (new \Eva\EvaEngine\Db\DbAdapterCreator())->create( $config->dbAdapter->master->adapter, - $config->dbAdapter->master->options->toArray(), + $config->dbAdapter->master->toArray(), eva_get('eventsManager') ); }, @@ -237,7 +258,7 @@ function () { return (new \Eva\EvaEngine\Db\DbAdapterCreator())->create( $config->dbAdapter->slave->$slaveKey->adapter, - $config->dbAdapter->slave->$slaveKey->options->toArray(), + $config->dbAdapter->slave->$slaveKey->toArray(), eva_get('eventsManager') ); }, @@ -266,12 +287,12 @@ function () { return $worker; }, - 'mailer' => function () use ($self) { - return $self->diMailer(); - }, - 'smsSender' => function () use ($self) { - return $self->diSmsSender(); - }, +// 'mailer' => function () { +// return $self->diMailer(); +// }, +// 'smsSender' => function () { +// return $self->diSmsSender(); +// }, 'url' => function () { $config = eva_get('config'); $url = new Eva\EvaEngine\Mvc\Url(); @@ -282,41 +303,12 @@ function () { return $url; }, 'escaper' => 'Phalcon\Escaper', - 'tag' => - function () { -// \Eva\EvaEngine\Tag::setDi($di); -// $self->registerViewHelpers(); -// -// return new Tag(); - }, - 'flash' => 'Phalcon\Flash\Session', - 'cookies' => function () { - $cookies = new \Phalcon\Http\Response\Cookies(); - $cookies->useEncryption(false); + 'tag' => function () { + \Eva\EvaEngine\Tag::setDi(\Phalcon\DI::getDefault()); + \Eva\EvaEngine\Tag::registerHelpers(eva_get('moduleManager')->getAllViewHelpers()); - return $cookies; + return new \Eva\EvaEngine\Tag(); }, - 'translate' => - function () { - $config = eva_get('config'); - $file = $config->translate->path . $config->translate->forceLang . '.csv'; - if (false === file_exists($file)) { - //empty translator - return new \Phalcon\Translate\Adapter\NativeArray( - array( - 'content' => array() - ) - ); - } - $translate = new \Phalcon\Translate\Adapter\Csv( - array( - 'file' => $file, - 'delimiter' => ',', - ) - ); - - return $translate; - }, 'fileSystem' => function () { // return $di->diFileSystem(); diff --git a/config/routes.cli.php b/config/routes.console.php similarity index 83% rename from config/routes.cli.php rename to config/routes.console.php index db91277..0265b4c 100644 --- a/config/routes.cli.php +++ b/config/routes.console.php @@ -8,4 +8,6 @@ // +---------------------------------------------------------------------- // + routes.cli.php // +---------------------------------------------------------------------- -return array(); \ No newline at end of file +return array( + 'debug:routes' => \Eva\EvaEngine\Commands\Debug\Routes\ListCommand::class, +); \ No newline at end of file diff --git a/console.php b/console.php new file mode 100644 index 0000000..2533f46 --- /dev/null +++ b/console.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/19 下午4:28 +// +---------------------------------------------------------------------- +// + console.php +// +---------------------------------------------------------------------- +foreach (array( + __DIR__ . '/../../autoload.php', + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/vendor/autoload.php' + ) as $file) { + if (file_exists($file)) { + require $file; + break; + } +} + +use Eva\EvaEngine\Engine; + +$app = new \Eva\EvaEngine\Console\ConsoleApplication(empty($_SERVER['APPLICATION_NAME']) ? 'evaengine' : $_SERVER['APPLICATION_NAME']); +$engine = new Engine(__DIR__ . '/..', $app); + +$engine + ->loadModules(include __DIR__ . '/../config/modules.' . $app->getName() . '.php') + ->bootstrap() + ->run(); diff --git a/examples/console.php b/examples/console.php new file mode 100644 index 0000000..bb69bea --- /dev/null +++ b/examples/console.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/19 下午4:28 +// +---------------------------------------------------------------------- +// + console.php +// +---------------------------------------------------------------------- +$projectRoot = __DIR__ . '/../../'; +require $projectRoot . 'vendor/autoload.php'; + +use Eva\EvaEngine\Engine; + +$app = new \Eva\EvaEngine\Console\ConsoleApplication('wallstreetcn.com console', '0.1'); +$engine = new Engine($projectRoot, $app); + +$engine + ->loadModules(include $engine->getProjectRoot() . '/config/modules.wscn.php') + ->bootstrap() + ->run(); diff --git a/src/EvaEngine/Application.php b/src/EvaEngine/Application.php deleted file mode 100644 index 85d75ec..0000000 --- a/src/EvaEngine/Application.php +++ /dev/null @@ -1,133 +0,0 @@ - -// +---------------------------------------------------------------------- -// + Datetime: 15/6/12 下午4:44 -// +---------------------------------------------------------------------- -// + Application.php -// +---------------------------------------------------------------------- - -namespace Eva\EvaEngine; - - -use Eva\EvaEngine\Exception\RuntimeException; -use Eva\EvaEngine\Module\Manager; -use Phalcon\Cache\Backend\File; -use Phalcon\Cache\Frontend\Data; -use Phalcon\DI\FactoryDefault; - -class Application -{ - protected $project_root = __DIR__; - protected $mode = 'web'; - protected $di; - protected static $instance = null; - protected $appName; - /** - * @var \Phalcon\Mvc\Application - */ - protected $application; - - protected function __construct($project_root = __DIR__, $mode = 'web', $appName = '', $systemCache = null) - { - $this->project_root = $project_root; - $this->appName = empty($_SERVER['APPLICATION_NAME']) ? $appName : $_SERVER['APPLICATION_NAME']; - $this->mode = $mode; - $this->di = new FactoryDefault(); - $this->di->setShared('evaengine', $this); - if ($systemCache == null) { - $systemCache = function () { - return new File(new Data(), array( - 'cacheDir' => $this->getProjectRoot() . '/cache/system/', - 'prefix' => $this->getAppName() . '/' - )); - }; - } - $this->di->setShared('systemCache', $systemCache); - } - - public function loadModules(array $modulesConfig) - { - $moduleManager = new Manager($this->getProjectRoot() . '/modules', $modulesConfig); - $moduleManager->registerModules($modulesConfig); - $this->di->set('moduleManager', $moduleManager); - $this->application->registerModules($moduleManager->getModulesForPhalcon()); - } - - public function initialize() - { - // 注册 DI - $config = eva_get('config'); - foreach ($config->di as $name => $diDefinition) { - $this->di->set($name, $diDefinition); - } - $this->registerServices(); - $this->initializeEvents(); - $this->application->setDI($this->di); - $this->application->setEventsManager(eva_get('eventsManager')); - } - - protected function registerServices() - { - /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ - $moduleManager = eva_get('moduleManager'); - foreach ($moduleManager->getAllDIDefinition() as $name => $definition) { - $this->di->set($name, $definition['definition'], $definition['share']); - } - } - - protected function initializeEvents() - { - $eventsManager = eva_get('eventsManager'); - /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ - $moduleManager = eva_get('moduleManager'); - foreach ($moduleManager->getAllListeners() as $moduleListeners) { - foreach ($moduleListeners as $eventType => $listener) { - $priority = 0; - if (is_string($listener)) { - $listenerClass = $listener; - } elseif (true === is_array($listener) && count($listener) > 1) { - $listenerClass = $listener[0]; - $priority = (int)$listener[1]; - } else { - throw new \Exception(sprintf( - "Module listener format not correct: %s", - var_export($listener, true) - )); - } - if (false === class_exists($listenerClass)) { - throw new \Exception(sprintf("Module listener %s not exist", $listenerClass)); - } - $eventsManager->attach($eventType, new $listenerClass, $priority); - } - } - } - - public function getProjectRoot() - { - return $this->project_root; - } - - public function getAppName() - { - return $this->appName; - } - - public static function getInstance($app_root = __DIR__, $mode = 'web') - { - if (static::$instance == null) { - static::$instance = new static($app_root, $mode); - } - - return static::$instance; - } - - - public function getDI() - { - return $this->di; - } - -} \ No newline at end of file diff --git a/src/EvaEngine/CLI.php b/src/EvaEngine/CLI.php new file mode 100644 index 0000000..e881ed4 --- /dev/null +++ b/src/EvaEngine/CLI.php @@ -0,0 +1,64 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/17 下午6:51 +// +---------------------------------------------------------------------- +// + CLI.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine; + + +use Symfony\Component\Console\Application; + +class CLI extends Application implements \Phalcon\DI\InjectionAwareInterface, \Phalcon\Events\EventsAwareInterface +{ + protected $_dependencyInjector; + + protected $_eventsManager; + + protected $_modules; + + /** + * Sets the events manager + * + * @param \Phalcon\Events\ManagerInterface $eventsManager + */ + public function setEventsManager($eventsManager) + { + $this->_eventsManager = $eventsManager; + } + + /** + * Returns the internal event manager + * + * @return \Phalcon\Events\ManagerInterface + */ + public function getEventsManager() + { + return $this->_eventsManager; + } + + /** + * Sets the dependency injector + * + * @param \Phalcon\DiInterface $dependencyInjector + */ + public function setDI($dependencyInjector) + { + $this->_dependencyInjector = $dependencyInjector; + } + + /** + * Returns the internal dependency injector + * + * @return \Phalcon\DiInterface + */ + public function getDI() + { + return $this->_dependencyInjector; + } +} \ No newline at end of file diff --git a/src/EvaEngine/Cache/CacheAdapterCreator.php b/src/EvaEngine/Cache/CacheAdapterCreator.php index bf8aa9f..0d7a14c 100644 --- a/src/EvaEngine/Cache/CacheAdapterCreator.php +++ b/src/EvaEngine/Cache/CacheAdapterCreator.php @@ -18,7 +18,7 @@ class CacheAdapterCreator extends AdapterCreator /** * {@inheritdoc} */ - protected function getAdaptersMapping($category = 'default') + protected function getAdaptersMapping() { return array( 'frontend' => array( @@ -56,7 +56,9 @@ protected function getAdaptersMapping($category = 'default') */ public function create($frontendAdapter, array $frontendOptions, $backendAdapter, array $backendOptions) { + $frontendAdapter = strtolower($frontendAdapter); $frontendCacheClass = $this->getAdapterClass($frontendAdapter, 'frontend'); + $backendAdapter = strtolower($backendAdapter); $backendCacheClass = $this->getAdapterClass($backendAdapter, 'backend'); diff --git a/src/EvaEngine/Commands/Debug/Routes/ListCommand.php b/src/EvaEngine/Commands/Debug/Routes/ListCommand.php new file mode 100644 index 0000000..2a337fa --- /dev/null +++ b/src/EvaEngine/Commands/Debug/Routes/ListCommand.php @@ -0,0 +1,87 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/19 下午4:47 +// +---------------------------------------------------------------------- +// + DebugListRoutesCommand.php +// +---------------------------------------------------------------------- +namespace Eva\EvaEngine\Commands\Debug\Routes; + +use Eva\EvaEngine\Console\CommandBase; +use Eva\EvaEngine\Mvc\WebApplication; +use Symfony\Component\Console\Input\InputOption; + +class ListCommand extends CommandBase +{ + public function configure() + { + $this->setDescription('列出所有路由'); + $this->addOption('sort-by', null, InputOption::VALUE_OPTIONAL, '排序依据,支持 pattern 和 definition', 'pattern'); + } + + public function fire() + { + $moduleManager = eva_get('moduleManager'); + $router = eva_get('router'); + WebApplication::initializeRouter($moduleManager, $router); +// $routes = $moduleManager->getAllWebRoutes(); + +// dd($routes); + $methodStyles = array( + 'PUT' => '%s', + 'POST' => '%s', + 'DELETE' => '%s' + ); + $header = array( + 'method', + 'pattern', + 'handler', + '_dispatch_cache', + 'paths' + ); + $rows = array(); + foreach ($router->getRoutes() as $route) { + $style = !empty($methodStyles[$route->getHttpMethods()]) + ? $methodStyles[$route->getHttpMethods()] + : '%s'; +// $this->output->writeln(); +// $this->error($route->getCompiledPattern()); +// $this->info($route->getName()); + $paths = $route->getPaths(); + $module = isset($paths['module']) ? $paths['module'] : $moduleManager->getDefaultModule()->getName(); + $controller = isset($paths['controller']) ? $paths['controller'] : '*'; + $action = isset($paths['action']) ? $paths['action'] : '*'; + + $handler = $module . ':' . $controller . ':' . $action; + unset($paths['module']); + unset($paths['controller']); + unset($paths['action']); + $dispatch_cache = @$paths['_dispatch_cache']; + unset($paths['_dispatch_cache']); + + $rows[] = array( + sprintf($style, $route->getHttpMethods()), + $route->getPattern(), + $handler, + $dispatch_cache, + !empty($paths) ? var_export($paths, true) . PHP_EOL : '' + ); +// p($route->getPaths()); + } +// foreach ($rows as $key => $row) { +// $volume[$key] = 1; +// } +// array_multisort($volume, SORT_ASC, SORT_STRING, $rows); + + + if ($this->option('sort-by') == 'pattern') { + usort($rows, function ($a, $b) { + return $a[1] > $b[1]; + }); + } + $this->table($header, $rows); + } +} \ No newline at end of file diff --git a/src/EvaEngine/Console/CommandBase.php b/src/EvaEngine/Console/CommandBase.php new file mode 100644 index 0000000..8356e10 --- /dev/null +++ b/src/EvaEngine/Console/CommandBase.php @@ -0,0 +1,328 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/17 下午7:25 +// +---------------------------------------------------------------------- +// + CommandBase.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Console; + + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +abstract class CommandBase extends Command +{ + private $logger; + /** + * The input interface implementation. + * + * @var \Symfony\Component\Console\Input\InputInterface + */ + protected $input; + /** + * The output interface implementation. + * + * @var \Symfony\Component\Console\Style\SymfonyStyle + */ + protected $output; + + public function getLogger() + { + if ($this->logger == null) { + $this->logger = eva_get('consoleLogger'); + } + } + + public function setLogger($logger) + { + $this->logger = $logger; + } + + /** + * Run the console command. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return int + */ + public function run(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = new SymfonyStyle($input, $output); + + return parent::run($input, $output); + } + + /** + * Execute the console command. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return mixed + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + return $this->fire(); + } + + abstract public function fire(); + + /** + * Call another console command. + * + * @param string $command + * @param array $arguments + * @return int + */ +// public function call($command, array $arguments = []) +// { +// $instance = $this->getApplication()->find($command); +// $arguments['command'] = $command; +// +// return $instance->run(new ArrayInput($arguments), $this->output); +// } + + /** + * Call another console command silently. + * + * @param string $command + * @param array $arguments + * @return int + */ + public function callSilent($command, array $arguments = []) + { + $instance = $this->getApplication()->find($command); + $arguments['command'] = $command; + + return $instance->run(new ArrayInput($arguments), new NullOutput); + } + + /** + * Get the value of a command argument. + * + * @param string $key + * @return string|array + */ + public function argument($key = null) + { + if (is_null($key)) { + return $this->input->getArguments(); + } + + return $this->input->getArgument($key); + } + + /** + * Get the value of a command option. + * + * @param string $key + * @return string|array + */ + public function option($key = null) + { + if (is_null($key)) { + return $this->input->getOptions(); + } + + return $this->input->getOption($key); + } + + /** + * Confirm a question with the user. + * + * @param string $question + * @param bool $default + * @return bool + */ + public function confirm($question, $default = false) + { + return $this->output->confirm($question, $default); + } + + /** + * Prompt the user for input. + * + * @param string $question + * @param string $default + * @return string + */ + public function ask($question, $default = null) + { + return $this->output->ask($question, $default); + } + + /** + * Prompt the user for input with auto completion. + * + * @param string $question + * @param array $choices + * @param string $default + * @return string + */ + public function anticipate($question, array $choices, $default = null) + { + return $this->askWithCompletion($question, $choices, $default); + } + + /** + * Prompt the user for input with auto completion. + * + * @param string $question + * @param array $choices + * @param string $default + * @return string + */ + public function askWithCompletion($question, array $choices, $default = null) + { + $question = new Question($question, $default); + $question->setAutocompleterValues($choices); + + return $this->output->askQuestion($question); + } + + /** + * Prompt the user for input but hide the answer from the console. + * + * @param string $question + * @param bool $fallback + * @return string + */ + public function secret($question, $fallback = true) + { + $question = new Question($question); + $question->setHidden(true)->setHiddenFallback($fallback); + + return $this->output->askQuestion($question); + } + + /** + * Give the user a single choice from an array of answers. + * + * @param string $question + * @param array $choices + * @param string $default + * @param mixed $attempts + * @param bool $multiple + * @return bool + */ + public function choice($question, array $choices, $default = null, $attempts = null, $multiple = null) + { + $question = new ChoiceQuestion($question, $choices, $default); + $question->setMaxAttempts($attempts)->setMultiselect($multiple); + + return $this->output->askQuestion($question); + } + + /** + * Format input to textual table. + * + * @param array $headers + * @param array $rows + * @param string $style + * @return void + */ + public function table(array $headers, array $rows, $style = 'default') + { + $table = new Table($this->output); + + $table->setHeaders($headers)->setRows($rows)->setStyle($style)->render(); + } + + /** + * Write a string as information output. + * + * @param string $string + * @return void + */ + public function info($string) + { + $this->output->writeln("$string"); + } + + /** + * Write a string as standard output. + * + * @param string $string + * @return void + */ + public function line($string) + { + $this->output->writeln($string); + } + + /** + * Write a string as comment output. + * + * @param string $string + * @return void + */ + public function comment($string) + { + $this->output->writeln("$string"); + } + + /** + * Write a string as question output. + * + * @param string $string + * @return void + */ + public function question($string) + { + $this->output->writeln("$string"); + } + + /** + * Write a string as error output. + * + * @param string $string + * @return void + */ + public function error($string) + { + $this->output->writeln("$string"); + } + + /** + * Get the console command arguments. + * + * @return array + */ + protected function getArguments() + { + return []; + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return []; + } + + /** + * Get the output implementation. + * + * @return \Symfony\Component\Console\Output\OutputInterface + */ + public function getOutput() + { + return $this->output; + } + +} \ No newline at end of file diff --git a/src/EvaEngine/Console/ConsoleApplication.php b/src/EvaEngine/Console/ConsoleApplication.php new file mode 100644 index 0000000..e811a65 --- /dev/null +++ b/src/EvaEngine/Console/ConsoleApplication.php @@ -0,0 +1,99 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/17 下午6:56 +// +---------------------------------------------------------------------- +// + CLIApplication.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Console; + + +use Eva\EvaEngine\Foundation\ApplicationInterface; +use Imagine\Exception\RuntimeException; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\Input; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; + +class ConsoleApplication extends Application implements ApplicationInterface +{ + protected $output; + protected $input; + protected $di; + protected $debug; + + public function __construct( + $name = 'EvaEngine', + $debug = true + ) { + parent::__construct($name); + $this->input = new ArgvInput(); + $this->output = new ConsoleOutput(ConsoleOutput::VERBOSITY_VERY_VERBOSE); + } + + public function initializeErrorHandler() + { + set_error_handler(function () { +// dd(func_get_args()); + }); +// new Formatter + set_exception_handler(function (\Exception $e) { + $this->renderException($e, $this->output); + }); + register_shutdown_function(function () { +// dd(func_get_args()); + }); + + return $this; + } + + public function initialize() + { + $moduleManager = eva_get('moduleManager'); + + foreach ($moduleManager->getAllRoutesConsole()->toArray() as $commandName => $commandClass) { + if (!class_exists($commandClass)) { + + throw new RuntimeException(sprintf('Class of Command "%s" not found, class: %s', $commandName, + $commandClass)); + } + /** @var \Symfony\Component\Console\Command\Command $command */ + $command = new $commandClass($commandName); + + $this->add($command); + } + + return $this; + } + + public function fire() + { + $this->run($this->input, $this->output); + } + + /** + * Sets the dependency injector + * + * @param \Phalcon\DiInterface $dependencyInjector + */ + public function setDI($dependencyInjector) + { + $this->di = $dependencyInjector; + } + + /** + * Returns the internal dependency injector + * + * @return \Phalcon\DiInterface + */ + public function getDI() + { + return $this->di; + } +} \ No newline at end of file diff --git a/src/EvaEngine/Db/DbAdapterCreator.php b/src/EvaEngine/Db/DbAdapterCreator.php index 8b65e59..62494b1 100644 --- a/src/EvaEngine/Db/DbAdapterCreator.php +++ b/src/EvaEngine/Db/DbAdapterCreator.php @@ -15,15 +15,11 @@ class DbAdapterCreator extends AdapterCreator { - protected static $adapterMapping = array( - 'mysql' => 'Phalcon\Db\Adapter\Pdo\Mysql', - 'oracle' => 'Phalcon\Db\Adapter\Pdo\Oracle', - 'postgresql' => 'Phalcon\Db\Adapter\Pdo\Postgresql', - 'sqlite' => 'Phalcon\Db\Adapter\Pdo\Sqlite', - ); + public function create($adapter, array $options, $eventsManager = null) { + $adapterClass = $this->getAdapterClass($adapter); $options['charset'] = isset($options['charset']) && $options['charset'] ? $options['charset'] : 'utf8'; /** @var \Phalcon\Db\Adapter\Pdo $dbAdapter */ diff --git a/src/EvaEngine/Engine.bak..php b/src/EvaEngine/Engine.bak..php new file mode 100644 index 0000000..a5e5e5f --- /dev/null +++ b/src/EvaEngine/Engine.bak..php @@ -0,0 +1,1398 @@ + + * $engine = new Engine(__DIR__ . '/..'); + * $engine->loadModules(include __DIR__ . '/../config/modules.php') + * ->bootstrap() + * ->run(); + * + * + * Class Engine + * + * @package Eva\EvaEngine + */ +class EngineBak +{ + /** + * @var float + */ + public static $appStartTime; + + /** + * @var null|string + */ + protected $appRoot; + + /** + * @var string + */ + protected $appName; //for cache prefix + + /** + * @var string + */ + protected $modulesPath; + + /** + * @var DiInterface + */ + protected $di; + + /** + * @var Application + */ + protected $application; + + /** + * @var string + */ + protected $configPath; + + /** + * @var bool + */ + protected $cacheEnable = false; + + /** + * @var string + */ + protected $environment; //development | test | production + + /** + * @var Debug + */ + protected $debugger; + + /** + * @var string + */ + protected $appMode = 'web'; + + /** + * @return string + */ + public function getEnvironment() + { + return $this->environment; + } + + /** + * @param $environment + * @return $this + */ + public function setEnvironment($environment) + { + $this->environment = $environment; + + return $this; + } + + /** + * @param $appRoot + * @return $this + */ + public function setAppRoot($appRoot) + { + $this->appRoot = $appRoot; + + return $this; + } + + /** + * @return null|string + */ + public function getAppRoot() + { + return $this->appRoot; + } + + /** + * @param $name + * @return $this + */ + public function setAppName($name) + { + $this->appName = $name; + + return $this; + } + + /** + * @return string + */ + public function getAppName() + { + return $this->appName; + } + + /** + * @param $path + * @return $this + */ + public function setConfigPath($path) + { + $this->configPath = $path; + + return $this; + } + + /** + * @return string + */ + public function getConfigPath() + { + if ($this->configPath) { + return $this->configPath; + } + + return $this->configPath = $this->appRoot . '/config'; + } + + + /** + * @param $modulesPath + * @return $this + */ + public function setModulesPath($modulesPath) + { + $this->modulesPath = $modulesPath; + + return $this; + } + + /** + * @return string + */ + public function getModulesPath() + { + if ($this->modulesPath) { + return $this->modulesPath; + } + + return $this->modulesPath = $this->appRoot . '/modules'; + } + + + /** + * + * @param $cacheFile cache file path + * @param bool $serialize + * @return mixed|null + */ + public function readCache($cacheFile, $serialize = false) + { + if (file_exists($cacheFile) && $cache = include $cacheFile) { + return true === $serialize ? unserialize($cache) : $cache; + } + + return null; + } + + /** + * @param $cacheFile + * @param $content + * @param bool $serialize + * @return bool + */ + public function writeCache($cacheFile, $content, $serialize = false) + { + if ($cacheFile && $fh = fopen($cacheFile, 'w')) { + if (true === $serialize) { + fwrite($fh, "debugger) { + return $this->debugger; + } + + $debugger = new Debug(); + $debugger->setShowFileFragment(true); + $debugger->listen(true, true); + + return $this->debugger = $debugger; + } + + /** + * @return string + */ + public function getAppMode() + { + return $this->appMode; + } + + /** + * @return Console|Application + */ + public function getApplication() + { + if (!$this->application) { + if ($this->appMode == 'cli') { + $this->application = new Console(); + } else { + $this->application = new Application(); + } + } + + return $this->application; + } + + + /** + * Load modules from input settings, and call phalcon application->registerModules() for register + * + * below events will be trigger + * - module:beforeLoadModule + * - module:afterLoadModule + * + * @param array $moduleSettings + * @return $this + */ + public function loadModules(array $moduleSettings) + { + $moduleManager = $this->getDI()->getModuleManager(); + + if ($this->getEnvironment() == 'production') { + $cachePrefix = $this->getAppName(); + $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.modules.php"; + $moduleManager->setCacheFile($cacheFile); + } + + + $moduleManager + ->setDefaultPath($this->getModulesPath()) + ->loadModules($moduleSettings, $this->getAppName()); + + if ($this->getDI()->getConfig()->debug) { + $cachePrefix = $this->getAppName(); + $this->writeCache( + $this->getConfigPath() . "/_debug.$cachePrefix.modules.php", + $moduleManager->getModules() + ); + } + + $this->getApplication()->registerModules($moduleManager->getModules()); + //Overwirte default modulemanager + $this->getDI()->set('moduleManager', $moduleManager); + + return $this; + } + + /** + * @return $this + */ + public function attachModuleEvents() + { + $di = $this->getDI(); + $cachePrefix = $this->getAppName(); + $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.events.php"; + $listeners = $this->readCache($cacheFile); + $cacheLoaded = false; + + if (!$listeners) { + $moduleManager = $this->getDI()->getModuleManager(); + $modules = $moduleManager->getModules(); + $listeners = array(); + foreach ($modules as $moduleName => $module) { + $moduleListeners = $moduleManager->getModuleListeners($moduleName); + if ($moduleListeners) { + $listeners[$moduleName] = $moduleListeners; + } + } + } else { + $cacheLoaded = true; + } + + if (!is_array($listeners)) { + return $this; + } + + $eventsManager = $this->getDI()->getEventsManager(); + foreach ($listeners as $moduleName => $moduleListeners) { + if (!$moduleListeners) { + continue; + } + foreach ($moduleListeners as $eventType => $listener) { + $eventsManager->attach($eventType, new $listener); + } + } + + if ($di->getConfig()->debug) { + $debugger = $this->getDebugger(); + $debugger->debugVar($listeners, 'events'); + } + + if (!$di->getConfig()->debug && false === $cacheLoaded && $listeners) { + $this->writeCache($cacheFile, $listeners); + } + + return $this; + } + + + /** + * @return $this + */ + public function registerViewHelpers() + { + $di = $this->getDI(); + $cachePrefix = $this->getAppName(); + $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.helpers.php"; + $helpers = $this->readCache($cacheFile); + if ($helpers) { + Tag::registerHelpers($helpers); + + return $this; + } + + $helpers = array(); + $moduleManager = $di->getModuleManager(); + $modules = $moduleManager->getModules(); + foreach ($modules as $moduleName => $module) { + $moduleHelpers = $moduleManager->getModuleViewHelpers($moduleName); + if (is_array($moduleHelpers)) { + $helpers += $moduleHelpers; + } + } + Tag::registerHelpers($helpers); + + if (!$di->getConfig()->debug && $helpers) { + $this->writeCache($cacheFile, $helpers); + } + + return $this; + } + + + /** + * @param DiInterface $di + * @return $this + */ + public function setDI(DiInterface $di) + { + $this->di = $di; + + return $this; + } + + /** + * Configuration application default DI + * + * @return FactoryDefault| CLI + */ + public function getDI() + { + if ($this->di) { + return $this->di; + } + if ($this->appMode == 'cli') { + $di = new FactoryDefault\CLI(); + } else { + $di = new FactoryDefault(); + } + + //PHP5.3 not support $this in closure + $self = $this; + + /********************************** + * DI initialize for MVC core + ***********************************/ + //$di->set('application', $this); + + //call loadmodules will overwrite this + $di->set( + 'moduleManager', + function () use ($di) { + $moduleManager = new ModuleManager(); + $moduleManager->setEventsManager($di->getEventsManager()); + + return $moduleManager; + }, + true + ); + + //System global events manager + $di->set( + 'eventsManager', + function () use ($di) { + $eventsManager = new EventsManager(); + $eventsManager->enablePriorities(true); + // dispatch caching event handler + $eventsManager->attach( + "dispatch", + new DispatchInterceptor(), + -1 + ); + $eventsManager->enablePriorities(true); + + return $eventsManager; + }, + true + ); + + $di->set( + 'config', + function () use ($self) { + return $self->diConfig(); + }, + true + ); + + $di->set( + 'router', + function () use ($self) { + return $self->diRouter(); + }, + true + ); + + $di->set( + 'dispatcher', + function () use ($di) { + $dispatcher = new Dispatcher(); + $dispatcher->setEventsManager($di->getEventsManager()); + + return $dispatcher; + }, + true + ); + + $di->set( + 'modelsMetadata', + function () use ($self) { + return $self->diModelsMetadata(); + }, + true + ); + + $di->set( + 'modelsManager', + function () use ($di) { + $config = $di->getConfig(); + ModelManager::setDefaultPrefix($config->dbAdapter->prefix); + //for solving db master/slave under static find method + $modelsManager = new ModelManager(); + + return $modelsManager; + } + ); + + $di->set( + 'view', + function () use ($di) { + $view = new View(); + $view->setViewsDir(__DIR__ . '/views/'); + $view->setEventsManager($di->getEventsManager()); + + return $view; + } + ); + + $di->set( + 'session', + function () use ($self) { + return $self->diSession(); + } + ); + + $di->set( + 'tokenStorage', + function () use ($self) { + return $self->diTokenStorage(); + }, + true //token Storage MUST set as shared + ); + + /********************************** + * DI initialize for database + ***********************************/ + $di->set( + 'dbMaster', + function () use ($self) { + return $self->diDbMaster(); + }, + true + ); + + $di->set( + 'dbSlave', + function () use ($self) { + return $self->diDbSlave(); + }, + true + ); + + $di->set( + 'transactions', + function () use ($di) { + $transactions = new \Phalcon\Mvc\Model\Transaction\Manager(); + $transactions->setDbService('dbMaster'); + + return $transactions; + }, + true + ); + + /********************************** + * DI initialize for cache + ***********************************/ + $di->set( + 'globalCache', + function () use ($self) { + return $self->diGlobalCache(); + }, + true + ); + + $di->set( + 'viewCache', + function () use ($self) { + return $self->diViewCache(); + }, + true + ); + + $di->set( + 'modelsCache', + function () use ($self) { + return $self->diModelsCache(); + }, + true + ); + + $di->set( + 'apiCache', + function () use ($self) { + return $self->diApiCache(); + }, + true + ); + + $di->set( + 'fastCache', + function () use ($self) { + return $self->diFastCache(); + }, + true + ); + + /********************************** + * DI initialize for queue + ***********************************/ + $di->set( + 'queue', + function () use ($di) { + $config = $di->getConfig(); + $client = new \GearmanClient(); + $client->setTimeout(1000); + foreach ($config->queue->servers as $key => $server) { + $client->addServer($server->host, $server->port); + } + + return $client; + }, + true + ); + + $di->set( + 'worker', + function () use ($di) { + $config = $di->getConfig(); + $worker = new \GearmanWorker(); + foreach ($config->queue->servers as $key => $server) { + $worker->addServer($server->host, $server->port); + } + + return $worker; + }, + true + ); + + + /********************************** + * DI initialize for email + ***********************************/ + $di->set( + 'mailer', + function () use ($self) { + return $self->diMailer(); + }, + true + ); + + $di->set('mailMessage', 'Eva\EvaEngine\MailMessage'); + + $di->set( + 'smsSender', + function () use ($self) { + return $self->diSmsSender(); + }, + true + ); + /********************************** + * DI initialize for helpers + ***********************************/ + $di->set( + 'url', + function () use ($di) { + $config = $di->getConfig(); + $url = new UrlResolver(); + $url->setVersionFile($config->staticBaseUriVersionFile); + $url->setBaseUri($config->baseUri); + $url->setStaticBaseUri($config->staticBaseUri); + + return $url; + }, + true + ); + + $di->set('escaper', 'Phalcon\Escaper'); + + $di->set( + 'tag', + function () use ($di, $self) { + Tag::setDi($di); +// $self->registerViewHelpers(); + dd(eva_get('moduleManager')->getAllViewHelpers()); + return new Tag(); + } + ); + + $di->set('flash', 'Phalcon\Flash\Session'); + + $di->set('placeholder', 'Eva\EvaEngine\View\Helper\Placeholder'); + + $di->set( + 'cookies', + function () { + $cookies = new \Phalcon\Http\Response\Cookies(); + $cookies->useEncryption(false); + + return $cookies; + } + ); + + $di->set( + 'translate', + function () use ($self) { + return $self->diTranslate(); + } + ); + + $di->set( + 'fileSystem', + function () use ($self) { + return $self->diFileSystem(); + } + ); + + $di->set( + 'logException', + function () use ($di) { + $config = $di->getConfig(); + + return new FileLogger($config->logger->path . 'error.log'); + } + ); + if ($this->appMode == 'cli') { + $this->cliDI($di); + } + + IoC::setDI($di); + + + return $this->di = $di; + } + + /** + * CLI 模式下的 DI 配置 + * + * @param CLI $di + */ + protected function cliDI(CLI $di) + { + global $argv; + + $di->set( + 'router', + function () use ($di, $argv) { + $router = new CLIRouter(); + $router->setDI($di); + + return $router; + } + ); + + $di->set( + 'output', + function () { + return new ConsoleOutput(); + } + ); + $di->set( + 'dispatcher', + function () use ($di, $argv) { + $dispatcher = new CLIDispatcher(); + $dispatcher->setDI($di); + + $moduleName = array_shift($argv); + $taskName = array_shift($argv); + $actionName = 'main'; + if (strpos($taskName, ':') > 0) { + @list($taskName, $actionName) = preg_split("/:/", $taskName); + } + if ($moduleName) { + $dispatcher->setTaskName(ucwords($taskName)); + $dispatcher->setActionName($actionName); + $dispatcher->setParams($argv); + if ($moduleName == '_current') { + $_appName = ucwords($this->getAppName()); + $dispatcher->setNamespaceName("{$_appName}\\Tasks"); + } else { + $dispatcher->setNamespaceName("Eva\\{$moduleName}\\Tasks"); + } + } else { + $dispatcher->setTaskName('Main'); + $dispatcher->setParams($argv); + $dispatcher->setNamespaceName("Eva\\EvaEngine\\Tasks"); + } + + return $dispatcher; + } + ); + } + + public function diConfig() + { + $di = $this->getDI(); + $cachePrefix = $this->getAppName(); + $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.config.php"; + if ($cache = $this->readCache($cacheFile)) { + return new Config($cache); + } + + $config = new Config(include __DIR__ . '/../../config/config.php'); + + + //merge all loaded module configs + $moduleManager = $di->getModuleManager(); + if (!$moduleManager || !$modules = $moduleManager->getModules()) { + return $config; + throw new Exception\RuntimeException(sprintf('Config need at least one module loaded')); + } + + foreach ($modules as $moduleName => $module) { + $moduleConfig = $moduleManager->getModuleConfig($moduleName); + if ($moduleConfig instanceof Config) { + $config->merge($moduleConfig); + } else { + $config->merge(new Config($moduleConfig)); + } + } + //merge config default + $config->merge(new Config(include $this->getConfigPath() . "/config.default.php")); + + //merge config local + if (false === file_exists($this->getConfigPath() . "/config.local.php")) { + return $config; + } + $config->merge(new Config(include $this->getConfigPath() . "/config.local.php")); + + if (!$config->debug) { + $this->writeCache($cacheFile, $config->toArray()); + } + + return $config; + } + + public function diRouter() + { + $di = $this->getDI(); + $cachePrefix = $this->getAppName(); + $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.router.php"; + if ($router = $this->readCache($cacheFile, true)) { + return $router; + } + + $moduleManager = $di->getModuleManager(); + $config = new Config(); + $moduleName = ''; + if ($moduleManager && $modulesArray = $moduleManager->getModules()) { + foreach ($modulesArray as $moduleName => $module) { + //NOTICE: EvaEngine Load front-end router at last + $config->merge(new Config($moduleManager->getModuleRoutesFrontend($moduleName))); + $config->merge(new Config($moduleManager->getModuleRoutesBackend($moduleName))); + } + } + + //Disable default router + $router = new Router(false); + //Last extra slash + $router->removeExtraSlashes(true); + //Set last module as default module + $router->setDefaultModule($moduleName); + //NOTICE: Set a strange controller here to make router not match default index/index + $router->setDefaultController('EvaEngineDefaultController'); + + $config = $config->toArray(); + foreach ($config as $url => $route) { + if (count($route) !== count($route, COUNT_RECURSIVE)) { + if (isset($route['pattern']) && isset($route['paths'])) { + $method = isset($route['httpMethods']) ? $route['httpMethods'] : null; + $router->add($route['pattern'], $route['paths'], $method); + } else { + throw new Exception\RuntimeException(sprintf('No route pattern and paths found by route %s', $url)); + } + } else { + $router->add($url, $route); + } + } + + if (!$di->getConfig()->debug) { + $this->writeCache($cacheFile, $router, true); + } else { + //Dump merged routers for debug + $this->writeCache($this->getConfigPath() . "/_debug.$cachePrefix.router.php", $router, true); + } + + return $router; + } + + public function diModelsMetadata() + { + $adapterMapping = array( + 'apc' => 'Phalcon\Mvc\Model\MetaData\Apc', + 'files' => 'Phalcon\Mvc\Model\MetaData\Files', + 'memory' => 'Phalcon\Mvc\Model\MetaData\Memory', + 'xcache' => 'Phalcon\Mvc\Model\MetaData\Xcache', + 'memcache' => 'Phalcon\Mvc\Model\MetaData\Memcache', + 'redis' => 'Phalcon\Mvc\Model\MetaData\Redis', + 'wincache' => 'Phalcon\Mvc\Model\MetaData\Wincache', + ); + + $config = $this->getDI()->getConfig(); + if (!$config->modelsMetadata->enable) { + return new \Phalcon\Mvc\Model\MetaData\Memory(); + } + + $adapterKey = $config->modelsMetadata->adapter; + $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; + //Allow full class name as adapter name + $adapterClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; + if (!class_exists($adapterClass)) { + throw new Exception\RuntimeException(sprintf('No metadata adapter found by %s', $adapterClass)); + } + + return new $adapterClass($config->modelsMetadata->options->toArray()); + } + + public function diDbMaster() + { + $config = $this->getDI()->getConfig(); + if (!isset($config->dbAdapter->master->adapter) || !$config->dbAdapter->master) { + throw new Exception\RuntimeException(sprintf('No DB Master options found')); + } + + return $this->diDbAdapter( + $config->dbAdapter->master->adapter, + $config->dbAdapter->master->toArray(), + $this->getDI() + ); + } + + public function diDbSlave() + { + $config = $this->getDI()->getConfig(); + $slaves = $config->dbAdapter->slave; + $slaveKey = array_rand($slaves->toArray()); + if (!isset($slaves->$slaveKey) || count($slaves) < 1) { + throw new Exception\RuntimeException(sprintf('No DB slave options found')); + } + + return $this->diDbAdapter($slaves->$slaveKey->adapter, $slaves->$slaveKey->toArray(), $this->getDI()); + } + + + public static function diDbAdapter($adapterKey, array $options, $di) + { + $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; + $adapterMapping = array( + 'mysql' => 'Phalcon\Db\Adapter\Pdo\Mysql', + 'oracle' => 'Phalcon\Db\Adapter\Pdo\Oracle', + 'postgresql' => 'Phalcon\Db\Adapter\Pdo\Postgresql', + 'sqlite' => 'Phalcon\Db\Adapter\Pdo\Sqlite', + ); + + $adapterClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; + + if (false === class_exists($adapterClass)) { + throw new Exception\RuntimeException(sprintf('No matched DB adapter found by %s', $adapterClass)); + } + + $options['charset'] = isset($options['charset']) && $options['charset'] ? $options['charset'] : 'utf8'; + $dbAdapter = new $adapterClass($options); + + + $config = $di->getConfig(); + + if ($config->debug) { + $eventsManager = $di->getEventsManager(); + $logger = new FileLogger($config->logger->path . 'db_query.log'); + + //database service name hardcore as db + $eventsManager->attach( + 'db', + function ($event, $dbAdapter) use ($logger) { + if ($event->getType() == 'beforeQuery') { + $sqlVariables = $dbAdapter->getSQLVariables(); + if (count($sqlVariables)) { + $query = str_replace(array('%', '?'), array('%%', "'%s'"), $dbAdapter->getSQLStatement()); + $query = vsprintf($query, $sqlVariables); + // + $logger->log($query, \Phalcon\Logger::INFO); + } else { + $logger->log($dbAdapter->getSQLStatement(), \Phalcon\Logger::INFO); + } + } + } + ); + $dbAdapter->setEventsManager($eventsManager); + } + + return $dbAdapter; + } + + public function diGlobalCache() + { + return $this->diCache('globalCache', $this->getAppName() . '_global_'); + } + + public function diViewCache() + { + return $this->diCache('viewCache', $this->getAppName() . '_view_'); + } + + public function diModelsCache() + { + return $this->diCache('modelsCache', $this->getAppName() . '_models_'); + } + + public function diApiCache() + { + return $this->diCache('apiCache', $this->getAppName() . '_api_'); + } + + public function diFastCache() + { + $config = $this->getDI()->getConfig(); + if (!($config->cache->fastCache->enable)) { + return false; + } + + $redis = new \Redis(); + $redis->connect( + $config->cache->fastCache->host, + $config->cache->fastCache->port, + $config->cache->fastCache->timeout + ); + + return $redis; + } + + protected function diCache($configKey, $prefix = 'eva_') + { + $config = $this->getDI()->getConfig(); + $adapterMapping = array( + 'apc' => 'Phalcon\Cache\Backend\Apc', + 'file' => 'Phalcon\Cache\Backend\File', + 'libmemcached' => 'Phalcon\Cache\Backend\Libmemcached', + 'memcache' => 'Phalcon\Cache\Backend\Memcache', + 'memory' => 'Phalcon\Cache\Backend\Memory', + 'mongo' => 'Phalcon\Cache\Backend\Mongo', + 'xcache' => 'Phalcon\Cache\Backend\Xcache', + 'redis' => 'Phalcon\Cache\Backend\Redis', + 'wincache' => 'Phalcon\Cache\Backend\Wincache', + 'base64' => 'Phalcon\Cache\Frontend\Base64', + 'data' => 'Phalcon\Cache\Frontend\Data', + 'igbinary' => 'Phalcon\Cache\Frontend\Igbinary', + 'json' => 'Phalcon\Cache\Frontend\Json', + 'none' => 'Phalcon\Cache\Frontend\None', + 'output' => 'Phalcon\Cache\Frontend\Output', + ); + + $frontCacheClassName = $config->cache->$configKey->frontend->adapter; + $frontCacheClassName = false === strpos( + $frontCacheClassName, + '\\' + ) ? strtolower($frontCacheClassName) : $frontCacheClassName; + $frontCacheClass = + empty($adapterMapping[$frontCacheClassName]) + ? $frontCacheClassName + : $adapterMapping[$frontCacheClassName]; + if (false === class_exists($frontCacheClass)) { + throw new Exception\RuntimeException(sprintf('No cache adapter found by %s', $frontCacheClass)); + } + $frontCache = new $frontCacheClass( + $config->cache->$configKey->frontend->options->toArray() + ); + + if (!$config->cache->enable || !$config->cache->$configKey->enable) { + $cache = new \Eva\EvaEngine\Cache\Backend\Disable($frontCache); + } else { + $backendCacheClassName = $config->cache->$configKey->backend->adapter; + $backendCacheClassName = false === strpos( + $backendCacheClassName, + '\\' + ) ? strtolower($backendCacheClassName) : $backendCacheClassName; + $backendCacheClass = + !empty($adapterMapping[$backendCacheClassName]) + ? $adapterMapping[$backendCacheClassName] + : $backendCacheClassName; + + if (!class_exists($backendCacheClass)) { + throw new Exception\RuntimeException(sprintf('No cache adapter found by %s', $backendCacheClassName)); + } + $cache = new $backendCacheClass($frontCache, array_merge( + array( + 'prefix' => $prefix, + ), + $config->cache->$configKey->backend->options->toArray() + )); + } + + return $cache; + } + + public function diSmsSender() + { + $config = $this->getDI()->getConfig(); + $adapterMapping = array( + 'submail' => 'Eva\EvaSms\Providers\Submail', + ); + $adapterKey = $config->smsSender->provider; + $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; + $adapterClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; + if (false === class_exists($adapterClass)) { + throw new Exception\RuntimeException(sprintf('No sms provider found by %s', $adapterClass)); + } + + $sender = new Sender(); + $sender->setProvider(new $adapterClass($config->smsSender->appid, $config->smsSender->appkey)); + if ($config->smsSender->timeout) { + $sender::setDefaultTimeout($config->smsSender->timeout); + } + + return $sender; + } + + public function diMailer() + { + $config = $this->getDI()->getConfig(); + if ($config->mailer->transport == 'smtp') { + $transport = \Swift_SmtpTransport::newInstance() + ->setHost($config->mailer->host) + ->setPort($config->mailer->port) + ->setEncryption($config->mailer->encryption) + ->setUsername($config->mailer->username) + ->setPassword($config->mailer->password); + } else { + $transport = \Swift_SendmailTransport::newInstance($config->mailer->sendmailCommand); + } + if ($config->mailer->transport == 'sendcloud') { + $mailer = SendCloudMailer::newInstance() + ->setHost($config->mailer->host) + ->setUsername($config->mailer->username) + ->setPassword($config->mailer->password); + } else { + $mailer = \Swift_Mailer::newInstance($transport); + } + + return $mailer; + } + + public function diSession() + { + $adapterMapping = array( + 'files' => 'Phalcon\Session\Adapter\Files', + 'database' => 'Phalcon\Session\Adapter\Database', + 'memcache' => 'Phalcon\Session\Adapter\Memcache', + 'libmemcached' => 'Eva\EvaEngine\Session\Adapter\Libmemcached', + 'mongo' => 'Phalcon\Session\Adapter\Mongo', + 'redis' => 'Phalcon\Session\Adapter\Redis', + 'handlersocket' => 'Phalcon\Session\Adapter\HandlerSocket', + ); + + $config = $this->getDI()->getConfig(); + $adapterKey = $config->session->adapter; + $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; + $sessionClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; + if (false === class_exists($sessionClass)) { + throw new Exception\RuntimeException(sprintf('No session adapter found by %s', $sessionClass)); + } + if (!empty($config->session->session_name)) { + session_name($config->session->session_name); + } + if (!empty($config->session->cookie_params)) { + $cookie_params = $config->session->cookie_params; + session_set_cookie_params( + @$cookie_params->lifetime, + @$cookie_params->path, + @$cookie_params->domain, + @$cookie_params->secure, + @$cookie_params->httponly + ); + } + $session = new $sessionClass(array_merge( + array( + 'uniqueId' => $this->getAppName(), + ), + $config->session->options->toArray() + )); + if (!$session->isStarted()) { + //NOTICE: Get php warning here, not found reason + @$session->start(); + } + + return $session; + } + + public function diTokenStorage() + { + $config = $this->getDI()->getConfig(); + + return new TokenStorage( + array_merge( + array( + 'uniqueId' => $this->getAppName(), + ), + $config->tokenStorage->toArray() + ) + ); + } + + public function diTranslate() + { + $config = $this->getDI()->getConfig(); + $file = $config->translate->path . $config->translate->forceLang . '.csv'; + if (false === file_exists($file)) { + //empty translator + return new \Phalcon\Translate\Adapter\NativeArray( + array( + 'content' => array() + ) + ); + } + $translate = new \Phalcon\Translate\Adapter\Csv( + array( + 'file' => $file, + 'delimiter' => ',', + ) + ); + + return $translate; + } + + public function diFileSystem() + { + $config = $this->getDI()->getConfig(); + $adapterClass = $config->filesystem->default->adapter; + + $adapter = new $adapterClass($config->filesystem->default->uploadPath); + $filesystem = new \Gaufrette\Filesystem($adapter); + + return $filesystem; + } + + /** + * Application Bootstrap, init DI, register Modules, init events, init ErrorHandler + * @return $this + */ + public function bootstrap() + { + // 注册 facade 别名 + foreach ($this->getDI()->getConfig()->alias as $aliasName => $facade) { + class_alias($facade, $aliasName); + } + if ($this->getDI()->getConfig()->debug) { + $debugger = $this->getDebugger(); + $debugger->debugVar($this->getDI()->getModuleManager()->getModules(), 'modules'); + } + $this->getApplication() + ->setDI($this->getDI()); + $this->getApplication() + ->setEventsManager($this->getDI()->getEventsManager()); + $this->attachModuleEvents(); + //Error Handler must run before router start + if ($this->appMode == 'cli') { + $this->initErrorHandler(new Error\CLIErrorHandler()); + } else { + $this->initErrorHandler(new Error\ErrorHandler); + } + + return $this; + } + + /** + * Run application + */ + public function run() + { + $response = $this->getApplication()->handle(); + echo $response->getContent(); + } + + /** + * Register default error handler + * @param Error\ErrorHandlerInterface $errorHandler + * @return $this + */ + public function initErrorHandler(Error\ErrorHandlerInterface $errorHandler) + { + $this->getDI()->getEventsManager()->attach( + 'dispatch:beforeException', + function ($event, $dispatcher, $exception) { + //For fixing phalcon weird behavior https://github.com/phalcon/cphalcon/issues/2558 + throw $exception; + } + ); + + if ($this->getDI()->getConfig()->debug && $this->appMode != 'cli') { + return $this; + } + + $errorClass = get_class($errorHandler); + set_error_handler("$errorClass::errorHandler"); + set_exception_handler("$errorClass::exceptionHandler"); + register_shutdown_function("$errorClass::shutdownHandler"); + + return $this; + } + + + /** + * A custum version for Application->run() + * WARNING: This method not able to replace phalcon default run() + */ + public function runCustom() + { + if ($this->appMode == 'cli') { + return; + } + $di = $this->getDI(); + + //$debug = $di->getConfig()->debug; + /* + if ($debug) { + $debugger = $this->getDebugger(); + } + */ + + //Roter + $router = $di['router']; + $router->handle(); + + //Module handle + $modules = $this->getApplication()->getModules(); + $routeModule = $router->getModuleName(); + if (isset($modules[$routeModule])) { + $moduleClass = new $modules[$routeModule]['className'](); + $moduleClass->registerAutoloaders(); + $moduleClass->registerServices($di); + } + + //dispatch + $dispatcher = $di['dispatcher']; + $dispatcher->setModuleName($router->getModuleName()); + $dispatcher->setControllerName($router->getControllerName()); + $dispatcher->setActionName($router->getActionName()); + $dispatcher->setParams($router->getParams()); + + //view + $view = $di['view']; + $view->start(); + $dispatcher->dispatch(); + //Not able to call render in controller or else will repeat output + $view->render( + $dispatcher->getControllerName(), + $dispatcher->getActionName(), + $dispatcher->getParams() + ); + $view->finish(); + + //NOTICE: not able to output flash session content + $response = $di['response']; + $response->setContent($view->getContent()); + $response->sendHeaders(); + echo $response->getContent(); + } + + + /** + * Constructor + * + * @param string Application root path + * @param string Application name for some cache prefix + * @param string Application mode, 'cli' or 'web' , 'cli' for CLI mode + */ + public function __construct($appRoot = null, $appName = 'evaengine', $appMode = 'web') + { + self::$appStartTime = microtime(true); + $this->appRoot = $appRoot ? $appRoot : __DIR__; + $this->appName = empty($_SERVER['APPLICATION_NAME']) ? $appName : $_SERVER['APPLICATION_NAME']; + $this->environment = empty($_SERVER['APPLICATION_ENV']) ? 'development' : $_SERVER['APPLICATION_ENV']; + + $appMode = strtolower($appMode); + $this->appMode = in_array($appMode, array('web', 'cli')) ? $appMode : 'web'; + } +} diff --git a/src/EvaEngine/Engine.php b/src/EvaEngine/Engine.php index 795dffe..1165864 100644 --- a/src/EvaEngine/Engine.php +++ b/src/EvaEngine/Engine.php @@ -1,1398 +1,176 @@ +// +---------------------------------------------------------------------- +// + Datetime: 15/6/12 下午4:44 +// +---------------------------------------------------------------------- +// + Engine.php +// +---------------------------------------------------------------------- namespace Eva\EvaEngine; -//use Eva\EvaEngine\Events\DispatchCacheListener; -use Eva\EvaEngine\Interceptor\Dispatch as DispatchInterceptor; -use Eva\EvaEngine\SDK\SendCloudMailer; -use Eva\EvaSms\Sender; -use Phalcon\CLI\Console; -use Phalcon\Mvc\Router; -use Eva\EvaEngine\Mvc\Url as UrlResolver; -use Phalcon\DI\FactoryDefault; + +use Eva\EvaEngine\Exception\RuntimeException; +use Eva\EvaEngine\Foundation\ApplicationInterface; +use Eva\EvaEngine\Module\Manager; +use Phalcon\Cache\Backend\File; +use Phalcon\Cache\Frontend\None; use Phalcon\Config; +use Phalcon\DI; +use Phalcon\DI\FactoryDefault; use Phalcon\Loader; -use Phalcon\Mvc\Application; -use Phalcon\Debug; -use Phalcon\Events\Manager as EventsManager; -use Phalcon\Logger\Adapter\File as FileLogger; -use Phalcon\Mvc\Dispatcher; -use Eva\EvaEngine\Mvc\View; -use Eva\EvaEngine\Module\Manager as ModuleManager; -use Eva\EvaEngine\Mvc\Model\Manager as ModelManager; -use Phalcon\CLI\Router as CLIRouter; -use Phalcon\CLI\Dispatcher as CLIDispatcher; -use Phalcon\DI\FactoryDefault\CLI; -use Eva\EvaEngine\Service\TokenStorage; -use Phalcon\DiInterface; -use Symfony\Component\Console\Output\ConsoleOutput; +use SuperClosure\Analyzer\AstAnalyzer; +use SuperClosure\Serializer; +use Whoops\Provider\Phalcon\WhoopsServiceProvider; -/** - * Core application configuration / bootstrap - * - * The most common workflow is: - * - * $engine = new Engine(__DIR__ . '/..'); - * $engine->loadModules(include __DIR__ . '/../config/modules.php') - * ->bootstrap() - * ->run(); - * - * - * Class Engine - * - * @package Eva\EvaEngine - */ class Engine { - /** - * @var float - */ - public static $appStartTime; - - /** - * @var null|string - */ - protected $appRoot; + const MODE_WEB = 'web'; + const MODE_CLI = 'cli'; - /** - * @var string - */ - protected $appName; //for cache prefix - - /** - * @var string - */ - protected $modulesPath; - - /** - * @var DiInterface - */ + protected $project_root = __DIR__; + protected $mode = Engine::MODE_WEB; protected $di; - + protected static $instance = null; /** - * @var Application + * @var ApplicationInterface */ protected $application; - /** - * @var string - */ - protected $configPath; - - /** - * @var bool - */ - protected $cacheEnable = false; - - /** - * @var string - */ - protected $environment; //development | test | production - - /** - * @var Debug - */ - protected $debugger; - - /** - * @var string - */ - protected $appMode = 'web'; - - /** - * @return string - */ - public function getEnvironment() - { - return $this->environment; - } - - /** - * @param $environment - * @return $this - */ - public function setEnvironment($environment) - { - $this->environment = $environment; - - return $this; - } - - /** - * @param $appRoot - * @return $this - */ - public function setAppRoot($appRoot) - { - $this->appRoot = $appRoot; - - return $this; - } - - /** - * @return null|string - */ - public function getAppRoot() - { - return $this->appRoot; - } - - /** - * @param $name - * @return $this - */ - public function setAppName($name) - { - $this->appName = $name; - - return $this; - } - - /** - * @return string - */ - public function getAppName() + public function __construct($project_root, ApplicationInterface $application, $systemCache = null) { - return $this->appName; - } - - /** - * @param $path - * @return $this - */ - public function setConfigPath($path) - { - $this->configPath = $path; - - return $this; - } - - /** - * @return string - */ - public function getConfigPath() - { - if ($this->configPath) { - return $this->configPath; + $this->project_root = $project_root; + $this->application = $application; + $this->di = new FactoryDefault(); + $this->di->setShared('evaengine', $this); + $this->di->setShared('loader', new Loader()); + if ($systemCache == null) { + $systemCache = function () { + return new File(new None(), array( + 'cacheDir' => $this->getProjectRoot() . '/cache/system/', + 'prefix' => $this->getAppName() + )); + }; } - - return $this->configPath = $this->appRoot . '/config'; + $this->di->setShared('systemCache', $systemCache); } - - /** - * @param $modulesPath - * @return $this - */ - public function setModulesPath($modulesPath) + public function loadModules(array $modulesConfig) { - $this->modulesPath = $modulesPath; + $moduleManager = new Manager($this->getProjectRoot() . '/modules', $modulesConfig); + $moduleManager->registerModules(array( + 'EvEngine' => array( + 'path' => __DIR__ . '/../../Module.php', + 'className' => 'Eva\EvaEngine\Module' + ) + )); + $moduleManager->registerModules($modulesConfig); + $this->di->set('moduleManager', $moduleManager); return $this; } - /** - * @return string - */ - public function getModulesPath() - { - if ($this->modulesPath) { - return $this->modulesPath; - } - - return $this->modulesPath = $this->appRoot . '/modules'; - } - - - /** - * - * @param $cacheFile cache file path - * @param bool $serialize - * @return mixed|null - */ - public function readCache($cacheFile, $serialize = false) - { - if (file_exists($cacheFile) && $cache = include $cacheFile) { - return true === $serialize ? unserialize($cache) : $cache; - } - - return null; - } - - /** - * @param $cacheFile - * @param $content - * @param bool $serialize - * @return bool - */ - public function writeCache($cacheFile, $content, $serialize = false) - { - if ($cacheFile && $fh = fopen($cacheFile, 'w')) { - if (true === $serialize) { - fwrite($fh, "debugger) { - return $this->debugger; - } - - $debugger = new Debug(); - $debugger->setShowFileFragment(true); - $debugger->listen(true, true); - - return $this->debugger = $debugger; - } - - /** - * @return string - */ - public function getAppMode() - { - return $this->appMode; - } - - /** - * @return Console|Application - */ - public function getApplication() - { - if (!$this->application) { - if ($this->appMode == 'cli') { - $this->application = new Console(); - } else { - $this->application = new Application(); - } - } - - return $this->application; - } - - - /** - * Load modules from input settings, and call phalcon application->registerModules() for register - * - * below events will be trigger - * - module:beforeLoadModule - * - module:afterLoadModule - * - * @param array $moduleSettings - * @return $this - */ - public function loadModules(array $moduleSettings) + public function bootstrap() { - $moduleManager = $this->getDI()->getModuleManager(); - - if ($this->getEnvironment() == 'production') { - $cachePrefix = $this->getAppName(); - $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.modules.php"; - $moduleManager->setCacheFile($cacheFile); - } - + $this->initializeConfig() + ->initializeAutoLoaders() + ->registerServices(); - $moduleManager - ->setDefaultPath($this->getModulesPath()) - ->loadModules($moduleSettings, $this->getAppName()); - - if ($this->getDI()->getConfig()->debug) { - $cachePrefix = $this->getAppName(); - $this->writeCache( - $this->getConfigPath() . "/_debug.$cachePrefix.modules.php", - $moduleManager->getModules() - ); - } - - $this->getApplication()->registerModules($moduleManager->getModules()); - //Overwirte default modulemanager - $this->getDI()->set('moduleManager', $moduleManager); + $this->application->initializeErrorHandler()->initialize()->setDI($this->di); + $this->initializeEvents(); return $this; } - /** - * @return $this - */ - public function attachModuleEvents() + protected function initializeConfig() { - $di = $this->getDI(); - $cachePrefix = $this->getAppName(); - $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.events.php"; - $listeners = $this->readCache($cacheFile); - $cacheLoaded = false; + $moduleManager = eva_get('moduleManager'); - if (!$listeners) { - $moduleManager = $this->getDI()->getModuleManager(); - $modules = $moduleManager->getModules(); - $listeners = array(); - foreach ($modules as $moduleName => $module) { - $moduleListeners = $moduleManager->getModuleListeners($moduleName); - if ($moduleListeners) { - $listeners[$moduleName] = $moduleListeners; - } - } - } else { - $cacheLoaded = true; - } - - if (!is_array($listeners)) { - return $this; - } - - $eventsManager = $this->getDI()->getEventsManager(); - foreach ($listeners as $moduleName => $moduleListeners) { - if (!$moduleListeners) { - continue; - } - foreach ($moduleListeners as $eventType => $listener) { - $eventsManager->attach($eventType, new $listener); - } - } + $config = $moduleManager->getAllConfig(); + $config->merge( + new Config(include $this->getProjectRoot() . '/config/config.default.php') + ); - if ($di->getConfig()->debug) { - $debugger = $this->getDebugger(); - $debugger->debugVar($listeners, 'events'); - } + $config->merge( + new Config(include $this->getProjectRoot() . '/config/config.local.php') + ); - if (!$di->getConfig()->debug && false === $cacheLoaded && $listeners) { - $this->writeCache($cacheFile, $listeners); - } + $this->di->set('config', $config); return $this; } - /** - * @return $this - */ - public function registerViewHelpers() + protected function initializeAutoLoaders() { - $di = $this->getDI(); - $cachePrefix = $this->getAppName(); - $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.helpers.php"; - $helpers = $this->readCache($cacheFile); - if ($helpers) { - Tag::registerHelpers($helpers); - - return $this; - } - - $helpers = array(); - $moduleManager = $di->getModuleManager(); - $modules = $moduleManager->getModules(); - foreach ($modules as $moduleName => $module) { - $moduleHelpers = $moduleManager->getModuleViewHelpers($moduleName); - if (is_array($moduleHelpers)) { - $helpers += $moduleHelpers; - } - } - Tag::registerHelpers($helpers); - - if (!$di->getConfig()->debug && $helpers) { - $this->writeCache($cacheFile, $helpers); - } + $loader = new Loader(); + $moduleManager = eva_get('moduleManager'); + $loader->registerNamespaces($moduleManager->getAllAutoLoaders()); + $loader->register(); return $this; } - /** - * @param DiInterface $di - * @return $this - */ - public function setDI(DiInterface $di) - { - $this->di = $di; - - return $this; - } - - /** - * Configuration application default DI - * - * @return FactoryDefault| CLI - */ - public function getDI() + public function getMode() { - if ($this->di) { - return $this->di; - } - if ($this->appMode == 'cli') { - $di = new FactoryDefault\CLI(); - } else { - $di = new FactoryDefault(); - } - - //PHP5.3 not support $this in closure - $self = $this; - - /********************************** - * DI initialize for MVC core - ***********************************/ - //$di->set('application', $this); - - //call loadmodules will overwrite this - $di->set( - 'moduleManager', - function () use ($di) { - $moduleManager = new ModuleManager(); - $moduleManager->setEventsManager($di->getEventsManager()); - - return $moduleManager; - }, - true - ); - - //System global events manager - $di->set( - 'eventsManager', - function () use ($di) { - $eventsManager = new EventsManager(); - $eventsManager->enablePriorities(true); - // dispatch caching event handler - $eventsManager->attach( - "dispatch", - new DispatchInterceptor(), - -1 - ); - $eventsManager->enablePriorities(true); - - return $eventsManager; - }, - true - ); - - $di->set( - 'config', - function () use ($self) { - return $self->diConfig(); - }, - true - ); - - $di->set( - 'router', - function () use ($self) { - return $self->diRouter(); - }, - true - ); - - $di->set( - 'dispatcher', - function () use ($di) { - $dispatcher = new Dispatcher(); - $dispatcher->setEventsManager($di->getEventsManager()); - - return $dispatcher; - }, - true - ); - - $di->set( - 'modelsMetadata', - function () use ($self) { - return $self->diModelsMetadata(); - }, - true - ); - - $di->set( - 'modelsManager', - function () use ($di) { - $config = $di->getConfig(); - ModelManager::setDefaultPrefix($config->dbAdapter->prefix); - //for solving db master/slave under static find method - $modelsManager = new ModelManager(); - - return $modelsManager; - } - ); - - $di->set( - 'view', - function () use ($di) { - $view = new View(); - $view->setViewsDir(__DIR__ . '/views/'); - $view->setEventsManager($di->getEventsManager()); - - return $view; - } - ); - - $di->set( - 'session', - function () use ($self) { - return $self->diSession(); - } - ); - - $di->set( - 'tokenStorage', - function () use ($self) { - return $self->diTokenStorage(); - }, - true //token Storage MUST set as shared - ); - - /********************************** - * DI initialize for database - ***********************************/ - $di->set( - 'dbMaster', - function () use ($self) { - return $self->diDbMaster(); - }, - true - ); - - $di->set( - 'dbSlave', - function () use ($self) { - return $self->diDbSlave(); - }, - true - ); - - $di->set( - 'transactions', - function () use ($di) { - $transactions = new \Phalcon\Mvc\Model\Transaction\Manager(); - $transactions->setDbService('dbMaster'); - - return $transactions; - }, - true - ); - - /********************************** - * DI initialize for cache - ***********************************/ - $di->set( - 'globalCache', - function () use ($self) { - return $self->diGlobalCache(); - }, - true - ); - - $di->set( - 'viewCache', - function () use ($self) { - return $self->diViewCache(); - }, - true - ); - - $di->set( - 'modelsCache', - function () use ($self) { - return $self->diModelsCache(); - }, - true - ); - - $di->set( - 'apiCache', - function () use ($self) { - return $self->diApiCache(); - }, - true - ); - - $di->set( - 'fastCache', - function () use ($self) { - return $self->diFastCache(); - }, - true - ); - - /********************************** - * DI initialize for queue - ***********************************/ - $di->set( - 'queue', - function () use ($di) { - $config = $di->getConfig(); - $client = new \GearmanClient(); - $client->setTimeout(1000); - foreach ($config->queue->servers as $key => $server) { - $client->addServer($server->host, $server->port); - } - - return $client; - }, - true - ); - - $di->set( - 'worker', - function () use ($di) { - $config = $di->getConfig(); - $worker = new \GearmanWorker(); - foreach ($config->queue->servers as $key => $server) { - $worker->addServer($server->host, $server->port); - } - - return $worker; - }, - true - ); - - - /********************************** - * DI initialize for email - ***********************************/ - $di->set( - 'mailer', - function () use ($self) { - return $self->diMailer(); - }, - true - ); - - $di->set('mailMessage', 'Eva\EvaEngine\MailMessage'); - - $di->set( - 'smsSender', - function () use ($self) { - return $self->diSmsSender(); - }, - true - ); - /********************************** - * DI initialize for helpers - ***********************************/ - $di->set( - 'url', - function () use ($di) { - $config = $di->getConfig(); - $url = new UrlResolver(); - $url->setVersionFile($config->staticBaseUriVersionFile); - $url->setBaseUri($config->baseUri); - $url->setStaticBaseUri($config->staticBaseUri); - - return $url; - }, - true - ); - - $di->set('escaper', 'Phalcon\Escaper'); - - $di->set( - 'tag', - function () use ($di, $self) { - Tag::setDi($di); - $self->registerViewHelpers(); - - return new Tag(); - } - ); - - $di->set('flash', 'Phalcon\Flash\Session'); - - $di->set('placeholder', 'Eva\EvaEngine\View\Helper\Placeholder'); - - $di->set( - 'cookies', - function () { - $cookies = new \Phalcon\Http\Response\Cookies(); - $cookies->useEncryption(false); - - return $cookies; - } - ); - - $di->set( - 'translate', - function () use ($self) { - return $self->diTranslate(); - } - ); - - $di->set( - 'fileSystem', - function () use ($self) { - return $self->diFileSystem(); - } - ); - - $di->set( - 'logException', - function () use ($di) { - $config = $di->getConfig(); - - return new FileLogger($config->logger->path . 'error.log'); - } - ); - if ($this->appMode == 'cli') { - $this->cliDI($di); - } - - IoC::setDI($di); - - - return $this->di = $di; + return $this->mode; } - /** - * CLI 模式下的 DI 配置 - * - * @param CLI $di - */ - protected function cliDI(CLI $di) + public function run() { - global $argv; - - $di->set( - 'router', - function () use ($di, $argv) { - $router = new CLIRouter(); - $router->setDI($di); - - return $router; - } - ); - - $di->set( - 'output', - function () { - return new ConsoleOutput(); - } - ); - $di->set( - 'dispatcher', - function () use ($di, $argv) { - $dispatcher = new CLIDispatcher(); - $dispatcher->setDI($di); - - $moduleName = array_shift($argv); - $taskName = array_shift($argv); - $actionName = 'main'; - if (strpos($taskName, ':') > 0) { - @list($taskName, $actionName) = preg_split("/:/", $taskName); - } - if ($moduleName) { - $dispatcher->setTaskName(ucwords($taskName)); - $dispatcher->setActionName($actionName); - $dispatcher->setParams($argv); - if ($moduleName == '_current') { - $_appName = ucwords($this->getAppName()); - $dispatcher->setNamespaceName("{$_appName}\\Tasks"); - } else { - $dispatcher->setNamespaceName("Eva\\{$moduleName}\\Tasks"); - } - } else { - $dispatcher->setTaskName('Main'); - $dispatcher->setParams($argv); - $dispatcher->setNamespaceName("Eva\\EvaEngine\\Tasks"); - } - - return $dispatcher; - } - ); + $this->application->fire(); } - public function diConfig() + protected function registerServices() { - $di = $this->getDI(); - $cachePrefix = $this->getAppName(); - $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.config.php"; - if ($cache = $this->readCache($cacheFile)) { - return new Config($cache); - } - - $config = new Config(include __DIR__ . '/../../config/config.php'); - - - //merge all loaded module configs - $moduleManager = $di->getModuleManager(); - if (!$moduleManager || !$modules = $moduleManager->getModules()) { - return $config; - throw new Exception\RuntimeException(sprintf('Config need at least one module loaded')); - } - - foreach ($modules as $moduleName => $module) { - $moduleConfig = $moduleManager->getModuleConfig($moduleName); - if ($moduleConfig instanceof Config) { - $config->merge($moduleConfig); - } else { - $config->merge(new Config($moduleConfig)); - } - } - //merge config default - $config->merge(new Config(include $this->getConfigPath() . "/config.default.php")); - - //merge config local - if (false === file_exists($this->getConfigPath() . "/config.local.php")) { - return $config; + /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ + $moduleManager = $this->di->get('moduleManager'); + foreach ($moduleManager->getAllDIDefinition() as $name => $definition) { + $this->di->set($name, $definition['definition'], $definition['share']); } - $config->merge(new Config(include $this->getConfigPath() . "/config.local.php")); - if (!$config->debug) { - $this->writeCache($cacheFile, $config->toArray()); - } - - return $config; + return $this; } - public function diRouter() + protected function initializeEvents() { - $di = $this->getDI(); - $cachePrefix = $this->getAppName(); - $cacheFile = $this->getConfigPath() . "/_cache.$cachePrefix.router.php"; - if ($router = $this->readCache($cacheFile, true)) { - return $router; - } - - $moduleManager = $di->getModuleManager(); - $config = new Config(); - $moduleName = ''; - if ($moduleManager && $modulesArray = $moduleManager->getModules()) { - foreach ($modulesArray as $moduleName => $module) { - //NOTICE: EvaEngine Load front-end router at last - $config->merge(new Config($moduleManager->getModuleRoutesFrontend($moduleName))); - $config->merge(new Config($moduleManager->getModuleRoutesBackend($moduleName))); - } - } - - //Disable default router - $router = new Router(false); - //Last extra slash - $router->removeExtraSlashes(true); - //Set last module as default module - $router->setDefaultModule($moduleName); - //NOTICE: Set a strange controller here to make router not match default index/index - $router->setDefaultController('EvaEngineDefaultController'); - - $config = $config->toArray(); - foreach ($config as $url => $route) { - if (count($route) !== count($route, COUNT_RECURSIVE)) { - if (isset($route['pattern']) && isset($route['paths'])) { - $method = isset($route['httpMethods']) ? $route['httpMethods'] : null; - $router->add($route['pattern'], $route['paths'], $method); + $eventsManager = eva_get('eventsManager'); + /** @var \Eva\EvaEngine\Module\Manager $moduleManager */ + $moduleManager = eva_get('moduleManager'); + foreach ($moduleManager->getAllListeners() as $moduleListeners) { + foreach ($moduleListeners as $eventType => $listener) { + $priority = 0; + if (is_string($listener)) { + $listenerClass = $listener; + } elseif (true === is_array($listener) && count($listener) > 1) { + $listenerClass = $listener[0]; + $priority = (int)$listener[1]; } else { - throw new Exception\RuntimeException(sprintf('No route pattern and paths found by route %s', $url)); + throw new \Exception(sprintf( + "Module listener format not correct: %s", + var_export($listener, true) + )); } - } else { - $router->add($url, $route); - } - } - - if (!$di->getConfig()->debug) { - $this->writeCache($cacheFile, $router, true); - } else { - //Dump merged routers for debug - $this->writeCache($this->getConfigPath() . "/_debug.$cachePrefix.router.php", $router, true); - } - - return $router; - } - - public function diModelsMetadata() - { - $adapterMapping = array( - 'apc' => 'Phalcon\Mvc\Model\MetaData\Apc', - 'files' => 'Phalcon\Mvc\Model\MetaData\Files', - 'memory' => 'Phalcon\Mvc\Model\MetaData\Memory', - 'xcache' => 'Phalcon\Mvc\Model\MetaData\Xcache', - 'memcache' => 'Phalcon\Mvc\Model\MetaData\Memcache', - 'redis' => 'Phalcon\Mvc\Model\MetaData\Redis', - 'wincache' => 'Phalcon\Mvc\Model\MetaData\Wincache', - ); - - $config = $this->getDI()->getConfig(); - if (!$config->modelsMetadata->enable) { - return new \Phalcon\Mvc\Model\MetaData\Memory(); - } - - $adapterKey = $config->modelsMetadata->adapter; - $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; - //Allow full class name as adapter name - $adapterClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; - if (!class_exists($adapterClass)) { - throw new Exception\RuntimeException(sprintf('No metadata adapter found by %s', $adapterClass)); - } - - return new $adapterClass($config->modelsMetadata->options->toArray()); - } - - public function diDbMaster() - { - $config = $this->getDI()->getConfig(); - if (!isset($config->dbAdapter->master->adapter) || !$config->dbAdapter->master) { - throw new Exception\RuntimeException(sprintf('No DB Master options found')); - } - - return $this->diDbAdapter( - $config->dbAdapter->master->adapter, - $config->dbAdapter->master->toArray(), - $this->getDI() - ); - } - - public function diDbSlave() - { - $config = $this->getDI()->getConfig(); - $slaves = $config->dbAdapter->slave; - $slaveKey = array_rand($slaves->toArray()); - if (!isset($slaves->$slaveKey) || count($slaves) < 1) { - throw new Exception\RuntimeException(sprintf('No DB slave options found')); - } - - return $this->diDbAdapter($slaves->$slaveKey->adapter, $slaves->$slaveKey->toArray(), $this->getDI()); - } - - - public static function diDbAdapter($adapterKey, array $options, $di) - { - $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; - $adapterMapping = array( - 'mysql' => 'Phalcon\Db\Adapter\Pdo\Mysql', - 'oracle' => 'Phalcon\Db\Adapter\Pdo\Oracle', - 'postgresql' => 'Phalcon\Db\Adapter\Pdo\Postgresql', - 'sqlite' => 'Phalcon\Db\Adapter\Pdo\Sqlite', - ); - - $adapterClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; - - if (false === class_exists($adapterClass)) { - throw new Exception\RuntimeException(sprintf('No matched DB adapter found by %s', $adapterClass)); - } - - $options['charset'] = isset($options['charset']) && $options['charset'] ? $options['charset'] : 'utf8'; - $dbAdapter = new $adapterClass($options); - - - $config = $di->getConfig(); - - if ($config->debug) { - $eventsManager = $di->getEventsManager(); - $logger = new FileLogger($config->logger->path . 'db_query.log'); - - //database service name hardcore as db - $eventsManager->attach( - 'db', - function ($event, $dbAdapter) use ($logger) { - if ($event->getType() == 'beforeQuery') { - $sqlVariables = $dbAdapter->getSQLVariables(); - if (count($sqlVariables)) { - $query = str_replace(array('%', '?'), array('%%', "'%s'"), $dbAdapter->getSQLStatement()); - $query = vsprintf($query, $sqlVariables); - // - $logger->log($query, \Phalcon\Logger::INFO); - } else { - $logger->log($dbAdapter->getSQLStatement(), \Phalcon\Logger::INFO); - } - } + if (false === class_exists($listenerClass)) { + throw new \Exception(sprintf("Module listener %s not exist", $listenerClass)); } - ); - $dbAdapter->setEventsManager($eventsManager); - } - - return $dbAdapter; - } - - public function diGlobalCache() - { - return $this->diCache('globalCache', $this->getAppName() . '_global_'); - } - - public function diViewCache() - { - return $this->diCache('viewCache', $this->getAppName() . '_view_'); - } - - public function diModelsCache() - { - return $this->diCache('modelsCache', $this->getAppName() . '_models_'); - } - - public function diApiCache() - { - return $this->diCache('apiCache', $this->getAppName() . '_api_'); - } - - public function diFastCache() - { - $config = $this->getDI()->getConfig(); - if (!($config->cache->fastCache->enable)) { - return false; - } - - $redis = new \Redis(); - $redis->connect( - $config->cache->fastCache->host, - $config->cache->fastCache->port, - $config->cache->fastCache->timeout - ); - - return $redis; - } - - protected function diCache($configKey, $prefix = 'eva_') - { - $config = $this->getDI()->getConfig(); - $adapterMapping = array( - 'apc' => 'Phalcon\Cache\Backend\Apc', - 'file' => 'Phalcon\Cache\Backend\File', - 'libmemcached' => 'Phalcon\Cache\Backend\Libmemcached', - 'memcache' => 'Phalcon\Cache\Backend\Memcache', - 'memory' => 'Phalcon\Cache\Backend\Memory', - 'mongo' => 'Phalcon\Cache\Backend\Mongo', - 'xcache' => 'Phalcon\Cache\Backend\Xcache', - 'redis' => 'Phalcon\Cache\Backend\Redis', - 'wincache' => 'Phalcon\Cache\Backend\Wincache', - 'base64' => 'Phalcon\Cache\Frontend\Base64', - 'data' => 'Phalcon\Cache\Frontend\Data', - 'igbinary' => 'Phalcon\Cache\Frontend\Igbinary', - 'json' => 'Phalcon\Cache\Frontend\Json', - 'none' => 'Phalcon\Cache\Frontend\None', - 'output' => 'Phalcon\Cache\Frontend\Output', - ); - - $frontCacheClassName = $config->cache->$configKey->frontend->adapter; - $frontCacheClassName = false === strpos( - $frontCacheClassName, - '\\' - ) ? strtolower($frontCacheClassName) : $frontCacheClassName; - $frontCacheClass = - empty($adapterMapping[$frontCacheClassName]) - ? $frontCacheClassName - : $adapterMapping[$frontCacheClassName]; - if (false === class_exists($frontCacheClass)) { - throw new Exception\RuntimeException(sprintf('No cache adapter found by %s', $frontCacheClass)); - } - $frontCache = new $frontCacheClass( - $config->cache->$configKey->frontend->options->toArray() - ); - - if (!$config->cache->enable || !$config->cache->$configKey->enable) { - $cache = new \Eva\EvaEngine\Cache\Backend\Disable($frontCache); - } else { - $backendCacheClassName = $config->cache->$configKey->backend->adapter; - $backendCacheClassName = false === strpos( - $backendCacheClassName, - '\\' - ) ? strtolower($backendCacheClassName) : $backendCacheClassName; - $backendCacheClass = - !empty($adapterMapping[$backendCacheClassName]) - ? $adapterMapping[$backendCacheClassName] - : $backendCacheClassName; - - if (!class_exists($backendCacheClass)) { - throw new Exception\RuntimeException(sprintf('No cache adapter found by %s', $backendCacheClassName)); - } - $cache = new $backendCacheClass($frontCache, array_merge( - array( - 'prefix' => $prefix, - ), - $config->cache->$configKey->backend->options->toArray() - )); - } - - return $cache; - } - - public function diSmsSender() - { - $config = $this->getDI()->getConfig(); - $adapterMapping = array( - 'submail' => 'Eva\EvaSms\Providers\Submail', - ); - $adapterKey = $config->smsSender->provider; - $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; - $adapterClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; - if (false === class_exists($adapterClass)) { - throw new Exception\RuntimeException(sprintf('No sms provider found by %s', $adapterClass)); - } - - $sender = new Sender(); - $sender->setProvider(new $adapterClass($config->smsSender->appid, $config->smsSender->appkey)); - if ($config->smsSender->timeout) { - $sender::setDefaultTimeout($config->smsSender->timeout); - } - - return $sender; - } - - public function diMailer() - { - $config = $this->getDI()->getConfig(); - if ($config->mailer->transport == 'smtp') { - $transport = \Swift_SmtpTransport::newInstance() - ->setHost($config->mailer->host) - ->setPort($config->mailer->port) - ->setEncryption($config->mailer->encryption) - ->setUsername($config->mailer->username) - ->setPassword($config->mailer->password); - } else { - $transport = \Swift_SendmailTransport::newInstance($config->mailer->sendmailCommand); - } - if ($config->mailer->transport == 'sendcloud') { - $mailer = SendCloudMailer::newInstance() - ->setHost($config->mailer->host) - ->setUsername($config->mailer->username) - ->setPassword($config->mailer->password); - } else { - $mailer = \Swift_Mailer::newInstance($transport); - } - - return $mailer; - } - - public function diSession() - { - $adapterMapping = array( - 'files' => 'Phalcon\Session\Adapter\Files', - 'database' => 'Phalcon\Session\Adapter\Database', - 'memcache' => 'Phalcon\Session\Adapter\Memcache', - 'libmemcached' => 'Eva\EvaEngine\Session\Adapter\Libmemcached', - 'mongo' => 'Phalcon\Session\Adapter\Mongo', - 'redis' => 'Phalcon\Session\Adapter\Redis', - 'handlersocket' => 'Phalcon\Session\Adapter\HandlerSocket', - ); - - $config = $this->getDI()->getConfig(); - $adapterKey = $config->session->adapter; - $adapterKey = false === strpos($adapterKey, '\\') ? strtolower($adapterKey) : $adapterKey; - $sessionClass = empty($adapterMapping[$adapterKey]) ? $adapterKey : $adapterMapping[$adapterKey]; - if (false === class_exists($sessionClass)) { - throw new Exception\RuntimeException(sprintf('No session adapter found by %s', $sessionClass)); - } - if (!empty($config->session->session_name)) { - session_name($config->session->session_name); - } - if (!empty($config->session->cookie_params)) { - $cookie_params = $config->session->cookie_params; - session_set_cookie_params( - @$cookie_params->lifetime, - @$cookie_params->path, - @$cookie_params->domain, - @$cookie_params->secure, - @$cookie_params->httponly - ); - } - $session = new $sessionClass(array_merge( - array( - 'uniqueId' => $this->getAppName(), - ), - $config->session->options->toArray() - )); - if (!$session->isStarted()) { - //NOTICE: Get php warning here, not found reason - @$session->start(); - } - - return $session; - } - - public function diTokenStorage() - { - $config = $this->getDI()->getConfig(); - - return new TokenStorage( - array_merge( - array( - 'uniqueId' => $this->getAppName(), - ), - $config->tokenStorage->toArray() - ) - ); - } - - public function diTranslate() - { - $config = $this->getDI()->getConfig(); - $file = $config->translate->path . $config->translate->forceLang . '.csv'; - if (false === file_exists($file)) { - //empty translator - return new \Phalcon\Translate\Adapter\NativeArray( - array( - 'content' => array() - ) - ); - } - $translate = new \Phalcon\Translate\Adapter\Csv( - array( - 'file' => $file, - 'delimiter' => ',', - ) - ); - - return $translate; - } - - public function diFileSystem() - { - $config = $this->getDI()->getConfig(); - $adapterClass = $config->filesystem->default->adapter; - - $adapter = new $adapterClass($config->filesystem->default->uploadPath); - $filesystem = new \Gaufrette\Filesystem($adapter); - - return $filesystem; - } - - /** - * Application Bootstrap, init DI, register Modules, init events, init ErrorHandler - * @return $this - */ - public function bootstrap() - { - // 注册 facade 别名 - foreach ($this->getDI()->getConfig()->alias as $aliasName => $facade) { - class_alias($facade, $aliasName); - } - if ($this->getDI()->getConfig()->debug) { - $debugger = $this->getDebugger(); - $debugger->debugVar($this->getDI()->getModuleManager()->getModules(), 'modules'); - } - $this->getApplication() - ->setDI($this->getDI()); - $this->getApplication() - ->setEventsManager($this->getDI()->getEventsManager()); - $this->attachModuleEvents(); - //Error Handler must run before router start - if ($this->appMode == 'cli') { - $this->initErrorHandler(new Error\CLIErrorHandler()); - } else { - $this->initErrorHandler(new Error\ErrorHandler); - } - - return $this; - } - - /** - * Run application - */ - public function run() - { - $response = $this->getApplication()->handle(); - echo $response->getContent(); - } - - /** - * Register default error handler - * @param Error\ErrorHandlerInterface $errorHandler - * @return $this - */ - public function initErrorHandler(Error\ErrorHandlerInterface $errorHandler) - { - $this->getDI()->getEventsManager()->attach( - 'dispatch:beforeException', - function ($event, $dispatcher, $exception) { - //For fixing phalcon weird behavior https://github.com/phalcon/cphalcon/issues/2558 - throw $exception; + $eventsManager->attach($eventType, new $listenerClass, $priority); } - ); - - if ($this->getDI()->getConfig()->debug && $this->appMode != 'cli') { - return $this; } - $errorClass = get_class($errorHandler); - set_error_handler("$errorClass::errorHandler"); - set_exception_handler("$errorClass::exceptionHandler"); - register_shutdown_function("$errorClass::shutdownHandler"); - return $this; } - - /** - * A custum version for Application->run() - * WARNING: This method not able to replace phalcon default run() - */ - public function runCustom() + public function getProjectRoot() { - if ($this->appMode == 'cli') { - return; - } - $di = $this->getDI(); - - //$debug = $di->getConfig()->debug; - /* - if ($debug) { - $debugger = $this->getDebugger(); - } - */ - - //Roter - $router = $di['router']; - $router->handle(); - - //Module handle - $modules = $this->getApplication()->getModules(); - $routeModule = $router->getModuleName(); - if (isset($modules[$routeModule])) { - $moduleClass = new $modules[$routeModule]['className'](); - $moduleClass->registerAutoloaders(); - $moduleClass->registerServices($di); - } - - //dispatch - $dispatcher = $di['dispatcher']; - $dispatcher->setModuleName($router->getModuleName()); - $dispatcher->setControllerName($router->getControllerName()); - $dispatcher->setActionName($router->getActionName()); - $dispatcher->setParams($router->getParams()); - - //view - $view = $di['view']; - $view->start(); - $dispatcher->dispatch(); - //Not able to call render in controller or else will repeat output - $view->render( - $dispatcher->getControllerName(), - $dispatcher->getActionName(), - $dispatcher->getParams() - ); - $view->finish(); - - //NOTICE: not able to output flash session content - $response = $di['response']; - $response->setContent($view->getContent()); - $response->sendHeaders(); - echo $response->getContent(); + return $this->project_root; } - - /** - * Constructor - * - * @param string Application root path - * @param string Application name for some cache prefix - * @param string Application mode, 'cli' or 'web' , 'cli' for CLI mode - */ - public function __construct($appRoot = null, $appName = 'evaengine', $appMode = 'web') + public function getAppName() { - self::$appStartTime = microtime(true); - $this->appRoot = $appRoot ? $appRoot : __DIR__; - $this->appName = empty($_SERVER['APPLICATION_NAME']) ? $appName : $_SERVER['APPLICATION_NAME']; - $this->environment = empty($_SERVER['APPLICATION_ENV']) ? 'development' : $_SERVER['APPLICATION_ENV']; - - $appMode = strtolower($appMode); - $this->appMode = in_array($appMode, array('web', 'cli')) ? $appMode : 'web'; + return $this->application->getName(); } -} +} \ No newline at end of file diff --git a/src/EvaEngine/Foundation/AdapterCreator.php b/src/EvaEngine/Foundation/AdapterCreator.php index a1ab2ae..6b735ae 100644 --- a/src/EvaEngine/Foundation/AdapterCreator.php +++ b/src/EvaEngine/Foundation/AdapterCreator.php @@ -25,7 +25,6 @@ abstract class AdapterCreator * protected function getAdaptersMapping() * { * return array( - * // `default` 对应 `getAdapterClass` 方法的 category 参数 * 'default' => array( * 'adapter1' => 'Some\Adapter\XXX1', * 'adapter2' => 'Some\Adapter\XXX2' @@ -36,6 +35,7 @@ abstract class AdapterCreator * @return array */ abstract protected function getAdaptersMapping(); + /** * @param string $adapter 适配器名称,可以为完整的类名 * @param string $category 分类,可选 @@ -44,11 +44,13 @@ abstract protected function getAdaptersMapping(); */ protected function getAdapterClass($adapter, $category = 'default') { - $adaptersMapping = $this->getAdaptersMapping($category); + $adaptersMapping = $this->getAdaptersMapping(); $adapterClass = $adapter; - if (isset($adaptersMapping[$category]) && in_array($adapter, $adaptersMapping[$category])) { - $adapterClass = $adaptersMapping[$category][$adapter]; + $adapterName = strtolower($adapter); + if (isset($adaptersMapping[$category]) && isset($adaptersMapping[$category][$adapterName])) { + $adapterClass = $adaptersMapping[$category][$adapterName]; } + if (!class_exists($adapterClass)) { throw new RuntimeException( sprintf( diff --git a/src/EvaEngine/Foundation/ApplicationInterface.php b/src/EvaEngine/Foundation/ApplicationInterface.php new file mode 100644 index 0000000..1d8483f --- /dev/null +++ b/src/EvaEngine/Foundation/ApplicationInterface.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/17 下午6:54 +// +---------------------------------------------------------------------- +// + ApplicationInterface.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Foundation; + + +use Phalcon\DI\InjectionAwareInterface; + +interface ApplicationInterface extends InjectionAwareInterface +{ + /** + * @return $this + */ + public function initializeErrorHandler(); + + + public function getName(); + + /** + * @return $this + */ + public function initialize(); + + public function fire(); +} \ No newline at end of file diff --git a/src/EvaEngine/IoC.php b/src/EvaEngine/IoC.php index ede9767..b5f6ddb 100644 --- a/src/EvaEngine/IoC.php +++ b/src/EvaEngine/IoC.php @@ -19,57 +19,26 @@ */ class IoC { - /** - * @var DI - */ - private static $di; /** * @return DI */ public static function getDI() { - return self::$di; + return \Phalcon\DI::getDefault(); } - /** - * @param DI $di - */ - public static function setDI(DI $di) - { - self::$di = $di; - } /** * Resolves the service based on its configuration * * @param string $name - * @param array $parameters + * @param array $parameters * @throws RuntimeException * @return mixed */ public static function get($name, $parameters = null) { - if (self::$di == null) { - throw new RuntimeException('IoC container is null!'); - } - return self::$di->get($name); - } - - /** - * Registers a service in the services container - * - * @param string $name - * @param mixed $definition - * @param boolean $shared - * @throws RuntimeException - * @return \Phalcon\DI\ServiceInterface - */ - public static function set($name, $definition, $shared = null) - { - if (self::$di == null) { - throw new RuntimeException('IoC container is null!'); - } - self::$di->set($name, $definition, $shared); + return self::getDI()->get($name); } } diff --git a/src/EvaEngine/Module/Manager.php b/src/EvaEngine/Module/Manager.php index 8497f51..33541fc 100644 --- a/src/EvaEngine/Module/Manager.php +++ b/src/EvaEngine/Module/Manager.php @@ -15,6 +15,9 @@ use Eva\EvaEngine\Mvc\Model; use Phalcon\Mvc\Application; +use SuperClosure\Analyzer\AstAnalyzer; +use SuperClosure\Analyzer\TokenAnalyzer; +use SuperClosure\Serializer; /** * Module Manager for module register / load @@ -47,7 +50,7 @@ class Manager protected $di; - protected $allAutoLoaders; + protected $allAutoLoaders = array(); /** * @var array */ @@ -59,7 +62,7 @@ class Manager /** * @var Config */ - protected $allRoutesCLI; + protected $allRoutesConsole; protected $allListeners = array(); protected $allRelations = array(); @@ -95,7 +98,12 @@ public function __construct($defaultModulesDir) $this->allConfig = new Config(); $this->allRoutesBackend = new Config(); $this->allRoutesFrontend = new Config(); - $this->allRoutesCLI = new Config(); + $this->allRoutesConsole = new Config(); + } + + public function hasModule($name) + { + return isset($this->modules[$name]); } /** @@ -182,15 +190,20 @@ public function register(Module $module) $this->allConfig->merge(new Config($module->getConfig())); $this->allRoutesFrontend->merge(new Config($module->getRoutesFrontend())); $this->allRoutesBackend->merge(new Config($module->getRoutesBackend())); - $this->allRoutesCLI->merge(new Config($module->getRoutesCLI())); - $this->allListeners[] = $module->getListeners(); + $this->allRoutesConsole->merge(new Config($module->getRoutesConsole())); + if (!empty($module->getListeners())) { + $this->allListeners[] = $module->getListeners(); + } $this->allViewHelpers = array_merge($this->allViewHelpers, $module->getViewHelpers()); $this->allErrorHandlers = array_merge($this->allErrorHandlers, $module->getErrorHandlers()); - if (is_array($module->getRelations()) && !empty($module->getRelations())) { - foreach ($module->getRelations() as $entity => $relationDefinition) { - $this->allRelations[$entity][] = $relationDefinition; - } - } +// if (is_array($module->getRelations()) && !empty($module->getRelations())) { +// foreach ($module->getRelations() as $entity => $relationDefinition) { +// $this->allRelations[$entity][] = $relationDefinition; +// } +// } + $this->allRelations = array_merge($this->allRelations, $module->getRelations()); + $this->allDIDefinition = array_merge($this->allDIDefinition, $module->getDiDefinition()); + $this->allAutoLoaders = array_merge($this->allAutoLoaders, $module->getAutoLoaders()); } /** @@ -200,7 +213,7 @@ public function register(Module $module) */ public function getModulesForPhalcon() { - return $this->getModulesForPhalcon(); + return $this->phalconModules; } /** @@ -254,17 +267,17 @@ public function setAllRoutesBackend($allRoutesBackend) /** * @return Config */ - public function getAllRoutesCLI() + public function getAllRoutesConsole() { - return $this->allRoutesCLI; + return $this->allRoutesConsole; } /** - * @param Config $allRoutesCLI + * @param Config $allRoutesConsole */ - public function setAllRoutesCLI($allRoutesCLI) + public function setAllRoutesConsole($allRoutesConsole) { - $this->allRoutesCLI = $allRoutesCLI; + $this->allRoutesConsole = $allRoutesConsole; } /** @@ -362,4 +375,54 @@ public function setAllDIDefinition($allDIDefinition) { $this->allDIDefinition = $allDIDefinition; } + + public function getAllWebRoutes() + { + $routes = $this->getAllRoutesBackend(); + $routes->merge($this->getAllRoutesFrontend()); + + return $routes; + } + + public function serialize() + { +// dd(get_object_vars($this)); +// dd($this->getAllDIDefinition()); +// $files = array(); + foreach ($this->getAllDIDefinition() as $name => $definition) { +// p(gettype($definition['definition'] )); + if (!$definition['definition'] instanceof \Closure) { + continue; + } + $serializer = new Serializer(new TokenAnalyzer()); + p($name); + p($serializer->getData($definition['definition'], true)); + /** + * 创建一个反射: + */ +// $reflection = new \ReflectionFunction($definition['definition']); +// p($reflection->getParameters()); +// +// p($reflection->getFileName()); +// p($reflection->getStartLine()); +// p($reflection->getEndLine()); +// p($reflection->getClosure()); +// /** +// * 参数可以直接得到了: +// */ +// $params = $reflection->getParameters(); +// +// /** +// * 获得Closure的函数体和use变量,形如: +// * function($arg1, $arg2, ...) use ($val1, $val2, ...) { +// * // 要获得这个部分的代码! +// * } +// * 办法很多,你可以直接用正则、字符串查找或者Tokenizer,等等等等。 +// * 比如可以先从reflection里得到函数的开始行和结束行: +// */ +// $startLine = $reflection->getStartLine(); +// $endLine = $reflection->getEndLine(); + } + + } } diff --git a/src/EvaEngine/Module/Module.php b/src/EvaEngine/Module/Module.php index fafb416..9c48583 100644 --- a/src/EvaEngine/Module/Module.php +++ b/src/EvaEngine/Module/Module.php @@ -40,9 +40,9 @@ class Module */ protected $routesBackend = array(); /** - * @var array routes in CLI + * @var array routes in console mode */ - protected $routesCLI = array(); + protected $routesConsole = array(); /** * @var array 监听器 */ @@ -185,17 +185,17 @@ public function setRoutesBackend($routesBackend) /** * @return array */ - public function getRoutesCLI() + public function getRoutesConsole() { - return $this->routesCLI; + return $this->routesConsole; } /** - * @param array $routesCLI + * @param array $routesConsole */ - public function setRoutesCLI($routesCLI) + public function setRoutesConsole($routesConsole) { - $this->routesCLI = $routesCLI; + $this->routesConsole = $routesConsole; } /** diff --git a/src/EvaEngine/Module/ModuleParser.php b/src/EvaEngine/Module/ModuleParser.php index c7a6670..2daf82b 100644 --- a/src/EvaEngine/Module/ModuleParser.php +++ b/src/EvaEngine/Module/ModuleParser.php @@ -29,10 +29,11 @@ public function __construct($defaultModulesPath) protected function parseOptions(Module $module, $options) { // 模块定义所在的文件名 - $options['path'] = isset($options['path']) ? + $options['path'] = !empty($options['path']) ? $options['path'] : - rtrim($this->defaultModulesPath, '/') . '/Module.php'; + rtrim($this->defaultModulesPath, '/') . '/' . $module->getName() . '/Module.php'; $options['path'] = realpath($options['path']); + // 模块根目录 $options['dir'] = isset($options['dir']) ? @@ -41,7 +42,7 @@ protected function parseOptions(Module $module, $options) $options['dir'] = realpath(rtrim($options['dir'], '/')); // 模块定义的类名 $options['className'] = isset($options['className']) ? - $options['className'] : $module->getName() . '\\Module'; + $options['className'] : 'Eva\\' . $module->getName() . '\\Module'; // 是否允许加载前台路由,默认是允许 $options['routesFrontend'] = isset($options['routesFrontend']) ? @@ -51,6 +52,10 @@ protected function parseOptions(Module $module, $options) $options['routesBackend'] = isset($options['routesBackend']) ? boolval($options['routesBackend']) : true; + // 是否允许加载 console 路由,默认是允许 + $options['routesConsole'] = isset($options['routesConsole']) ? + boolval($options['routesConsole']) : true; + // 是否允许加载模块的配置文件,默认为允许 $options['configEnable'] = isset($options['configEnable']) ? boolval($options['configEnable']) : true; @@ -79,7 +84,7 @@ protected function parseOptions(Module $module, $options) } - public function parse($name, $options) + public function parse($name, $options = array()) { $module = new Module($name); @@ -92,6 +97,7 @@ public function parse($name, $options) ->parseConfig($module, $options) ->parseRoutesFrontend($module, $options) ->parseRoutesBackend($module, $options) + ->parseRoutesConsole($module, $options) ->parseAdminMenus($module, $options) ->parseListeners($module, $options) ->parseRelations($module, $options) @@ -106,14 +112,18 @@ protected function parseErrorHandler(Module $module, $options) { if ($options['errorHandlerEnable']) { $moduleClass = $module->getClassName(); - $module->setErrorHandlers($moduleClass::registerErrorHandlers()); + $errorHandlers = $moduleClass::registerErrorHandlers(); + $errorHandlers = !is_array($errorHandlers) ? array() : $errorHandlers; + $module->setErrorHandlers($errorHandlers); } } protected function parseGlobalAutoLoaders(Module $module, $options) { $moduleClass = $module->getClassName(); - $module->setAutoLoaders($moduleClass::registerGlobalAutoloaders()); + $autoLoaders = $moduleClass::registerGlobalAutoloaders(); + $autoLoaders = !is_array($autoLoaders) ? array() : $autoLoaders; + $module->setAutoLoaders($autoLoaders); return $this; } @@ -122,7 +132,9 @@ protected function parseViewHelpers(Module $module, $options) { $moduleClass = $module->getClassName(); if ($options['viewHelpersEnable']) { - $module->setViewHelpers($moduleClass::registerGlobalViewHelpers()); + $viewHelpers = $moduleClass::registerGlobalViewHelpers(); + $viewHelpers = !is_array($viewHelpers) ? array() : $viewHelpers; + $module->setViewHelpers($viewHelpers); } return $this; @@ -132,7 +144,9 @@ protected function parseListeners(Module $module, $options) { $moduleClass = $module->getClassName(); if ($options['eventsEnable']) { - $module->setListeners($moduleClass::registerGlobalEventListeners()); + $listeners = $moduleClass::registerGlobalEventListeners(); + $listeners = !is_array($listeners) ? array() : $listeners; + $module->setListeners($listeners); } return $this; @@ -142,7 +156,9 @@ protected function parseRelations(Module $module, $options) { $moduleClass = $module->getClassName(); if ($options['relationsEnable']) { - $module->setRelations($moduleClass::registerGlobalRelations()); + $relations = $moduleClass::registerGlobalRelations(); + $relations = !is_array($relations) ? array() : $relations; + $module->setRelations($relations); } return $this; @@ -166,7 +182,9 @@ protected function parseConfig(Module $module, $options) ); } if (is_file($module->getDir() . '/config/config.php')) { - $moduleConfig->merge(new Config(include $module->getDir() . '/config/config.php')); + $config = include $module->getDir() . '/config/config.php'; + + $moduleConfig->merge($config instanceof Config ? $config : new Config($config)); } } @@ -209,6 +227,16 @@ protected function parseRoutesFrontend(Module $module, $options) return $this; } + protected function parseRoutesConsole(Module $module, $options) + { + // 加载模块的前台路由 + if ($options['routesConsole'] && is_file($module->getDir() . '/config/routes.console.php')) { + $module->setRoutesConsole(include $module->getDir() . '/config/routes.console.php'); + } + + return $this; + } + protected function parseRoutesBackend(Module $module, $options) { // 加载模块的后台路由 @@ -222,7 +250,7 @@ protected function parseRoutesBackend(Module $module, $options) protected function parseAdminMenus(Module $module, $options) { // 加载模块的管理后台菜单 - if ($options['adminMenus'] && is_file($module->getDir() . '/config/admin.menu.php')) { + if ($options['adminMenusEnable'] && is_file($module->getDir() . '/config/admin.menu.php')) { $module->setAdminMenusFile($module->getDir() . '/config/admin.menu.php'); } diff --git a/src/EvaEngine/Mvc/Model.php b/src/EvaEngine/Mvc/Model.php index 5449904..5803090 100644 --- a/src/EvaEngine/Mvc/Model.php +++ b/src/EvaEngine/Mvc/Model.php @@ -144,7 +144,7 @@ public function dump(array $dataStructure = null) */ public function loadRelations() { - $relations = $this->getDI()->getModuleManager()->getInjectRelations($this); + $relations = eva_get('moduleManager')->getInjectedRelations($this); if (!$relations) { return $this; } diff --git a/src/EvaEngine/Mvc/View.php b/src/EvaEngine/Mvc/View.php index f9d46aa..5bd87fa 100644 --- a/src/EvaEngine/Mvc/View.php +++ b/src/EvaEngine/Mvc/View.php @@ -85,12 +85,11 @@ public function getModuleLayout() */ public function setModuleLayout($moduleName, $layoutPath) { - $moduleManager = $this->getDI()->getModuleManager(); + $moduleManager = eva_get('moduleManager'); if (!$moduleManager) { return $this; } - - $moduleLayout = $moduleManager->getModulePath($moduleName) . $layoutPath; + $moduleLayout = $moduleManager->getModule($moduleName)->getDir() . $layoutPath; $this->moduleLayout = realpath(dirname($moduleLayout)); $this->moduleLayoutName = basename($moduleLayout); if ($this->moduleViewsDir) { @@ -115,12 +114,12 @@ public function getModuleViewsDir() */ public function setModuleViewsDir($moduleName, $viewsDir) { - $moduleManager = $this->getDI()->getModuleManager(); + $moduleManager = eva_get('moduleManager'); if (!$moduleManager) { return $this; } - $modulePath = $moduleManager->getModulePath($moduleName); + $modulePath = $moduleManager->getModule($moduleName)->getDir(); $this->moduleViewsDir = $moduleViewsDir = realpath($modulePath . $viewsDir); $this->setViewsDir($moduleViewsDir); if ($this->moduleLayout) { @@ -140,12 +139,12 @@ public function setModuleViewsDir($moduleName, $viewsDir) */ public function setModulePartialsDir($moduleName, $partialsDir) { - $moduleManager = $this->getDI()->getModuleManager(); + $moduleManager = eva_get('moduleManager'); if (!$moduleManager) { return $this; } - $modulePath = $moduleManager->getModulePath($moduleName); + $modulePath = $moduleManager->getModule($moduleName)->getDir(); $this->modulePartialsDir = $modulePartialsDir = realpath($modulePath . $partialsDir); if ($this->moduleViewsDir) { $this->caculatePartialsRelatedPath(); diff --git a/src/EvaEngine/Mvc/WebApplication.php b/src/EvaEngine/Mvc/WebApplication.php new file mode 100644 index 0000000..690fbed --- /dev/null +++ b/src/EvaEngine/Mvc/WebApplication.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +// + Datetime: 15/6/17 下午6:54 +// +---------------------------------------------------------------------- +// + WebApplication.php +// +---------------------------------------------------------------------- + +namespace Eva\EvaEngine\Mvc; + + +use Eva\EvaEngine\Exception\RuntimeException; +use Eva\EvaEngine\Foundation\ApplicationInterface; +use Eva\EvaEngine\Module\Manager; +use Phalcon\Mvc\Application; +use Phalcon\Mvc\Router; +use Whoops\Provider\Phalcon\WhoopsServiceProvider; + +class WebApplication extends Application implements ApplicationInterface +{ + protected $name = 'evaengine'; + + public function __construct($name = null) + { + $this->name = $name; + $this->name = empty($_SERVER['APPLICATION_NAME']) ? $name : $_SERVER['APPLICATION_NAME']; + $this->environment = empty($_SERVER['APPLICATION_ENV']) ? 'development' : $_SERVER['APPLICATION_ENV']; + } + + public function initializeErrorHandler() + { + $whoops = new WhoopsServiceProvider($this->di); + eva_get('whoops.pretty_page_handler')->setEditor(function ($file, $line) { + return "phpstorm://open?file=$file&line=$line"; + }); + return $this; + } + + public function fire() + { + $response = $this->handle(); + echo $response->getContent(); + } + + public function getName() + { + return $this->name; + } + + public static function initializeRouter(Manager $moduleManager, Router &$router) + { + + //NOTICE: EvaEngine Load front-end router at last + + $routes = $moduleManager->getAllWebRoutes(); + foreach ($routes->toArray() as $url => $route) { + if (count($route) !== count($route, COUNT_RECURSIVE)) { + if (isset($route['pattern']) && isset($route['paths'])) { + $method = isset($route['httpMethods']) ? $route['httpMethods'] : null; + $router->add($route['pattern'], $route['paths'], $method); + } else { + throw new RuntimeException( + sprintf('No route pattern and paths found by route %s', $url) + ); + } + } else { + $router->add($url, $route); + } + } + } + + public function initialize() + { + $moduleManager = eva_get('moduleManager'); + $router = eva_get('router'); + static::initializeRouter($moduleManager, $router); + + $this->setEventsManager(eva_get('eventsManager')); + $this->registerModules($moduleManager->getModulesForPhalcon()); + $this->setDefaultModule($moduleManager->getDefaultModule()->getName()); + + return $this; + } + +} \ No newline at end of file diff --git a/src/EvaEngine/Session/SessionAdapterCreator.php b/src/EvaEngine/Session/SessionAdapterCreator.php index 9dccfad..55c605c 100644 --- a/src/EvaEngine/Session/SessionAdapterCreator.php +++ b/src/EvaEngine/Session/SessionAdapterCreator.php @@ -17,15 +17,6 @@ class SessionAdapterCreator extends AdapterCreator { - protected static $adapterMapping = array( - 'files' => 'Phalcon\Session\Adapter\Files', - 'database' => 'Phalcon\Session\Adapter\Database', - 'memcache' => 'Phalcon\Session\Adapter\Memcache', - 'libmemcached' => 'Eva\EvaEngine\Session\Adapter\Libmemcached', - 'mongo' => 'Phalcon\Session\Adapter\Mongo', - 'redis' => 'Phalcon\Session\Adapter\Redis', - 'handlersocket' => 'Phalcon\Session\Adapter\HandlerSocket', - ); public function create( $adapter, diff --git a/src/EvaEngine/helpers.php b/src/EvaEngine/helpers.php index 7eb0f16..52616fb 100644 --- a/src/EvaEngine/helpers.php +++ b/src/EvaEngine/helpers.php @@ -158,6 +158,6 @@ function eva_domain($domainName) */ function eva_get($serviceName) { - return IoC::get($serviceName); + return \Phalcon\DI::getDefault()->get($serviceName); } } \ No newline at end of file