Issue #728702 by Crell, daffie, pillarsdotnet, pjcdawkins, Damien Tournoud, David_Rothstein, reglogge, amateescu, tim.plunkett, tstoeckler, xjm, hussainweb, alexpott, cweagans, Tor Arne Thune, catch: Visiting index.php should redirect to install.php if settings.php already has database credentials but database is empty
parent
fbf3e5cedf
commit
29ce04e5d0
|
@ -1197,6 +1197,11 @@ services:
|
|||
tags:
|
||||
- { name: event_subscriber }
|
||||
arguments: ['@config.manager', '@config.storage', '@config.storage.snapshot']
|
||||
exception.needs_installer:
|
||||
class: Drupal\Core\EventSubscriber\ExceptionDetectNeedsInstallSubscriber
|
||||
arguments: ['@database']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
exception.default_json:
|
||||
class: Drupal\Core\EventSubscriber\ExceptionJsonSubscriber
|
||||
tags:
|
||||
|
|
|
@ -1444,6 +1444,26 @@ abstract class Connection {
|
|||
return $this->connection->quote($string, $parameter_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the SQLSTATE error from the PDOException.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* The exception
|
||||
*
|
||||
* @return string
|
||||
* The five character error code.
|
||||
*/
|
||||
protected static function getSQLState(\Exception $e) {
|
||||
// The PDOException code is not always reliable, try to see whether the
|
||||
// message has something usable.
|
||||
if (preg_match('/^SQLSTATE\[(\w{5})\]/', $e->getMessage(), $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
else {
|
||||
return $e->getCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the database connection from being serialized.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Database;
|
||||
|
||||
/**
|
||||
* Exception thrown if access credentials fail.
|
||||
*/
|
||||
class DatabaseAccessDeniedException extends \RuntimeException implements DatabaseException {}
|
|
@ -5,4 +5,4 @@ namespace Drupal\Core\Database;
|
|||
/**
|
||||
* Exception thrown if specified database is not found.
|
||||
*/
|
||||
class DatabaseNotFoundException extends \RuntimeException {}
|
||||
class DatabaseNotFoundException extends \RuntimeException implements DatabaseException {}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\Core\Database\Driver\mysql;
|
||||
|
||||
use Drupal\Core\Database\DatabaseAccessDeniedException;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
@ -26,6 +27,11 @@ class Connection extends DatabaseConnection {
|
|||
*/
|
||||
const DATABASE_NOT_FOUND = 1049;
|
||||
|
||||
/**
|
||||
* Error code for "Access denied" error.
|
||||
*/
|
||||
const ACCESS_DENIED = 1045;
|
||||
|
||||
/**
|
||||
* Error code for "Can't initialize character set" error.
|
||||
*/
|
||||
|
@ -139,7 +145,18 @@ class Connection extends DatabaseConnection {
|
|||
$connection_options['pdo'] += [\PDO::MYSQL_ATTR_MULTI_STATEMENTS => FALSE];
|
||||
}
|
||||
|
||||
$pdo = new \PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
|
||||
try {
|
||||
$pdo = new \PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
|
||||
}
|
||||
catch (\PDOException $e) {
|
||||
if ($e->getCode() == static::DATABASE_NOT_FOUND) {
|
||||
throw new DatabaseNotFoundException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
if ($e->getCode() == static::ACCESS_DENIED) {
|
||||
throw new DatabaseAccessDeniedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Force MySQL to use the UTF-8 character set. Also set the collation, if a
|
||||
// certain one has been set; otherwise, MySQL defaults to
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\Core\Database\Driver\pgsql;
|
|||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Connection as DatabaseConnection;
|
||||
use Drupal\Core\Database\DatabaseAccessDeniedException;
|
||||
use Drupal\Core\Database\DatabaseNotFoundException;
|
||||
|
||||
/**
|
||||
|
@ -26,6 +27,14 @@ class Connection extends DatabaseConnection {
|
|||
*/
|
||||
const DATABASE_NOT_FOUND = 7;
|
||||
|
||||
/**
|
||||
* Error code for "Connection failure" errors.
|
||||
*
|
||||
* Technically this is an internal error code that will only be shown in the
|
||||
* PDOException message. It will need to get extracted.
|
||||
*/
|
||||
const CONNECTION_FAILURE = '08006';
|
||||
|
||||
/**
|
||||
* The list of PostgreSQL reserved key words.
|
||||
*
|
||||
|
@ -113,7 +122,21 @@ class Connection extends DatabaseConnection {
|
|||
// Convert numeric values to strings when fetching.
|
||||
\PDO::ATTR_STRINGIFY_FETCHES => TRUE,
|
||||
);
|
||||
$pdo = new \PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
|
||||
|
||||
try {
|
||||
$pdo = new \PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
|
||||
}
|
||||
catch (\PDOException $e) {
|
||||
if (static::getSQLState($e) == static::CONNECTION_FAILURE) {
|
||||
if (strpos($e->getMessage(), 'password authentication failed for user') !== FALSE) {
|
||||
throw new DatabaseAccessDeniedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
elseif (strpos($e->getMessage(), 'database') !== FALSE && strpos($e->getMessage(), 'does not exist') !== FALSE) {
|
||||
throw new DatabaseNotFoundException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ namespace Drupal\Core\Database\Driver\pgsql\Install;
|
|||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Install\Tasks as InstallTasks;
|
||||
use Drupal\Core\Database\Driver\pgsql\Connection;
|
||||
use Drupal\Core\Database\DatabaseNotFoundException;
|
||||
|
||||
/**
|
||||
|
@ -66,7 +65,7 @@ class Tasks extends InstallTasks {
|
|||
}
|
||||
catch (\Exception $e) {
|
||||
// Attempt to create the database if it is not found.
|
||||
if ($e->getCode() == Connection::DATABASE_NOT_FOUND) {
|
||||
if ($e instanceof DatabaseNotFoundException) {
|
||||
// Remove the database string from connection info.
|
||||
$connection_info = Database::getConnectionInfo();
|
||||
$database = $connection_info['default']['database'];
|
||||
|
|
|
@ -99,7 +99,19 @@ class Connection extends DatabaseConnection {
|
|||
// Convert numeric values to strings when fetching.
|
||||
\PDO::ATTR_STRINGIFY_FETCHES => TRUE,
|
||||
);
|
||||
$pdo = new \PDO('sqlite:' . $connection_options['database'], '', '', $connection_options['pdo']);
|
||||
|
||||
try {
|
||||
$pdo = new \PDO('sqlite:' . $connection_options['database'], '', '', $connection_options['pdo']);
|
||||
}
|
||||
catch (\PDOException $e) {
|
||||
if ($e->getCode() == static::DATABASE_NOT_FOUND) {
|
||||
throw new DatabaseNotFoundException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
// SQLite doesn't have a distinct error code for access denied, so don't
|
||||
// deal with that case.
|
||||
throw $e;
|
||||
}
|
||||
|
||||
|
||||
// Create functions needed by SQLite.
|
||||
$pdo->sqliteCreateFunction('if', array(__CLASS__, 'sqlFunctionIf'));
|
||||
|
|
|
@ -16,6 +16,7 @@ use Drupal\Core\DependencyInjection\YamlFileLoader;
|
|||
use Drupal\Core\Extension\ExtensionDiscovery;
|
||||
use Drupal\Core\File\MimeType\MimeTypeGuesser;
|
||||
use Drupal\Core\Http\TrustedHostsRequestFactory;
|
||||
use Drupal\Core\Installer\InstallerRedirectTrait;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\Test\TestDatabase;
|
||||
|
@ -45,6 +46,7 @@ use Symfony\Component\Routing\Route;
|
|||
* container, or modify existing services.
|
||||
*/
|
||||
class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
||||
use InstallerRedirectTrait;
|
||||
|
||||
/**
|
||||
* Holds the class used for dumping the container to a PHP array.
|
||||
|
@ -644,7 +646,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
// installed yet (i.e., if no $databases array has been defined in the
|
||||
// settings.php file) and we are not already installing.
|
||||
if (!Database::getConnectionInfo() && !drupal_installation_attempted() && PHP_SAPI !== 'cli') {
|
||||
$response = new RedirectResponse($request->getBasePath() . '/core/install.php');
|
||||
$response = new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']);
|
||||
}
|
||||
else {
|
||||
$this->boot();
|
||||
|
@ -683,14 +685,17 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
* If the passed in exception cannot be turned into a response.
|
||||
*/
|
||||
protected function handleException(\Exception $e, $request, $type) {
|
||||
if ($this->shouldRedirectToInstaller($e, $this->container ? $this->container->get('database') : NULL)) {
|
||||
return new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']);
|
||||
}
|
||||
|
||||
if ($e instanceof HttpExceptionInterface) {
|
||||
$response = new Response($e->getMessage(), $e->getStatusCode());
|
||||
$response->headers->add($e->getHeaders());
|
||||
return $response;
|
||||
}
|
||||
else {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Installer\InstallerRedirectTrait;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Exception handler to determine if an exception indicates an uninstalled site.
|
||||
*/
|
||||
class ExceptionDetectNeedsInstallSubscriber implements EventSubscriberInterface {
|
||||
use InstallerRedirectTrait;
|
||||
|
||||
/**
|
||||
* The default database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a new ExceptionDetectNeedsInstallSubscriber.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The default database connection.
|
||||
*/
|
||||
public function __construct(Connection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors for this subscriber.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onException(GetResponseForExceptionEvent $event) {
|
||||
$exception = $event->getException();
|
||||
if ($this->shouldRedirectToInstaller($exception, $this->connection)) {
|
||||
// Only redirect if this is an HTML response (i.e., a user trying to view
|
||||
// the site in a web browser before installing it).
|
||||
$request = $event->getRequest();
|
||||
$format = $request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT, $request->getRequestFormat());
|
||||
if ($format == 'html') {
|
||||
$event->setResponse(new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the methods in this class that should be listeners.
|
||||
*
|
||||
* @return array
|
||||
* An array of event listener definitions.
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[KernelEvents::EXCEPTION][] = ['onException', 100];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Installer;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\DatabaseException;
|
||||
use Drupal\Core\Database\DatabaseNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides methods for checking if Drupal is already installed.
|
||||
*/
|
||||
trait InstallerRedirectTrait {
|
||||
|
||||
/**
|
||||
* Returns whether the current PHP process runs on CLI.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCli() {
|
||||
return PHP_SAPI === 'cli';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an exception handler should redirect to the installer.
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* The exception to check.
|
||||
* @param \Drupal\Core\Database\Connection|null $connection
|
||||
* (optional) The default database connection. If not provided, a less
|
||||
* comprehensive check will be performed. This can be the case if the
|
||||
* exception occurs early enough that a database connection object isn't
|
||||
* available from the container yet.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the exception handler should redirect to the installer because
|
||||
* Drupal is not installed yet, or FALSE otherwise.
|
||||
*/
|
||||
protected function shouldRedirectToInstaller(\Exception $exception, Connection $connection = NULL) {
|
||||
// Never redirect on the command line.
|
||||
if ($this->isCli()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Never redirect if we're already in the installer.
|
||||
if (drupal_installation_attempted()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// If the database wasn't found, assume the user hasn't entered it properly
|
||||
// and redirect to the installer. This check needs to come first because a
|
||||
// DatabaseNotFoundException is also an instance of DatabaseException.
|
||||
if ($exception instanceof DatabaseNotFoundException) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// To avoid unnecessary queries, only act if the exception is one that is
|
||||
// expected to occur when Drupal has not yet been installed. This includes
|
||||
// NotFoundHttpException because an uninstalled site won't have route
|
||||
// information available yet and therefore can return 404 errors.
|
||||
if (!($exception instanceof \PDOException || $exception instanceof DatabaseException || $exception instanceof NotFoundHttpException)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Redirect if there isn't even any database connection information in
|
||||
// settings.php yet, since that means Drupal is not installed.
|
||||
if (!Database::getConnectionInfo()) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Redirect if the database is empty.
|
||||
if ($connection) {
|
||||
try {
|
||||
return !$connection->schema()->tableExists('sessions');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If we still have an exception at this point, we need to be careful
|
||||
// since we should not redirect if the exception represents an error on
|
||||
// an already-installed site (for example, if the database server went
|
||||
// down). Assume we shouldn't redirect, just in case.
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// When in doubt, don't redirect.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
|
@ -223,7 +223,7 @@ class UncaughtExceptionTest extends WebTestBase {
|
|||
'value' => $incorrect_username,
|
||||
'required' => TRUE,
|
||||
);
|
||||
$settings['databases']['default']['default']['passowrd'] = (object) array(
|
||||
$settings['databases']['default']['default']['password'] = (object) array(
|
||||
'value' => $this->randomMachineName(16),
|
||||
'required' => TRUE,
|
||||
);
|
||||
|
@ -232,7 +232,7 @@ class UncaughtExceptionTest extends WebTestBase {
|
|||
|
||||
$this->drupalGet('');
|
||||
$this->assertResponse(500);
|
||||
$this->assertRaw('PDOException');
|
||||
$this->assertRaw('DatabaseAccessDeniedException');
|
||||
$this->assertErrorLogged($this->expectedExceptionMessage);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Installer;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\Core\Database\DatabaseNotFoundException;
|
||||
use Drupal\Core\Database\Schema;
|
||||
use Drupal\Core\Installer\InstallerRedirectTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
// The function drupal_installation_attempted() has to be included.
|
||||
$root = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
|
||||
require_once $root . '/core/includes/bootstrap.inc';
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Installer\InstallerRedirectTrait
|
||||
*
|
||||
* @group Installer
|
||||
*/
|
||||
class InstallerRedirectTraitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Data provider for testShouldRedirectToInstaller().
|
||||
*
|
||||
* @return array
|
||||
* - Expected result from shouldRedirectToInstaller().
|
||||
* - Exceptions to be handled by shouldRedirectToInstaller()
|
||||
* - Whether or not there is a database connection.
|
||||
* - Whether or not there is database connection info.
|
||||
* - Whether or not there exists a sessions table in the database.
|
||||
*/
|
||||
public function providerShouldRedirectToInstaller() {
|
||||
return array(
|
||||
[TRUE, DatabaseNotFoundException::class, FALSE, FALSE],
|
||||
[TRUE, DatabaseNotFoundException::class, TRUE, FALSE],
|
||||
[TRUE, DatabaseNotFoundException::class, FALSE, TRUE],
|
||||
[TRUE, DatabaseNotFoundException::class, TRUE, TRUE],
|
||||
[TRUE, DatabaseNotFoundException::class, TRUE, TRUE, FALSE],
|
||||
|
||||
[TRUE, \PDOException::class, FALSE, FALSE],
|
||||
[TRUE, \PDOException::class, TRUE, FALSE],
|
||||
[FALSE, \PDOException::class, FALSE, TRUE],
|
||||
[FALSE, \PDOException::class, TRUE, TRUE],
|
||||
[TRUE, \PDOException::class, TRUE, TRUE, FALSE],
|
||||
|
||||
[TRUE, DatabaseExceptionWrapper::class, FALSE, FALSE],
|
||||
[TRUE, DatabaseExceptionWrapper::class, TRUE, FALSE],
|
||||
[FALSE, DatabaseExceptionWrapper::class, FALSE, TRUE],
|
||||
[FALSE, DatabaseExceptionWrapper::class, TRUE, TRUE],
|
||||
[TRUE, DatabaseExceptionWrapper::class, TRUE, TRUE, FALSE],
|
||||
|
||||
[TRUE, NotFoundHttpException::class, FALSE, FALSE],
|
||||
[TRUE, NotFoundHttpException::class, TRUE, FALSE],
|
||||
[FALSE, NotFoundHttpException::class, FALSE, TRUE],
|
||||
[FALSE, NotFoundHttpException::class, TRUE, TRUE],
|
||||
[TRUE, NotFoundHttpException::class, TRUE, TRUE, FALSE],
|
||||
|
||||
[FALSE, \Exception::class, FALSE, FALSE],
|
||||
[FALSE, \Exception::class, TRUE, FALSE],
|
||||
[FALSE, \Exception::class, FALSE, TRUE],
|
||||
[FALSE, \Exception::class, TRUE, TRUE],
|
||||
[FALSE, \Exception::class, TRUE, TRUE, FALSE],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::shouldRedirectToInstaller
|
||||
* @dataProvider providerShouldRedirectToInstaller
|
||||
*/
|
||||
function testShouldRedirectToInstaller($expected, $exception, $connection, $connection_info, $session_table_exists = TRUE) {
|
||||
try {
|
||||
throw new $exception();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Mock the trait.
|
||||
$trait = $this->getMockBuilder(InstallerRedirectTrait::class)
|
||||
->setMethods(array('isCli'))
|
||||
->getMockForTrait();
|
||||
|
||||
// Make sure that the method thinks we are not using the cli.
|
||||
$trait->expects($this->any())
|
||||
->method('isCli')
|
||||
->willReturn(FALSE);
|
||||
|
||||
// Un-protect the method using reflection.
|
||||
$method_ref = new \ReflectionMethod($trait, 'shouldRedirectToInstaller');
|
||||
$method_ref->setAccessible(TRUE);
|
||||
|
||||
// Mock the database connection info.
|
||||
$db = $this->getMockForAbstractClass(Database::class);
|
||||
$property_ref = new \ReflectionProperty($db, 'databaseInfo');
|
||||
$property_ref->setAccessible(TRUE);
|
||||
$property_ref->setValue($db, ['default' => $connection_info]);
|
||||
|
||||
if ($connection) {
|
||||
// Mock the database connection.
|
||||
$connection = $this->getMockBuilder(Connection::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('schema'))
|
||||
->getMockForAbstractClass();
|
||||
|
||||
if ($connection_info) {
|
||||
// Mock the database schema class.
|
||||
$schema = $this->getMockBuilder(Schema::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('tableExists'))
|
||||
->getMockForAbstractClass();
|
||||
|
||||
$schema->expects($this->any())
|
||||
->method('tableExists')
|
||||
->with('sessions')
|
||||
->willReturn($session_table_exists);
|
||||
|
||||
$connection->expects($this->any())
|
||||
->method('schema')
|
||||
->willReturn($schema);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Set the database connection if there is none.
|
||||
$connection = NULL;
|
||||
}
|
||||
|
||||
// Call shouldRedirectToInstaller.
|
||||
$this->assertSame($expected, $method_ref->invoke($trait, $e, $connection));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue