Browse Source

Merge branch '3.next' into 3.7-client-response

ADmad 7 years ago
parent
commit
19d27a643e

+ 8 - 4
.travis.yml

@@ -11,11 +11,12 @@ dist: trusty
 
 env:
   matrix:
+    - DB=sqlite db_dsn='sqlite:///:memory:'
     - DB=mysql db_dsn='mysql://root@127.0.0.1/cakephp_test?init[]=SET sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"'
     - DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test'
-    - DB=sqlite db_dsn='sqlite:///:memory:'
   global:
     - DEFAULT=1
+    - CODECOVERAGE=0
 
 services:
   - memcached
@@ -41,6 +42,9 @@ matrix:
     - php: 7.1
       env: PHPSTAN=1 DEFAULT=0
 
+    - php: 7.2
+      env: CODECOVERAGE=1 DEFAULT=0
+
 before_install:
   - echo cakephp version && tail -1 VERSION.txt
   - if [[ ${TRAVIS_PHP_VERSION} != "7.3" ]]; then phpenv config-rm xdebug.ini; fi
@@ -72,14 +76,14 @@ before_script:
   - composer install --prefer-dist --no-interaction
 
 script:
-  - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.0 ]]; then export CODECOVERAGE=1; phpdbg -qrr vendor/bin/phpunit --coverage-clover=clover.xml; fi
-  - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION != 7.0 ]]; then vendor/bin/phpunit; fi
+  - if [[ $DEFAULT = 1 ]]; then vendor/bin/phpunit; fi
+  - if [[ $CODECOVERAGE = 1 ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=clover.xml; fi
 
   - if [[ $PHPCS = 1 ]]; then composer cs-check; fi
   - if [[ $PHPSTAN = 1 ]]; then composer require --dev "phpstan/phpstan:^0.10" && vendor/bin/phpstan analyse -c phpstan.neon -l 2 src; fi
 
 after_success:
-  - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.0 ]]; then bash <(curl -s https://codecov.io/bash); fi
+  - if [[ $CODECOVERAGE = 1 ]]; then bash <(curl -s https://codecov.io/bash); fi
 
 notifications:
   email: false

+ 1 - 1
VERSION.txt

@@ -16,4 +16,4 @@
 // @license       https://opensource.org/licenses/mit-license.php MIT License
 // +--------------------------------------------------------------------------------------------+ //
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-3.7.0-RC2
+3.7.0-RC3

+ 2 - 2
composer.json

@@ -94,8 +94,8 @@
             "@cs-check",
             "@test"
         ],
-        "cs-check": "phpcs --colors -p ./src ./tests",
-        "cs-fix": "phpcbf --colors ./src ./tests",
+        "cs-check": "phpcs --colors -p src/ tests/",
+        "cs-fix": "phpcbf --colors src/ tests/",
         "test": "phpunit",
         "test-coverage": "phpunit --coverage-clover=clover.xml"
     }

+ 183 - 0
src/Cache/Engine/ArrayEngine.php

@@ -0,0 +1,183 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         3.7.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Cache\Engine;
+
+use Cake\Cache\CacheEngine;
+
+/**
+ * Array storage engine for cache.
+ *
+ * Not actually a persistent cache engine. All data is only
+ * stored in memory for the duration of a single process. While not
+ * useful in production settings this engine can be useful in tests
+ * or console tools where you don't want the overhead of interacting
+ * with a cache servers, but want the work saving properties a cache
+ * provides.
+ */
+class ArrayEngine extends CacheEngine
+{
+    /**
+     * Cached data.
+     *
+     * Structured as [key => [exp => expiration, val => value]]
+     *
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * Write data for key into cache
+     *
+     * @param string $key Identifier for the data
+     * @param mixed $value Data to be cached
+     * @return bool True if the data was successfully cached, false on failure
+     */
+    public function write($key, $value)
+    {
+        $key = $this->_key($key);
+        $expires = time() + $this->_config['duration'];
+        $this->data[$key] = ['exp' => $expires, 'val' => $value];
+
+        return true;
+    }
+
+    /**
+     * Read a key from the cache
+     *
+     * @param string $key Identifier for the data
+     * @return mixed The cached data, or false if the data doesn't exist,
+     *   has expired, or if there was an error fetching it
+     */
+    public function read($key)
+    {
+        $key = $this->_key($key);
+        if (!isset($this->data[$key])) {
+            return false;
+        }
+        $data = $this->data[$key];
+
+        // Check expiration
+        $now = time();
+        if ($data['exp'] <= $now) {
+            unset($this->data[$key]);
+
+            return false;
+        }
+
+        return $data['val'];
+    }
+
+    /**
+     * Increments the value of an integer cached key
+     *
+     * @param string $key Identifier for the data
+     * @param int $offset How much to increment
+     * @return bool|int New incremented value, false otherwise
+     */
+    public function increment($key, $offset = 1)
+    {
+        if (!$this->read($key)) {
+            $this->write($key, 0);
+        }
+        $key = $this->_key($key);
+        $this->data[$key]['val'] += $offset;
+
+        return $this->data[$key]['val'];
+    }
+
+    /**
+     * Decrements the value of an integer cached key
+     *
+     * @param string $key Identifier for the data
+     * @param int $offset How much to subtract
+     * @return bool|int New decremented value, false otherwise
+     */
+    public function decrement($key, $offset = 1)
+    {
+        if (!$this->read($key)) {
+            $this->write($key, 0);
+        }
+        $key = $this->_key($key);
+        $this->data[$key]['val'] -= $offset;
+
+        return $this->data[$key]['val'];
+    }
+
+    /**
+     * Delete a key from the cache
+     *
+     * @param string $key Identifier for the data
+     * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
+     */
+    public function delete($key)
+    {
+        $key = $this->_key($key);
+        unset($this->data[$key]);
+
+        return true;
+    }
+
+    /**
+     * Delete all keys from the cache. This will clear every cache config using APC.
+     *
+     * @param bool $check Unused argument required by interface.
+     * @return bool True Returns true.
+     */
+    public function clear($check)
+    {
+        $this->data = [];
+
+        return true;
+    }
+
+    /**
+     * Returns the `group value` for each of the configured groups
+     * If the group initial value was not found, then it initializes
+     * the group accordingly.
+     *
+     * @return array
+     */
+    public function groups()
+    {
+        $result = [];
+        foreach ($this->_config['groups'] as $group) {
+            $key = $this->_config['prefix'] . $group;
+            if (!isset($this->data[$key])) {
+                $this->data[$key] = ['exp' => PHP_INT_MAX, 'val' => 1];
+            }
+            $value = $this->data[$key]['val'];
+            $result[] = $group . $value;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Increments the group value to simulate deletion of all keys under a group
+     * old values will remain in storage until they expire.
+     *
+     * @param string $group The group to clear.
+     * @return bool success
+     */
+    public function clearGroup($group)
+    {
+        $key = $this->_config['prefix'] . $group;
+        if (isset($this->data[$key])) {
+            $this->data[$key]['val'] += 1;
+        }
+
+        return true;
+    }
+}

+ 3 - 3
src/Command/HelpCommand.php

@@ -54,9 +54,9 @@ class HelpCommand extends Command implements CommandCollectionAwareInterface
     {
         if (!$args->getOption('xml')) {
             $io->out('<info>Current Paths:</info>', 2);
-            $io->out('* app:  ' . APP_DIR);
-            $io->out('* root: ' . rtrim(ROOT, DIRECTORY_SEPARATOR));
-            $io->out('* core: ' . rtrim(CORE_PATH, DIRECTORY_SEPARATOR));
+            $io->out('* app:  ' . APP_DIR . DIRECTORY_SEPARATOR);
+            $io->out('* root: ' . ROOT . DIRECTORY_SEPARATOR);
+            $io->out('* core: ' . CORE_PATH);
             $io->out('');
 
             $io->out('<info>Available Commands:</info>', 2);

+ 3 - 3
src/Routing/Middleware/RoutingMiddleware.php

@@ -39,7 +39,7 @@ class RoutingMiddleware
     /**
      * The application that will have its routing hook invoked.
      *
-     * @var \Cake\Http\BaseApplication|null
+     * @var \Cake\Core\HttpApplicationInterface|null
      */
     protected $app;
 
@@ -54,10 +54,10 @@ class RoutingMiddleware
     /**
      * Constructor
      *
-     * @param \Cake\Http\BaseApplication|null $app The application instance that routes are defined on.
+     * @param \Cake\Core\HttpApplicationInterface|null $app The application instance that routes are defined on.
      * @param string|null $cacheConfig The cache config name to use or null to disable routes cache
      */
-    public function __construct(BaseApplication $app = null, $cacheConfig = null)
+    public function __construct(HttpApplicationInterface $app = null, $cacheConfig = null)
     {
         $this->app = $app;
         $this->cacheConfig = $cacheConfig;

+ 3 - 3
src/Shell/CommandListShell.php

@@ -74,9 +74,9 @@ class CommandListShell extends Shell
     {
         if (!$this->param('xml') && !$this->param('version')) {
             $this->out('<info>Current Paths:</info>', 2);
-            $this->out('* app:  ' . APP_DIR);
-            $this->out('* root: ' . rtrim(ROOT, DIRECTORY_SEPARATOR));
-            $this->out('* core: ' . rtrim(CORE_PATH, DIRECTORY_SEPARATOR));
+            $this->out('* app:  ' . APP_DIR . DIRECTORY_SEPARATOR);
+            $this->out('* root: ' . ROOT . DIRECTORY_SEPARATOR);
+            $this->out('* core: ' . CORE_PATH);
             $this->out('');
 
             $this->out('<info>Available Shells:</info>', 2);

+ 281 - 0
tests/TestCase/Cache/Engine/ArrayEngineTest.php

@@ -0,0 +1,281 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         3.7.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\Cache\Engine;
+
+use Cake\Cache\Cache;
+use Cake\TestSuite\TestCase;
+
+/**
+ * ArrayEngineTest class
+ */
+class ArrayEngineTest extends TestCase
+{
+    /**
+     * setUp method
+     *
+     * @return void
+     */
+    public function setUp()
+    {
+        parent::setUp();
+
+        Cache::enable();
+        $this->_configCache();
+        Cache::clearAll();
+    }
+
+    /**
+     * tearDown method
+     *
+     * @return void
+     */
+    public function tearDown()
+    {
+        parent::tearDown();
+        Cache::drop('array');
+        Cache::drop('array_groups');
+    }
+
+    /**
+     * Helper method for testing.
+     *
+     * @param array $config
+     * @return void
+     */
+    protected function _configCache($config = [])
+    {
+        $defaults = [
+            'className' => 'Array',
+            'prefix' => 'cake_',
+            'warnOnWriteFailures' => true,
+        ];
+        Cache::drop('array');
+        Cache::setConfig('array', array_merge($defaults, $config));
+    }
+
+    /**
+     * testReadAndWriteCache method
+     *
+     * @return void
+     */
+    public function testReadAndWriteCache()
+    {
+        $this->_configCache(['duration' => 1]);
+
+        $result = Cache::read('test', 'array');
+        $expecting = '';
+        $this->assertEquals($expecting, $result);
+
+        $data = 'this is a test of the emergency broadcasting system';
+        $result = Cache::write('test', $data, 'array');
+        $this->assertTrue($result);
+
+        $result = Cache::read('test', 'array');
+        $expecting = $data;
+        $this->assertEquals($expecting, $result);
+
+        Cache::delete('test', 'array');
+    }
+
+    /**
+     * testExpiry method
+     *
+     * @return void
+     */
+    public function testExpiry()
+    {
+        $this->_configCache(['duration' => 1]);
+
+        $result = Cache::read('test', 'array');
+        $this->assertFalse($result);
+
+        $data = 'this is a test of the emergency broadcasting system';
+        $result = Cache::write('other_test', $data, 'array');
+        $this->assertTrue($result);
+
+        sleep(2);
+        $result = Cache::read('other_test', 'array');
+        $this->assertFalse($result);
+    }
+
+    /**
+     * testDeleteCache method
+     *
+     * @return void
+     */
+    public function testDeleteCache()
+    {
+        $data = 'this is a test of the emergency broadcasting system';
+        $result = Cache::write('delete_test', $data, 'array');
+        $this->assertTrue($result);
+
+        $result = Cache::delete('delete_test', 'array');
+        $this->assertTrue($result);
+    }
+
+    /**
+     * testDecrement method
+     *
+     * @return void
+     */
+    public function testDecrement()
+    {
+        $result = Cache::write('test_decrement', 5, 'array');
+        $this->assertTrue($result);
+
+        $result = Cache::decrement('test_decrement', 1, 'array');
+        $this->assertEquals(4, $result);
+
+        $result = Cache::read('test_decrement', 'array');
+        $this->assertEquals(4, $result);
+
+        $result = Cache::decrement('test_decrement', 2, 'array');
+        $this->assertEquals(2, $result);
+
+        $result = Cache::read('test_decrement', 'array');
+        $this->assertEquals(2, $result);
+    }
+
+    /**
+     * testIncrement method
+     *
+     * @return void
+     */
+    public function testIncrement()
+    {
+        $result = Cache::write('test_increment', 5, 'array');
+        $this->assertTrue($result);
+
+        $result = Cache::increment('test_increment', 1, 'array');
+        $this->assertSame(6, $result);
+
+        $result = Cache::read('test_increment', 'array');
+        $this->assertSame(6, $result);
+
+        $result = Cache::increment('test_increment', 2, 'array');
+        $this->assertSame(8, $result);
+
+        $result = Cache::read('test_increment', 'array');
+        $this->assertSame(8, $result);
+    }
+
+    /**
+     * test the clearing of cache keys
+     *
+     * @return void
+     */
+    public function testClear()
+    {
+        Cache::write('some_value', 'value', 'array');
+
+        $result = Cache::clear(false, 'array');
+        $this->assertTrue($result);
+        $this->assertFalse(Cache::read('some_value', 'array'));
+    }
+
+    /**
+     * Tests that configuring groups for stored keys return the correct values when read/written
+     * Shows that altering the group value is equivalent to deleting all keys under the same
+     * group
+     *
+     * @return void
+     */
+    public function testGroupsReadWrite()
+    {
+        Cache::setConfig('array_groups', [
+            'engine' => 'array',
+            'duration' => 30,
+            'groups' => ['group_a', 'group_b'],
+            'prefix' => 'test_',
+            'warnOnWriteFailures' => true,
+        ]);
+        $this->assertTrue(Cache::write('test_groups', 'value', 'array_groups'));
+        $this->assertEquals('value', Cache::read('test_groups', 'array_groups'));
+
+        Cache::clearGroup('group_a', 'array_groups');
+        $this->assertFalse(Cache::read('test_groups', 'array_groups'));
+        $this->assertTrue(Cache::write('test_groups', 'value2', 'array_groups'));
+        $this->assertEquals('value2', Cache::read('test_groups', 'array_groups'));
+
+        Cache::clearGroup('group_b', 'array_groups');
+        $this->assertFalse(Cache::read('test_groups', 'array_groups'));
+        $this->assertTrue(Cache::write('test_groups', 'value3', 'array_groups'));
+        $this->assertEquals('value3', Cache::read('test_groups', 'array_groups'));
+    }
+
+    /**
+     * Tests that deleting from a groups-enabled config is possible
+     *
+     * @return void
+     */
+    public function testGroupDelete()
+    {
+        Cache::setConfig('array_groups', [
+            'engine' => 'array',
+            'duration' => 10,
+            'groups' => ['group_a', 'group_b'],
+            'prefix' => 'test_',
+            'warnOnWriteFailures' => true,
+        ]);
+        $this->assertTrue(Cache::write('test_groups', 'value', 'array_groups'));
+        $this->assertEquals('value', Cache::read('test_groups', 'array_groups'));
+
+        $this->assertTrue(Cache::delete('test_groups', 'array_groups'));
+        $this->assertFalse(Cache::read('test_groups', 'array_groups'));
+    }
+
+    /**
+     * Test clearing a cache group
+     *
+     * @return void
+     */
+    public function testGroupClear()
+    {
+        Cache::setConfig('array_groups', [
+            'engine' => 'array',
+            'duration' => 10,
+            'groups' => ['group_a', 'group_b'],
+            'prefix' => 'test_',
+            'warnOnWriteFailures' => true,
+        ]);
+
+        $this->assertTrue(Cache::write('test_groups', 'value', 'array_groups'));
+        $this->assertTrue(Cache::clearGroup('group_a', 'array_groups'));
+        $this->assertFalse(Cache::read('test_groups', 'array_groups'));
+
+        $this->assertTrue(Cache::write('test_groups', 'value2', 'array_groups'));
+        $this->assertTrue(Cache::clearGroup('group_b', 'array_groups'));
+        $this->assertFalse(Cache::read('test_groups', 'array_groups'));
+    }
+
+    /**
+     * Test add
+     *
+     * @return void
+     */
+    public function testAdd()
+    {
+        Cache::delete('test_add_key', 'array');
+
+        $result = Cache::add('test_add_key', 'test data', 'array');
+        $this->assertTrue($result);
+
+        $expected = 'test data';
+        $result = Cache::read('test_add_key', 'array');
+        $this->assertEquals($expected, $result);
+
+        $result = Cache::add('test_add_key', 'test data 2', 'array');
+        $this->assertFalse($result);
+    }
+}