Browse Source

Merge branch 'master' into 3.next

Mark Story 9 years ago
parent
commit
bd09e3959f

+ 28 - 2
src/Console/ConsoleInputOption.php

@@ -62,6 +62,13 @@ class ConsoleInputOption
     protected $_default;
 
     /**
+     * Can the option accept multiple value definition.
+     *
+     * @var bool
+     */
+    protected $_multiple;
+
+    /**
      * An array of choices for the option.
      *
      * @var array
@@ -77,10 +84,18 @@ class ConsoleInputOption
      * @param bool $boolean Whether this option is a boolean option. Boolean options don't consume extra tokens
      * @param string $default The default value for this option.
      * @param array $choices Valid choices for this option.
+     * @param bool $multiple Whether this option can accept multiple value definition.
      * @throws \Cake\Console\Exception\ConsoleException
      */
-    public function __construct($name, $short = '', $help = '', $boolean = false, $default = '', $choices = [])
-    {
+    public function __construct(
+        $name,
+        $short = '',
+        $help = '',
+        $boolean = false,
+        $default = '',
+        $choices = [],
+        $multiple = false
+    ) {
         if (is_array($name) && isset($name['name'])) {
             foreach ($name as $key => $value) {
                 $this->{'_' . $key} = $value;
@@ -92,6 +107,7 @@ class ConsoleInputOption
             $this->_boolean = $boolean;
             $this->_default = $default;
             $this->_choices = $choices;
+            $this->_multiple = $multiple;
         }
         if (strlen($this->_short) > 1) {
             throw new ConsoleException(
@@ -186,6 +202,16 @@ class ConsoleInputOption
     }
 
     /**
+     * Check if this option accepts multiple values.
+     *
+     * @return bool
+     */
+    public function acceptsMultiple()
+    {
+        return (bool)$this->_multiple;
+    }
+
+    /**
      * Check that a value is a valid choice for this option.
      *
      * @param string $value The choice to validate.

+ 7 - 1
src/Console/ConsoleOptionParser.php

@@ -413,6 +413,8 @@ class ConsoleOptionParser
      * - `boolean` - The option uses no value, it's just a boolean switch. Defaults to false.
      *    If an option is defined as boolean, it will always be added to the parsed params. If no present
      *    it will be false, if present it will be true.
+     * - `multiple` - The option can be provided multiple times. The parsed option
+     *   will be an array of values when this option is enabled.
      * - `choices` A list of valid choices for this option. If left empty all values are valid..
      *   An exception will be raised when parse() encounters an invalid value.
      *
@@ -809,7 +811,11 @@ class ConsoleOptionParser
             $value = $option->defaultValue();
         }
         if ($option->validChoice($value)) {
-            $params[$name] = $value;
+            if ($option->acceptsMultiple($value)) {
+                $params[$name][] = $value;
+            } else {
+                $params[$name] = $value;
+            }
 
             return $params;
         }

+ 6 - 1
src/Http/RequestTransformer.php

@@ -40,7 +40,12 @@ class RequestTransformer
     public static function toCake(PsrRequest $request)
     {
         $post = $request->getParsedBody();
-        $server = $request->getServerParams();
+        $headers = [];
+        foreach ($request->getHeaders() as $k => $value) {
+            $name = sprintf('HTTP_%s', strtoupper(str_replace('-', '_', $k)));
+            $headers[$name] = implode(',', $value);
+        }
+        $server = $headers + $request->getServerParams();
 
         $files = static::getFiles($request);
         if (!empty($files)) {

+ 1 - 1
src/ORM/Behavior/TreeBehavior.php

@@ -158,7 +158,7 @@ class TreeBehavior extends Behavior
      *
      * Manages updating level of descendents of currently saved entity.
      *
-     * @param \Cake\Event\Event $event The beforeSave event that was fired
+     * @param \Cake\Event\Event $event The afterSave event that was fired
      * @param \Cake\Datasource\EntityInterface $entity the entity that is going to be saved
      * @return void
      */

+ 1 - 1
src/Shell/Helper/TableHelper.php

@@ -45,7 +45,7 @@ class TableHelper extends Helper
         foreach ($rows as $line) {
             foreach ($line as $k => $v) {
                 $columnLength = mb_strwidth($line[$k]);
-                if ($columnLength > (isset($widths[$k]) ? $widths[$k] : 0)) {
+                if ($columnLength >= (isset($widths[$k]) ? $widths[$k] : 0)) {
                     $widths[$k] = $columnLength;
                 }
             }

+ 1 - 1
src/Utility/Hash.php

@@ -837,7 +837,7 @@ class Hash
     public static function maxDimensions(array $data)
     {
         $depth = [];
-        if (is_array($data) && reset($data) !== false) {
+        if (is_array($data) && !empty($data)) {
             foreach ($data as $value) {
                 if (is_array($value)) {
                     $depth[] = static::maxDimensions($value) + 1;

+ 26 - 1
src/basics.php

@@ -31,7 +31,7 @@ if (!function_exists('debug')) {
      * Prints out debug information about given variable and returns the
      * variable that was passed.
      *
-     * Only runs if debug level is greater than zero.
+     * Only runs if debug mode is enabled.
      *
      * @param mixed $var Variable to show debug information for.
      * @param bool|null $showHtml If set to true, the method prints the debug data in a browser-friendly way.
@@ -151,3 +151,28 @@ if (!function_exists('breakpoint')) {
         );
     }
 }
+
+if (!function_exists('dd')) {
+    /**
+     * Prints out debug information about given variable and dies.
+     *
+     * Only runs if debug mode is enabled.
+     * It will otherwise just continue code execution and ignore this function.
+     *
+     * @param mixed $var Variable to show debug information for.
+     * @param bool|null $showHtml If set to true, the method prints the debug data in a browser-friendly way.
+     * @return void
+     * @link http://book.cakephp.org/3.0/en/development/debugging.html#basic-debugging
+     */
+    function dd($var, $showHtml = null)
+    {
+        if (!Configure::read('debug')) {
+            return;
+        }
+
+        debug($var, $showHtml, false);
+        $backtrace = debug_backtrace(false, 1);
+        pr('dd-location: ' . $backtrace[0]['file'] . ':' . $backtrace[0]['line']);
+        die(1);
+    }
+}

+ 70 - 0
tests/TestCase/Console/ConsoleOptionParserTest.php

@@ -181,6 +181,29 @@ class ConsoleOptionParserTest extends TestCase
     }
 
     /**
+     * test adding an option and using the short value for parsing.
+     *
+     * @return void
+     */
+    public function testAddOptionWithMultipleShort()
+    {
+        $parser = new ConsoleOptionParser('test', false);
+        $parser->addOption('source', [
+            'multiple' => true,
+            'short' => 's'
+        ]);
+        $result = $parser->parse(['-s', 'mysql', '-s', 'postgres']);
+        $this->assertEquals(
+            [
+                'source' => ['mysql', 'postgres'],
+                'help' => false
+            ],
+            $result[0],
+            'Short parameter did not parse out'
+        );
+    }
+
+    /**
      * Test that adding an option using a two letter short value causes an exception.
      * As they will not parse correctly.
      *
@@ -252,6 +275,53 @@ class ConsoleOptionParserTest extends TestCase
     }
 
     /**
+     * test adding an option that accepts multiple values.
+     *
+     * @return void
+     */
+    public function testAddOptionWithMultiple()
+    {
+        $parser = new ConsoleOptionParser('test', false);
+        $parser->addOption('source', ['short' => 's', 'multiple' => true]);
+
+        $result = $parser->parse(['--source', 'mysql', '-s', 'postgres']);
+        $expected = [
+            'source' => [
+                'mysql',
+                'postgres'
+            ],
+            'help' => false
+        ];
+        $this->assertEquals($expected, $result[0], 'options with multiple values did not parse');
+    }
+
+    /**
+     * test adding multiple options, including one that accepts multiple values.
+     *
+     * @return void
+     */
+    public function testAddMultipleOptionsWithMultiple()
+    {
+        $parser = new ConsoleOptionParser('test', false);
+        $parser
+            ->addOption('source', ['multiple' => true])
+            ->addOption('name')
+            ->addOption('export', ['boolean' => true]);
+
+        $result = $parser->parse(['--export', '--source', 'mysql', '--name', 'annual-report', '--source', 'postgres']);
+        $expected = [
+            'export' => true,
+            'source' => [
+                'mysql',
+                'postgres'
+            ],
+            'name' => 'annual-report',
+            'help' => false
+        ];
+        $this->assertEquals($expected, $result[0], 'options with multiple values did not parse');
+    }
+
+    /**
      * Test adding multiple options.
      *
      * @return void

+ 6 - 0
tests/TestCase/Http/RequestTransformerTest.php

@@ -87,12 +87,18 @@ class RequestTransformerTest extends TestCase
             'SERVER_PORT' => 443,
         ];
         $psr = ServerRequestFactory::fromGlobals($server);
+        $psr = $psr->withHeader('Api-Token', 'abc123')
+            ->withAddedHeader('X-thing', 'one')
+            ->withAddedHeader('X-thing', 'two');
+
         $cake = RequestTransformer::toCake($psr);
         $this->assertEmpty($cake->query);
         $this->assertEmpty($cake->data);
         $this->assertEmpty($cake->cookie);
 
         $this->assertSame('application/json', $cake->header('accept'));
+        $this->assertSame('abc123', $cake->header('Api-Token'));
+        $this->assertSame('one,two', $cake->header('X-thing'));
         $this->assertSame('PATCH', $cake->method());
         $this->assertSame('https', $cake->scheme());
         $this->assertSame(443, $cake->port());

+ 59 - 11
tests/TestCase/Log/LogTest.php

@@ -255,6 +255,54 @@ class LogTest extends TestCase
         Log::config('spam', [
             'engine' => 'File',
             'path' => LOGS,
+            'levels' => 'debug',
+            'file' => 'spam',
+        ]);
+        Log::config('eggs', [
+            'engine' => 'File',
+            'path' => LOGS,
+            'levels' => ['eggs', 'debug', 'error', 'warning'],
+            'file' => 'eggs',
+        ]);
+
+        $testMessage = 'selective logging';
+        Log::write('warning', $testMessage);
+
+        $this->assertFileExists(LOGS . 'eggs.log');
+        $this->assertFileNotExists(LOGS . 'spam.log');
+
+        Log::write('debug', $testMessage);
+        $this->assertFileExists(LOGS . 'spam.log');
+
+        $contents = file_get_contents(LOGS . 'spam.log');
+        $this->assertContains('Debug: ' . $testMessage, $contents);
+        $contents = file_get_contents(LOGS . 'eggs.log');
+        $this->assertContains('Debug: ' . $testMessage, $contents);
+
+        if (file_exists(LOGS . 'spam.log')) {
+            unlink(LOGS . 'spam.log');
+        }
+        if (file_exists(LOGS . 'eggs.log')) {
+            unlink(LOGS . 'eggs.log');
+        }
+    }
+
+    /**
+     * test selective logging by level using the `types` attribute
+     *
+     * @return void
+     */
+    public function testSelectiveLoggingByLevelUsingTypes()
+    {
+        if (file_exists(LOGS . 'spam.log')) {
+            unlink(LOGS . 'spam.log');
+        }
+        if (file_exists(LOGS . 'eggs.log')) {
+            unlink(LOGS . 'eggs.log');
+        }
+        Log::config('spam', [
+            'engine' => 'File',
+            'path' => LOGS,
             'types' => 'debug',
             'file' => 'spam',
         ]);
@@ -292,13 +340,13 @@ class LogTest extends TestCase
         Log::config('debug', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['notice', 'info', 'debug'],
+            'levels' => ['notice', 'info', 'debug'],
             'file' => 'debug',
         ]);
         Log::config('error', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['warning', 'error', 'critical', 'alert', 'emergency'],
+            'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
             'file' => 'error',
         ]);
     }
@@ -337,7 +385,7 @@ class LogTest extends TestCase
         Log::config('shops', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['info', 'debug', 'warning'],
+            'levels' => ['info', 'debug', 'warning'],
             'scopes' => ['transactions', 'orders'],
             'file' => 'shops',
         ]);
@@ -378,14 +426,14 @@ class LogTest extends TestCase
         Log::config('debug', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['notice', 'info', 'debug'],
+            'levels' => ['notice', 'info', 'debug'],
             'file' => 'debug',
             'scopes' => false
         ]);
         Log::config('shops', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['info', 'debug', 'warning'],
+            'levels' => ['info', 'debug', 'warning'],
             'file' => 'shops',
             'scopes' => ['transactions', 'orders'],
         ]);
@@ -424,7 +472,7 @@ class LogTest extends TestCase
         Log::config('shops', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['info', 'debug', 'notice', 'warning'],
+            'levels' => ['info', 'debug', 'notice', 'warning'],
             'scopes' => ['transactions', 'orders'],
             'file' => 'shops',
         ]);
@@ -465,14 +513,14 @@ class LogTest extends TestCase
         Log::config('shops', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['debug', 'notice', 'warning'],
+            'levels' => ['debug', 'notice', 'warning'],
             'scopes' => ['transactions', 'orders'],
             'file' => 'shops.log',
         ]);
         Log::config('eggs', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['debug', 'notice', 'warning'],
+            'levels' => ['debug', 'notice', 'warning'],
             'scopes' => ['eggs'],
             'file' => 'eggs.log',
         ]);
@@ -500,7 +548,7 @@ class LogTest extends TestCase
         Log::config('scope_test', [
             'engine' => 'TestApp',
             'path' => LOGS,
-            'types' => ['notice', 'info', 'debug'],
+            'levels' => ['notice', 'info', 'debug'],
             'scopes' => ['foo', 'bar'],
         ]);
 
@@ -527,13 +575,13 @@ class LogTest extends TestCase
         Log::config('debug', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['notice', 'info', 'debug'],
+            'levels' => ['notice', 'info', 'debug'],
             'file' => 'debug',
         ]);
         Log::config('error', [
             'engine' => 'File',
             'path' => LOGS,
-            'types' => ['emergency', 'alert', 'critical', 'error', 'warning'],
+            'levels' => ['emergency', 'alert', 'critical', 'error', 'warning'],
             'file' => 'error',
         ]);
 

+ 58 - 0
tests/TestCase/Shell/Helper/TableHelperTest.php

@@ -24,6 +24,20 @@ use Cake\TestSuite\TestCase;
  */
 class TableHelperTest extends TestCase
 {
+    /**
+     * @var ConsoleOutput
+     */
+    public $stub;
+
+    /**
+     * @var ConsoleIo
+     */
+    public $io;
+
+    /**
+     * @var TableHelper
+     */
+    public $helper;
 
     /**
      * setUp method
@@ -64,6 +78,50 @@ class TableHelperTest extends TestCase
     }
 
     /**
+     * Test that output works when data contains just empty strings.
+     */
+    public function testEmptyStrings()
+    {
+        $data = [
+            ['Header 1', 'Header', 'Empty'],
+            ['short', 'Longish thing', ''],
+            ['Longer thing', 'short', ''],
+        ];
+        $this->helper->output($data);
+        $expected = [
+            '+--------------+---------------+-------+',
+            '| <info>Header 1</info>     | <info>Header</info>        | <info>Empty</info> |',
+            '+--------------+---------------+-------+',
+            '| short        | Longish thing |       |',
+            '| Longer thing | short         |       |',
+            '+--------------+---------------+-------+',
+        ];
+        $this->assertEquals($expected, $this->stub->messages());
+    }
+
+    /**
+     * Test that output works when data contains nulls.
+     */
+    public function testNullValues()
+    {
+        $data = [
+            ['Header 1', 'Header', 'Empty'],
+            ['short', 'Longish thing', null],
+            ['Longer thing', 'short', null],
+        ];
+        $this->helper->output($data);
+        $expected = [
+            '+--------------+---------------+-------+',
+            '| <info>Header 1</info>     | <info>Header</info>        | <info>Empty</info> |',
+            '+--------------+---------------+-------+',
+            '| short        | Longish thing |       |',
+            '| Longer thing | short         |       |',
+            '+--------------+---------------+-------+',
+        ];
+        $this->assertEquals($expected, $this->stub->messages());
+    }
+
+    /**
      * Test output with multi-byte characters
      *
      * @return void

+ 13 - 6
tests/TestCase/Utility/HashTest.php

@@ -470,19 +470,19 @@ class HashTest extends TestCase
     {
         $data = [];
         $result = Hash::maxDimensions($data);
-        $this->assertEquals(0, $result);
+        $this->assertSame(0, $result);
 
         $data = ['a', 'b'];
         $result = Hash::maxDimensions($data);
-        $this->assertEquals(1, $result);
+        $this->assertSame(1, $result);
 
         $data = ['1' => '1.1', '2', '3' => ['3.1' => '3.1.1']];
         $result = Hash::maxDimensions($data);
-        $this->assertEquals($result, 2);
+        $this->assertSame(2, $result);
 
         $data = ['1' => ['1.1' => '1.1.1'], '2', '3' => ['3.1' => ['3.1.1' => '3.1.1.1']]];
         $result = Hash::maxDimensions($data);
-        $this->assertEquals($result, 3);
+        $this->assertSame(3, $result);
 
         $data = [
             '1' => ['1.1' => '1.1.1'],
@@ -490,7 +490,7 @@ class HashTest extends TestCase
             '3' => ['3.1' => ['3.1.1' => '3.1.1.1']]
         ];
         $result = Hash::maxDimensions($data);
-        $this->assertEquals($result, 4);
+        $this->assertSame(4, $result);
 
         $data = [
            '1' => [
@@ -505,7 +505,14 @@ class HashTest extends TestCase
            '2' => ['2.1' => '2.1.1']
         ];
         $result = Hash::maxDimensions($data);
-        $this->assertEquals($result, 5);
+        $this->assertSame(5, $result);
+
+        $data = [
+           '1' => false,
+           '2' => ['2.1' => '2.1.1']
+        ];
+        $result = Hash::maxDimensions($data);
+        $this->assertSame(2, $result);
     }
 
     /**