Browse Source

Merge branch '3.next' into 4.x

Mark Story 7 years ago
parent
commit
58ff6e1128

+ 17 - 0
.mailmap

@@ -113,3 +113,20 @@ Patrick Conroy <patrick.conroy@rockstargames.com>
 Patrick Conroy <patrick.conroy@rockstargames.com> <patrick@rockstargames.com>
 saeideng <saeideng@yahoo.com>
 saeideng <saeideng@yahoo.com> <saeideng@users.noreply.github.com>
+Albert Cansado Solà <albert.cansado@gmail.com>
+Albert Cansado Solà <albert.cansado@gmail.com> <albert@iMac-de-Albert.Home>
+Albert Cansado Solà <albert.cansado@gmail.com> <albert@MacBook-Pro-de-Albert.local>
+Albert Cansado Solà <albert.cansado@gmail.com> <cansa1703@gmail.com>
+Alejandro Ibarra <ajibarra@gmail.com> <alejandro.ibarra@cakedc.com>
+Andreas Kristiansen <mail@ankr.dk> <ak@bownty.com>
+Bob Fanger <bfanger@gmail.com> <bob.fanger@noprotocol.nl>
+Cees-Jan Kiewiet <ceesjank@gmail.com>
+Cees-Jan Kiewiet <ceesjank@gmail.com> <Cees-Jan@xps8500.home.wyrihaxim.us>
+Dmitriy Romanov <dmitriy.romanov@firstlinesoftware.com>
+Dmitrii Romanov <dmitriy.romanov@firstlinesoftware.com> <dmromanov@users.noreply.github.com>
+Dmitrii Romanov <dmitriy.romanov@firstlinesoftware.com> <githubcom.5.dimon3000@spamgourmet.com>
+Dmitrii Romanov <dmitriy.romanov@firstlinesoftware.com> <git.5.dimon3000@spamgourmet.com>
+Edgaras Janušauskas <edgaras.janusauskas@gmail.com>
+Eric Büttner <eric.buettner@tuffz.com>
+Eric Büttner <eric.buettner@tuffz.com> <tuffz@tuffz.mac>
+Eric Büttner <eric.buettner@tuffz.com> <tuffz@users.noreply.github.com>

+ 31 - 0
src/Console/Command.php

@@ -240,4 +240,35 @@ class Command
     {
         throw new StopException('Command aborted', $code);
     }
+
+    /**
+     * Execute another command with the provided set of arguments.
+     *
+     * @param string|\Cake\Console\Command $command The command class name or command instance.
+     * @param array $args The arguments to invoke the command with.
+     * @param \Cake\Console\ConsoleIo $io The ConsoleIo instance to use for the executed command.
+     * @return int|null The exit code or null for success of the command.
+     */
+    public function executeCommand($command, array $args = [], ConsoleIo $io = null)
+    {
+        if (is_string($command)) {
+            if (!class_exists($command)) {
+                throw new InvalidArgumentException("Command class '{$command}' does not exist.");
+            }
+            $command = new $command();
+        }
+        if (!$command instanceof Command) {
+            $commandType = getTypeName($command);
+            throw new InvalidArgumentException(
+                "Command '{$commandType}' is not a subclass of Cake\Console\Command."
+            );
+        }
+        $io = $io ?: new ConsoleIo();
+
+        try {
+            return $command->run($args, $io);
+        } catch (StopException $e) {
+            return $e->getCode();
+        }
+    }
 }

+ 1 - 1
src/Database/Schema/PostgresSchema.php

@@ -400,7 +400,7 @@ class PostgresSchema extends BaseSchema
                 $type = ' CHAR';
             }
             $out .= $type;
-            if (isset($data['length']) && $data['length'] !== 36) {
+            if (isset($data['length'])) {
                 $out .= '(' . (int)$data['length'] . ')';
             }
         }

+ 4 - 1
src/Datasource/ModelAwareTrait.php

@@ -34,7 +34,10 @@ trait ModelAwareTrait
      * Plugin classes should use `Plugin.Comments` style names to correctly load
      * models from the correct plugin.
      *
-     * @var string
+     * Use false to not use auto-loading on this object. Null auto-detects based on
+     * controller name.
+     *
+     * @var string|false|null
      */
     public $modelClass;
 

+ 45 - 1
src/Http/Response.php

@@ -491,7 +491,7 @@ class Response implements ResponseInterface
             return;
         }
         $whitelist = [
-            'application/javascript', 'application/json', 'application/xml', 'application/rss+xml',
+            'application/javascript', 'application/xml', 'application/rss+xml',
         ];
 
         $charset = false;
@@ -1328,8 +1328,52 @@ class Response implements ResponseInterface
     }
 
     /**
+     * Get a new instance with provided cookie collection.
+     *
+     * @param \Cake\Http\Cookie\CookieCollection $cookieCollection Cookie collection to set.
+     * @return static
+     */
+    public function withCookieCollection(CookieCollection $cookieCollection)
+    {
+        $new = clone $this;
+        $new->_cookies = $cookieCollection;
+
+        return $new;
+    }
+
+    /**
      * Get a CorsBuilder instance for defining CORS headers.
      *
+     * This method allow multiple ways to setup the domains, see the examples
+     *
+     * ### Full URI
+     * ```
+     * cors($request, 'https://www.cakephp.org');
+     * ```
+     *
+     * ### URI with wildcard
+     * ```
+     * cors($request, 'https://*.cakephp.org');
+     * ```
+     *
+     * ### Ignoring the requested protocol
+     * ```
+     * cors($request, 'www.cakephp.org');
+     * ```
+     *
+     * ### Any URI
+     * ```
+     * cors($request, '*');
+     * ```
+     *
+     * ### Whitelist of URIs
+     * ```
+     * cors($request, ['http://www.cakephp.org', '*.google.com', 'https://myproject.github.io']);
+     * ```
+     *
+     * *Note* The `$allowedDomains`, `$allowedMethods`, `$allowedHeaders` parameters are deprecated.
+     * Instead the builder object should be used.
+     *
      * @param \Cake\Http\ServerRequest $request Request object
      * @return \Cake\Http\CorsBuilder A builder object the provides a fluent interface for defining
      *   additional CORS headers.

+ 12 - 0
src/Mailer/Transport/SmtpTransport.php

@@ -79,6 +79,18 @@ class SmtpTransport extends AbstractTransport
     }
 
     /**
+     * Unserialize handler.
+     *
+     * Ensure that the socket property isn't reinitialized in a broken state.
+     *
+     * @return void
+     */
+    public function __wakeup()
+    {
+        $this->_socket = null;
+    }
+
+    /**
      * Connect to the SMTP server.
      *
      * This method tries to connect only in case there is no open

+ 1 - 3
src/TestSuite/Constraint/Response/HeaderEquals.php

@@ -14,8 +14,6 @@ declare(strict_types=1);
  */
 namespace Cake\TestSuite\Constraint\Response;
 
-use Cake\Http\Response;
-
 /**
  * HeaderEquals
  *
@@ -34,7 +32,7 @@ class HeaderEquals extends ResponseBase
      * @param \Cake\Http\Response $response A response instance.
      * @param string $headerName Header name
      */
-    public function __construct(Response $response, string $headerName)
+    public function __construct($response, $headerName)
     {
         parent::__construct($response);
 

+ 1 - 1
src/TestSuite/Constraint/Response/HeaderSet.php

@@ -34,7 +34,7 @@ class HeaderSet extends ResponseBase
      * @param \Cake\Http\Response $response A response instance.
      * @param string $headerName Header name
      */
-    public function __construct(Response $response, string $headerName)
+    public function __construct($response, $headerName)
     {
         parent::__construct($response);
 

+ 1 - 1
src/TestSuite/IntegrationTestTrait.php

@@ -1214,7 +1214,7 @@ trait IntegrationTestTrait
     /**
      * Asserts that a file with the given name was sent in the response
      *
-     * @param string $expected The file name that should be sent in the response
+     * @param string $expected The absolute file path that should be sent in the response.
      * @param string $message The failure message that will be appended to the generated message.
      * @return void
      */

+ 98 - 0
tests/TestCase/Console/CommandTest.php

@@ -22,6 +22,8 @@ use Cake\ORM\Locator\TableLocator;
 use Cake\ORM\Table;
 use Cake\TestSuite\Stub\ConsoleOutput;
 use Cake\TestSuite\TestCase;
+use InvalidArgumentException;
+use TestApp\Command\AbortCommand;
 use TestApp\Command\AutoLoadModelCommand;
 use TestApp\Command\DemoCommand;
 
@@ -257,6 +259,102 @@ class CommandTest extends TestCase
         $command->abort(99);
     }
 
+    /**
+     * test executeCommand with a string class
+     *
+     * @return void
+     */
+    public function testExecuteCommandString()
+    {
+        $output = new ConsoleOutput();
+        $command = new Command();
+        $result = $command->executeCommand(DemoCommand::class, [], $this->getMockIo($output));
+        $this->assertNull($result);
+        $this->assertEquals(['Quiet!', 'Demo Command!'], $output->messages());
+    }
+
+    /**
+     * test executeCommand with an invalid string class
+     *
+     * @return void
+     */
+    public function testExecuteCommandStringInvalid()
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage("Command class 'Nope' does not exist");
+
+        $command = new Command();
+        $command->executeCommand('Nope', [], $this->getMockIo(new ConsoleOutput()));
+    }
+
+    /**
+     * test executeCommand with arguments
+     *
+     * @return void
+     */
+    public function testExecuteCommandArguments()
+    {
+        $output = new ConsoleOutput();
+        $command = new Command();
+        $command->executeCommand(DemoCommand::class, ['Jane'], $this->getMockIo($output));
+        $this->assertEquals(['Quiet!', 'Demo Command!', 'Jane'], $output->messages());
+    }
+
+    /**
+     * test executeCommand with arguments
+     *
+     * @return void
+     */
+    public function testExecuteCommandArgumentsOptions()
+    {
+        $output = new ConsoleOutput();
+        $command = new Command();
+        $command->executeCommand(DemoCommand::class, ['--quiet', 'Jane'], $this->getMockIo($output));
+        $this->assertEquals(['Quiet!'], $output->messages());
+    }
+
+    /**
+     * test executeCommand with an instance
+     *
+     * @return void
+     */
+    public function testExecuteCommandInstance()
+    {
+        $output = new ConsoleOutput();
+        $command = new Command();
+        $result = $command->executeCommand(new DemoCommand(), [], $this->getMockIo($output));
+        $this->assertNull($result);
+        $this->assertEquals(['Quiet!', 'Demo Command!'], $output->messages());
+    }
+
+    /**
+     * test executeCommand with an abort
+     *
+     * @return void
+     */
+    public function testExecuteCommandAbort()
+    {
+        $output = new ConsoleOutput();
+        $command = new Command();
+        $result = $command->executeCommand(AbortCommand::class, [], $this->getMockIo($output));
+        $this->assertSame(127, $result);
+        $this->assertEquals(['<error>Command aborted</error>'], $output->messages());
+    }
+
+    /**
+     * test executeCommand with an invalid instance
+     *
+     * @return void
+     */
+    public function testExecuteCommandInstanceInvalid()
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage("Command 'stdClass' is not a subclass");
+
+        $command = new Command();
+        $command->executeCommand(new \stdClass, [], $this->getMockIo(new ConsoleOutput()));
+    }
+
     protected function getMockIo($output)
     {
         $io = $this->getMockBuilder(ConsoleIo::class)

+ 14 - 0
tests/TestCase/Database/Schema/PostgresSchemaTest.php

@@ -177,6 +177,10 @@ SQL;
                 ['type' => 'string', 'fixed' => true, 'length' => 10],
             ],
             [
+                ['type' => 'CHAR(36)'],
+                ['type' => 'string', 'fixed' => true, 'length' => 36]
+            ],
+            [
                 ['type' => 'CHARACTER(10)'],
                 ['type' => 'string', 'fixed' => true, 'length' => 10],
             ],
@@ -696,6 +700,11 @@ SQL;
                 '"id" CHAR(32) NOT NULL',
             ],
             [
+                'title',
+                ['type' => 'string', 'length' => 36, 'fixed' => true, 'null' => false],
+                '"title" CHAR(36) NOT NULL'
+            ],
+            [
                 'id',
                 ['type' => 'uuid', 'length' => 36, 'null' => false],
                 '"id" UUID NOT NULL',
@@ -717,6 +726,11 @@ SQL;
             ],
             [
                 'title',
+                ['type' => 'string', 'length' => 36],
+                '"title" VARCHAR(36)'
+            ],
+            [
+                'title',
                 ['type' => 'string', 'length' => 255, 'null' => false, 'collate' => 'C'],
                 '"title" VARCHAR(255) COLLATE "C" NOT NULL',
             ],

+ 1 - 1
tests/TestCase/Error/Middleware/ErrorHandlerMiddlewareTest.php

@@ -159,7 +159,7 @@ class ErrorHandlerMiddlewareTest extends TestCase
         $this->assertInstanceOf('Cake\Http\Response', $result);
         $this->assertEquals(404, $result->getStatusCode());
         $this->assertContains('"message": "whoops"', '' . $result->getBody());
-        $this->assertEquals('application/json; charset=UTF-8', $result->getHeaderLine('Content-type'));
+        $this->assertEquals('application/json', $result->getHeaderLine('Content-type'));
     }
 
     /**

+ 17 - 2
tests/TestCase/Http/ResponseTest.php

@@ -180,7 +180,7 @@ class ResponseTest extends TestCase
         );
         $this->assertSame('application/pdf', $new->getHeaderLine('Content-Type'));
         $this->assertSame(
-            'application/json; charset=UTF-8',
+            'application/json',
             $new->withType('json')->getHeaderLine('Content-Type')
         );
     }
@@ -228,7 +228,6 @@ class ResponseTest extends TestCase
         return [
             ['mp3', 'audio/mpeg'],
             ['js', 'application/javascript; charset=UTF-8'],
-            ['json', 'application/json; charset=UTF-8'],
             ['xml', 'application/xml; charset=UTF-8'],
             ['txt', 'text/plain; charset=UTF-8'],
         ];
@@ -1015,6 +1014,22 @@ class ResponseTest extends TestCase
     }
 
     /**
+     * Test withCookieCollection()
+     *
+     * @return void
+     */
+    public function testWithCookieCollection()
+    {
+        $response = new Response();
+        $collection = new CookieCollection([new Cookie('foo', 'bar')]);
+        $newResponse = $response->withCookieCollection($collection);
+
+        $this->assertNotSame($response, $newResponse);
+        $this->assertNotSame($response->getCookieCollection(), $newResponse->getCookieCollection());
+        $this->assertSame($newResponse->getCookie('foo')['value'], 'bar');
+    }
+
+    /**
      * Test that cors() returns a builder.
      *
      * @return void

+ 21 - 0
tests/TestCase/Mailer/Transport/SmtpTransportTest.php

@@ -682,4 +682,25 @@ class SmtpTransportTest extends TestCase
 
         $this->SmtpTransport->send($message);
     }
+
+    /**
+     * Ensure that unserialized transports have no connection.
+     *
+     * @return void
+     */
+    public function testSerializeCleanupSocket()
+    {
+        $this->socket->expects($this->at(0))->method('connect')->will($this->returnValue(true));
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
+        $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
+        $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 OK\r\n"));
+
+        $smtpTransport = new SmtpTestTransport();
+        $smtpTransport->setSocket($this->socket);
+        $smtpTransport->connect();
+
+        $result = unserialize(serialize($smtpTransport));
+        $this->assertAttributeEquals(null, '_socket', $result);
+        $this->assertFalse($result->connected());
+    }
 }

+ 2 - 2
tests/TestCase/TestSuite/IntegrationTestTraitTest.php

@@ -303,7 +303,7 @@ class IntegrationTestTraitTest extends TestCase
         $this->_request['headers'] = [ "Accept" => "application/json" ];
         $this->get('/json_response/api_get_data');
         $this->assertResponseCode(403);
-        $this->assertHeader('Content-Type', 'application/json; charset=UTF-8');
+        $this->assertHeader('Content-Type', 'application/json');
         $this->assertResponseContains('"message": "Sample Message"');
         $this->assertResponseContains('"code": 403');
     }
@@ -1071,7 +1071,7 @@ class IntegrationTestTraitTest extends TestCase
     public function testContentTypeInAction()
     {
         $this->get('/tests_apps/set_type');
-        $this->assertHeader('Content-Type', 'application/json; charset=UTF-8');
+        $this->assertHeader('Content-Type', 'application/json');
         $this->assertContentType('json');
         $this->assertContentType('application/json');
     }

+ 3 - 0
tests/test_app/TestApp/Command/DemoCommand.php

@@ -13,5 +13,8 @@ class DemoCommand extends Command
         $io->quiet('Quiet!');
         $io->out('Demo Command!');
         $io->verbose('Verbose!');
+        if ($args->hasArgumentAt(0)) {
+            $io->out($args->getArgumentAt(0));
+        }
     }
 }