Repository: fire015/flintstone Branch: master Commit: 9eab326bc5fc Files: 28 Total size: 40.7 KB Directory structure: gitextract_ocavop58/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── UPGRADE.md ├── composer.json ├── phpunit.xml.dist ├── src/ │ ├── Cache/ │ │ ├── ArrayCache.php │ │ └── CacheInterface.php │ ├── Config.php │ ├── Database.php │ ├── Exception.php │ ├── Flintstone.php │ ├── Formatter/ │ │ ├── FormatterInterface.php │ │ ├── JsonFormatter.php │ │ └── SerializeFormatter.php │ ├── Line.php │ └── Validation.php └── tests/ ├── Cache/ │ └── ArrayCacheTest.php ├── ConfigTest.php ├── DatabaseTest.php ├── FlintstoneTest.php ├── Formatter/ │ ├── JsonFormatterTest.php │ └── SerializeFormatterTest.php ├── LineTest.php └── ValidationTest.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ github: fire015 ================================================ FILE: .gitignore ================================================ /vendor composer.lock .idea .phpunit.result.cache ================================================ FILE: .travis.yml ================================================ language: php sudo: false dist: bionic php: - 7.3 - 7.4 - 8.0 - 8.1.0 install: - travis_retry composer install --no-interaction --prefer-dist script: vendor/bin/phpunit ================================================ FILE: CHANGELOG.md ================================================ Change Log ========== ### 19/01/2021 - 2.3 * Bump minimum PHP version to 7.3 * Update PHPUnit to version 9 (ensure Flintstone is compatible with PHP 8) ### 12/03/2019 - 2.2 * Bump minimum PHP version to 7.0 * Update PHPUnit to version 6 * Removed data type validation for storing * Added param and return types ### 09/06/2017 - 2.1.1 * Update `Database::writeTempToFile` to correctly close the file pointer and free up memory ### 24/05/2017 - 2.1 * Bump minimum PHP version to 5.6 * Tidy up of Flintstone class, moved some code into `Database` * Added `Line` and `Validation` classes * Closed off public methods `Database::openFile` and `Database::closeFile` ### 20/01/2016 - 2.0 * Major refactor, class names have changed and the whole codebase is much more extensible * Removed the static `load` and `unload` methods and the `FlinstoneDB` class * The `replace` method is no longer public * The `getFile` method has been removed * Default swap memory limit has been increased to 2MB * Ability to pass any instance for cache that implements `Flintstone\Cache\CacheInterface` ### 25/03/2015 - 1.9 * Added `getAll` method and some refactoring ### 15/10/2014 - 1.8 * Added formatter option so that you can control how data is encoded/decoded (default is serialize but also ships with json) ### 09/10/2014 - 1.7 * Moved from fopen to SplFileObject * Moved composer loader from PSR-0 to PSR-4 * Code is now PSR-2 compliant * Added PHP 5.6 to travis ### 30/09/2014 - 1.6 * Updated limits on valid characters in key name and size * Improved unit tests ### 29/05/2014 - 1.5 * Reduced some internal complexity * Fixed gzip compression * Unit tests now running against all options * Removed `setOptions` method, must be passed into the `load` method ### 11/03/2014 - 1.4 * Now using Composer ### 16/07/2013 - 1.3 * Changed the load method to static so that multiple instances can be loaded without conflict (use Flintstone::load now instead of $db->load) * Exception thrown is now FlintstoneException ### 23/01/2013 - 1.2 * Removed the multibyte unserialize method as it seems to work without ### 22/06/2012 - 1.1 * Added new method getKeys() to return an array of keys in the database ### 17/06/2011 - 1.0 * Initial release ================================================ FILE: LICENSE.md ================================================ # MIT License Copyright (c) 2010-2017 Jason M Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. **THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.** ================================================ FILE: README.md ================================================ Flintstone ========== [![Total Downloads](https://img.shields.io/packagist/dm/fire015/flintstone.svg)](https://packagist.org/packages/fire015/flintstone) [![Build Status](https://travis-ci.com/fire015/flintstone.svg?branch=master)](https://travis-ci.com/github/fire015/flintstone) A key/value database store using flat files for PHP. Features include: * Memory efficient * File locking * Caching * Gzip compression * Easy to use ### Installation The easiest way to install Flintstone is via [composer](http://getcomposer.org/). Run the following command to install it. ``` composer require fire015/flintstone ``` ```php '/path/to/database/dir/']); ``` ### Requirements - PHP 7.3+ ### Data types Flintstone can store any data type that can be formatted into a string. By default this uses `serialize()`. See [Changing the formatter](#changing-the-formatter) for more details. ### Options |Name |Type |Default Value |Description | |--- |--- |--- |--- | |dir |string |the current working directory |The directory where the database files are stored (this should be somewhere that is not web accessible) e.g. /path/to/database/ | |ext |string |.dat |The database file extension to use | |gzip |boolean |false |Use gzip to compress the database | |cache |boolean or object |true |Whether to cache `get()` results for faster data retrieval | |formatter |null or object |null |The formatter class used to encode/decode data | |swap_memory_limit |integer |2097152 |The amount of memory to use before writing to a temporary file | ### Usage examples ```php '/path/to/database/dir/']); // Set a key $users->set('bob', ['email' => 'bob@site.com', 'password' => '123456']); // Get a key $user = $users->get('bob'); echo 'Bob, your email is ' . $user['email']; // Retrieve all key names $keys = $users->getKeys(); // returns array('bob') // Retrieve all data $data = $users->getAll(); // returns array('bob' => array('email' => 'bob@site.com', 'password' => '123456')); // Delete a key $users->delete('bob'); // Flush the database $users->flush(); ``` ### Changing the formatter By default Flintstone will encode/decode data using PHP's serialize functions, however you can override this with your own class if you prefer. Just make sure it implements `Flintstone\Formatter\FormatterInterface` and then you can provide it as the `formatter` option. If you wish to use JSON as the formatter, Flintstone already ships with this as per the example below: ```php __DIR__, 'formatter' => new JsonFormatter() ]); ``` ### Changing the cache To speed up data retrieval Flintstone can store the results of `get()` in a cache store. By default this uses a simple array that only persist's for as long as the `Flintstone` object exists. If you want to use your own cache store (such as Memcached) you can pass a class as the `cache` option. Just make sure it implements `Flintstone\Cache\CacheInterface`. ================================================ FILE: UPGRADE.md ================================================ Upgrading from version 1.x to 2.x ================================= As Flintstone is no longer loaded statically the major change required is to switch from using the static `load` method to just instantiating a new instance of Flinstone. The `FlinstoneDB` class has also been removed and `Flintstone\FlintstoneException` is now `Flintstone\Exception`. ### Version 1.x: ```php '/path/to/database/dir/')); } catch (FlintstoneException $e) { } ``` ### Version 2.x: ```php '/path/to/database/dir/')); } catch (Exception $e) { } ``` See CHANGELOG.md for further changes. ================================================ FILE: composer.json ================================================ { "name": "fire015/flintstone", "type": "library", "description": "A key/value database store using flat files for PHP", "keywords": ["flintstone", "database", "cache", "files", "memory"], "homepage": "https://github.com/fire015/flintstone", "license": "MIT", "authors": [ { "name": "Jason M", "email": "emailfire@gmail.com" } ], "require": { "php": ">=7.3" }, "autoload": { "psr-4": { "Flintstone\\": "src/" } }, "require-dev" : { "phpunit/phpunit": "^9" } } ================================================ FILE: phpunit.xml.dist ================================================ ./src ./tests ================================================ FILE: src/Cache/ArrayCache.php ================================================ 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 = []; } } ================================================ FILE: src/Cache/CacheInterface.php ================================================ 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; } } ================================================ FILE: src/Database.php ================================================ [ '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; } } ================================================ FILE: src/Exception.php ================================================ 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); } } ================================================ FILE: src/Formatter/FormatterInterface.php ================================================ 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()); } } ================================================ FILE: src/Formatter/SerializeFormatter.php ================================================ 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; } } ================================================ FILE: src/Line.php ================================================ 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]; } } ================================================ FILE: src/Validation.php ================================================ cache = new ArrayCache(); } /** * @test */ public function canGetAndSet() { $this->cache->set('foo', 'bar'); $this->assertTrue($this->cache->contains('foo')); $this->assertEquals('bar', $this->cache->get('foo')); } /** * @test */ public function canDelete() { $this->cache->set('foo', 'bar'); $this->cache->delete('foo'); $this->assertFalse($this->cache->contains('foo')); } /** * @test */ public function canFlush() { $this->cache->set('foo', 'bar'); $this->cache->flush(); $this->assertFalse($this->cache->contains('foo')); } } ================================================ FILE: tests/ConfigTest.php ================================================ assertEquals(getcwd().DIRECTORY_SEPARATOR, $config->getDir()); $this->assertEquals('.dat', $config->getExt()); $this->assertFalse($config->useGzip()); $this->assertInstanceOf(ArrayCache::class, $config->getCache()); $this->assertInstanceOf(SerializeFormatter::class, $config->getFormatter()); $this->assertEquals(2097152, $config->getSwapMemoryLimit()); } /** * @test */ public function constructorConfigOverride() { $config = new Config([ 'dir' => __DIR__, 'ext' => 'test', 'gzip' => true, 'cache' => false, 'formatter' => null, 'swap_memory_limit' => 100, ]); $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR, $config->getDir()); $this->assertEquals('.test.gz', $config->getExt()); $this->assertTrue($config->useGzip()); $this->assertFalse($config->getCache()); $this->assertInstanceOf(SerializeFormatter::class, $config->getFormatter()); $this->assertEquals(100, $config->getSwapMemoryLimit()); } /** * @test */ public function setValidFormatter() { $config = new Config(); $config->setFormatter(new JsonFormatter()); $this->assertInstanceOf(JsonFormatter::class, $config->getFormatter()); } /** * @test */ public function setInvalidFormatter() { $this->expectException(\Flintstone\Exception::class); $config = new Config(); $config->setFormatter(new self()); } /** * @test */ public function invalidDirSet() { $this->expectException(\Flintstone\Exception::class); $config = new Config(); $config->setDir('/x/y/z/foo'); } /** * @test */ public function invalidCacheSet() { $this->expectException(\Flintstone\Exception::class); $config = new Config(); $config->setCache(new self()); } } ================================================ FILE: tests/DatabaseTest.php ================================================ __DIR__, ]); $this->db = new Database('test', $config); } protected function tearDown(): void { if (is_file($this->db->getPath())) { unlink($this->db->getPath()); } } /** * @test */ public function databaseHasInvalidName() { $this->expectException(\Flintstone\Exception::class); $config = new Config(); new Database('test!123', $config); } /** * @test */ public function canGetDatabaseAndConfig() { $this->assertEquals('test', $this->db->getName()); $this->assertInstanceOf(Config::class, $this->db->getConfig()); $this->assertEquals(__DIR__ . DIRECTORY_SEPARATOR . 'test.dat', $this->db->getPath()); } /** * @test */ public function canAppendToFile() { $this->db->appendToFile('foo=bar'); $this->assertEquals('foo=bar', file_get_contents($this->db->getPath())); } /** * @test */ public function canFlushFile() { $this->db->appendToFile('foo=bar'); $this->db->flushFile(); $this->assertEmpty(file_get_contents($this->db->getPath())); } /** * @test */ public function canReadFromFile() { $this->db->appendToFile('foo=bar'); $file = $this->db->readFromFile(); foreach ($file as $line) { $this->assertInstanceOf(Line::class, $line); $this->assertEquals('foo', $line->getKey()); $this->assertEquals('bar', $line->getData()); } } /** * @test */ public function canWriteTempToFile() { $tmpFile = new SplTempFileObject(); $tmpFile->fwrite('foo=bar'); $tmpFile->rewind(); $this->db->writeTempToFile($tmpFile); $this->assertEquals('foo=bar', file_get_contents($this->db->getPath())); } } ================================================ FILE: tests/FlintstoneTest.php ================================================ __DIR__, 'cache' => false, ]); $this->assertInstanceOf(Database::class, $db->getDatabase()); $this->assertInstanceOf(Config::class, $db->getConfig()); } /** * @test */ public function keyHasInvalidName() { $this->expectException(\Flintstone\Exception::class); $db = new Flintstone('test', []); $db->get('test!123'); } /** * @test */ public function canRunAllOperations() { $this->runOperationsTests([ 'dir' => __DIR__, 'cache' => false, 'gzip' => false, ]); $this->runOperationsTests([ 'dir' => __DIR__, 'cache' => true, 'gzip' => true, ]); $this->runOperationsTests([ 'dir' => __DIR__, 'cache' => false, 'gzip' => false, 'formatter' => new JsonFormatter(), ]); } private function runOperationsTests(array $config) { $db = new Flintstone('test', $config); $arr = ['foo' => "new\nline"]; $this->assertFalse($db->get('foo')); $db->set('foo', 1); $db->set('name', 'john'); $db->set('arr', $arr); $this->assertEquals(1, $db->get('foo')); $this->assertEquals('john', $db->get('name')); $this->assertEquals($arr, $db->get('arr')); $db->set('foo', 2); $this->assertEquals(2, $db->get('foo')); $this->assertEquals('john', $db->get('name')); $this->assertEquals($arr, $db->get('arr')); $db->delete('name'); $this->assertFalse($db->get('name')); $this->assertEquals($arr, $db->get('arr')); $keys = $db->getKeys(); $this->assertEquals(2, count($keys)); $this->assertEquals('foo', $keys[0]); $this->assertEquals('arr', $keys[1]); $data = $db->getAll(); $this->assertEquals(2, count($data)); $this->assertEquals(2, $data['foo']); $this->assertEquals($arr, $data['arr']); $db->flush(); $this->assertFalse($db->get('foo')); $this->assertFalse($db->get('arr')); $this->assertEquals(0, count($db->getKeys())); $this->assertEquals(0, count($db->getAll())); unlink($db->getDatabase()->getPath()); } } ================================================ FILE: tests/Formatter/JsonFormatterTest.php ================================================ formatter = new JsonFormatter(); } /** * @test * @dataProvider validData */ public function encodesValidData($originalValue, $encodedValue) { $this->assertSame($encodedValue, $this->formatter->encode($originalValue)); } /** * @test * @dataProvider validData */ public function decodesValidData($originalValue, $encodedValue) { $this->assertSame($originalValue, $this->formatter->decode($encodedValue)); } /** * @test */ public function decodesAnObject() { $originalValue = (object)['foo' => 'bar']; $formatter = new JsonFormatter(false); $encodedValue = $formatter->encode($originalValue); $this->assertEquals($originalValue, $formatter->decode($encodedValue)); } /** * @test */ public function encodingInvalidDataThrowsException() { $this->expectException(\Flintstone\Exception::class); $this->formatter->encode(chr(241)); } /** * @test */ public function decodingInvalidDataThrowsException() { $this->expectException(\Flintstone\Exception::class); $this->formatter->decode('{'); } public function validData(): array { return [ [null, 'null'], [1, '1'], ['foo', '"foo"'], [["test", "new\nline"], '["test","new\nline"]'], ]; } } ================================================ FILE: tests/Formatter/SerializeFormatterTest.php ================================================ formatter = new SerializeFormatter(); } /** * @test * @dataProvider validData */ public function encodesValidData($originalValue, $encodedValue) { $this->assertSame($encodedValue, $this->formatter->encode($originalValue)); } /** * @test * @dataProvider validData */ public function decodesValidData($originalValue, $encodedValue) { $this->assertSame($originalValue, $this->formatter->decode($encodedValue)); } public function validData(): array { return [ [null, 'N;'], [1, 'i:1;'], ['foo', 's:3:"foo";'], [["test", "new\nline"], 'a:2:{i:0;s:4:"test";i:1;s:9:"new\nline";}'], ]; } } ================================================ FILE: tests/LineTest.php ================================================ line = new Line('foo=bar'); } /** * @test */ public function canGetLine() { $this->assertEquals('foo=bar', $this->line->getLine()); } /** * @test */ public function canGetKey() { $this->assertEquals('foo', $this->line->getKey()); } /** * @test */ public function canGetData() { $this->assertEquals('bar', $this->line->getData()); } /** * @test */ public function canGetKeyAndDataWithMultipleEquals() { $line = new Line('foo=bar=baz'); $this->assertEquals('foo', $line->getKey()); $this->assertEquals('bar=baz', $line->getData()); } } ================================================ FILE: tests/ValidationTest.php ================================================ expectException(\Flintstone\Exception::class); Validation::validateKey('test!123'); } /** * @test */ public function validateDatabaseName() { $this->expectException(\Flintstone\Exception::class); Validation::validateDatabaseName('test!123'); } }