From: Andrey Kutejko Date: Thu, 14 Mar 2019 18:18:17 +0000 (+0100) Subject: introduce pimple as DI container X-Git-Url: https://git.andy128k.dev/?a=commitdiff_plain;h=d15424848a6e0f3f85a5863ebdb116e3778fa688;p=ipf.git introduce pimple as DI container --- diff --git a/composer.json b/composer.json index 122b559..01a35ac 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "andy128k/migrations": "dev-master", "andy128k/pegp": "0.1.*@dev", "andy128k/routeexpression": "dev-master", - "andy128k/ipf-legacy-template": "dev-master" + "andy128k/ipf-legacy-template": "dev-master", + "pimple/pimple": "~3.0" }, "require-dev": { "phpunit/phpunit": "5" diff --git a/composer.lock b/composer.lock index 348c147..6c907b7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "79837da54a36204e6bdf7388ad043a9b", + "content-hash": "6efa15ba9b218a571e0da69898cfc2d1", "packages": [ { "name": "andy128k/ipf-legacy-template", @@ -918,6 +918,105 @@ "exception" ], "time": "2015-02-10T20:07:52+00:00" + }, + { + "name": "pimple/pimple", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "21e3e0dc89183c96fad7f12e09e1d345558e9a56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/21e3e0dc89183c96fad7f12e09e1d345558e9a56", + "reference": "21e3e0dc89183c96fad7f12e09e1d345558e9a56", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/container": "^1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "https://pimple.symfony.com", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2018-04-20T06:17:25+00:00" + }, + { + "name": "psr/container", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "014d250daebff39eba15ba990eeb2a140798e77c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/014d250daebff39eba15ba990eeb2a140798e77c", + "reference": "014d250daebff39eba15ba990eeb2a140798e77c", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2018-12-29T15:36:03+00:00" } ], "packages-dev": [ diff --git a/ipf/admin/app.php b/ipf/admin/app.php index cfd0c08..ab04895 100644 --- a/ipf/admin/app.php +++ b/ipf/admin/app.php @@ -1,9 +1,23 @@ container = $container; + } + public static function urls($prefix='admin') { return array('prefix' => '#^/'.$prefix.'/', 'urls' => array( @@ -36,13 +50,21 @@ class IPF_Admin_App extends IPF_Application public function appList($request) { + /** @var Connection $connection */ + $connection = $this->container['db']; + /** @var IPF_Application[] $apps */ + $apps = $this->container['apps']; + $app_list = array(); - foreach ($this->getProject()->appList() as $app) { + foreach ($apps as $app) { $components = array(); foreach ($this->applicationComponents($app) as $component) { if ($component->isAccessible(array('view'), $request)) { - $component->request = $request; - $components[] = $component; + $components[] = (object)array( + 'slug' => $component->slug(), + 'name' => $component->verbose_name(), + 'can_add' => $component->isAccessible(array('add'), $request), + ); } } @@ -68,6 +90,10 @@ class IPF_Admin_App extends IPF_Application private $appComponents = array(); + /** + * @param IPF_Application $app + * @return IPF_Admin_Component[] + */ public function applicationComponents($app) { if (!array_key_exists($app->slug(), $this->appComponents)) { @@ -79,8 +105,10 @@ class IPF_Admin_App extends IPF_Application $section = new IPF_Admin_SimpleSection($section); } - foreach ($section->components($this->getProject(), $app) as $component) { + foreach ($section->components($this->container, $app) as $component) { $component->app = $app; + $component->admin_log = $this->container['admin_log']; + $component->auth_app = $this->container['apps']['auth']; $components[] = $component; } } @@ -92,7 +120,10 @@ class IPF_Admin_App extends IPF_Application public function getApplicationBySlug($slug) { - foreach ($this->getProject()->appList() as $app) + /** @var IPF_Application[] $apps */ + $apps = $this->container['apps']; + + foreach ($apps as $app) if ($app->slug() === $slug) return $app; return null; @@ -113,7 +144,7 @@ class IPF_Admin_App extends IPF_Application public function commands() { return array( - new IPF_Admin_Command_SyncPerms($this), + new IPF_Admin_Command_SyncPerms($this, $this->container['db']), ); } } @@ -133,4 +164,3 @@ class IPF_Admin_AccessDenied extends IPF_Router_Shortcut return new IPF_HTTP_Response_NotFound($request); } } - diff --git a/ipf/admin/commands/syncperms.php b/ipf/admin/commands/syncperms.php index 3fa8e7b..7210c41 100644 --- a/ipf/admin/commands/syncperms.php +++ b/ipf/admin/commands/syncperms.php @@ -1,5 +1,7 @@ admin_app = $admin_app; + $this->connection = $connection; } public function run($args=null) @@ -54,8 +60,7 @@ class IPF_Admin_Command_SyncPerms private function fetchExistingPermissions() { - $connection = \PFF\Container::databaseConnection(); - $stmt = $connection->prepare('SELECT name FROM auth_permission ORDER BY name'); + $stmt = $this->connection->prepare('SELECT name FROM auth_permission ORDER BY name'); $stmt->execute(); $existing = []; foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { @@ -66,16 +71,14 @@ class IPF_Admin_Command_SyncPerms private function addPermission($permission) { - $connection = \PFF\Container::databaseConnection(); - $stmt = $connection->prepare('INSERT INTO auth_permission (name) VALUES (:name)'); + $stmt = $this->connection->prepare('INSERT INTO auth_permission (name) VALUES (:name)'); $stmt->bindValue('name', $permission, PDO::PARAM_STR); $stmt->execute(); } private function deletePermission($permission) { - $connection = \PFF\Container::databaseConnection(); - $stmt = $connection->prepare('DELETE FROM auth_permission WHERE name = :name'); + $stmt = $this->connection->prepare('DELETE FROM auth_permission WHERE name = :name'); $stmt->bindValue('name', $permission, PDO::PARAM_STR); $stmt->execute(); } diff --git a/ipf/admin/component.php b/ipf/admin/component.php index 4d32842..272ee74 100644 --- a/ipf/admin/component.php +++ b/ipf/admin/component.php @@ -1,8 +1,8 @@ request; if (!$request) throw new IPF_Exception('No request.'); if (count(array_diff($what, $this->getPerms($request)))) return false; - if ($request->user->is_superuser || !\PFF\Container::auth()->arePermissionsEnabled()) - return true; - $authPermissions = F::bind('array_map', - F::bind('sprintf', '%s|%s|%s', $this->app->slug(), $this->slug(), P::p()), - P::p()) + F::bind('sprintf', '%s|%s|%s', $this->app->slug(), $this->slug(), P::p()), + P::p()) ->call($what); - return $request->user->can($authPermissions); + return $this->auth_app->userCan($request->user, $authPermissions); } protected function context($request) @@ -184,7 +183,7 @@ abstract class IPF_Admin_Component if ($form->isValid()) { list($id, $object) = $this->saveObject($form, null); - IPF_Admin_Log::logObject($this, 'add', $object, $id); + $this->admin_log->logObject($this, 'add', $object, $id); return $this->changeSucceed(); } $errors = true; @@ -206,6 +205,9 @@ abstract class IPF_Admin_Component 'errors' => $errors, 'objecttools' => array(), 'ipf_referrer' => $this->referrerOfChangeAction(), + 'can_add' => $this->isAccessible(array('add'), $this->request), + 'can_change' => $this->isAccessible(array('change'), $this->request), + 'can_delete' => $this->isAccessible(array('delete'), $this->request), )); } @@ -216,7 +218,7 @@ abstract class IPF_Admin_Component throw new IPF_HTTP_Error404; if ($this->request->method == 'POST') { - IPF_Admin_Log::logObject($this, 'delete', $object); + $this->admin_log->logObject($this, 'delete', $object); $this->deleteObject($object); return $this->changeSucceed(); } @@ -243,7 +245,7 @@ abstract class IPF_Admin_Component if ($form->isValid()) { list($id, $object) = $this->saveObject($form, $object); - IPF_Admin_Log::logObject($this, 'change', $object, $id); + $this->admin_log->logObject($this, 'change', $object, $id); return $this->changeSucceed(); } $errors = true; @@ -280,6 +282,9 @@ abstract class IPF_Admin_Component 'errors' => $errors, 'objecttools' => $objecttools, 'ipf_referrer' => $this->referrerOfChangeAction(), + 'can_add' => $this->isAccessible(array('add'), $this->request), + 'can_change' => $this->isAccessible(array('change'), $this->request), + 'can_delete' => $this->isAccessible(array('delete'), $this->request), )); } @@ -356,6 +361,7 @@ abstract class IPF_Admin_Component 'filters' => $filters, 'is_search' => $this->searcheable(), 'search_value' => $searchValue, + 'can_add' => $this->isAccessible(array('add'), $this->request), )); } diff --git a/ipf/admin/controllers/components.php b/ipf/admin/controllers/components.php index 86b0e5d..7786173 100644 --- a/ipf/admin/controllers/components.php +++ b/ipf/admin/controllers/components.php @@ -69,6 +69,7 @@ class IPF_Admin_Components_Controller extends IPF_Admin_Base_Controller protected function checkPermissions() { + $connection = $this->container['db']; $requiredPermissions = func_get_args(); if (!$this->component->isAccessible($requiredPermissions, $this->request)) throw new IPF_Admin_AccessDenied; diff --git a/ipf/admin/controllers/dashboard.php b/ipf/admin/controllers/dashboard.php index 277b4e3..35d514b 100644 --- a/ipf/admin/controllers/dashboard.php +++ b/ipf/admin/controllers/dashboard.php @@ -2,13 +2,21 @@ class IPF_Admin_Dashboard_Controller extends IPF_Admin_Base_Controller { + /** + * @return IPF_Admin_ILog + */ + private function adminLog() + { + return $this->container['admin_log']; + } + function index() { $this->ensureUserIsStaff(); $context = array( 'page_title' => __('Site Administration'), - 'admin_log' => IPF_Admin_Log::recent(), + 'admin_log' => $this->adminLog()->recent(), ); return $this->render('admin/index.html', $context); } @@ -19,7 +27,7 @@ class IPF_Admin_Dashboard_Controller extends IPF_Admin_Base_Controller if (!$currentPage) $currentPage = 1; - $count = IPF_Admin_Log::count(); + $count = $this->adminLog()->count(); $perPage = 20; $pagerLayout = new IPF_Pager_Layout; @@ -27,7 +35,7 @@ class IPF_Admin_Dashboard_Controller extends IPF_Admin_Base_Controller $context = array( 'page_title' => __('Recent Actions'), - 'admin_log' => IPF_Admin_Log::recent($perPage, ($currentPage - 1) * $perPage), + 'admin_log' => $this->adminLog()->recent($perPage, ($currentPage - 1) * $perPage), 'count' => $count, 'current_page' => $currentPage, 'pages' => $pages, @@ -35,4 +43,3 @@ class IPF_Admin_Dashboard_Controller extends IPF_Admin_Base_Controller return $this->render('admin/log.html', $context); } } - diff --git a/ipf/admin/controllers/user.php b/ipf/admin/controllers/user.php index 00319fa..8a56139 100644 --- a/ipf/admin/controllers/user.php +++ b/ipf/admin/controllers/user.php @@ -20,14 +20,16 @@ class IPF_Admin_User_Controller extends IPF_Admin_Base_Controller if (!$success_url) $success_url = $this->project->router->reverse(array('IPF_Admin_Dashboard_Controller', 'index')); + $auth_app = $this->authApp(); + if ($this->request->method == 'POST') { - $form = new IPF_Admin_Forms_Login($this->request->POST); + $form = new IPF_Admin_Forms_Login($this->request->POST, array('auth_app' => $auth_app)); if ($form->isValid()) { - $this->authApp()->login($this->request, $form->user); + $auth_app->login($this->request, $form->user); return new IPF_HTTP_Response_Redirect($success_url); } } else { - $form = new IPF_Admin_Forms_Login(array('next'=>$success_url)); + $form = new IPF_Admin_Forms_Login(array('next'=>$success_url), array('auth_app' => $auth_app)); } $context = array( diff --git a/ipf/admin/forms/login.php b/ipf/admin/forms/login.php index b05b32f..55289f3 100644 --- a/ipf/admin/forms/login.php +++ b/ipf/admin/forms/login.php @@ -5,8 +5,13 @@ class IPF_Admin_Forms_Login extends IPF_Form public $message = null; public $user = null; + /** @var IPF_Auth_App */ + private $auth_app; + protected function initFields($extra=array()) { + $this->auth_app = $extra['auth_app']; + $this->fields['username'] = new IPF_Form_Field_Varchar(array('required'=>true)); $this->fields['password'] = new IPF_Form_Field_Varchar(array('required'=>true,'widget'=>'IPF_Form_Widget_PasswordInput')); $this->fields['next'] = new IPF_Form_Field_Varchar(array('required'=>false,'widget'=>'IPF_Form_Widget_HiddenInput')); @@ -16,7 +21,7 @@ class IPF_Admin_Forms_Login extends IPF_Form { $data = parent::clean(); - $user = \PFF\Container::auth()->findUserByUsername($data['username']); + $user = $this->auth_app->findUserByUsername($data['username']); if (!$user || !$user->checkPassword($data['password'])) throw new IPF_Exception_Form(__('The login or the password is not valid. The login and the password are case sensitive.')); @@ -29,4 +34,3 @@ class IPF_Admin_Forms_Login extends IPF_Form return $this->renderLayout(new IPF_Admin_Form_Layout); } } - diff --git a/ipf/admin/log.php b/ipf/admin/log.php index 747cbbd..3750655 100644 --- a/ipf/admin/log.php +++ b/ipf/admin/log.php @@ -1,30 +1,71 @@ connection = $connection; + $this->router = $router; + } + + public function count() { - $connection = \PFF\Container::databaseConnection(); - $stmt = $connection->prepare('SELECT COUNT(1) AS cnt FROM admin_log'); + $stmt = $this->connection->prepare('SELECT COUNT(1) AS cnt FROM admin_log'); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row['cnt']; } - public static function recent($limit=10, $offset=0) + public function recent($limit=10, $offset=0) { - $connection = \PFF\Container::databaseConnection(); - $stmt = $connection->prepare('SELECT * FROM admin_log ORDER BY created_at DESC LIMIT :limit OFFSET :offset'); + $stmt = $this->connection->prepare('SELECT * FROM admin_log ORDER BY created_at DESC LIMIT :limit OFFSET :offset'); $stmt->bindValue('limit', (int)$limit, PDO::PARAM_INT); $stmt->bindValue('offset', (int)$offset, PDO::PARAM_INT); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_OBJ); } - public static function log($who, $action, $repr, $class, $url=null) + public function log($who, $action, $repr, $class, $url=null) { - $connection = \PFF\Container::databaseConnection(); - $stmt = $connection->prepare('INSERT INTO admin_log ' . + $stmt = $this->connection->prepare('INSERT INTO admin_log ' . '(username, user_id, action, object_class, object_repr, object_url)' . ' VALUES ' . '(:username, :user_id, :action, :object_class, :object_repr, :object_url)' @@ -38,9 +79,11 @@ class IPF_Admin_Log $stmt->execute(); } - public static function logObject($component, $action, $object, $object_id=null) + public function logObject($component, $action, $object, $object_id=null) { - $url = $object_id ? IPF_HTTP_URL::urlForView(array('IPF_Admin_Components_Controller', 'editItem'), array($component->app->slug(), $component->slug(), $object_id)) : ''; - self::log($component->request->user, $action, (string)$object, $component->verbose_name(), $url); + $url = $object_id + ? $this->router->reverse(['IPF_Admin_Components_Controller', 'editItem'], [$component->app->slug(), $component->slug(), $object_id]) + : ''; + $this->log($component->request->user, $action, (string)$object, $component->verbose_name(), $url); } } diff --git a/ipf/admin/section.php b/ipf/admin/section.php index 1f2cac7..ac52878 100644 --- a/ipf/admin/section.php +++ b/ipf/admin/section.php @@ -1,13 +1,15 @@ component_names as $c) { diff --git a/ipf/admin/templates/admin/base.html b/ipf/admin/templates/admin/base.html index 882bccd..d4dee94 100644 --- a/ipf/admin/templates/admin/base.html +++ b/ipf/admin/templates/admin/base.html @@ -45,7 +45,7 @@

{$app.name}

diff --git a/ipf/admin/templates/admin/change.html b/ipf/admin/templates/admin/change.html index a8482dd..2126e49 100644 --- a/ipf/admin/templates/admin/change.html +++ b/ipf/admin/templates/admin/change.html @@ -44,9 +44,9 @@ {/block}
- {if ($mode=='change') && $component->isAccessible(array('delete'))}

{trans 'Delete'}

{/if} - {if ($mode=='change') && $component->isAccessible(array('change'))}{/if} - {if ($mode=='add') && $component->isAccessible(array('add'))}{/if} + {if ($mode=='change') && $can_delete}

{trans 'Delete'}

{/if} + {if ($mode=='change') && $can_change}{/if} + {if ($mode=='add') && $can_add}{/if}
diff --git a/ipf/admin/templates/admin/index.html b/ipf/admin/templates/admin/index.html index 95b300b..88f673b 100644 --- a/ipf/admin/templates/admin/index.html +++ b/ipf/admin/templates/admin/index.html @@ -14,9 +14,9 @@ {foreach $app.components as $component} - {$component->verbose_name()} - {if $component->isAccessible(array('add'))}{trans 'Add'}{/if} - {trans 'Change'} + {$component->name} + {if $component->can_add}{trans 'Add'}{/if} + {trans 'Change'} {/foreach} diff --git a/ipf/admin/templates/admin/items.html b/ipf/admin/templates/admin/items.html index bbf31c8..6b48a6d 100644 --- a/ipf/admin/templates/admin/items.html +++ b/ipf/admin/templates/admin/items.html @@ -8,7 +8,7 @@
diff --git a/ipf/application.php b/ipf/application.php index 71bb436..3c6da45 100644 --- a/ipf/application.php +++ b/ipf/application.php @@ -2,54 +2,35 @@ abstract class IPF_Application { - /** @var IPF_Project */ - private $project; - - /** @var string */ - private $name, $path; - - public function __construct(IPF_Project $project) - { - $this->project = $project; - - $this->name = preg_replace('/_App$/', '', get_class($this)); - - $rc = new ReflectionClass($this); - $this->path = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR; - } - - public function getProject() + /** + * Configures application + * + * @param \Pimple\Container $container + * @param IPF_Settings $settings + */ + public function configure(\Pimple\Container $container, IPF_Settings $settings) { - return $this->project; } public function getName() { - return $this->name; + return preg_replace('/_App$/', '', get_class($this)); } public function getTitle() { - return $this->name; + return $this->getName(); } public function getPath() { - return $this->path; + $rc = new ReflectionClass($this); + return dirname($rc->getFileName()) . DIRECTORY_SEPARATOR; } public function slug() { - return strtolower(preg_replace('/^IPF_/', '', $this->name)); - } - - /** - * Configures application - * - * @param IPF_Settings $settings - */ - public function configure(IPF_Settings $settings) - { + return strtolower(preg_replace('/^IPF_/', '', preg_replace('/_App$/', '', get_class($this)))); } /** diff --git a/ipf/auth/admin.php b/ipf/auth/admin.php index 5d222d2..7f35324 100644 --- a/ipf/auth/admin.php +++ b/ipf/auth/admin.php @@ -2,13 +2,17 @@ namespace IPF\Auth\Admin; +use Doctrine\DBAL\Connection; +use IPF\Auth\Permission as Permission; +use IPF\Auth\Role as Role; use IPF_Admin_App; -use IPF_Auth_App; -use IPF_Project; +use IPF_Admin_Component; +use IPF_Admin_ILog; use IPF_Application; -use \IPF\Auth\Role as Role; -use \IPF\Auth\Permission as Permission; -use \PFF\HtmlBuilder\Text as Text; +use IPF_Auth_App; +use IPF_Database; +use PFF\HtmlBuilder\Text as Text; +use Pimple\Container; class UserForm extends \IPF_ObjectForm { @@ -18,6 +22,7 @@ class UserForm extends \IPF_ObjectForm { $auth_app = $extra['auth_app']; $permissions_choices = $extra['permissions_choices']; + $roles_choices = $extra['roles_choices']; $this->isAdd = $extra['is_add']; @@ -78,7 +83,7 @@ class UserForm extends \IPF_ObjectForm $this->fields['roles'] = new \IPF_Admin_Form_Field_MultipleChoice(array( 'label' => __('Groups'), 'help_text' => __('In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in.'), - 'choices' => \PFF\Collection::columnByProperty(Role::all(), 'id', 'name'), + 'choices' => $roles_choices, )); } @@ -109,16 +114,13 @@ class UserForm extends \IPF_ObjectForm abstract class Component extends \IPF_Admin_Component { - /** @var IPF_Project */ - protected $project; - - /** @var IPF_Auth_App */ - private $auth_app; + /** @var Container */ + protected $container; - function __construct(IPF_Project $project, IPF_Auth_App $auth_app) + function __construct(Container $container, IPF_Auth_App $auth_app) { - $this->project = $project; $this->auth_app = $auth_app; + $this->container = $container; } /** @@ -129,10 +131,13 @@ abstract class Component extends \IPF_Admin_Component return $this->auth_app; } - protected function permissionChoices() + protected function permissionChoices(Connection $connection) { + /** @var IPF_Application[] $apps */ + $apps = $this->container['apps']; + $admin_app = null; - foreach ($this->project->appList() as $a) + foreach ($apps as $a) if ($a instanceof IPF_Admin_App) $admin_app = $a; @@ -140,7 +145,7 @@ abstract class Component extends \IPF_Admin_Component throw new \Exception('Admin application is not registered.'); $choices = array(); - foreach (Permission::all() as $p) { + foreach (Permission::all($connection) as $p) { list($appSlug, $componentSlug, $permission) = explode('|', $p->name); $component = $admin_app->getComponentBySlugs($appSlug, $componentSlug); @@ -155,15 +160,22 @@ abstract class Component extends \IPF_Admin_Component return $choices; } + + /** + * @return Connection + */ + protected function getConnection() + { + return $this->container['db']; + } } class AdminUser extends Component { public function getItems($searchValue, $filters, $page, $pageSize) { - $connection = \PFF\Container::db(); $userModelClass = $this->authApp()->userModel; - $items = \IPF_Database::queryAllObjects($connection, $userModelClass, + $items = \IPF_Database::queryAllObjects($this->getConnection(), $userModelClass, 'SELECT * FROM auth_user' . ' ORDER BY username' . ' LIMIT ' . $pageSize . @@ -174,7 +186,7 @@ class AdminUser extends Component public function itemsCount($searchValue, $filters) { - return \PFF\Container::db()->fetchColumn('SELECT COUNT(1) FROM auth_user'); + return $this->getConnection()->fetchColumn('SELECT COUNT(1) FROM auth_user'); } public function list_display() @@ -194,7 +206,7 @@ class AdminUser extends Component public function column_groups($obj) { - return Text::escape(implode(' / ', \PFF\Arr::pluck($obj->roles(), 'name'))); + return Text::escape(implode(' / ', \PFF\Arr::pluck($obj->roles($this->getConnection()), 'name'))); } function _searchFields() @@ -224,14 +236,17 @@ class AdminUser extends Component protected function _getForm($user, $data) { + $connection = $this->getConnection(); + $extra = array( 'auth_app' => $this->authApp(), - 'permissions_choices' => $this->permissionChoices(), + 'permissions_choices' => $this->permissionChoices($connection), + 'roles_choices' => \PFF\Collection::columnByProperty(Role::all($connection), 'id', 'name'), ); if ($user) { $extra['initial'] = array( - 'permissions' => \PFF\Arr::pluck($user->permissions(), 'id'), - 'roles' => \PFF\Arr::pluck($user->roles(), 'id'), + 'permissions' => \PFF\Arr::pluck($user->permissions($connection), 'id'), + 'roles' => \PFF\Arr::pluck($user->roles($connection), 'id'), ); $extra['is_add'] = false; } else { @@ -242,22 +257,24 @@ class AdminUser extends Component public function saveObject($form, $user) { + $connection = $this->getConnection(); + if (!$user) $user = $this->authApp()->createUser(); $form->toObject($user); if ($form->cleaned_data['password1']) $user->setPassword($form->cleaned_data['password1']); - $user->save(); + $user->save($connection); if ($this->authApp()->arePermissionsEnabled()) { - Permission::revokeAll($user); - $permissions = Permission::findAll($form->cleaned_data['permissions']); - Permission::grantAll($permissions, $user); + Permission::revokeAll($connection, $user); + $permissions = Permission::findAll($connection, $form->cleaned_data['permissions']); + Permission::grantAll($connection, $permissions, $user); - Role::leaveAll($user); - foreach (Role::findAll($form->cleaned_data['roles']) as $role) { - $role->join($user); + Role::leaveAll($connection, $user); + foreach (Role::findAll($connection, $form->cleaned_data['roles']) as $role) { + $role->join($connection, $user); } } return array($user->id, $user); @@ -265,13 +282,15 @@ class AdminUser extends Component public function deleteObject($user) { - \PFF\Container::db()->delete('auth_user', ['id' => $user->id]); + $this->getConnection()->delete('auth_user', ['id' => $user->id]); } protected function objectTools($user) { + $router = $this->container['router']; + return array( - 'impersonate' => $this->project->router->reverse(array('IPF_Admin_User_Controller', 'impersonate'), array($user->id)), + 'impersonate' => $router->reverse(array('IPF_Admin_User_Controller', 'impersonate'), array($user->id)), ); } @@ -305,18 +324,17 @@ class AdminRole extends Component { public function getItems($searchValue, $filters, $page, $pageSize) { - $items = Role::queryAll('SELECT * FROM auth_role' . + $connection = $this->getConnection(); + $items = IPF_Database::queryAllObjects($connection, Role::class, 'SELECT * FROM auth_role' . ' ORDER BY name' . ' LIMIT ' . $pageSize . - ' OFFSET ' . (($page - 1) * $pageSize) - ); + ' OFFSET ' . (($page - 1) * $pageSize), [], []); return \PFF\Collection::indexByProperty($items, 'id'); } public function itemsCount($searchValue, $filters) { - $connection = \PFF\Container::db(); - return $connection->fetchColumn('SELECT COUNT(1) FROM auth_role'); + return $this->getConnection()->fetchColumn('SELECT COUNT(1) FROM auth_role'); } public function list_display() @@ -340,19 +358,21 @@ class AdminRole extends Component public function getObjectByID($id) { - return Role::queryOne('SELECT * FROM auth_role WHERE id = ?', [$id]); + return IPF_Database::queryOneObject($this->getConnection(), Role::class, 'SELECT * FROM auth_role WHERE id = ?', [$id], []); } protected function _getForm($role, $data) { + $connection = $this->getConnection(); + $extra = array( 'auth_app' => $this->authApp(), - 'permissions_choices' => $this->permissionChoices(), + 'permissions_choices' => $this->permissionChoices($connection), ); if ($role) { $extra['initial'] = array( - 'permissions' => \PFF\Arr::pluck($role->permissions(), 'id'), + 'permissions' => \PFF\Arr::pluck($role->permissions($connection, $connection), 'id'), ); return new RoleForm($data, $role, $extra); } else { @@ -362,22 +382,24 @@ class AdminRole extends Component public function saveObject($form, $role) { + $connection = $this->getConnection(); + if (!$role) $role = new Role; $form->toObject($role); - $role->save(); + $role->save($connection); - Permission::revokeAll($role); - $permissions = Permission::findAll($form->cleaned_data['permissions']); - Permission::grantAll($permissions, $role); + Permission::revokeAll($connection, $role); + $permissions = Permission::findAll($connection, $form->cleaned_data['permissions']); + Permission::grantAll($connection, $permissions, $role); return array($role->id, $role); } public function deleteObject($role) { - \PFF\Container::db()->delete('auth_role', ['id' => $role->id]); + $this->getConnection()->delete('auth_role', ['id' => $role->id]); } public function page_title() { return __('Role'); } @@ -387,16 +409,21 @@ class AdminRole extends Component class AuthAdminSection implements \IPF_Admin_ISection { - public function components(IPF_Project $project, IPF_Application $app) + /** + * @param Container $container + * @param IPF_Application $app + * @return IPF_Admin_Component[] + */ + public function components(Container $container, IPF_Application $app) { /** @var IPF_Auth_App $app */ $components = array( - new AdminUser($project, $app), + new AdminUser($container, $app), ); if ($app->arePermissionsEnabled()) { - $components[] = new AdminRole($project, $app); + $components[] = new AdminRole($container, $app); } return $components; diff --git a/ipf/auth/app.php b/ipf/auth/app.php index 1519050..1ad4ca9 100644 --- a/ipf/auth/app.php +++ b/ipf/auth/app.php @@ -1,11 +1,25 @@ container = $container; $this->userModel = $settings->get('auth_user_model', 'IPF\\Auth\\User'); } @@ -16,13 +30,13 @@ class IPF_Auth_App extends IPF_Application function findUser($id) { - $connection = \PFF\Container::db(); + $connection = $this->getConnection(); return \IPF_Database::queryOneObject($connection, $this->userModel, 'SELECT * FROM auth_user WHERE id = ?', [$id]); } function findUserByUsername($username) { - $connection = \PFF\Container::db(); + $connection = $this->getConnection(); return \IPF_Database::queryOneObject($connection, $this->userModel, 'SELECT * FROM auth_user WHERE username = ?', [$username]); } @@ -53,15 +67,31 @@ class IPF_Auth_App extends IPF_Application function arePermissionsEnabled() { if ($this->permissionsEnabled === null) { - $this->permissionsEnabled = (bool)\PFF\Container::db()->fetchColumn('SELECT COUNT(1) FROM auth_permission'); + $connection = $this->getConnection(); + $this->permissionsEnabled = (bool)$connection->fetchColumn('SELECT COUNT(1) FROM auth_permission'); } return $this->permissionsEnabled; } + public function userCan($user, $authPermissions) + { + return $user->is_superuser + || !$this->arePermissionsEnabled() + || $user->can($this->getConnection(), $authPermissions); + } + public function commands() { return array( - new \IPF\Auth\Commands\CreateSuperUser, + new \IPF\Auth\Commands\CreateSuperUser($this), ); } + + /** + * @return Connection + */ + private function getConnection() + { + return $this->container['db']; + } } diff --git a/ipf/auth/commands/createsuperuser.php b/ipf/auth/commands/createsuperuser.php index fa88d93..43a179d 100644 --- a/ipf/auth/commands/createsuperuser.php +++ b/ipf/auth/commands/createsuperuser.php @@ -7,6 +7,14 @@ class CreateSuperUser public $command = 'createsuperuser'; public $description = 'Create superuser'; + /** @var \IPF_Auth_App */ + private $auth_app; + + public function __construct(\IPF_Auth_App $auth_app) + { + $this->auth_app = $auth_app; + } + public function run($args=null) { print "Create superuser\n"; @@ -15,7 +23,7 @@ class CreateSuperUser $password = \IPF_Shell::ask(' Password: '); $email = \IPF_Shell::ask(' E-mail: '); - $su = \PFF\Container::auth()->createUser(); + $su = $this->auth_app->createUser(); $su->username = $username; $su->email = $email; $su->is_staff = true; @@ -26,4 +34,3 @@ class CreateSuperUser print "Done\n"; } } - diff --git a/ipf/auth/middleware.php b/ipf/auth/middleware.php index 2d203c8..ee2d567 100644 --- a/ipf/auth/middleware.php +++ b/ipf/auth/middleware.php @@ -12,7 +12,8 @@ class Middleware extends \IPF_Middleware $user_id = \PFF\Arr::get($request->session->data, self::SessionKey, 0); if ($user_id > 0) { - $request->user = \PFF\Container::auth()->findUser($user_id); + $auth_app = $this->container['apps']['auth']; + $request->user = $auth_app->findUser($user_id); if ($request->user && 43200 < time() - strtotime($request->user->last_login.' GMT')) { $request->user->last_login = gmdate('Y-m-d H:i:s'); $request->user->save(); @@ -31,4 +32,3 @@ class Middleware extends \IPF_Middleware return $response; } } - diff --git a/ipf/auth/models.php b/ipf/auth/models.php index 378ff8d..4af5767 100644 --- a/ipf/auth/models.php +++ b/ipf/auth/models.php @@ -2,24 +2,15 @@ namespace IPF\Auth; +use Doctrine\DBAL\Connection; +use IPF_Database; + abstract class DBObject { public $id; protected $table = 'specify table name here'; - public static function queryOne($sql, $params = [], $types = []) - { - $connection = \PFF\Container::db(); - return \IPF_Database::queryOneObject($connection, static::class, $sql, $params, $types); - } - - public static function queryAll($sql, $params = [], $types = []) - { - $connection = \PFF\Container::db(); - return \IPF_Database::queryAllObjects($connection, static::class, $sql, $params, $types); - } - - public function save() + public function save(Connection $connection) { $r = new \ReflectionObject($this); @@ -35,7 +26,6 @@ abstract class DBObject $values[$name] = $value; } - $connection = \PFF\Container::db(); if ($this->id) { $connection->update($this->table, $values, ['id' => $this->id]); } else { @@ -59,7 +49,7 @@ class AnonymousUser return true; } - public function can(/* $permission ... */) + public function can(Connection $connection, ...$permissions) { return false; } @@ -101,29 +91,25 @@ class User extends DBObject return false; } - public function permissions() + public function permissions(Connection $connection) { - return Permission::queryAll('SELECT auth_permission.* FROM auth_permission ' . + return IPF_Database::queryAllObjects($connection, Permission::class, 'SELECT auth_permission.* FROM auth_permission ' . ' INNER JOIN auth_user_permission ON permission_id = id ' . ' WHERE user_id = ? ' . - ' ORDER BY name', - [$this->id] - ); + ' ORDER BY name', [$this->id], []); } - public function roles() + public function roles(Connection $connection) { - return Role::queryAll('SELECT auth_role.* FROM auth_role ' . + return IPF_Database::queryAllObjects($connection, Role::class, 'SELECT auth_role.* FROM auth_role ' . ' INNER JOIN auth_user_role ON role_id = id ' . ' WHERE user_id = ? ' . - ' ORDER BY name', - [$this->id] - ); + ' ORDER BY name', [$this->id], []); } - public function effectivePermissions() + public function effectivePermissions(Connection $connection) { - return Permission::queryAll('SELECT auth_permission.* FROM auth_permission ' . + return IPF_Database::queryAllObjects($connection, Permission::class, 'SELECT auth_permission.* FROM auth_permission ' . 'WHERE ' . 'EXISTS(SELECT 1 FROM auth_user_permission AS up ' . ' WHERE up.permission_id = auth_permission.id' . @@ -133,16 +119,14 @@ class User extends DBObject ' WHERE rp.permission_id = auth_permission.id' . ' AND rp.role_id = ur.role_id' . ' AND ur.user_id = :user_id)' . - ' ORDER BY name', - ['user_id' => $this->id] - ); + ' ORDER BY name', ['user_id' => $this->id], []); } - public function can(/* $permission ... */) + public function can(Connection $connection, ...$permissions) { $cond = array(); $params = array(); - foreach (func_get_args() as $permission) { + foreach ($permissions as $permission) { if ($permission instanceof Permission) { $cond[] = 'id = ?'; $params[] = $permission->id; @@ -157,7 +141,7 @@ class User extends DBObject } } - return $this->effectivePermissions() + return $this->effectivePermissions($connection) ->select(null)->select('COUNT(1)') ->where(implode(' OR ', $cond), $params) ->fetchColumn() == count($params); @@ -185,6 +169,7 @@ class User extends DBObject class Role extends DBObject { protected $table = 'auth_role'; + /** @var string */ public $name; public function __toString() @@ -192,51 +177,46 @@ class Role extends DBObject return $this->name; } - public static function all() + public static function all(Connection $connection) { - return static::queryAll('SELECT * FROM auth_role ORDER BY name'); + return IPF_Database::queryAllObjects($connection, Role::class, 'SELECT * FROM auth_role ORDER BY name', [], []); } - public static function findAll($ids) + public static function findAll(Connection $connection, $ids) { if (!$ids) { return []; } - return static::queryAll('SELECT * FROM auth_role WHERE id IN (?) ORDER BY name', - [$ids], - [\Doctrine\DBAL\Connection::PARAM_INT_ARRAY] - ); + return IPF_Database::queryAllObjects($connection, Role::class, 'SELECT * FROM auth_role WHERE id IN (?) ORDER BY name', [$ids], [\Doctrine\DBAL\Connection::PARAM_INT_ARRAY]); } - public function permissions() + public function permissions(Connection $connection) { - return Permission::queryAll('SELECT auth_permission.* FROM auth_permission ' . + return IPF_Database::queryAllObjects($connection, Permission::class, 'SELECT auth_permission.* FROM auth_permission ' . ' INNER JOIN auth_role_permission ON permission_id = id ' . ' WHERE role_id = ? ' . - ' ORDER BY name', - [$this->id] - ); + ' ORDER BY name', [$this->id], []); } - public function join($user) + public function join(Connection $connection, $user) { - \PFF\Container::db()->insert('auth_user_role', ['user_id' => $user->id, 'role_id' => $this->id]); + $connection->insert('auth_user_role', ['user_id' => $user->id, 'role_id' => $this->id]); } - public function leave($user) + public function leave(Connection $connection, $user) { if ($this->id) { - self::leaveAll($user, $this->id); + self::leaveAll($connection, $user, $this->id); } } - public static function leaveAll($user, $only=null) + public static function leaveAll(Connection $connection, $user, $only=null) { $cond = array('user_id' => $user->id); if ($only) { $cond['role_id'] = $only; } - \PFF\Container::db()->delete('auth_user_role', $cond); + $connection->delete('auth_user_role', $cond); } } @@ -245,30 +225,27 @@ class Permission extends DBObject protected $table = 'auth_permission'; public $name; - public static function all() + public static function all(Connection $connection) { - return static::queryAll('SELECT * FROM auth_permission ORDER BY name'); + return IPF_Database::queryAllObjects($connection, Permission::class, 'SELECT * FROM auth_permission ORDER BY name', [], []); } - public static function findAll($ids) + public static function findAll(Connection $connection, $ids) { if (!$ids) { return []; } - return static::queryAll('SELECT * FROM auth_permission WHERE id IN (?) ORDER BY name', - [$ids], - [\Doctrine\DBAL\Connection::PARAM_INT_ARRAY] - ); + return IPF_Database::queryAllObjects($connection, Permission::class, 'SELECT * FROM auth_permission WHERE id IN (?) ORDER BY name', [$ids], [\Doctrine\DBAL\Connection::PARAM_INT_ARRAY]); } - public function grant($obj) + public function grant(Connection $connection, $obj) { list($table, $data) = self::link($obj); $data['permission_id'] = $this->id; - \PFF\Container::db()->insert($table, $data); + $connection->insert($table, $data); } - public static function grantAll($permissions, $obj) + public static function grantAll(Connection $connection, $permissions, $obj) { if (!$permissions) { return; @@ -277,25 +254,24 @@ class Permission extends DBObject list($table, $data) = self::link($obj); foreach ($permissions as $p) { $values = array_merge($data, array('permission_id' => $p->id)); - \PFF\Container::db()->insert($table, $values); + $connection->insert($table, $values); } } - public function revoke($obj) + public function revoke(Connection $connection, $obj) { if ($this->id) { - self::revokeAll($obj, $this->id); + self::revokeAll($connection, $obj, $this->id); } } - public static function revokeAll($obj, $only=null) + public static function revokeAll(Connection $connection, $obj, $only=null) { list($table, $cond) = self::link($obj); if ($only) { $cond['permission_id'] = $only; } - $connection = \PFF\Container::db(); $connection->delete($table, $cond); } diff --git a/ipf/cli.php b/ipf/cli.php index 58df021..bf69e69 100644 --- a/ipf/cli.php +++ b/ipf/cli.php @@ -8,23 +8,15 @@ public function run($args=null) { ... body */ -class IPF_Cli -{ - /** @var IPF_Project */ - private $project; +use Pimple\Container; +use Pimple\ServiceProviderInterface; - /** @var array */ - protected $commands; - - public function __construct(IPF_Project $project) - { - $this->project = $project; - } +class IPF_Cli_Provider implements ServiceProviderInterface { - public function findCommands() + public function register(Container $container) { - $this->commands = array( - 'IPF' => array( + $container['ipf_commands'] = function($c) { + return array( new IPF_Command_Shell, new IPF_Command_DebugServer, new IPF_Command_Routes, @@ -35,24 +27,54 @@ class IPF_Cli new IPF_Command_Pack, new IPF_Command_Unpack, new IPF_Command_CreateMigration, - new IPF_Command_Migrate, - ), - ); - - foreach ($this->project->appList() as $app) { - $commands = $app->commands(); - if (count($commands)) - $this->commands[$app->getName()] = $commands; - } + new IPF_Command_Migrate($c['apps'], $c['db']), + ); + }; + + $container['project_commands'] = function($c) { + $commands = array(); + foreach (IPF::get('commands', array()) as $cmd) { + if (is_string($cmd)) + $cmd = new $cmd; + $commands[] = $cmd; + } + return $commands; + }; + + $container['cli_commands'] = function ($c) { + $commands = [ + 'IPF' => $c['ipf_commands'], + ]; + + /** @var IPF_Application[] $apps */ + $apps = $c['apps']; + foreach ($apps as $app) { + $app_commands = $app->commands(); + if (count($app_commands)) + $commands[$app->getName()] = $app_commands; + } - $project = array(); - foreach (IPF::get('commands', array()) as $cmd) { - if (is_string($cmd)) - $cmd = new $cmd; - $project[] = $cmd; - } - if (count($project)) - $this->commands['Project'] = $project; + $project_commands = $c['project_commands']; + if (count($project_commands)) + $commands['Project'] = $project_commands; + + return $commands; + }; + + $container['cli'] = function ($c) { + return new IPF_Cli($c['cli_commands']); + }; + } +} + +class IPF_Cli +{ + /** @var array */ + private $commands; + + public function __construct(array $commands) + { + $this->commands = $commands; } private function getCommand($commandName) @@ -64,7 +86,7 @@ class IPF_Cli return null; } - protected function usage() + private function usage() { print "Usage: php index.php [options] [args]\n"; diff --git a/ipf/command/migrate.php b/ipf/command/migrate.php index ed50c45..2535ec9 100644 --- a/ipf/command/migrate.php +++ b/ipf/command/migrate.php @@ -1,22 +1,31 @@ appList = $appList; + $this->connection = $connection; + } + public function run($args=null) + { $paths = array(IPF::get('project_root').'/project/db/migrations'); - foreach ($project->appList() as $app) { + foreach ($this->appList as $app) { $paths[] = $app->getPath() . 'migrations'; } - $connection = \PFF\Container::databaseConnection(); $m = new \PFF\Migrations\Migrations; - $m->migrate($connection, $paths); + $m->migrate($this->connection, $paths); } } - diff --git a/ipf/controller/base.php b/ipf/controller/base.php index 6e0b7cc..2b12d41 100644 --- a/ipf/controller/base.php +++ b/ipf/controller/base.php @@ -1,15 +1,20 @@ project = $project; + $this->container = $container; } function process($action, $request, $matches) diff --git a/ipf/middleware/base.php b/ipf/middleware/base.php index 42e2896..5a493d1 100644 --- a/ipf/middleware/base.php +++ b/ipf/middleware/base.php @@ -1,26 +1,33 @@ next = $next; $this->settings = $settings; $this->project = $project; + $this->container = $container; } function processRequest($request) diff --git a/ipf/middleware/dispatch.php b/ipf/middleware/dispatch.php index abfe8f4..01ff379 100644 --- a/ipf/middleware/dispatch.php +++ b/ipf/middleware/dispatch.php @@ -15,8 +15,9 @@ class IPF_Dispatch_Middleware extends IPF_Middleware $request->params = $match; try { - $controller = $route->controller(); - $controller = new $controller($this->project); + $controllerClass = $route->controller(); + /** @var IPF_Controller $controller */ + $controller = new $controllerClass($this->project, $this->container); return $controller->process($route->action(), $request, $match); } catch (IPF_Router_Shortcut $e) { return $e->response($request); diff --git a/ipf/project.php b/ipf/project.php index c54bb2a..d66551c 100644 --- a/ipf/project.php +++ b/ipf/project.php @@ -1,7 +1,11 @@ container = new Container(); + + $apps = array(); foreach (IPF::get('applications') as $name) { $className = $name.'_App'; /** @var IPF_Application $app */ - $app = new $className($this); - $app->configure(IPF::$settings); + $app = new $className(); + $app->configure($this->container, IPF::$settings); $this->apps[$name] = $app; - \PFF\Container::set($app->slug(), $app); + $apps[$app->slug()] = $app; } - $this->router = new IPF_Router; + $this->container['apps'] = $apps; + $this->container['router'] = function ($c) { + return new IPF_Router; + }; + $this->router = $this->container['router']; } private function __clone() @@ -46,25 +57,28 @@ final class IPF_Project $m = null; foreach (array_reverse($middlewares) as $mw) { - $m = new $mw($m, IPF::$settings, $this); + $m = new $mw($m, IPF::$settings, $this, $this->container); } return $m; } public function run() { - \PFF\Container::setFactory('databaseConnection', array('IPF_Database', 'connect')); + $this->container['databaseConnection'] = function ($c) { + return IPF_Database::connect(); + }; + $this->container['db'] = $this->container->factory(function ($c) { + return IPF_Database::connectDBAL(); + }); - \PFF\Container::setFunction('db', array('IPF_Database', 'connectDBAL')); + $this->container->register(new IPF_Cli_Provider()); if (IPF::get('debug')) { error_reporting(E_ALL); } if (php_sapi_name() === 'cli') { - $cli = new IPF_Cli($this); - $cli->findCommands(); - $cli->run(); + $this->container['cli']->run(); } else { $this->request = new IPF_HTTP_Request; @@ -75,4 +89,3 @@ final class IPF_Project } } } - diff --git a/ipf/router.php b/ipf/router.php index bceeb36..0572b6a 100644 --- a/ipf/router.php +++ b/ipf/router.php @@ -2,6 +2,7 @@ class IPF_Router { + /** @var IPF_Route[] */ private $routes = array(); public function __construct() @@ -143,11 +144,17 @@ class IPF_Route return $this->matcher; } + /** + * @return string + */ public function controller() { return \PFF\Arr::get($this->params, 'controller', 'IPF_Controller_Function'); } + /** + * @return string + */ public function action() { return \PFF\Arr::get($this->params, 'func', 'index'); diff --git a/ipf/session/app.php b/ipf/session/app.php index 61db679..d94f596 100644 --- a/ipf/session/app.php +++ b/ipf/session/app.php @@ -2,89 +2,80 @@ class IPF_Session_App extends IPF_Application { -} - - -abstract class Session -{ - public static $backends = array( - 'CookieSession', - 'DBSession', - ); + /** @var SessionBackend[] */ + private $backends; - public static function get($key) + public function configure(\Pimple\Container $container, IPF_Settings $settings) { - if (!$key) - return null; - - list($backend, $data) = self::detectBackend($key); - if (!$backend) - return null; - - $session = new $backend($key, $data); - - if ($session->expired()) { - $session->delete(); - return null; - } - - return $session; + $this->backends = [ + new CookieSessionBackend(), + new DBSessionBackend($container, $settings->get('secret_key')), + ]; } - private static function detectBackend($key) + /** + * @param string $session_cookie + * @return Session + */ + public function getSession($session_cookie) { - foreach (self::$backends as $backend) { - $data = $backend::getData($key); - if ($data) - return array($backend, $data); + $session = $this->detectSession($session_cookie); + if ($session && $session->expired()) { + $session->delete(); + $session = null; + } + if ($session) { + return $session; + } else { + return new Session(null, array(), $this->backends[0]); } - return null; - } - - public static function create() - { - $backend = self::$backends[0]; - return new $backend(null, array()); } - - protected $key = null; - public $data = array(); - - public function __construct($key, $data) + /** + * @param string $key + * @return Session + */ + private function detectSession($key) { - $this->key = $key; - $this->data = $data; - } + if (!$key) { + return null; + } - public function delete() - { - } + foreach ($this->backends as $backend) { + $data = $backend->getData($key); + if ($data) + return new Session($key, $data, $backend); + } - public function cookie() - { - $this->data['updated_at'] = time(); - $this->save(); - return $this->key; + return null; } +} - protected abstract function save(); - - public function updatedAt() - { - return $this->data['updated_at']; - } - public function expired() - { - return time() - $this->updatedAt() > 60 * 60 * 24 * 7; - } +interface SessionBackend +{ + /** + * @param string $key + * @return array|null + */ + public function getData($key); + + /** + * @param string $key + * @param array $data + * @return string + */ + public function save($key, $data); + + /** + * @param string $key + */ + public function delete($key); } - -class CookieSession extends Session +class CookieSessionBackend implements SessionBackend { - public static function getData($key) + public function getData($key) { $key = explode('|', $key, 2); if (count($key) !== 2) @@ -96,24 +87,34 @@ class CookieSession extends Session return unserialize(base64_decode($data)); } - protected function save() + public function save($key, $data) { - $data = base64_encode(serialize($this->data)); - $this->key = $data.'|'.IPF_Crypto::sign($data); + $encoded = base64_encode(serialize($data)); + return $encoded . '|' . IPF_Crypto::sign($encoded); } -} + public function delete($key) + { + // DO NOTHING + } +} -class DBSession extends Session +class DBSessionBackend implements SessionBackend { - private static function getConnection() + /** @var \Pimple\Container */ + private $container; + /** @var string */ + private $secret_key; + + public function __construct(Pimple\Container $container, $secret_key) { - return \PFF\Container::databaseConnection(); + $this->container = $container; + $this->secret_key = $secret_key; } - public static function getData($key) + public function getData($key) { - $connection = self::getConnection(); + $connection = $this->getDatabaseConnection(); $stmt = $connection->prepare('SELECT data FROM session WHERE session_key = :key'); $stmt->bindValue('key', $key, PDO::PARAM_STR); $stmt->execute(); @@ -124,34 +125,78 @@ class DBSession extends Session return null; } - public function delete() + public function save($key, $data) { - $connection = self::getConnection(); + $connection = $this->getDatabaseConnection(); + if ($key) { + $stmt = $connection->prepare('UPDATE session SET data = :data, updated_at = NOW() WHERE session_key = :key'); + } else { + $key = $this->getNewSessionKey(); + $stmt = $connection->prepare('INSERT INTO session (session_key, data) VALUES (:key, :data)'); + } + $stmt->bindValue('key', $key, PDO::PARAM_STR); + $stmt->bindValue('data', serialize($data), PDO::PARAM_STR); + $stmt->execute(); + return $key; + } + + private function getNewSessionKey() + { + return md5(microtime() . rand(0, 123456789) . rand(0, 123456789) . $this->secret_key); + } + + public function delete($key) + { + $connection = $this->getDatabaseConnection(); $stmt = $connection->prepare('DELETE FROM session WHERE session_key = :key'); - $stmt->bindValue('key', $this->key, PDO::PARAM_STR); + $stmt->bindValue('key', $key, PDO::PARAM_STR); $stmt->execute(); + } + + /** + * @return \PDO + */ + private function getDatabaseConnection() + { + return $this->container['databaseConnection']; + } +} + +final class Session +{ + /** @var SessionBackend */ + private $backend; + private $key = null; + public $data = array(); + + public function __construct($key, $data, SessionBackend $backend) + { + $this->key = $key; + $this->data = $data; + $this->backend = $backend; + } + + public function delete() + { + $this->backend->delete($this->key); $this->key = null; } - protected function save() + public function cookie() { - $connection = self::getConnection(); - if ($this->key) { - $stmt = $connection->prepare('UPDATE session SET data = :data, updated_at = NOW() WHERE session_key = :key'); - } else { - $this->key = self::getNewSessionKey(); - $stmt = $connection->prepare('INSERT INTO session (session_key, data) VALUES (:key, :data)'); - } - $stmt->bindValue('key', $this->key, PDO::PARAM_STR); - $stmt->bindValue('data', serialize($this->data), PDO::PARAM_STR); - $stmt->execute(); + $this->data['updated_at'] = time(); + $this->key = $this->backend->save($this->key, $this->data); + return $this->key; + } + + public function updatedAt() + { + return $this->data['updated_at']; } - private static function getNewSessionKey($secret_key=null) + public function expired() { - if (!$secret_key) - $secret_key = IPF::get('secret_key'); - return md5(microtime().rand(0, 123456789).rand(0, 123456789).$secret_key); + return time() - $this->updatedAt() > 60 * 60 * 24 * 7; } } diff --git a/ipf/session/middleware.php b/ipf/session/middleware.php index 5048976..19e765e 100644 --- a/ipf/session/middleware.php +++ b/ipf/session/middleware.php @@ -4,12 +4,11 @@ class IPF_Session_Middleware extends IPF_Middleware { function processRequest($request) { - $session_cookie = \PFF\Arr::get($request->COOKIE, $this->settings->get('session_cookie_id')); - - $request->session = Session::get($session_cookie); - if (!$request->session) - $request->session = Session::create(); + /** @var IPF_Session_App $session_app */ + $session_app = $this->container['apps']['session']; + $session_cookie = \PFF\Arr::get($request->COOKIE, $this->settings->get('session_cookie_id')); + $request->session = $session_app->getSession($session_cookie); return false; } @@ -20,4 +19,3 @@ class IPF_Session_Middleware extends IPF_Middleware return $response; } } -