From 170ca41178f11680dc9ec281c86879619f9e1add Mon Sep 17 00:00:00 2001 From: Mostafa Barmshory Date: Thu, 2 Apr 2020 02:21:38 +0430 Subject: [PATCH] [skip ci] update pluf cache --- .buildpath | 10 + composer.json | 3 +- phpunit.xml | 77 ++++--- src/Pluf.php | 83 ++++---- src/Pluf/Cache.php | 111 ---------- src/Pluf/Cache/File.php | 131 ------------ src/Pluf/Model.php | 200 ++++++++---------- src6/Cache.php | 137 ++++++++++++ src/Pluf/Cache/Apc.php => src6/Cache/Apcu.php | 56 ++--- src6/Cache/ArrayCache.php | 40 ++++ src6/Cache/File.php | 128 +++++++++++ {src/Pluf => src6}/Cache/Memcached.php | 63 +++--- .../InvalidRelationKeyException.php | 2 +- src6/{Model => Data}/Query.php | 2 +- src6/{Model => Data}/QueryBuilder.php | 2 +- src6/{Model => Data}/Repository.php | 50 ++++- tests/Cache/ApcuTest.php | 165 +++++++++++++++ tests/Cache/BasicTest.php | 23 ++ tests/Cache/FileTest.php | 48 ++++- tests/Pluf_DB/PlufDBTest.php | 2 +- tests/conf/config.php | 21 ++ tests/conf/mysql.conf.php | 13 -- 22 files changed, 847 insertions(+), 520 deletions(-) delete mode 100755 src/Pluf/Cache.php delete mode 100755 src/Pluf/Cache/File.php create mode 100755 src6/Cache.php rename src/Pluf/Cache/Apc.php => src6/Cache/Apcu.php (68%) create mode 100644 src6/Cache/ArrayCache.php create mode 100755 src6/Cache/File.php rename {src/Pluf => src6}/Cache/Memcached.php (58%) rename src6/{Model => Data}/InvalidRelationKeyException.php (98%) rename src6/{Model => Data}/Query.php (99%) rename src6/{Model => Data}/QueryBuilder.php (99%) rename src6/{Model => Data}/Repository.php (85%) create mode 100755 tests/Cache/ApcuTest.php create mode 100644 tests/Cache/BasicTest.php diff --git a/.buildpath b/.buildpath index 94cfd6be..0f41cabe 100644 --- a/.buildpath +++ b/.buildpath @@ -238,6 +238,11 @@ + + + + + @@ -388,6 +393,11 @@ + + + + + diff --git a/composer.json b/composer.json index 488362d8..48c1b612 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "squizlabs/php_codesniffer" : "~3.3", "slevomat/coding-standard" : "~4.5", "mediawiki/mediawiki-codesniffer" : "~23.0", - "phpstan/phpstan" : "~0.10.6" + "phpstan/phpstan" : "~0.10.6", + "phpunit/dbunit" : "*" }, "support" : { "wiki" : "https://github.com/pluf/core/wiki", diff --git a/phpunit.xml b/phpunit.xml index ab5ee2e8..d290c389 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,4 +1,3 @@ - + + + + + + - - tests/Cache/ - - - tests/Pluf/ - tests/Pluf_Crypt/ - tests/Pluf_Utils/ - tests/Pluf_Encoder/ - tests/Pluf_Tenant/ - tests/Pluf_Tenant_Template/ - tests/Pluf_Mail/ - tests/Pluf_Migration/ - - - tests/Pluf_DB/ - tests/Pluf_DB_Schema/ - tests/Pluf_SQL/ - tests/Pluf_Model/ - - - tests/Middleware/ - tests/Dispatcher/ - tests/Pluf_Dispatcher/ - tests/Pluf_Paginator/ - tests/Pluf_Text_Wiki/ - tests/Pluf_Text_HTML_Filter/ - tests/Pluf_Form/ - tests/Pluf_Form_Field/ - tests/Pluf_HTTP_Response - - - tests/Pluf_Graphql_Compiler/ - tests/Pluf_Graphql/ + + + + + + + + + + + + + + + tests/Db/ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Pluf.php b/src/Pluf.php index a0625b84..e15cec17 100755 --- a/src/Pluf.php +++ b/src/Pluf.php @@ -19,6 +19,7 @@ use Pluf\ModelUtils; use Pluf\Module; use Pluf\Options; +use Pluf\Cache; /** * The main class of the framework. @@ -29,6 +30,10 @@ class Pluf { + public static ?Options $options = null; + + public static ?Cache $cache = null; + /** * Start the framework * @@ -43,53 +48,66 @@ public static function start($config) $GLOBALS['_PX_signal'] = array(); $GLOBALS['_PX_locale'] = array(); - Pluf::loadConfig($config); + // Load options + if (is_array($config)) { + $GLOBALS['_PX_config'] = $config; + } else if (false !== ($file = Pluf::fileExists($config))) { + $GLOBALS['_PX_config'] = require $file; + } else { + throw new Exception('Configuration file does not exist: ' . $config); + } + self::$options = new Options($GLOBALS['_PX_config']); + + // load cache + self::$cache = Cache::getInstance(self::getConfigByPrefix('cache_', true)); + + // Load the relations for each installed application. Each + // application folder must be in the include path. + ModelUtils::loadRelations(! Pluf::getConfig('debug', false)); - date_default_timezone_set(Pluf::f('time_zone', 'UTC')); - mb_internal_encoding(Pluf::f('encoding', 'UTF-8')); - mb_regex_encoding(Pluf::f('encoding', 'UTF-8')); + date_default_timezone_set(Pluf::getConfig('time_zone', 'UTC')); + mb_internal_encoding(Pluf::getConfig('encoding', 'UTF-8')); + mb_regex_encoding(Pluf::getConfig('encoding', 'UTF-8')); // Load modules Module::loadModules(); } /** - * Load the given configuration file. - * - * The configuration is saved in the $GLOBALS['_PX_config'] array. - * The relations between the models are loaded in $GLOBALS[ModelUtils::MODEL_KEY]. * - * @param - * string Configuration file to load. + * @deprecated */ - static function loadConfig($config_file) + public static function f($cfg, $default = '') { - if (is_array($config_file)) { - $GLOBALS['_PX_config'] = $config_file; - } else if (false !== ($file = Pluf::fileExists($config_file))) { - $GLOBALS['_PX_config'] = require $file; - } else { - throw new Exception('Configuration file does not exist: ' . $config_file); + if (isset($GLOBALS['_PX_config'][$cfg])) { + return $GLOBALS['_PX_config'][$cfg]; } + return $default; + } - // Load the relations for each installed application. Each - // application folder must be in the include path. - ModelUtils::loadRelations(! Pluf::f('debug', false)); + /** + * + * @deprecated + */ + public static function pf(string $prefix, $strip = false) + { + return self::getConfigByPrefix($prefix, $strip); } /** * Gets system configuration * - * @param - * string Configuration variable + * @param string $key + * Configuration key * @param * mixed Possible default value if value is not set ('') * @return mixed Configuration variable or default value if not defined. */ - public static function f($cfg, $default = '') + public static function getConfig(string $key, $default = '') { - if (isset($GLOBALS['_PX_config'][$cfg])) { - return $GLOBALS['_PX_config'][$cfg]; + $val = self::$options->$key; + if (isset($val)) { + return $val; } return $default; } @@ -104,20 +122,9 @@ public static function f($cfg, $default = '') * bool Strip the prefix from the keys (false). * @return array Configuration variables. */ - static function pf($pfx, $strip = false) + public static function getConfigByPrefix(string $prefix, bool $strip = false) { - $ret = array(); - $pfx_len = strlen($pfx); - foreach ($GLOBALS['_PX_config'] as $key => $val) { - if (0 === strpos($key, $pfx)) { - if (! $strip) { - $ret[$key] = $val; - } else { - $ret[substr($key, $pfx_len)] = $val; - } - } - } - return $ret; + return self::$options->startsWith($prefix, $strip); } /** diff --git a/src/Pluf/Cache.php b/src/Pluf/Cache.php deleted file mode 100755 index 73b030c6..00000000 --- a/src/Pluf/Cache.php +++ /dev/null @@ -1,111 +0,0 @@ -. - */ -/** - * کلاس کلی کش کردن - * - * مهم‌ترین نیاز در سیستم‌ها کش کردن داده‌هایی است که با استفاده از پردازش - * در سیستم ایجاد می‌شوند. این کار باعث بهبود کارایی سیستم خواهد شد. این - * کلاس ساختار کلی کش را در سیستم تعیین می‌کند. - * - * نکته: شما نباید به صورت مستقیم از این کلاس نمونه ایجاد کنید اما نمونه‌های - * متفاوتی از این کلاس وجود دارد که با روش‌های متفاوتی عمل کش کردن در سیستم - * را پیاده سازی کرده اند. - * - * تعیین مولد مدیریت کش به صورت زیر انجام می‌شود: - * - *

- * 	cfg['cache_engine'] = 'Apc';
- * 
- * - * انواع متفاوتی که در حال حاضر برای این داده وجود دارد عبارتند از: - * - * - Apc - * - File - * - Memcached - * - * هر داده‌ای که در کش قرار می‌گیرد در یک بازه زمانی معتبر است و بعد از آن - * دور ریخته می‌شود این بازه زمانی به صورت زیر تعیین می‌شود (زمان بر اساس - * ثانیه تعیین می‌شود): - * - *

- * 	cfg['cache_timeout'] = 300;
- * 
- * - * نمونه کد زیر یک کش را گرفته و یک مقدار را در آن ذخیره می‌کند. این - * مقدار در فراخوانی‌های بعد قابل استفاده است. - * - *
- * $cache = new Pluf_Cache::factory();
- * if (null === ($foo=$cache->get('my-key'))) {
- *     $foo = run_complex_operation();
- *     $cache->set('my-key', $foo);
- * }
- * return $foo;
- * 
- * - * نکته: مقداری که در کش قرار می‌گیرد باید قابل سریال شده باشد در غیر این صورت - * خطا ایجاد خواهد شد. - * - * @see http://www.php.net/serialize - */ -class Pluf_Cache { - /** - * فراخوانی سازنده کش - * - * @return Pluf_Cache_* Cache object - */ - public static function factory() { - if (false === ($engine = Pluf::f ( 'cache_engine', false ))) { - throw new Pluf_Exception_SettingError ( '"cache_engine" setting not defined.' ); - } - if (! isset ( $GLOBALS ['_PX_Pluf_Cache-' . $engine] )) { - $GLOBALS ['_PX_Pluf_Cache-' . $engine] = new $engine (); - } - return $GLOBALS ['_PX_Pluf_Cache-' . $engine]; - } - - /** - * Set a value in the cache. - * - * @param - * string Key to store the information - * @param - * mixed Value to store - * @param - * int Timeout in seconds (null) - * @return bool Success - */ - public function set($key, $value, $timeout = null) { - throw new Pluf_Exception_NotImplemented (); - } - - /** - * Get value from the cache. - * - * @param - * string Key to get the information - * @param - * mixed Default value to return if cache miss (null) - * @param - * mixed Stored value or default - */ - public function get($key, $default = null) { - throw new Pluf_Exception_NotImplemented (); - } -} diff --git a/src/Pluf/Cache/File.php b/src/Pluf/Cache/File.php deleted file mode 100755 index 35574530..00000000 --- a/src/Pluf/Cache/File.php +++ /dev/null @@ -1,131 +0,0 @@ -. - */ -/** - * A file based cache - * - * در این مدل تمام داده‌هایی که کش می شود در پرونده‌هایی در یک پوشه قرار می‌گیرد. - * این پوشه نیز در تنظیم‌ها به صورت زیر تعیین می‌شود: - * - *

- * 	cfg['cache_file_folder'] = 'path';
- * 
- * - * تمام زیر پوشه‌هایی که در این مسیر ایجاد می‌شود با استفاده MD5 تعیین نام خواهد شد. - */ -class Pluf_Cache_File extends Pluf_Cache { - /** - * Is debug mode? - * - * @var boolean - */ - private $_debug; - - /** - * Mapping key => md5. - * - * @var array - */ - private $_keymap = array (); - public function __construct() { - if (! Pluf::f ( 'cache_file_folder', false )) { - throw new Pluf_Exception_SettingError ( '"cache_file_folder" setting not defined.' ); - } - - $this->_debug = Pluf::f ( 'debug', false ); - } - - /** - * Set a value in the cache. - * - * @param - * string Key to store the information - * @param - * mixed Value to store - * @param - * int Timeout in seconds (null) - * @return bool Success - */ - public function set($key, $value, $timeout = null) { - $fname = $this->_keyToFile ( $key ); - $dir = dirname ( $fname ); - if (null === $timeout) { - $timeout = Pluf::f ( 'cache_timeout', 300 ); - } - if (! file_exists ( $dir )) { - mkdir ( $dir, 0777, true ); - } - $expire = $_SERVER ['REQUEST_TIME'] + $timeout; - $success = file_put_contents ( $fname, $expire . "\n" . serialize ( $value ), LOCK_EX ); - chmod ( $fname, 0777 ); - - return (false === $success) ? false : true; - } - - /** - * Get value from the cache. - * - * @param - * string Key to get the information - * @param - * mixed Default value to return if cache miss (null) - * @param - * mixed Stored value or default - */ - public function get($key, $default = null) { - $fname = $this->_keyToFile ( $key ); - if (! file_exists ( $fname )) { - return $default; - } - - if ($this->_debug) { - ob_start (); - include $fname; - $data = ob_get_contents (); - ob_end_clean (); - } else { - $data = file_get_contents ( $fname ); - } - list ( $timeout, $content ) = explode ( "\n", $data, 2 ); - - if ($timeout < $_SERVER ['REQUEST_TIME']) { - @unlink ( $fname ); - return $default; - } - - return unserialize ( $content ); - } - - /** - * Convert a key into a path to a file. - * - * @param - * string Key - * @return string Path to file - */ - public function _keyToFile($key) { - if (isset ( $this->_keymap [$key] )) { - $md5 = $this->_keymap [$key]; - } else { - $md5 = md5 ( $key ); - $this->_keymap [$key] = $md5; - } - - return Pluf::f ( 'cache_file_folder' ) . '/' . substr ( $md5, 0, 2 ) . '/' . substr ( $md5, 2, 2 ) . '/' . substr ( $md5, 4 ); - } -} diff --git a/src/Pluf/Model.php b/src/Pluf/Model.php index fa6b20ab..fcf72e37 100755 --- a/src/Pluf/Model.php +++ b/src/Pluf/Model.php @@ -27,7 +27,7 @@ * به عنوان نتیجه از یک مدل * استفاده شود. */ -class Pluf_Model implements JsonSerializable +class Pluf_Model extends \Pluf\Data\Model { /** @@ -117,7 +117,6 @@ class Pluf_Model implements JsonSerializable // added by some fields function __construct($pk = null, $values = array()) { - // --> $this->_model = get_class($this); $this->_a['model'] = $this->_model; @@ -136,7 +135,7 @@ function __construct($pk = null, $values = array()) * کلاس‌ها * باید این کلاس را پیاده سازی کنند و ساختارهای داده‌ای خود را ایجاد کنند. */ - function init() + function init(): void { // Define it yourself. } @@ -749,29 +748,29 @@ function create($raw = false) return true; } - /** - * Get models affected by delete. - * - * @return array Models deleted if deleting current model. - */ - function getDeleteSideEffect() - { - $affected = array(); - foreach ($this->_m['list'] as $method => $details) { - if (is_array($details)) { - // foreignkey - $related = $this->$method(); - $affected = array_merge($affected, (array) $related); - foreach ($related as $rel) { - if ($details[0] == $this->_a['model'] and $rel->id == $this->_data['id']) { - continue; // $rel == $this - } - $affected = array_merge($affected, (array) $rel->getDeleteSideEffect()); - } - } - } - return Pluf_Model_RemoveDuplicates($affected); - } +// /** +// * Get models affected by delete. +// * +// * @return array Models deleted if deleting current model. +// */ +// function getDeleteSideEffect() +// { +// $affected = array(); +// foreach ($this->_m['list'] as $method => $details) { +// if (is_array($details)) { +// // foreignkey +// $related = $this->$method(); +// $affected = array_merge($affected, (array) $related); +// foreach ($related as $rel) { +// if ($details[0] == $this->_a['model'] and $rel->id == $this->_data['id']) { +// continue; // $rel == $this +// } +// $affected = array_merge($affected, (array) $rel->getDeleteSideEffect()); +// } +// } +// } +// return Pluf_Model_RemoveDuplicates($affected); +// } /** * Delete the current model from the database. @@ -910,27 +909,27 @@ function _fromDb($val, $col) return $this->getEngine()->_fromDb($val, $this->_a['cols'][$col]['type']); } - /** - * Display value. - * - * When you have a list of choices for a field and you want to get - * the display value of the current stored value. - * - * @param - * string Field to display the value. - * @return mixed Display value, if not available default to the value. - */ - function displayVal($col) - { - if (! isset($this->_a['cols'][$col]['choices'])) { - return $this->_data[$col]; // will on purposed failed if not set - } - $val = array_search($this->_data[$col], $this->_a['cols'][$col]['choices']); - if ($val !== false) { - return $val; - } - return $this->_data[$col]; - } +// /** +// * Display value. +// * +// * When you have a list of choices for a field and you want to get +// * the display value of the current stored value. +// * +// * @param +// * string Field to display the value. +// * @return mixed Display value, if not available default to the value. +// */ +// function displayVal($col) +// { +// if (! isset($this->_a['cols'][$col]['choices'])) { +// return $this->_data[$col]; // will on purposed failed if not set +// } +// $val = array_search($this->_data[$col], $this->_a['cols'][$col]['choices']); +// if ($val !== false) { +// return $val; +// } +// return $this->_data[$col]; +// } /** * متدهای اتوماتیک را برای مدل ورودی ایجاد می‌کند. @@ -1019,29 +1018,6 @@ public function getId() return $this->id; } - /** - * (non-PHPdoc) - * - * @see JsonSerializable::jsonSerialize() - */ - public function jsonSerialize() - { - $coded = array(); - foreach ($this->_data as $col => $val) { - /* - * خصوصیت‌هایی که قابل خواندن نیستن سریال نخواهند شد - */ - if (array_key_exists($col, $this->_a['cols']) && array_key_exists('readable', $this->_a['cols'][$col]) && ! $this->_a['cols'][$col]['readable']) - continue; - /* - * If parameter ins null, zero, empty, ... we will not encode - */ - if ($val) - $coded[$col] = $val; - } - return $coded; - } - public function getName() { return array_key_exists('verbose', $this->_a) ? $this->_a['verbose'] : $this->getClass(); @@ -1183,46 +1159,46 @@ public function loadIndexes(): array } } -/** - * Check if a model is already in an array of models. - * - * It is not possible to override the == function in PHP to directly - * use in_array. - * - * @param - * Pluf_Model The model to test - * @param - * Array The models - * @return bool - */ -function Pluf_Model_InArray($model, $array) -{ - if ($model->id == '') { - return false; - } - foreach ($array as $modelin) { - if ($modelin->_a['model'] == $model->_a['model'] and $modelin->id == $model->id) { - return true; - } - } - return false; -} - -/** - * Return a list of unique models. - * - * @param - * array Models with duplicates - * @return array Models with duplicates. - */ -function Pluf_Model_RemoveDuplicates($array) -{ - $res = array(); - foreach ($array as $model) { - if (! Pluf_Model_InArray($model, $res)) { - $res[] = $model; - } - } - return $res; -} +// /** +// * Check if a model is already in an array of models. +// * +// * It is not possible to override the == function in PHP to directly +// * use in_array. +// * +// * @param +// * Pluf_Model The model to test +// * @param +// * Array The models +// * @return bool +// */ +// function Pluf_Model_InArray($model, $array) +// { +// if ($model->id == '') { +// return false; +// } +// foreach ($array as $modelin) { +// if ($modelin->_a['model'] == $model->_a['model'] and $modelin->id == $model->id) { +// return true; +// } +// } +// return false; +// } + +// /** +// * Return a list of unique models. +// * +// * @param +// * array Models with duplicates +// * @return array Models with duplicates. +// */ +// function Pluf_Model_RemoveDuplicates($array) +// { +// $res = array(); +// foreach ($array as $model) { +// if (! Pluf_Model_InArray($model, $res)) { +// $res[] = $model; +// } +// } +// return $res; +// } diff --git a/src6/Cache.php b/src6/Cache.php new file mode 100755 index 00000000..f90741fe --- /dev/null +++ b/src6/Cache.php @@ -0,0 +1,137 @@ +. + */ +namespace Pluf; + +use Pluf; + +/** + * Pluf Cach + * + * مهم‌ترین نیاز در سیستم‌ها کش کردن داده‌هایی است که با استفاده از پردازش + * در سیستم ایجاد می‌شوند. این کار باعث بهبود کارایی سیستم خواهد شد. این + * کلاس ساختار کلی کش را در سیستم تعیین می‌کند. + * + * نکته: شما نباید به صورت مستقیم از این کلاس نمونه ایجاد کنید اما نمونه‌های + * متفاوتی از این کلاس وجود دارد که با روش‌های متفاوتی عمل کش کردن در سیستم + * را پیاده سازی کرده اند. + * + * تعیین مولد مدیریت کش به صورت زیر انجام می‌شود: + * + *

+ * cfg['cache_engine'] = 'Apc';
+ * 
+ * + * There are many types of cache engine implemented by Pluf Cache. Here is + * list of them: + * + * - array + * - apc + * - file + * - memcached + * + * هر داده‌ای که در کش قرار می‌گیرد در یک بازه زمانی معتبر است و بعد از آن + * دور ریخته می‌شود این بازه زمانی به صورت زیر تعیین می‌شود (زمان بر اساس + * ثانیه تعیین می‌شود): + * + *

+ * cfg['cache_file_timeout'] = 300;
+ * 
+ * + * نمونه کد زیر یک کش را گرفته و یک مقدار را در آن ذخیره می‌کند. این + * مقدار در فراخوانی‌های بعد قابل استفاده است. + * + *
+ * $cache = new Pluf_Cache::getInstance($options);
+ * if (null === ($foo=$cache->get('my-key'))) {
+ * $foo = run_complex_operation();
+ * $cache->set('my-key', $foo);
+ * }
+ * return $foo;
+ * 
+ * + * نکته: مقداری که در کش قرار می‌گیرد باید قابل سریال شده باشد در غیر این صورت + * خطا ایجاد خواهد شد. + * + * NOTE: It is not possible to push a non serialable object into the cache system. + * + * @see http://www.php.net/serialize + */ +abstract class Cache +{ + + /** + * Creates new instance of Cache + * + * @return Cache Cache object + */ + public static function getInstance(?Options $options = null): Cache + { + if (! isset($options)) { + $options = Pluf::getConfigByPrifix('cache_', true); + } + $type = $options->engine; + if (! isset($type)) { + $type = 'array'; + } + switch ($type) { + case 'array': + $engine = new Cache\Apcu($options->startsWith('array_', true)); + break; + case 'apcu': + $engine = new Cache\Apcu($options->startsWith('apcu_', true)); + break; + case 'file': + $engine = new Cache\File($options->startsWith('file_', true)); + break; + case 'memcached': + $engine = new Cache\Apcu($options->startsWith('memcached_', true)); + break; + default: + throw new Exception('Unsupported cache engine: ' . $options->engine); + } + + return $engine; + } + + /** + * Set a value in the cache. + * + * @param + * string Key to store the information + * @param + * mixed Value to store + * @param + * int Timeout in seconds (null) + * @return bool Success + */ + public abstract function set($key, $value, $timeout = null); + + /** + * Get value from the cache. + * + * @param + * string Key to get the information + * @param + * mixed Default value to return if cache miss (null) + * @param + * mixed Stored value or default + */ + public abstract function get($key, $default = null); +} diff --git a/src/Pluf/Cache/Apc.php b/src6/Cache/Apcu.php similarity index 68% rename from src/Pluf/Cache/Apc.php rename to src6/Cache/Apcu.php index ddc11f49..80805204 100755 --- a/src/Pluf/Cache/Apc.php +++ b/src6/Cache/Apcu.php @@ -17,12 +17,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +namespace Pluf\Cache; + +use Pluf\Options; /** * APC cache * - * Warning: This extension is considered unmaintained and dead. However, - * the source code for this extension is still available within PECL + * Warning: This extension is considered unmaintained and dead. However, + * the source code for this extension is still available within PECL * GIT here: http://git.php.net/?p=pecl/caching/apc.git. * * You need APC installed on your server for this cache system to @@ -46,9 +49,11 @@ * @see http://www.php.net/gzdeflate * @see http://www.php.net/gzinflate */ -class Pluf_Cache_Apc extends Pluf_Cache +class Apcu extends \Pluf\Cache { + use \Pluf\DiContainerTrait; + /** * پیشوندی که به تمام کلیدهای کش اضافه می‌شود. * این کلید در تنظیم‌های @@ -63,55 +68,50 @@ class Pluf_Cache_Apc extends Pluf_Cache */ private $compress = false; + private $timeout = null; + /** - * یک نمونه جدید از این کلاس ایجاد می‌کند + * Creates new instance of the class * * نمونه ایجاد شده با استفاده از تنظیم‌هایی که در تنظیم‌های سرور تعیین شده\ * است مقدار دهی و راه اندازی می‌شود. */ - public function __construct() + public function __construct(?Options $options = null) { - $this->keyprefix = Pluf::f('cache_apc_keyprefix', ''); - $this->compress = Pluf::f('cache_apc_compress', false); + $this->setDefaults($options); } /** - * یک مقدار را در کش قرار می‌دهد * - * @param - * string Key کلیدی که برای ذخیره سازی استفاده می‌شود - * @param - * mixed Value مقداری که باید کش شود - * @param - * int Timeout زمان انقضا را بر اساس ثانیه تعیین می‌کند - * @return bool حالت موفقیت انجام این عمل را تعیین می‌کند. + * {@inheritdoc} + * @see \Pluf\Cache::set() */ public function set($key, $value, $timeout = null) { - if ($timeout == null) - $timeout = Pluf::f('cache_timeout', 300); + if ($timeout == null) { + $timeout = $this->timeout; + } $value = serialize($value); - if ($this->compress) + if ($this->compress) { $value = gzdeflate($value, 9); - return apc_store($this->keyprefix . $key, $value, $timeout); + } + return apcu_store($this->keyprefix . $key, $value, $timeout); } /** - * Get value from the cache. * - * @param - * string Key to get the information - * @param - * mixed Default value to return if cache miss (null) - * @return mixed Stored value or default + * {@inheritdoc} + * @see \Pluf\Cache::get() */ public function get($key, $default = null) { - $value = apc_fetch($this->keyprefix . $key); - if ($value === FALSE) + $value = apcu_fetch($this->keyprefix . $key); + if ($value === FALSE) { return $default; - if ($this->compress) + } + if ($this->compress) { $value = gzinflate($value); + } return unserialize($value); } } diff --git a/src6/Cache/ArrayCache.php b/src6/Cache/ArrayCache.php new file mode 100644 index 00000000..63de5884 --- /dev/null +++ b/src6/Cache/ArrayCache.php @@ -0,0 +1,40 @@ +cache[$key] = $value; + } + + /** + * + * {@inheritdoc} + * @see \Pluf\Cache::get() + */ + public function get($key, $default = null) + { + if (! array_key_exists($key, $this->cache)) { + return $default; + } + return $this->cache[$key]; + } +} + diff --git a/src6/Cache/File.php b/src6/Cache/File.php new file mode 100755 index 00000000..81f856f2 --- /dev/null +++ b/src6/Cache/File.php @@ -0,0 +1,128 @@ +. + */ +namespace Pluf\Cache; + +use Pluf\Options; +use Pluf; + +/** + * A file based cache + * + * در این مدل تمام داده‌هایی که کش می شود در پرونده‌هایی در یک پوشه قرار می‌گیرد. + * این پوشه نیز در تنظیم‌ها به صورت زیر تعیین می‌شود: + * + *

+ * cfg['cache_file_folder'] = 'path';
+ * 
+ * + * تمام زیر پوشه‌هایی که در این مسیر ایجاد می‌شود با استفاده MD5 تعیین نام خواهد شد. + */ +class File extends \Pluf\Cache +{ + + use \Pluf\DiContainerTrait; + + /** + * Mapping key => md5. + * + * @var array + */ + private $keymap = array(); + + private $folder = '/tmp'; + + private $timeout = null; + + /** + * Creates new instance of the file cache + * + * @param Options $options + */ + public function __construct(?Options $options = null) + { + $this->setDefaults($options); + } + + /** + * + * {@inheritdoc} + * @see \Pluf\Cache::set() + */ + public function set($key, $value, $timeout = null) + { + $fname = $this->_keyToFile($key); + $dir = dirname($fname); + if (!isset($timeout)) { + $timeout = $this->timeout; + } + if (!isset($timeout)) { + $timeout = 3000; + } + if (! file_exists($dir)) { + mkdir($dir, 0777, true); + } + $expire = $_SERVER['REQUEST_TIME'] + $timeout; + $success = file_put_contents($fname, $expire . "\n" . serialize($value), LOCK_EX); + chmod($fname, 0777); + + return (false === $success) ? false : true; + } + + /** + * + * {@inheritdoc} + * @see \Pluf\Cache::get() + */ + public function get($key, $default = null) + { + $fname = $this->_keyToFile($key); + if (! file_exists($fname)) { + return $default; + } + + $data = file_get_contents($fname); + list ($time, $content) = explode("\n", $data, 2); + + if (isset($this->timeout) && $this->timeout > $_SERVER['REQUEST_TIME'] - $time) { + @unlink($fname); + return $default; + } + + return unserialize($content); + } + + /** + * Convert a key into a path to a file. + * + * @param + * string Key + * @return string Path to file + */ + private function _keyToFile($key) + { + if (isset($this->_keymap[$key])) { + $md5 = $this->_keymap[$key]; + } else { + $md5 = md5($key); + $this->_keymap[$key] = $md5; + } + + return $this->folder . '/' . substr($md5, 0, 2) . '/' . substr($md5, 2, 2) . '/' . substr($md5, 4); + } +} diff --git a/src/Pluf/Cache/Memcached.php b/src6/Cache/Memcached.php similarity index 58% rename from src/Pluf/Cache/Memcached.php rename to src6/Cache/Memcached.php index 79a6af4c..16e15773 100755 --- a/src/Pluf/Cache/Memcached.php +++ b/src6/Cache/Memcached.php @@ -17,6 +17,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +namespace Pluf\Cache; + +use Pluf\Options; /** * Cache in memory @@ -27,64 +30,60 @@ * Example of configuration: * *
- * $cfg['cache_engine'] = 'Pluf_Cache_Memcached';
- * $cfg['cache_timeout'] = 300;
+ * $cfg['cache_engine'] = 'memcached';
+ *
+ * $cfg['cache_memcached_timeout'] = 300;
  * $cfg['cache_memcached_keyprefix'] = 'uniqueforapp';
  * $cfg['cache_memcached_server'] = 'localhost';
  * $cfg['cache_memcached_port'] = 11211;
  * $cfg['cache_memcached_compress'] = 0; (or MEMCACHE_COMPRESSED)
  * 
*/ -class Pluf_Cache_Memcached extends Pluf_Cache +class Memcached extends \Pluf\Cache { + use \Pluf\DiContainerTrait; private $memcache = null; private $keyprefix = ''; - public function __construct () + private $server = 'localhost'; + + private $port = 11211; + + private $timeout = null; + + private $compress = 0; + + /** + * Creates new instance of the cache + */ + public function __construct(?Options $options = null) { - $this->memcache = memcache_connect( - Pluf::f('cache_memcached_server', 'localhost'), - Pluf::f('cache_memcached_port', 11211)); - if (false === $this->memcache) { - $this->memcache = null; - } - $this->keyprefix = Pluf::f('cache_memcached_keyprefix', ''); + $this->setDefaults($options); } /** - * Set a value in the cache. * - * @param - * string Key to store the information - * @param - * mixed Value to store - * @param - * int Timeout in seconds (null) - * @return bool Success + * {@inheritdoc} + * @see \Pluf\Cache::set() */ - public function set ($key, $value, $timeout = null) + public function set($key, $value, $timeout = null) { if ($this->memcache) { - if ($timeout == null) - $timeout = Pluf::f('cache_timeout', 300); - $this->memcache->set($this->keyprefix . $key, serialize($value), - Pluf::f('cache_memcached_compress', 0), $timeout); + if ($timeout == null) { + $timeout = $this->timeout; + } + $this->memcache->set($this->keyprefix . $key, serialize($value), $this->compress, $timeout); } } /** - * Get value from the cache. * - * @param - * string Key to get the information - * @param - * mixed Default value to return if cache miss (null) - * @param - * mixed Stored value or default + * {@inheritdoc} + * @see \Pluf\Cache::get() */ - public function get ($key, $default = null) + public function get($key, $default = null) { if ($this->memcache) { $val = $this->memcache->get($this->keyprefix . $key); diff --git a/src6/Model/InvalidRelationKeyException.php b/src6/Data/InvalidRelationKeyException.php similarity index 98% rename from src6/Model/InvalidRelationKeyException.php rename to src6/Data/InvalidRelationKeyException.php index d34c76d0..8a824c14 100644 --- a/src6/Model/InvalidRelationKeyException.php +++ b/src6/Data/InvalidRelationKeyException.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -namespace Pluf\Model; +namespace Pluf\Data; use Pluf\Exception; diff --git a/src6/Model/Query.php b/src6/Data/Query.php similarity index 99% rename from src6/Model/Query.php rename to src6/Data/Query.php index 88d4fdac..802c2cd1 100644 --- a/src6/Model/Query.php +++ b/src6/Data/Query.php @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -namespace Pluf\Model; +namespace Pluf\Data; class Query { diff --git a/src6/Model/QueryBuilder.php b/src6/Data/QueryBuilder.php similarity index 99% rename from src6/Model/QueryBuilder.php rename to src6/Data/QueryBuilder.php index 728a00f9..039a9153 100644 --- a/src6/Model/QueryBuilder.php +++ b/src6/Data/QueryBuilder.php @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -namespace Pluf\Model; +namespace Pluf\Data; class QueryBuilder { diff --git a/src6/Model/Repository.php b/src6/Data/Repository.php similarity index 85% rename from src6/Model/Repository.php rename to src6/Data/Repository.php index 26fe2c9f..09ca8ceb 100644 --- a/src6/Model/Repository.php +++ b/src6/Data/Repository.php @@ -16,10 +16,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -namespace Pluf\Model; +namespace Pluf\Data; use Pluf_Model; +use Pluf\Db; + /** * Repositories are object or components that encapsulate the logic required to access data sources. * @@ -56,6 +58,7 @@ class Repository * @var \Pluf_Model */ private Pluf_Model $model; + private ?Db\Connection $dbConnection = null; private static array $repositoryMap = []; @@ -105,10 +108,20 @@ public function __construct(string $modelType) * int Id of the item. * @return mixed Item or false if not found. */ - public function get($id): ?Pluf_Model + public function get($id): ?Model { + // Create and exec query + $res = $this->dbConnection->query() + ->table($this->tableName) + ->where('id', $id) + ->getOne(); + + // create model $modelClassName = get_class($this->model); - $model = new $modelClassName($id); + $model = new $modelClassName(); + $model->fillFromData($res); + + // return result return $model; } @@ -132,6 +145,24 @@ public function getOne(Query $query): ?Pluf_Model return $this->model->getOne($query->toArray()); } + public function create(Pluf_Model $model): ?Pluf_Model + {} + + public function update(Pluf_Model $model): ?Pluf_Model + {} + + public function delete(Pluf_Model $model): ?Pluf_Model + {} + + public function createList(?array $models = []): array + {} + + public function updateList(?array $models = []): array + {} + + public function deleteList(?array $models = []): array + {} + /** * Gets list of object * @@ -146,7 +177,16 @@ public function getOne(Query $query): ?Pluf_Model * @return array of items or through an exception if * database failure */ - public function getList(Query $query): array + public function getListByQuery(Query $query): array + { + $res = $this->model->getList($query->toArray()); + if ($res instanceof \ArrayObject) { + return $res->getArrayCopy(); + } + return []; + } + + public function deleteListByQuery(Query $query): array { $res = $this->model->getList($query->toArray()); if ($res instanceof \ArrayObject) { @@ -195,7 +235,7 @@ public function getCount(Query $query): int * to run on related objects if the result is many objects * @return array Array of items */ - public function getRelated(\Pluf_Model $model, string $relationName = null, Query $query): array + public function getRelated(Pluf_Model $model, string $relationName = null, Query $query): array { if ($this->isForignKeyRelation($relationName)) { // TODO: support foring key diff --git a/tests/Cache/ApcuTest.php b/tests/Cache/ApcuTest.php new file mode 100755 index 00000000..a9ad47a9 --- /dev/null +++ b/tests/Cache/ApcuTest.php @@ -0,0 +1,165 @@ +. + */ +use PHPUnit\Framework\TestCase; +use Pluf\Cache; +use Pluf\Options; + +class ApceTest extends TestCase +{ + + /** + * + * @test + */ + public function createNewInstance() + { + $cache = Cache::getInstance(new Options([ + 'engine' => 'apcu' + ])); + $this->assertNotNull($cache); + } + + /** + * + * @test + */ + public function testBasic() + { + $options = new Options([ + 'timeout' => 300 + ]); + $cache = new Cache\Apcu($options); + $this->assertNotNull($cache); + + $var = 'foo1'; + $key = 'test1'; + + $cache->set($key, $var); + $this->assertEquals($var, $cache->get($key)); + } + + /** + * + * @test + */ + public function testGetUnknownKey() + { + $options = new Options([ + 'timeout' => 300 + ]); + $cache = new Cache\Apcu($options); + $this->assertNotNull($cache); + + $this->assertNull($cache->get('unknown')); + } + + /** + * + * @test + */ + public function testGetDefault() + { + $options = new Options([ + 'timeout' => 300 + ]); + $cache = new Cache\Apcu($options); + $this->assertNotNull($cache); + + $this->assertEquals('default', $cache->get('unknown', 'default')); + } + + /** + * + * @test + */ + public function testSerialized() + { + $arrayData = [ + 'hello' => 'world', + 'foo' => false, + 0 => array( + 'foo', + 'bar' + ) + ]; + $options = new Options([ + 'timeout' => 300 + ]); + $cache = new Cache\Apcu($options); + $this->assertNotNull($cache); + + $cache->set('array', $arrayData); + $this->assertEquals($arrayData, $cache->get('array')); + + $obj = new stdClass(); + $obj->foo = 'bar'; + $obj->hello = 'world'; + $success = $cache->set('object', $obj); + $this->assertTrue($success); + $this->assertEquals($obj, $cache->get('object')); + + unset($obj); + $this->assertInstanceOf(stdClass::class, $cache->get('object')); + $this->assertEquals('world', $cache->get('object')->hello); + } + + /** + * + * @test + */ + public function testSerializedObject() + { + $object = new MyObject(); + $options = new Options([ + 'timeout' => 300 + ]); + $cache = new Cache\Apcu($options); + $this->assertNotNull($cache); + + $cache->set('obj', $object); + $this->assertEquals($object, $cache->get('obj')); + } +} + +class MyObject +{ + + private $var1 = 'hi'; + + protected $var2 = 'me'; + + public $v3 = [ + 'hello' => 'world', + 'foo' => false, + 0 => array( + 'foo', + 'bar' + ) + ]; + + function getVar1() + { + return $this->var1; + } + + function getVar2() + { + return $this->var2; + } +} diff --git a/tests/Cache/BasicTest.php b/tests/Cache/BasicTest.php new file mode 100644 index 00000000..75c80008 --- /dev/null +++ b/tests/Cache/BasicTest.php @@ -0,0 +1,23 @@ +assertNotNull(Pluf::$cache); + $this->assertTrue(Pluf::$cache instanceof Pluf\Cache); + } +} + diff --git a/tests/Cache/FileTest.php b/tests/Cache/FileTest.php index f20d0468..2c6ac472 100755 --- a/tests/Cache/FileTest.php +++ b/tests/Cache/FileTest.php @@ -17,8 +17,10 @@ * along with this program. If not, see . */ use PHPUnit\Framework\TestCase; +use Pluf\Cache; +use Pluf\Options; -class Pluf_Tests_Cache_FileTest extends TestCase +class FileTest extends TestCase { private $_config; @@ -59,12 +61,14 @@ public function tearDownTest() /** * * @test - * @expectedException Pluf_Exception_SettingError */ public function testConstructor() { - unset($GLOBALS['_PX_config']['cache_file_folder']); - Pluf_Cache::factory(); + $options = new Options([ + 'engine' => 'file' + ]); + $cache = Cache::getInstance($options); + $this->assertNotNull($cache); } /** @@ -73,10 +77,17 @@ public function testConstructor() */ public function testBasic() { - $cache = Pluf_Cache::factory(); - $success = $cache->set('test1', 'foo1'); - $this->assertTrue($success); - $this->assertEquals('foo1', $cache->get('test1')); + $options = new Options([ + 'engine' => 'file' + ]); + $cache = Cache::getInstance($options); + $this->assertNotNull($cache); + + $var = 'foo1'; + $key = 'test1'; + + $cache->set($key, $var); + $this->assertEquals($var, $cache->get($key)); } /** @@ -85,7 +96,12 @@ public function testBasic() */ public function testGetUnknownKey() { - $cache = Pluf_Cache::factory(); + $options = new Options([ + 'engine' => 'file' + ]); + $cache = Cache::getInstance($options); + $this->assertNotNull($cache); + $this->assertNull($cache->get('unknown')); } @@ -95,7 +111,12 @@ public function testGetUnknownKey() */ public function testGetDefault() { - $cache = Pluf_Cache::factory(); + $options = new Options([ + 'engine' => 'file' + ]); + $cache = Cache::getInstance($options); + $this->assertNotNull($cache); + $this->assertEquals('default', $cache->get('unknown', 'default')); } @@ -105,7 +126,12 @@ public function testGetDefault() */ public function testSerialized() { - $cache = Pluf_Cache::factory(); + $options = new Options([ + 'engine' => 'file' + ]); + $cache = Cache::getInstance($options); + $this->assertNotNull($cache); + $success = $cache->set('array', $this->_arrayData); $this->assertTrue($success); $this->assertEquals($this->_arrayData, $cache->get('array')); diff --git a/tests/Pluf_DB/PlufDBTest.php b/tests/Pluf_DB/PlufDBTest.php index 40dac758..1aa6456d 100755 --- a/tests/Pluf_DB/PlufDBTest.php +++ b/tests/Pluf_DB/PlufDBTest.php @@ -85,7 +85,7 @@ public function testEscapeBoolean() ); foreach ($tests as $test) { $ok = current($res); - $this->assertEquals($ok, \Pluf\Db\Engine::booleanToDb($test, $this->db)); + $this->assertEquals($ok, \Pluf\Db\Engine::booleanToDb($test, $this->db), 'Fail to convert :', $test); next($res); } } diff --git a/tests/conf/config.php b/tests/conf/config.php index 13e599f5..6cc78ecc 100644 --- a/tests/conf/config.php +++ b/tests/conf/config.php @@ -109,6 +109,27 @@ // $cfg['view_api_base'] = ''; +// ------------------------------------------------------------------------- +// cache +// ------------------------------------------------------------------------- + +$cfg['cache_engine'] = 'array'; + + +$cfg['cache_file_folder'] = '/tmp'; +$cfg['cache_file_timeout'] = null; + +$cfg['cache_apcu_keyprefix'] = '/tmp'; +$cfg['cache_apcu_compress'] = false; +$cfg['cache_apcu_timeout'] = null; + + +$cfg['cache_memcached_timeout'] = 300; +$cfg['cache_memcached_keyprefix'] = 'uniqueforapp'; +$cfg['cache_memcached_server'] = 'localhost'; +$cfg['cache_memcached_port'] = 11211; +$cfg['cache_memcached_compress'] = 0; // (or MEMCACHE_COMPRESSED) + // ------------------------------------------------------------------------- // Tenants // ------------------------------------------------------------------------- diff --git a/tests/conf/mysql.conf.php b/tests/conf/mysql.conf.php index b2fc68f2..a37f3f99 100755 --- a/tests/conf/mysql.conf.php +++ b/tests/conf/mysql.conf.php @@ -5,23 +5,10 @@ $cfg['db_schema'] = '\Pluf\Db\MySQLSchema'; $cfg['db_version'] = '5.4.1'; -<<<<<<< HEAD -$cfg['db_login'] = 'root'; -$cfg['db_password'] = 'root'; -$cfg['db_server'] = '127.0.0.1'; -$cfg['db_database'] = 'test'; -======= $cfg['db_login'] = 'pluf'; $cfg['db_password'] = 'password'; $cfg['db_server'] = '127.0.0.1'; $cfg['db_database'] = 'plufdb'; ->>>>>>> branch 'develop' of https://mostafa.barmshory%40gmail.com@github.com/pluf/core.git - -<<<<<<< HEAD -$cfg['db_schema_table_prefix'] = rand() . '_'; -======= -$cfg['db_schema_table_prefix'] = 'core_' . rand() . '_'; ->>>>>>> branch 'develop' of https://mostafa.barmshory%40gmail.com@github.com/pluf/core.git return $cfg;