Browse Source

Remove newly deprecated features.

* Cleanup TransportFactory & add unit tests.
* Cleanup viewVars
Mark Story 7 years ago
parent
commit
9cb139ef59

+ 0 - 10
src/Controller/Controller.php

@@ -151,16 +151,6 @@ class Controller implements EventListenerInterface, EventDispatcherInterface
     protected $_components;
 
     /**
-     * These Controller properties will be passed from the Controller to the View as options.
-     *
-     * @var array
-     * @see \Cake\View\View
-     * @deprecated 3.7.0 Use ViewBuilder::setOptions() or any one of it's setter methods instead.
-     */
-    protected $_validViewOptions = [
-    ];
-
-    /**
      * Automatically set to the name of a plugin.
      *
      * @var string|null

+ 1 - 1
src/Database/Type.php

@@ -1,4 +1,4 @@
 <?php
 declare(strict_types=1);
-// @deprecated Backwards compatibility alias
+// @deprecated Backwards compatibility alias. Will be removed in 5.0
 class_alias('Cake\Database\TypeFactory', 'Cake\Database\Type');

+ 9 - 188
src/Mailer/Email.php

@@ -25,13 +25,9 @@ use Cake\Utility\Hash;
 use Cake\Utility\Security;
 use Cake\Utility\Text;
 use Cake\View\ViewVarsTrait;
-use Closure;
-use Exception;
 use InvalidArgumentException;
 use JsonSerializable;
 use LogicException;
-use PDO;
-use RuntimeException;
 use Serializable;
 use SimpleXMLElement;
 
@@ -292,15 +288,6 @@ class Email implements JsonSerializable, Serializable
     protected $_priority;
 
     /**
-     * An array mapping url schemes to fully qualified Transport class names.
-     * Unused.
-     *
-     * @var array
-     * @deprecated 3.7.0 This property is unused and will be removed in 4.0.0.
-     */
-    protected static $_dsnClassMap = [];
-
-    /**
      * A copy of the configuration profile for this
      * instance. This copy can be modified with Email::profile().
      *
@@ -1102,7 +1089,7 @@ class Email implements JsonSerializable, Serializable
      */
     public function getViewVars(): array
     {
-        return $this->viewVars;
+        return $this->viewBuilder()->getVars();
     }
 
     /**
@@ -1221,30 +1208,6 @@ class Email implements JsonSerializable, Serializable
     }
 
     /**
-     * Get/set the transport.
-     *
-     * When setting the transport you can either use the name
-     * of a configured transport or supply a constructed transport.
-     *
-     * @deprecated 3.4.0 Use setTransport()/getTransport() instead.
-     * @param string|\Cake\Mailer\AbstractTransport|null $name Either the name of a configured
-     *   transport, or a transport instance.
-     * @return \Cake\Mailer\AbstractTransport|$this
-     * @throws \LogicException When the chosen transport lacks a send method.
-     * @throws \InvalidArgumentException When $name is neither a string nor an object.
-     */
-    public function transport($name = null)
-    {
-        deprecationWarning('Email::transport() is deprecated. Use Email::setTransport() or Email::getTransport() instead.');
-
-        if ($name === null) {
-            return $this->getTransport();
-        }
-
-        return $this->setTransport($name);
-    }
-
-    /**
      * Sets message ID.
      *
      * @param bool|string $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID.
@@ -1456,113 +1419,6 @@ class Email implements JsonSerializable, Serializable
     }
 
     /**
-     * Sets transport configuration.
-     *
-     * Use this method to define transports to use in delivery profiles.
-     * Once defined you cannot edit the configurations, and must use
-     * Email::dropTransport() to flush the configuration first.
-     *
-     * When using an array of configuration data a new transport
-     * will be constructed for each message sent. When using a Closure, the
-     * closure will be evaluated for each message.
-     *
-     * The `className` is used to define the class to use for a transport.
-     * It can either be a short name, or a fully qualified class name
-     *
-     * @param string|array $key The configuration name to write. Or
-     *   an array of multiple transports to set.
-     * @param array|\Cake\Mailer\AbstractTransport|null $config Either an array of configuration
-     *   data, or a transport instance. Null when using key as array.
-     * @return void
-     * @deprecated 3.7.0 Use TransportFactory::setConfig() instead.
-     */
-    public static function setConfigTransport($key, $config = null): void
-    {
-        deprecationWarning('Email::setConfigTransport() is deprecated. Use TransportFactory::setConfig() instead.');
-
-        TransportFactory::setConfig($key, $config);
-    }
-
-    /**
-     * Gets current transport configuration.
-     *
-     * @param string $key The configuration name to read.
-     * @return array|null Transport config.
-     * @deprecated 3.7.0 Use TransportFactory::getConfig() instead.
-     */
-    public static function getConfigTransport(string $key): ?array
-    {
-        deprecationWarning('Email::getConfigTransport() is deprecated. Use TransportFactory::getConfig() instead.');
-
-        return TransportFactory::getConfig($key);
-    }
-
-    /**
-     * Add or read transport configuration.
-     *
-     * Use this method to define transports to use in delivery profiles.
-     * Once defined you cannot edit the configurations, and must use
-     * Email::dropTransport() to flush the configuration first.
-     *
-     * When using an array of configuration data a new transport
-     * will be constructed for each message sent. When using a Closure, the
-     * closure will be evaluated for each message.
-     *
-     * The `className` is used to define the class to use for a transport.
-     * It can either be a short name, or a fully qualified classname
-     *
-     * @deprecated 3.4.0 Use TransportFactory::setConfig()/getConfig() instead.
-     * @param string|array $key The configuration name to read/write. Or
-     *   an array of multiple transports to set.
-     * @param array|\Cake\Mailer\AbstractTransport|null $config Either an array of configuration
-     *   data, or a transport instance.
-     * @return array|null Either null when setting or an array of data when reading.
-     * @throws \BadMethodCallException When modifying an existing configuration.
-     */
-    public static function configTransport($key, $config = null)
-    {
-        deprecationWarning('Email::configTransport() is deprecated. Use TransportFactory::setConfig() or TransportFactory::getConfig() instead.');
-
-        if ($config === null && is_string($key)) {
-            return TransportFactory::getConfig($key);
-        }
-        if ($config === null && is_array($key)) {
-            TransportFactory::setConfig($key);
-
-            return null;
-        }
-
-        TransportFactory::setConfig($key, $config);
-    }
-
-    /**
-     * Returns an array containing the named transport configurations
-     *
-     * @return array Array of configurations.
-     * @deprecated 3.7.0 Use TransportFactory::configured() instead.
-     */
-    public static function configuredTransport(): array
-    {
-        deprecationWarning('Email::configuredTransport() is deprecated. Use TransportFactory::configured().');
-
-        return TransportFactory::configured();
-    }
-
-    /**
-     * Delete transport configuration.
-     *
-     * @param string $key The transport name to remove.
-     * @return void
-     * @deprecated 3.7.0 Use TransportFactory::drop() instead.
-     */
-    public static function dropTransport($key): void
-    {
-        deprecationWarning('Email::dropTransport() is deprecated. Use TransportFactory::drop().');
-
-        TransportFactory::drop($key);
-    }
-
-    /**
      * Sets the configuration profile to use for this instance.
      *
      * @param string|array $config String with configuration name, or
@@ -1791,12 +1647,13 @@ class Email implements JsonSerializable, Serializable
         $this->_profile = [];
         $this->_emailPattern = self::EMAIL_PATTERN;
 
-        $this->viewBuilder()->setLayout('default');
-        $this->viewBuilder()->setTemplate('');
-        $this->viewBuilder()->setClassName('Cake\View\View');
-        $this->viewVars = [];
-        $this->viewBuilder()->setTheme(null);
-        $this->viewBuilder()->setHelpers(['Html'], false);
+        $this->viewBuilder()
+            ->setLayout('default')
+            ->setTemplate('')
+            ->setClassName('Cake\View\View')
+            ->setTheme(null)
+            ->setHelpers(['Html'], false)
+            ->setVars([], false);
 
         return $this;
     }
@@ -2258,13 +2115,6 @@ class Email implements JsonSerializable, Serializable
      * Serializes the email object to a value that can be natively serialized and re-used
      * to clone this email instance.
      *
-     * It has certain limitations for viewVars that are good to know:
-     *
-     *    - ORM\Query executed and stored as resultset
-     *    - SimpleXMLElements stored as associative array
-     *    - Exceptions stored as strings
-     *    - Resources, \Closure and \PDO are not supported.
-     *
      * @return array Serializable array of configuration properties.
      * @throws \Exception When a view var object can not be properly serialized.
      */
@@ -2273,7 +2123,7 @@ class Email implements JsonSerializable, Serializable
         $properties = [
             '_to', '_from', '_sender', '_replyTo', '_cc', '_bcc', '_subject',
             '_returnPath', '_readReceipt', '_emailFormat', '_emailPattern', '_domain',
-            '_attachments', '_messageId', '_headers', '_appCharset', 'viewVars', 'charset', 'headerCharset',
+            '_attachments', '_messageId', '_headers', '_appCharset', 'charset', 'headerCharset',
         ];
 
         $array = ['viewConfig' => $this->viewBuilder()->jsonSerialize()];
@@ -2289,41 +2139,12 @@ class Email implements JsonSerializable, Serializable
             }
         });
 
-        array_walk_recursive($array['viewVars'], [$this, '_checkViewVars']);
-
         return array_filter($array, function ($i) {
             return !is_array($i) && strlen($i) || !empty($i);
         });
     }
 
     /**
-     * Iterates through hash to clean up and normalize.
-     *
-     * @param mixed $item Reference to the view var value.
-     * @param string $key View var key.
-     * @return void
-     * @throws \RuntimeException
-     */
-    protected function _checkViewVars(&$item, $key)
-    {
-        if ($item instanceof Exception) {
-            $item = (string)$item;
-        }
-
-        if (is_resource($item) ||
-            $item instanceof Closure ||
-            $item instanceof PDO
-        ) {
-            throw new RuntimeException(sprintf(
-                'Failed serializing the `%s` %s in the `%s` view var',
-                is_resource($item) ? get_resource_type($item) : get_class($item),
-                is_resource($item) ? 'resource' : 'object',
-                $key
-            ));
-        }
-    }
-
-    /**
      * Configures an email instance object from serialized config.
      *
      * @param array $config Email configuration array.

+ 0 - 11
src/View/Cell.php

@@ -81,17 +81,6 @@ abstract class Cell implements EventDispatcherInterface
     protected $args = [];
 
     /**
-     * These properties can be set directly on Cell and passed to the View as options.
-     *
-     * @var array
-     * @see \Cake\View\View
-     * @deprecated 3.7.0 Use ViewBuilder::setOptions() or any one of it's setter methods instead.
-     */
-    protected $_validViewOptions = [
-        'viewPath',
-    ];
-
-    /**
      * List of valid options (constructor's fourth arguments)
      * Override this property in subclasses to whitelist
      * which options you want set as properties in your Cell.

+ 31 - 0
src/View/View.php

@@ -153,6 +153,13 @@ class View implements EventDispatcherInterface
     protected $autoLayout = true;
 
     /**
+     * An array of variables
+     *
+     * @var array
+     */
+    protected $viewVars = [];
+
+    /**
      * File extension. Defaults to CakePHP's template ".ctp".
      *
      * @var string
@@ -762,6 +769,30 @@ class View implements EventDispatcherInterface
     }
 
     /**
+     * Saves a variable or an associative array of variables for use inside a template.
+     *
+     * @param string|array $name A string or an array of data.
+     * @param mixed $value Value in case $name is a string (which then works as the key).
+     *   Unused if $name is an associative array, otherwise serves as the values to $name's keys.
+     * @return $this
+     */
+    public function set($name, $value = null): self
+    {
+        if (is_array($name)) {
+            if (is_array($value)) {
+                $data = array_combine($name, $value);
+            } else {
+                $data = $name;
+            }
+        } else {
+            $data = [$name => $value];
+        }
+        $this->viewVars = $data + $this->viewVars;
+
+        return $this;
+    }
+
+    /**
      * Get the names of all the existing blocks.
      *
      * @return array An array containing the blocks.

+ 46 - 6
src/View/ViewBuilder.php

@@ -20,7 +20,11 @@ use Cake\Event\EventManager;
 use Cake\Http\Response;
 use Cake\Http\ServerRequest;
 use Cake\View\Exception\MissingViewException;
+use Closure;
+use Exception;
 use JsonSerializable;
+use PDO;
+use RuntimeException;
 use Serializable;
 
 /**
@@ -127,7 +131,7 @@ class ViewBuilder implements JsonSerializable, Serializable
      * @param mixed $value Value.
      * @return $this
      */
-    public function setVar($name, $value = null)
+    public function setVar(string $name, $value = null): self
     {
         $this->_vars[$name] = $value;
 
@@ -141,7 +145,7 @@ class ViewBuilder implements JsonSerializable, Serializable
      * @param bool $merge Whether to merge with existing vars, default true.
      * @return $this
      */
-    public function setVars($data, $merge = true)
+    public function setVars(array $data, bool $merge = true): self
     {
         if ($merge) {
             $this->_vars = $data + $this->_vars;
@@ -158,7 +162,7 @@ class ViewBuilder implements JsonSerializable, Serializable
      * @param string $name Var name
      * @return bool
      */
-    public function hasVar($name)
+    public function hasVar(string $name): bool
     {
         return array_key_exists($name, $this->_vars);
     }
@@ -169,7 +173,7 @@ class ViewBuilder implements JsonSerializable, Serializable
      * @param string $name Var name
      * @return mixed The var value or null if unset.
      */
-    public function getVar($name)
+    public function getVar(string $name)
     {
         return $this->_vars[$name] ?? null;
     }
@@ -179,7 +183,7 @@ class ViewBuilder implements JsonSerializable, Serializable
      *
      * @return array
      */
-    public function getVars()
+    public function getVars(): array
     {
         return $this->_vars;
     }
@@ -508,13 +512,20 @@ class ViewBuilder implements JsonSerializable, Serializable
      * Serializes the view builder object to a value that can be natively
      * serialized and re-used to clone this builder instance.
      *
+     * There are  limitations for viewVars that are good to know:
+     *
+     * - ORM\Query executed and stored as resultset
+     * - SimpleXMLElements stored as associative array
+     * - Exceptions stored as strings
+     * - Resources, \Closure and \PDO are not supported.
+     *
      * @return array Serializable array of configuration properties.
      */
     public function jsonSerialize(): array
     {
         $properties = [
             '_templatePath', '_template', '_plugin', '_theme', '_layout', '_autoLayout',
-            '_layoutPath', '_name', '_className', '_options', '_helpers',
+            '_layoutPath', '_name', '_className', '_options', '_helpers', '_vars',
         ];
 
         $array = [];
@@ -523,12 +534,41 @@ class ViewBuilder implements JsonSerializable, Serializable
             $array[$property] = $this->{$property};
         }
 
+        array_walk_recursive($array['_vars'], [$this, '_checkViewVars']);
+
         return array_filter($array, function ($i) {
             return !is_array($i) && strlen((string)$i) || !empty($i);
         });
     }
 
     /**
+     * Iterates through hash to clean up and normalize.
+     *
+     * @param mixed $item Reference to the view var value.
+     * @param string $key View var key.
+     * @return void
+     * @throws \RuntimeException
+     */
+    protected function _checkViewVars(&$item, $key)
+    {
+        if ($item instanceof Exception) {
+            $item = (string)$item;
+        }
+
+        if (is_resource($item) ||
+            $item instanceof Closure ||
+            $item instanceof PDO
+        ) {
+            throw new RuntimeException(sprintf(
+                'Failed serializing the `%s` %s in the `%s` view var',
+                is_resource($item) ? get_resource_type($item) : get_class($item),
+                is_resource($item) ? 'resource' : 'object',
+                $key
+            ));
+        }
+    }
+
+    /**
      * Configures a view builder instance from serialized config.
      *
      * @param array $config View builder configuration array.

+ 2 - 53
src/View/ViewVarsTrait.php

@@ -21,20 +21,10 @@ use Cake\Event\EventDispatcherInterface;
  *
  * Once collected context data can be passed to another object.
  * This is done in Controller, TemplateTask and View for example.
- *
- * @property array $_validViewOptions
  */
 trait ViewVarsTrait
 {
     /**
-     * Variables for the view
-     *
-     * @var array
-     * @deprecated 3.7.0 Use `$this->set()` instead.
-     */
-    public $viewVars = [];
-
-    /**
      * The view builder instance being used.
      *
      * @var \Cake\View\ViewBuilder
@@ -69,24 +59,15 @@ trait ViewVarsTrait
             $builder->setClassName($viewClass);
         }
 
-        $validViewOptions = $this->_validViewOptions ?? [];
-        $viewOptions = [];
-        foreach ($validViewOptions as $option) {
-            if (property_exists($this, $option)) {
-                $viewOptions[$option] = $this->{$option};
-            }
-        }
-
         foreach (['name', 'plugin'] as $prop) {
             if (isset($this->{$prop})) {
                 $method = 'set' . ucfirst($prop);
                 $builder->{$method}($this->{$prop});
             }
         }
-        $builder->setOptions($viewOptions);
 
         return $builder->build(
-            $this->viewVars,
+            [],
             $this->request ?? null,
             $this->response ?? null,
             $this instanceof EventDispatcherInterface ? $this->getEventManager() : null
@@ -112,40 +93,8 @@ trait ViewVarsTrait
         } else {
             $data = [$name => $value];
         }
-        $this->viewVars = $data + $this->viewVars;
+        $this->viewBuilder()->setVars($data);
 
         return $this;
     }
-
-    /**
-     * Get/Set valid view options in the object's _validViewOptions property. The property is
-     * created as an empty array if it is not set. If called without any parameters it will
-     * return the current list of valid view options. See `createView()`.
-     *
-     * @param string|array|null $options string or array of string to be appended to _validViewOptions.
-     * @param bool $merge Whether to merge with or override existing valid View options.
-     *   Defaults to `true`.
-     * @return array The updated view options as an array.
-     * @deprecated 3.7.0 Use ViewBuilder::setOptions() or any one of it's setter methods instead.
-     */
-    public function viewOptions($options = null, bool $merge = true): array
-    {
-        deprecationWarning(
-            'ViewVarsTrait::viewOptions() is deprecated, used ViewBuilder::setOptions() instead.'
-        );
-
-        if (!isset($this->_validViewOptions)) {
-            $this->_validViewOptions = [];
-        }
-
-        if ($options === null) {
-            return $this->_validViewOptions;
-        }
-
-        if (!$merge) {
-            return $this->_validViewOptions = (array)$options;
-        }
-
-        return $this->_validViewOptions = array_merge($this->_validViewOptions, (array)$options);
-    }
 }

+ 1 - 1
tests/TestCase/Controller/ControllerTest.php

@@ -1058,7 +1058,7 @@ class ControllerTest extends TestCase
         $controller->dispatchEvent('Controller.beforeRender');
         $view = $controller->createView();
 
-        $this->assertArrayHasKey('testVariable', $view->viewVars);
+        $this->assertNotEmpty('testVariable', $view->get('testVariable'));
     }
 
     /**

+ 0 - 1
tests/TestCase/Event/EventManagerTest.php

@@ -97,7 +97,6 @@ class EventManagerTest extends TestCase
     /**
      * Test attach() with a listener interface.
      *
-     * @group deprecated
      * @return void
      */
     public function testAttachListener()

+ 0 - 1
tests/TestCase/Http/ServerRequestTest.php

@@ -162,7 +162,6 @@ class ServerRequestTest extends TestCase
     /**
      * Test constructing with a string url.
      *
-     * @deprecated
      * @return void
      */
     public function testConstructStringUrlIgnoreServer()

+ 22 - 161
tests/TestCase/Mailer/EmailTest.php

@@ -19,7 +19,7 @@ use Cake\Core\Configure;
 use Cake\Core\Plugin;
 use Cake\Log\Log;
 use Cake\Mailer\Email;
-use Cake\Mailer\Transport\DebugTransport;
+use Cake\Mailer\TransportFactory;
 use Cake\TestSuite\TestCase;
 use Exception;
 use SimpleXmlElement;
@@ -138,9 +138,7 @@ class EmailTest extends TestCase
             ],
         ];
 
-        $this->deprecated(function () {
-            Email::setConfigTransport($this->transports);
-        });
+        TransportFactory::setConfig($this->transports);
     }
 
     /**
@@ -153,11 +151,9 @@ class EmailTest extends TestCase
         parent::tearDown();
         Log::drop('email');
         Email::drop('test');
-        $this->deprecated(function () {
-            Email::dropTransport('debug');
-            Email::dropTransport('badClassName');
-            Email::dropTransport('test_smtp');
-        });
+        TransportFactory::drop('debug');
+        TransportFactory::drop('badClassName');
+        TransportFactory::drop('test_smtp');
     }
 
     /**
@@ -966,135 +962,6 @@ class EmailTest extends TestCase
         $this->expectExceptionMessage('The value passed for the "$name" argument must be either a string, or an object, integer given.');
         $this->Email->setTransport(123);
     }
-
-    /**
-     * Test that using misconfigured transports fails.
-     *
-     */
-    public function testTransportMissingClassName()
-    {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('Transport config "debug" is invalid, the required `className` option is missing');
-        $this->deprecated(function () {
-            Email::dropTransport('debug');
-            Email::setConfigTransport('debug', []);
-        });
-
-        $this->Email->setTransport('debug');
-    }
-
-    /**
-     * Test configuring a transport.
-     *
-     * @return void
-     */
-    public function testConfigTransport()
-    {
-        $settings = [
-            'className' => 'Debug',
-            'log' => true,
-        ];
-        $this->deprecated(function () use ($settings) {
-            Email::dropTransport('debug');
-            $result = Email::setConfigTransport('debug', $settings);
-            $this->assertNull($result, 'No return.');
-
-            $result = Email::getConfigTransport('debug');
-            $this->assertEquals($settings, $result);
-        });
-    }
-
-    /**
-     * Test configuring multiple transports.
-     */
-    public function testConfigTransportMultiple()
-    {
-        $settings = [
-            'debug' => [
-                'className' => 'Debug',
-                'log' => true,
-            ],
-            'test_smtp' => [
-                'className' => 'Smtp',
-                'username' => 'mark',
-                'password' => 'password',
-                'host' => 'example.com',
-            ],
-        ];
-        $this->deprecated(function () use ($settings) {
-            Email::dropTransport('debug');
-            Email::setConfigTransport($settings);
-            $this->assertEquals($settings['debug'], Email::getConfigTransport('debug'));
-            $this->assertEquals($settings['test_smtp'], Email::getConfigTransport('test_smtp'));
-        });
-    }
-
-    /**
-     * Test that exceptions are raised when duplicate transports are configured.
-     *
-     */
-    public function testConfigTransportErrorOnDuplicate()
-    {
-        $this->expectException(\BadMethodCallException::class);
-        $settings = [
-            'className' => 'Debug',
-            'log' => true,
-        ];
-        $this->deprecated(function () use ($settings) {
-            Email::setConfigTransport('debug', $settings);
-            Email::setConfigTransport('debug', $settings);
-            Email::dropTransport('debug');
-        });
-    }
-
-    /**
-     * Test configTransport with an instance.
-     *
-     * @return void
-     */
-    public function testConfigTransportInstance()
-    {
-        $this->deprecated(function () {
-            Email::dropTransport('debug');
-            $instance = new DebugTransport();
-            Email::setConfigTransport('debug', $instance);
-            $this->assertEquals(['className' => $instance], Email::getConfigTransport('debug'));
-        });
-    }
-
-    /**
-     * Test enumerating all transport configurations
-     *
-     * @return void
-     */
-    public function testConfiguredTransport()
-    {
-        $this->deprecated(function () {
-            $result = Email::configuredTransport();
-            $this->assertInternalType('array', $result, 'Should have config keys');
-            $this->assertEquals(
-                array_keys($this->transports),
-                $result,
-                'Loaded transports should be present in enumeration.'
-            );
-        });
-    }
-
-    /**
-     * Test dropping a transport configuration
-     *
-     * @return void
-     */
-    public function testDropTransport()
-    {
-        $this->deprecated(function () {
-            $result = Email::getConfigTransport('debug');
-            $this->assertInternalType('array', $result, 'Should have config data');
-            Email::dropTransport('debug');
-            $this->assertNull(Email::getConfigTransport('debug'), 'Should not exist.');
-        });
-    }
-
     /**
      * Test reading/writing configuration profiles.
      *
@@ -2067,10 +1934,8 @@ class EmailTest extends TestCase
      */
     public function testDeliver()
     {
-        $this->deprecated(function () {
-            Email::dropTransport('default');
-            Email::setConfigTransport('default', ['className' => 'Debug']);
-        });
+        TransportFactory::drop('default');
+        TransportFactory::setConfig('default', ['className' => 'Debug']);
 
         $instance = Email::deliver('all@cakephp.org', 'About', 'Everything ok', ['from' => 'root@cakephp.org'], false);
         $this->assertInstanceOf('Cake\Mailer\Email', $instance);
@@ -2862,17 +2727,13 @@ HTML;
      */
     public function testMockTransport()
     {
-        $this->deprecated(function () {
-            Email::dropTransport('default');
-        });
+        TransportFactory::drop('default');
 
         $mock = $this->getMockBuilder('Cake\Mailer\AbstractTransport')->getMock();
         $config = ['from' => 'tester@example.org', 'transport' => 'default'];
 
         Email::setConfig('default', $config);
-        $this->deprecated(function () use ($mock) {
-            Email::setConfigTransport('default', $mock);
-        });
+        TransportFactory::setConfig('default', $mock);
 
         $em = new Email('default');
 
@@ -2940,8 +2801,8 @@ XML;
             ->setLayout('test');
 
         $result = json_decode(json_encode($this->Email), true);
-        $this->assertContains('test', $result['viewVars']['exception']);
-        unset($result['viewVars']['exception']);
+        $this->assertContains('test', $result['viewConfig']['_vars']['exception']);
+        unset($result['viewConfig']['_vars']['exception']);
 
         $encode = function ($path) {
             return chunk_split(base64_encode(file_get_contents($path)), 76, "\r\n");
@@ -2966,15 +2827,15 @@ XML;
                 '_helpers' => ['Html'],
                 '_className' => 'Cake\View\View',
                 '_autoLayout' => true,
-            ],
-            'viewVars' => [
-                'users' => [
-                    'id' => 1,
-                    'username' => 'mariano',
-                ],
-                'xml' => [
-                    'name' => 'CakePHP',
-                    'url' => 'http://cakephp.org',
+                '_vars' => [
+                    'users' => [
+                        'id' => 1,
+                        'username' => 'mariano',
+                    ],
+                    'xml' => [
+                        'name' => 'CakePHP',
+                        'url' => 'http://cakephp.org',
+                    ],
                 ],
             ],
             '_attachments' => [
@@ -2992,8 +2853,8 @@ XML;
         $this->assertEquals($expected, $result);
 
         $result = json_decode(json_encode(unserialize(serialize($this->Email))), true);
-        $this->assertContains('test', $result['viewVars']['exception']);
-        unset($result['viewVars']['exception']);
+        $this->assertContains('test', $result['viewConfig']['_vars']['exception']);
+        unset($result['viewConfig']['_vars']['exception']);
         $this->assertEquals($expected, $result);
     }
 

+ 170 - 0
tests/TestCase/Mailer/TransportFactoryTest.php

@@ -0,0 +1,170 @@
+<?php
+declare(strict_types=1);
+/**
+ * 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
+ * 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         4.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\Mailer;
+
+use Cake\Mailer\Transport\DebugTransport;
+use Cake\Mailer\TransportFactory;
+use Cake\TestSuite\TestCase;
+
+/**
+ * TransportFactory Test class
+ */
+class TransportFactoryTest extends TestCase
+{
+    public function setUp()
+    {
+        parent::setUp();
+        $this->transports = [
+            'debug' => [
+                'className' => 'Debug',
+            ],
+            'badClassName' => [
+                'className' => 'TestFalse',
+            ],
+        ];
+        TransportFactory::setConfig($this->transports);
+    }
+
+    /**
+     * tearDown method
+     *
+     * @return void
+     */
+    public function tearDown()
+    {
+        parent::tearDown();
+        TransportFactory::drop('debug');
+        TransportFactory::drop('badClassName');
+        TransportFactory::drop('test_smtp');
+    }
+
+    /**
+     * Test that using misconfigured transports fails.
+     *
+     * @return void
+     */
+    public function testGetMissingClassName()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Transport config "debug" is invalid, the required `className` option is missing');
+
+        TransportFactory::drop('debug');
+        TransportFactory::setConfig('debug', []);
+
+        TransportFactory::get('debug');
+    }
+
+    /**
+     * Test configuring a transport.
+     *
+     * @return void
+     */
+    public function testSetConfig()
+    {
+        $settings = [
+            'className' => 'Debug',
+            'log' => true,
+        ];
+        TransportFactory::drop('debug');
+        $result = TransportFactory::setConfig('debug', $settings);
+        $this->assertNull($result, 'No return.');
+
+        $result = TransportFactory::getConfig('debug');
+        $this->assertEquals($settings, $result);
+    }
+
+    /**
+     * Test configuring multiple transports.
+     *
+     * @return void
+     */
+    public function testSetConfigMultiple()
+    {
+        $settings = [
+            'debug' => [
+                'className' => 'Debug',
+                'log' => true,
+            ],
+            'test_smtp' => [
+                'className' => 'Smtp',
+                'username' => 'mark',
+                'password' => 'password',
+                'host' => 'example.com',
+            ],
+        ];
+        TransportFactory::drop('debug');
+        TransportFactory::setConfig($settings);
+        $this->assertEquals($settings['debug'], TransportFactory::getConfig('debug'));
+        $this->assertEquals($settings['test_smtp'], TransportFactory::getConfig('test_smtp'));
+    }
+
+    /**
+     * Test that exceptions are raised when duplicate transports are configured.
+     *
+     * @return void
+     */
+    public function testSetConfigErrorOnDuplicate()
+    {
+        $this->expectException(\BadMethodCallException::class);
+        $settings = [
+            'className' => 'Debug',
+            'log' => true,
+        ];
+        TransportFactory::setConfig('debug', $settings);
+        TransportFactory::setConfig('debug', $settings);
+        TransportFactory::drop('debug');
+    }
+
+    /**
+     * Test configTransport with an instance.
+     *
+     * @return void
+     */
+    public function testSetConfigInstance()
+    {
+        TransportFactory::drop('debug');
+        $instance = new DebugTransport();
+        TransportFactory::setConfig('debug', $instance);
+        $this->assertEquals(['className' => $instance], TransportFactory::getConfig('debug'));
+    }
+
+    /**
+     * Test enumerating all transport configurations
+     *
+     * @return void
+     */
+    public function testConfigured()
+    {
+        $result = TransportFactory::configured();
+        $this->assertInternalType('array', $result, 'Should have config keys');
+        foreach (array_keys($this->transports) as $key) {
+            $this->assertContains($key, $result, 'Loaded transports should be present.');
+        }
+    }
+
+    /**
+     * Test dropping a transport configuration
+     *
+     * @return void
+     */
+    public function testDrop()
+    {
+        $result = TransportFactory::getConfig('debug');
+        $this->assertInternalType('array', $result, 'Should have config data');
+        TransportFactory::drop('debug');
+        $this->assertNull(TransportFactory::getConfig('debug'), 'Should not exist.');
+    }
+}

+ 10 - 10
tests/TestCase/View/Helper/FormHelperTest.php

@@ -3694,7 +3694,7 @@ class FormHelperTest extends TestCase
         ]);
         $this->assertHtml($expected, $result);
 
-        $this->View->viewVars['users'] = ['value' => 'good', 'other' => 'bad'];
+        $this->View->set('users', ['value' => 'good', 'other' => 'bad']);
         $this->View->setRequest(
             $this->View->getRequest()->withData('Model', ['user_id' => 'value'])
         );
@@ -3719,7 +3719,7 @@ class FormHelperTest extends TestCase
         ];
         $this->assertHtml($expected, $result);
 
-        $this->View->viewVars['users'] = ['value' => 'good', 'other' => 'bad'];
+        $this->View->set('users', ['value' => 'good', 'other' => 'bad']);
         $this->View->setRequest(
             $this->View->getRequest()->withData('Thing', ['user_id' => null])
         );
@@ -3744,7 +3744,7 @@ class FormHelperTest extends TestCase
         ];
         $this->assertHtml($expected, $result);
 
-        $this->View->viewVars['users'] = ['value' => 'good', 'other' => 'bad'];
+        $this->View->set('users', ['value' => 'good', 'other' => 'bad']);
         $this->View->setRequest(
             $this->View->getRequest()->withData('Thing', ['user_id' => 'value'])
         );
@@ -3840,7 +3840,7 @@ class FormHelperTest extends TestCase
      */
     public function testControlOverridingMagicSelectType()
     {
-        $this->View->viewVars['users'] = ['value' => 'good', 'other' => 'bad'];
+        $this->View->set('users', ['value' => 'good', 'other' => 'bad']);
         $result = $this->Form->control('Model.user_id', ['type' => 'text']);
         $expected = [
             'div' => ['class' => 'input text'],
@@ -3851,7 +3851,7 @@ class FormHelperTest extends TestCase
         $this->assertHtml($expected, $result);
 
         //Check that magic types still work for plural/singular vars
-        $this->View->viewVars['types'] = ['value' => 'good', 'other' => 'bad'];
+        $this->View->set('types', ['value' => 'good', 'other' => 'bad']);
         $result = $this->Form->control('Model.type');
         $expected = [
             'div' => ['class' => 'input select'],
@@ -3874,7 +3874,7 @@ class FormHelperTest extends TestCase
      */
     public function testControlMagicTypeDoesNotOverride()
     {
-        $this->View->viewVars['users'] = ['value' => 'good', 'other' => 'bad'];
+        $this->View->set('users', ['value' => 'good', 'other' => 'bad']);
         $result = $this->Form->control('Model.user', ['type' => 'checkbox']);
         $expected = [
             'div' => ['class' => 'input checkbox'],
@@ -3948,7 +3948,7 @@ class FormHelperTest extends TestCase
         ]);
         $entity = new Entity(['balance' => 1]);
         $this->Form->create($entity, ['context' => ['table' => 'ValidateUsers']]);
-        $this->View->viewVars['balances'] = [0 => 'nothing', 1 => 'some', 100 => 'a lot'];
+        $this->View->set('balances', [0 => 'nothing', 1 => 'some', 100 => 'a lot']);
         $result = $this->Form->control('balance');
         $expected = [
             'div' => ['class' => 'input select'],
@@ -3994,7 +3994,7 @@ class FormHelperTest extends TestCase
      */
     public function testControlMagicSelectChangeToRadio()
     {
-        $this->View->viewVars['users'] = ['value' => 'good', 'other' => 'bad'];
+        $this->View->set('users', ['value' => 'good', 'other' => 'bad']);
         $result = $this->Form->control('Model.user_id', ['type' => 'radio']);
         $this->assertContains('input type="radio"', $result);
     }
@@ -5376,7 +5376,7 @@ class FormHelperTest extends TestCase
             1 => 'Orion',
             2 => 'Helios',
         ];
-        $this->View->viewVars['spacecraft'] = $spacecraft;
+        $this->View->set('spacecraft', $spacecraft);
         $this->Form->create();
         $result = $this->Form->control('spacecraft._ids');
         $expected = [
@@ -5415,7 +5415,7 @@ class FormHelperTest extends TestCase
             1 => 'Orion',
             2 => 'Helios',
         ];
-        $this->View->viewVars['spacecraft'] = $spacecraft;
+        $this->View->set('spacecraft', $spacecraft);
 
         $article = new Article();
         $article->setError('spacecraft', ['Invalid']);

+ 1 - 1
tests/TestCase/View/JsonViewTest.php

@@ -301,7 +301,7 @@ class JsonViewTest extends TestCase
         $this->assertSame('application/javascript', $View->getResponse()->getType());
 
         $View->setRequest($View->getRequest()->withQueryParams(['jsonCallback' => 'jfunc']));
-        $View->viewVars['_jsonp'] = 'jsonCallback';
+        $View->set('_jsonp', 'jsonCallback');
         $output = $View->render(false);
         $expected = 'jfunc(' . json_encode($data) . ')';
         $this->assertSame($expected, $output);

+ 76 - 4
tests/TestCase/View/ViewBuilderTest.php

@@ -224,10 +224,9 @@ class ViewBuilderTest extends TestCase
         $this->assertSame($request, $view->getRequest());
         $this->assertInstanceOf(Response::class, $view->getResponse());
         $this->assertSame($events, $view->getEventManager());
-        $this->assertEquals(
-            ['one' => 'value', 'foo' => 'bar', 'x' => 'new'],
-            $view->viewVars
-        );
+        $this->assertSame(['one', 'x', 'foo'], $view->getVars());
+        $this->assertSame('value', $view->get('one'));
+        $this->assertSame('bar', $view->get('foo'));
         $this->assertInstanceOf('Cake\View\Helper\HtmlHelper', $view->Html);
         $this->assertInstanceOf('Cake\View\Helper\FormHelper', $view->Form);
     }
@@ -327,4 +326,77 @@ class ViewBuilderTest extends TestCase
         $this->assertEquals(['Html'], $builder->getHelpers());
         $this->assertEquals('JsonView', $builder->getClassName());
     }
+
+    /**
+     * test setOptions() with 1 string param, merge true
+     *
+     * @return void
+     */
+    public function testSetOptionsOne()
+    {
+        $builder = new ViewBuilder();
+        $this->assertSame($builder, $builder->setOptions(['newOption']));
+        $this->assertContains('newOption', $builder->getOptions());
+    }
+
+    /**
+     * test setOptions() with 2 strings in array, merge true.
+     *
+     * @return void
+     */
+    public function testSetOptionsMultiple()
+    {
+        $builder = new ViewBuilder();
+        $builder->setOptions(['oldOption'], false);
+
+        $option = ['newOption', 'anotherOption'];
+        $builder->setOptions($option);
+        $expects = ['oldOption', 'newOption', 'anotherOption'];
+
+        $result = $builder->getOptions();
+        $this->assertContainsOnly('string', $result);
+        $this->assertEquals($expects, $result);
+    }
+
+    /**
+     * test empty params reads _viewOptions.
+     *
+     * @return void
+     */
+    public function testReadingViewOptions()
+    {
+        $builder = new ViewBuilder();
+        $builder->setOptions(['one', 'two', 'three'], false);
+
+        $this->assertEquals(['one', 'two', 'three'], $builder->getOptions());
+    }
+
+    /**
+     * test setting $merge `false` overrides correct options.
+     *
+     * @return void
+     */
+    public function testMergeFalseViewOptions()
+    {
+        $builder = new ViewBuilder();
+        $builder->setOptions(['one', 'two', 'three'], false);
+
+        $expected = ['four', 'five', 'six'];
+        $builder->setOptions($expected, false);
+        $this->assertEquals($expected, $builder->getOptions());
+    }
+
+    /**
+     * test _viewOptions is undefined and $opts is null, an empty array is returned.
+     *
+     * @return void
+     */
+    public function testUndefinedValidViewOptions()
+    {
+        $builder = new ViewBuilder();
+        $builder->setOptions([], false);
+        $result = $builder->getOptions();
+        $this->assertInternalType('array', $result);
+        $this->assertEmpty($result);
+    }
 }

+ 7 - 86
tests/TestCase/View/ViewVarsTraitTest.php

@@ -48,11 +48,11 @@ class ViewVarsTraitTest extends TestCase
     {
         $data = ['test' => 'val', 'foo' => 'bar'];
         $this->subject->set($data);
-        $this->assertEquals($data, $this->subject->viewVars);
+        $this->assertEquals($data, $this->subject->viewBuilder()->getVars());
 
         $update = ['test' => 'updated'];
         $this->subject->set($update);
-        $this->assertEquals('updated', $this->subject->viewVars['test']);
+        $this->assertEquals('updated', $this->subject->viewBuilder()->getVar('test'));
     }
 
     /**
@@ -63,7 +63,7 @@ class ViewVarsTraitTest extends TestCase
     public function testSetTwoParam()
     {
         $this->subject->set('testing', 'value');
-        $this->assertEquals(['testing' => 'value'], $this->subject->viewVars);
+        $this->assertEquals(['testing' => 'value'], $this->subject->viewBuilder()->getVars());
     }
 
     /**
@@ -76,7 +76,7 @@ class ViewVarsTraitTest extends TestCase
         $result = $this->subject->set('testing', 'value')
             ->set('foo', 'bar');
         $this->assertSame($this->subject, $result);
-        $this->assertEquals(['testing' => 'value', 'foo' => 'bar'], $this->subject->viewVars);
+        $this->assertEquals(['testing' => 'value', 'foo' => 'bar'], $this->subject->viewBuilder()->getVars());
     }
 
     /**
@@ -91,87 +91,8 @@ class ViewVarsTraitTest extends TestCase
         $this->subject->set($keys, $vals);
 
         $expected = ['one' => 'two', 'key' => 'val'];
-        $this->assertEquals($expected, $this->subject->viewVars);
+        $this->assertEquals($expected, $this->subject->viewBuilder()->getVars());
     }
-
-    /**
-     * test viewOptions() with 1 string param, merge true
-     *
-     * @return void
-     */
-    public function testAddOneViewOption()
-    {
-        $this->deprecated(function () {
-            $option = 'newOption';
-            $this->subject->viewOptions($option);
-
-            $this->assertContains($option, $this->subject->viewOptions());
-        });
-    }
-
-    /**
-     * test viewOptions() with 2 strings in array, merge true.
-     *
-     * @return void
-     */
-    public function testAddTwoViewOption()
-    {
-        $this->deprecated(function () {
-            $this->subject->viewOptions(['oldOption'], false);
-            $option = ['newOption', 'anotherOption'];
-            $result = $this->subject->viewOptions($option);
-            $expects = ['oldOption', 'newOption', 'anotherOption'];
-
-            $this->assertContainsOnly('string', $result);
-            $this->assertEquals($expects, $result);
-        });
-    }
-
-    /**
-     * test empty params reads _viewOptions.
-     *
-     * @return void
-     */
-    public function testReadingViewOptions()
-    {
-        $this->deprecated(function () {
-            $expected = $this->subject->viewOptions(['one', 'two', 'three'], false);
-            $result = $this->subject->viewOptions();
-
-            $this->assertEquals($expected, $result);
-        });
-    }
-
-    /**
-     * test setting $merge `false` overrides correct options.
-     *
-     * @return void
-     */
-    public function testMergeFalseViewOptions()
-    {
-        $this->deprecated(function () {
-            $this->subject->viewOptions(['one', 'two', 'three'], false);
-            $expected = ['four', 'five', 'six'];
-            $result = $this->subject->viewOptions($expected, false);
-
-            $this->assertEquals($expected, $result);
-        });
-    }
-
-    /**
-     * test _viewOptions is undefined and $opts is null, an empty array is returned.
-     *
-     * @return void
-     */
-    public function testUndefinedValidViewOptions()
-    {
-        $this->deprecated(function () {
-            $result = $this->subject->viewOptions([], false);
-            $this->assertInternalType('array', $result);
-            $this->assertEmpty($result);
-        });
-    }
-
     /**
      * test that createView() updates viewVars of View instance on each call.
      *
@@ -181,11 +102,11 @@ class ViewVarsTraitTest extends TestCase
     {
         $expected = ['one' => 'one'];
         $this->subject->set($expected);
-        $this->assertEquals($expected, $this->subject->createView()->viewVars);
+        $this->assertEquals('one', $this->subject->createView()->get('one'));
 
         $expected = ['one' => 'one', 'two' => 'two'];
         $this->subject->set($expected);
-        $this->assertEquals($expected, $this->subject->createView()->viewVars);
+        $this->assertEquals('two', $this->subject->createView()->get('two'));
     }
 
     /**

+ 2 - 1
tests/TestCase/View/XmlViewTest.php

@@ -259,7 +259,8 @@ class XmlViewTest extends TestCase
         $this->assertSame('application/xml', $View->getResponse()->getType());
 
         $data = ['no' => 'nope', 'user' => 'fake', 'list' => ['item1', 'item2']];
-        $Controller->viewVars = [];
+        $Controller = new Controller($Request, $Response);
+        $Controller->viewBuilder()->setClassName('Xml');
         $Controller->set($data);
         $Controller->set('_serialize', true);
         $View = $Controller->createView();