From 2325fc7f1c5b9b99f680b5b232af70bdcda2be9c Mon Sep 17 00:00:00 2001 From: fredtempez Date: Mon, 5 Aug 2019 08:05:06 +0200 Subject: [PATCH] Installation Flintstone WIP --- .htaccess | 2 +- core/core.php | 7 + core/vendor/flintstone/Cache/ArrayCache.php | 53 ++++ .../flintstone/Cache/CacheInterface.php | 44 +++ core/vendor/flintstone/Config.php | 209 +++++++++++++ core/vendor/flintstone/Database.php | 255 ++++++++++++++++ core/vendor/flintstone/Exception.php | 7 + core/vendor/flintstone/Flintstone.php | 285 ++++++++++++++++++ .../Formatter/FormatterInterface.php | 24 ++ .../flintstone/Formatter/JsonFormatter.php | 46 +++ .../Formatter/SerializeFormatter.php | 52 ++++ core/vendor/flintstone/Line.php | 37 +++ core/vendor/flintstone/Validation.php | 34 +++ 13 files changed, 1054 insertions(+), 1 deletion(-) create mode 100644 core/vendor/flintstone/Cache/ArrayCache.php create mode 100644 core/vendor/flintstone/Cache/CacheInterface.php create mode 100644 core/vendor/flintstone/Config.php create mode 100644 core/vendor/flintstone/Database.php create mode 100644 core/vendor/flintstone/Exception.php create mode 100644 core/vendor/flintstone/Flintstone.php create mode 100644 core/vendor/flintstone/Formatter/FormatterInterface.php create mode 100644 core/vendor/flintstone/Formatter/JsonFormatter.php create mode 100644 core/vendor/flintstone/Formatter/SerializeFormatter.php create mode 100644 core/vendor/flintstone/Line.php create mode 100644 core/vendor/flintstone/Validation.php diff --git a/.htaccess b/.htaccess index 61e24a43..574dd56b 100644 --- a/.htaccess +++ b/.htaccess @@ -36,4 +36,4 @@ Options -Indexes # Attention, surtout ne rien modifier ci-dessous ! -# URL rewriting +# URL rewriting \ No newline at end of file diff --git a/core/core.php b/core/core.php index aad95fdc..0729c69d 100644 --- a/core/core.php +++ b/core/core.php @@ -527,6 +527,13 @@ class common { // Save config core page module et user // 5 premières clés principales // Trois tentatives + + require_once "core/vendor/flintstone/Flintstone.php"; + + + $options = ['dir' => self::DATA_DIR]; + $users = new Flintstone\Flintstone('users', $options); + for($i = 0; $i < 3; $i++) { if(file_put_contents(self::DATA_DIR.'core.json', json_encode(array_slice($this->getData(),0,5)) , LOCK_EX) !== false) { break; diff --git a/core/vendor/flintstone/Cache/ArrayCache.php b/core/vendor/flintstone/Cache/ArrayCache.php new file mode 100644 index 00000000..5d2fcdc6 --- /dev/null +++ b/core/vendor/flintstone/Cache/ArrayCache.php @@ -0,0 +1,53 @@ +cache); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + return $this->cache[$key]; + } + + /** + * {@inheritdoc} + */ + public function set($key, $data) + { + $this->cache[$key] = $data; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + unset($this->cache[$key]); + } + + /** + * {@inheritdoc} + */ + public function flush() + { + $this->cache = []; + } +} diff --git a/core/vendor/flintstone/Cache/CacheInterface.php b/core/vendor/flintstone/Cache/CacheInterface.php new file mode 100644 index 00000000..9459e010 --- /dev/null +++ b/core/vendor/flintstone/Cache/CacheInterface.php @@ -0,0 +1,44 @@ +normalizeConfig($config); + $this->setDir($config['dir']); + $this->setExt($config['ext']); + $this->setGzip($config['gzip']); + $this->setCache($config['cache']); + $this->setFormatter($config['formatter']); + $this->setSwapMemoryLimit($config['swap_memory_limit']); + } + + /** + * Normalize the user supplied config. + * + * @param array $config + * + * @return array + */ + protected function normalizeConfig(array $config): array + { + $defaultConfig = [ + 'dir' => getcwd(), + 'ext' => '.dat', + 'gzip' => false, + 'cache' => true, + 'formatter' => null, + 'swap_memory_limit' => 2097152, // 2MB + ]; + + return array_replace($defaultConfig, $config); + } + + /** + * Get the dir. + * + * @return string + */ + public function getDir(): string + { + return $this->config['dir']; + } + + /** + * Set the dir. + * + * @param string $dir + * + * @throws Exception + */ + public function setDir(string $dir) + { + if (!is_dir($dir)) { + throw new Exception('Directory does not exist: ' . $dir); + } + + $this->config['dir'] = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR; + } + + /** + * Get the ext. + * + * @return string + */ + public function getExt(): string + { + if ($this->useGzip()) { + return $this->config['ext'] . '.gz'; + } + + return $this->config['ext']; + } + + /** + * Set the ext. + * + * @param string $ext + */ + public function setExt(string $ext) + { + if (substr($ext, 0, 1) !== '.') { + $ext = '.' . $ext; + } + + $this->config['ext'] = $ext; + } + + /** + * Use gzip? + * + * @return bool + */ + public function useGzip(): bool + { + return $this->config['gzip']; + } + + /** + * Set gzip. + * + * @param bool $gzip + */ + public function setGzip(bool $gzip) + { + $this->config['gzip'] = $gzip; + } + + /** + * Get the cache. + * + * @return CacheInterface|false + */ + public function getCache() + { + return $this->config['cache']; + } + + /** + * Set the cache. + * + * @param mixed $cache + * + * @throws Exception + */ + public function setCache($cache) + { + if (!is_bool($cache) && !$cache instanceof CacheInterface) { + throw new Exception('Cache must be a boolean or an instance of Flintstone\Cache\CacheInterface'); + } + + if ($cache === true) { + $cache = new ArrayCache(); + } + + $this->config['cache'] = $cache; + } + + /** + * Get the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(): FormatterInterface + { + return $this->config['formatter']; + } + + /** + * Set the formatter. + * + * @param FormatterInterface|null $formatter + * + * @throws Exception + */ + public function setFormatter($formatter) + { + if ($formatter === null) { + $formatter = new SerializeFormatter(); + } + + if (!$formatter instanceof FormatterInterface) { + throw new Exception('Formatter must be an instance of Flintstone\Formatter\FormatterInterface'); + } + + $this->config['formatter'] = $formatter; + } + + /** + * Get the swap memory limit. + * + * @return int + */ + public function getSwapMemoryLimit(): int + { + return $this->config['swap_memory_limit']; + } + + /** + * Set the swap memory limit. + * + * @param int $limit + */ + public function setSwapMemoryLimit(int $limit) + { + $this->config['swap_memory_limit'] = $limit; + } +} diff --git a/core/vendor/flintstone/Database.php b/core/vendor/flintstone/Database.php new file mode 100644 index 00000000..08deb521 --- /dev/null +++ b/core/vendor/flintstone/Database.php @@ -0,0 +1,255 @@ + [ + 'mode' => 'rb', + 'operation' => LOCK_SH, + ], + self::FILE_WRITE => [ + 'mode' => 'wb', + 'operation' => LOCK_EX, + ], + self::FILE_APPEND => [ + 'mode' => 'ab', + 'operation' => LOCK_EX, + ], + ]; + + /** + * Database name. + * + * @var string + */ + protected $name; + + /** + * Config class. + * + * @var Config + */ + protected $config; + + /** + * Constructor. + * + * @param string $name + * @param Config|null $config + */ + public function __construct(string $name, Config $config = null) + { + $this->setName($name); + + if ($config) { + $this->setConfig($config); + } + } + + /** + * Get the database name. + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * Set the database name. + * + * @param string $name + * + * @throws Exception + */ + public function setName(string $name) + { + Validation::validateDatabaseName($name); + $this->name = $name; + } + + /** + * Get the config. + * + * @return Config + */ + public function getConfig(): Config + { + return $this->config; + } + + /** + * Set the config. + * + * @param Config $config + */ + public function setConfig(Config $config) + { + $this->config = $config; + } + + /** + * Get the path to the database file. + * + * @return string + */ + public function getPath(): string + { + return $this->config->getDir() . $this->getName() . $this->config->getExt(); + } + + /** + * Open the database file. + * + * @param int $mode + * + * @throws Exception + * + * @return SplFileObject + */ + protected function openFile(int $mode): SplFileObject + { + $path = $this->getPath(); + + if (!is_file($path) && !@touch($path)) { + throw new Exception('Could not create file: ' . $path); + } + + if (!is_readable($path) || !is_writable($path)) { + throw new Exception('File does not have permission for read and write: ' . $path); + } + + if ($this->getConfig()->useGzip()) { + $path = 'compress.zlib://' . $path; + } + + $res = $this->fileAccessMode[$mode]; + $file = new SplFileObject($path, $res['mode']); + + if ($mode === self::FILE_READ) { + $file->setFlags(SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD); + } + + if (!$this->getConfig()->useGzip() && !$file->flock($res['operation'])) { + $file = null; + throw new Exception('Could not lock file: ' . $path); + } + + return $file; + } + + /** + * Open a temporary file. + * + * @return SplTempFileObject + */ + public function openTempFile(): SplTempFileObject + { + return new SplTempFileObject($this->getConfig()->getSwapMemoryLimit()); + } + + /** + * Close the database file. + * + * @param SplFileObject $file + * + * @throws Exception + */ + protected function closeFile(SplFileObject &$file) + { + if (!$this->getConfig()->useGzip() && !$file->flock(LOCK_UN)) { + $file = null; + throw new Exception('Could not unlock file'); + } + + $file = null; + } + + /** + * Read lines from the database file. + * + * @return \Generator + */ + public function readFromFile(): \Generator + { + $file = $this->openFile(static::FILE_READ); + + try { + foreach ($file as $line) { + yield new Line($line); + } + } finally { + $this->closeFile($file); + } + } + + /** + * Append a line to the database file. + * + * @param string $line + */ + public function appendToFile(string $line) + { + $file = $this->openFile(static::FILE_APPEND); + $file->fwrite($line); + $this->closeFile($file); + } + + /** + * Flush the database file. + */ + public function flushFile() + { + $file = $this->openFile(static::FILE_WRITE); + $this->closeFile($file); + } + + /** + * Write temporary file contents to database file. + * + * @param SplTempFileObject $tmpFile + */ + public function writeTempToFile(SplTempFileObject &$tmpFile) + { + $file = $this->openFile(static::FILE_WRITE); + + foreach ($tmpFile as $line) { + $file->fwrite($line); + } + + $this->closeFile($file); + $tmpFile = null; + } +} diff --git a/core/vendor/flintstone/Exception.php b/core/vendor/flintstone/Exception.php new file mode 100644 index 00000000..e7841979 --- /dev/null +++ b/core/vendor/flintstone/Exception.php @@ -0,0 +1,7 @@ +setDatabase($database); + $this->setConfig($config); + } + + /** + * Get the database. + * + * @return Database + */ + public function getDatabase(): Database + { + return $this->database; + } + + /** + * Set the database. + * + * @param Database $database + */ + public function setDatabase(Database $database) + { + $this->database = $database; + } + + /** + * Get the config. + * + * @return Config + */ + public function getConfig(): Config + { + return $this->config; + } + + /** + * Set the config. + * + * @param Config $config + */ + public function setConfig(Config $config) + { + $this->config = $config; + $this->getDatabase()->setConfig($config); + } + + /** + * Get a key from the database. + * + * @param string $key + * + * @return mixed + */ + public function get(string $key) + { + Validation::validateKey($key); + + // Fetch the key from cache + if ($cache = $this->getConfig()->getCache()) { + if ($cache->contains($key)) { + return $cache->get($key); + } + } + + // Fetch the key from database + $file = $this->getDatabase()->readFromFile(); + $data = false; + + foreach ($file as $line) { + /** @var Line $line */ + if ($line->getKey() == $key) { + $data = $this->decodeData($line->getData()); + break; + } + } + + // Save the data to cache + if ($cache && $data !== false) { + $cache->set($key, $data); + } + + return $data; + } + + /** + * Set a key in the database. + * + * @param string $key + * @param mixed $data + */ + public function set(string $key, $data) + { + Validation::validateKey($key); + + // If the key already exists we need to replace it + if ($this->get($key) !== false) { + $this->replace($key, $data); + return; + } + + // Write the key to the database + $this->getDatabase()->appendToFile($this->getLineString($key, $data)); + + // Delete the key from cache + if ($cache = $this->getConfig()->getCache()) { + $cache->delete($key); + } + } + + /** + * Delete a key from the database. + * + * @param string $key + */ + public function delete(string $key) + { + Validation::validateKey($key); + + if ($this->get($key) !== false) { + $this->replace($key, false); + } + } + + /** + * Flush the database. + */ + public function flush() + { + $this->getDatabase()->flushFile(); + + // Flush the cache + if ($cache = $this->getConfig()->getCache()) { + $cache->flush(); + } + } + + /** + * Get all keys from the database. + * + * @return array + */ + public function getKeys(): array + { + $keys = []; + $file = $this->getDatabase()->readFromFile(); + + foreach ($file as $line) { + /** @var Line $line */ + $keys[] = $line->getKey(); + } + + return $keys; + } + + /** + * Get all data from the database. + * + * @return array + */ + public function getAll(): array + { + $data = []; + $file = $this->getDatabase()->readFromFile(); + + foreach ($file as $line) { + /** @var Line $line */ + $data[$line->getKey()] = $this->decodeData($line->getData()); + } + + return $data; + } + + /** + * Replace a key in the database. + * + * @param string $key + * @param mixed $data + */ + protected function replace(string $key, $data) + { + // Write a new database to a temporary file + $tmpFile = $this->getDatabase()->openTempFile(); + $file = $this->getDatabase()->readFromFile(); + + foreach ($file as $line) { + /** @var Line $line */ + if ($line->getKey() == $key) { + if ($data !== false) { + $tmpFile->fwrite($this->getLineString($key, $data)); + } + } else { + $tmpFile->fwrite($line->getLine() . "\n"); + } + } + + $tmpFile->rewind(); + + // Overwrite the database with the temporary file + $this->getDatabase()->writeTempToFile($tmpFile); + + // Delete the key from cache + if ($cache = $this->getConfig()->getCache()) { + $cache->delete($key); + } + } + + /** + * Get the line string to write. + * + * @param string $key + * @param mixed $data + * + * @return string + */ + protected function getLineString(string $key, $data): string + { + return $key . '=' . $this->encodeData($data) . "\n"; + } + + /** + * Decode a string into data. + * + * @param string $data + * + * @return mixed + */ + protected function decodeData(string $data) + { + return $this->getConfig()->getFormatter()->decode($data); + } + + /** + * Encode data into a string. + * + * @param mixed $data + * + * @return string + */ + protected function encodeData($data): string + { + return $this->getConfig()->getFormatter()->encode($data); + } +} diff --git a/core/vendor/flintstone/Formatter/FormatterInterface.php b/core/vendor/flintstone/Formatter/FormatterInterface.php new file mode 100644 index 00000000..8666dd63 --- /dev/null +++ b/core/vendor/flintstone/Formatter/FormatterInterface.php @@ -0,0 +1,24 @@ +assoc = $assoc; + } + + /** + * {@inheritdoc} + */ + public function encode($data): string + { + $result = json_encode($data); + + if (json_last_error() === JSON_ERROR_NONE) { + return $result; + } + + throw new Exception(json_last_error_msg()); + } + + /** + * {@inheritdoc} + */ + public function decode(string $data) + { + $result = json_decode($data, $this->assoc); + + if (json_last_error() === JSON_ERROR_NONE) { + return $result; + } + + throw new Exception(json_last_error_msg()); + } +} diff --git a/core/vendor/flintstone/Formatter/SerializeFormatter.php b/core/vendor/flintstone/Formatter/SerializeFormatter.php new file mode 100644 index 00000000..c16087ec --- /dev/null +++ b/core/vendor/flintstone/Formatter/SerializeFormatter.php @@ -0,0 +1,52 @@ +preserveLines($data, false)); + } + + /** + * {@inheritdoc} + */ + public function decode(string $data) + { + return $this->preserveLines(unserialize($data), true); + } + + /** + * Preserve new lines, recursive function. + * + * @param mixed $data + * @param bool $reverse + * + * @return mixed + */ + protected function preserveLines($data, bool $reverse) + { + $search = ["\n", "\r"]; + $replace = ['\\n', '\\r']; + + if ($reverse) { + $search = ['\\n', '\\r']; + $replace = ["\n", "\r"]; + } + + if (is_string($data)) { + $data = str_replace($search, $replace, $data); + } elseif (is_array($data)) { + foreach ($data as &$value) { + $value = $this->preserveLines($value, $reverse); + } + unset($value); + } + + return $data; + } +} diff --git a/core/vendor/flintstone/Line.php b/core/vendor/flintstone/Line.php new file mode 100644 index 00000000..c9f52e03 --- /dev/null +++ b/core/vendor/flintstone/Line.php @@ -0,0 +1,37 @@ +line = $line; + $this->pieces = explode('=', $line, 2); + } + + public function getLine(): string + { + return $this->line; + } + + public function getKey(): string + { + return $this->pieces[0]; + } + + public function getData(): string + { + return $this->pieces[1]; + } +} diff --git a/core/vendor/flintstone/Validation.php b/core/vendor/flintstone/Validation.php new file mode 100644 index 00000000..29e901e5 --- /dev/null +++ b/core/vendor/flintstone/Validation.php @@ -0,0 +1,34 @@ +