mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Compare commits
4 Commits
2d388e9f5d
...
2d413619b5
Author | SHA1 | Date | |
---|---|---|---|
|
2d413619b5 | ||
|
a81b7855de | ||
|
e93dafb2fe | ||
|
6287b6ebeb |
@ -87,7 +87,7 @@ class Director implements TemplateGlobalProvider
|
|||||||
/**
|
/**
|
||||||
* List of rules that must only use the primary database and not a replica
|
* List of rules that must only use the primary database and not a replica
|
||||||
*/
|
*/
|
||||||
private static array $must_use_primary_db_rules = [
|
private static array $rule_patterns_must_use_primary_db = [
|
||||||
'dev',
|
'dev',
|
||||||
'Security',
|
'Security',
|
||||||
];
|
];
|
||||||
@ -306,8 +306,9 @@ class Director implements TemplateGlobalProvider
|
|||||||
|
|
||||||
// Check if primary database must be used based on request rules
|
// Check if primary database must be used based on request rules
|
||||||
// Note this check must happend before the rules are processed as
|
// Note this check must happend before the rules are processed as
|
||||||
// $shiftOnSuccess is true during $request->match() below
|
// $shiftOnSuccess param is passed as true in `$request->match($pattern, true)` later on in
|
||||||
$primaryDbOnlyRules = Director::config()->uninherited('must_use_primary_db_rules');
|
// this method, which modifies `$this->dirParts`, thus affecting `$request->match($rule)` directly below
|
||||||
|
$primaryDbOnlyRules = Director::config()->uninherited('rule_patterns_must_use_primary_db');
|
||||||
foreach ($primaryDbOnlyRules as $rule) {
|
foreach ($primaryDbOnlyRules as $rule) {
|
||||||
if ($request->match($rule)) {
|
if ($request->match($rule)) {
|
||||||
DB::setMustUsePrimary();
|
DB::setMustUsePrimary();
|
||||||
|
@ -16,7 +16,7 @@ class CliBypass implements Bypass
|
|||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be removed without equivalent functionality to replace it',
|
'Will be removed without equivalent functionality to replace it',
|
||||||
|
@ -6,6 +6,7 @@ use SilverStripe\Control\HTTPResponse_Exception;
|
|||||||
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
|
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
use SilverStripe\ORM\Connect\NullDatabase;
|
use SilverStripe\ORM\Connect\NullDatabase;
|
||||||
@ -15,6 +16,7 @@ use SilverStripe\ORM\Connect\NullDatabase;
|
|||||||
*/
|
*/
|
||||||
class CoreKernel extends BaseKernel
|
class CoreKernel extends BaseKernel
|
||||||
{
|
{
|
||||||
|
|
||||||
protected bool $bootDatabase = true;
|
protected bool $bootDatabase = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,12 +73,12 @@ class CoreKernel extends BaseKernel
|
|||||||
$msg = 'Silverstripe Framework requires a "database" key in DB::getConfig(). ' .
|
$msg = 'Silverstripe Framework requires a "database" key in DB::getConfig(). ' .
|
||||||
'Did you forget to set SS_DATABASE_NAME or SS_DATABASE_CHOOSE_NAME in your environment?';
|
'Did you forget to set SS_DATABASE_NAME or SS_DATABASE_CHOOSE_NAME in your environment?';
|
||||||
$this->detectLegacyEnvironment();
|
$this->detectLegacyEnvironment();
|
||||||
Deprecation::withNoReplacement(fn() => $this->redirectToInstaller($msg));
|
Deprecation::withSuppressedNotice(fn() => $this->redirectToInstaller($msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load default database configuration from the $database and $databaseConfig globals
|
* Load database configuration from the $database and $databaseConfig globals
|
||||||
*/
|
*/
|
||||||
protected function bootDatabaseGlobals()
|
protected function bootDatabaseGlobals()
|
||||||
{
|
{
|
||||||
@ -87,9 +89,9 @@ class CoreKernel extends BaseKernel
|
|||||||
global $databaseConfig;
|
global $databaseConfig;
|
||||||
global $database;
|
global $database;
|
||||||
|
|
||||||
for ($i = 0; $i <= 99; $i++) {
|
for ($i = 0; $i <= DB::MAX_REPLICAS; $i++) {
|
||||||
if ($i === 0) {
|
if ($i === 0) {
|
||||||
$key = 'default';
|
$key = DB::CONN_PRIMARY;
|
||||||
} else {
|
} else {
|
||||||
$key = DB::getReplicaConfigKey($i);
|
$key = DB::getReplicaConfigKey($i);
|
||||||
if (!DB::hasConfig($key)) {
|
if (!DB::hasConfig($key)) {
|
||||||
@ -120,20 +122,20 @@ class CoreKernel extends BaseKernel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load default database configuration from environment variables
|
* Load database configuration from environment variables
|
||||||
*/
|
*/
|
||||||
protected function bootDatabaseEnvVars()
|
protected function bootDatabaseEnvVars()
|
||||||
{
|
{
|
||||||
if (!$this->bootDatabase) {
|
if (!$this->bootDatabase) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Set default database config
|
// Set primary database config
|
||||||
$databaseConfig = $this->getDatabaseConfig();
|
$databaseConfig = $this->getDatabaseConfig();
|
||||||
$databaseConfig['database'] = $this->getDatabaseName();
|
$databaseConfig['database'] = $this->getDatabaseName();
|
||||||
DB::setConfig($databaseConfig);
|
DB::setConfig($databaseConfig, DB::CONN_PRIMARY);
|
||||||
|
|
||||||
// Set database replicas config
|
// Set database replicas config
|
||||||
for ($i = 1; $i <= 99; $i++) {
|
for ($i = 1; $i <= DB::MAX_REPLICAS; $i++) {
|
||||||
$envKey = $this->getReplicaEnvKey('SS_DATABASE_SERVER', $i);
|
$envKey = $this->getReplicaEnvKey('SS_DATABASE_SERVER', $i);
|
||||||
if (!Environment::hasEnv($envKey)) {
|
if (!Environment::hasEnv($envKey)) {
|
||||||
break;
|
break;
|
||||||
@ -156,15 +158,24 @@ class CoreKernel extends BaseKernel
|
|||||||
|
|
||||||
private function getDatabaseReplicaConfig(int $replica)
|
private function getDatabaseReplicaConfig(int $replica)
|
||||||
{
|
{
|
||||||
|
if ($replica <= 0) {
|
||||||
|
throw new InvalidArgumentException('Replica number must be greater than 0');
|
||||||
|
}
|
||||||
return $this->getSingleDataBaseConfig($replica);
|
return $this->getSingleDataBaseConfig($replica);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a database key to a replica key
|
* Convert a database key to a replica key
|
||||||
* e.g. SS_DATABASE_SERVER -> SS_DATABASE_SERVER_REPLICA_01
|
* e.g. SS_DATABASE_SERVER -> SS_DATABASE_SERVER_REPLICA_01
|
||||||
|
*
|
||||||
|
* @param string $key - The key to look up in the environment
|
||||||
|
* @param int $replica - Replica number
|
||||||
*/
|
*/
|
||||||
private function getReplicaEnvKey(string $key, int $replica): string
|
private function getReplicaEnvKey(string $key, int $replica): string
|
||||||
{
|
{
|
||||||
|
if ($replica <= 0) {
|
||||||
|
throw new InvalidArgumentException('Replica number must be greater than 0');
|
||||||
|
}
|
||||||
// Do not allow replicas to define keys that could lead to unexpected behaviour if
|
// Do not allow replicas to define keys that could lead to unexpected behaviour if
|
||||||
// they do not match the primary database configuration
|
// they do not match the primary database configuration
|
||||||
if (in_array($key, ['SS_DATABASE_CLASS', 'SS_DATABASE_NAME', 'SS_DATABASE_CHOOSE_NAME'])) {
|
if (in_array($key, ['SS_DATABASE_CLASS', 'SS_DATABASE_NAME', 'SS_DATABASE_CHOOSE_NAME'])) {
|
||||||
@ -177,10 +188,13 @@ class CoreKernel extends BaseKernel
|
|||||||
/**
|
/**
|
||||||
* Reads a single database configuration variable from the environment
|
* Reads a single database configuration variable from the environment
|
||||||
* For replica databases, it will first attempt to find replica-specific configuration
|
* For replica databases, it will first attempt to find replica-specific configuration
|
||||||
* before falling back to the default configuration.
|
* before falling back to the primary configuration.
|
||||||
*
|
*
|
||||||
* Replicate specific configuration has `_REPLICA_01` appended to the key
|
* Replicate specific configuration has `_REPLICA_01` appended to the key
|
||||||
* where 01 is the replica number.
|
* where 01 is the replica number.
|
||||||
|
*
|
||||||
|
* @param string $key - The key to look up in the environment
|
||||||
|
* @param int $replica - Replica number. Passing 0 will return the primary database configuration
|
||||||
*/
|
*/
|
||||||
private function getDatabaseConfigVariable(string $key, int $replica): string
|
private function getDatabaseConfigVariable(string $key, int $replica): string
|
||||||
{
|
{
|
||||||
@ -193,6 +207,9 @@ class CoreKernel extends BaseKernel
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $replica - Replica number. Passing 0 will return the primary database configuration
|
||||||
|
*/
|
||||||
private function getSingleDataBaseConfig(int $replica): array
|
private function getSingleDataBaseConfig(int $replica): array
|
||||||
{
|
{
|
||||||
$databaseConfig = [
|
$databaseConfig = [
|
||||||
|
@ -96,7 +96,7 @@ abstract class BuildTask
|
|||||||
*/
|
*/
|
||||||
public function getDescription()
|
public function getDescription()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(
|
Deprecation::withSuppressedNotice(
|
||||||
fn() => Deprecation::notice('5.4.0', 'Will be replaced with a static method with the same name')
|
fn() => Deprecation::notice('5.4.0', 'Will be replaced with a static method with the same name')
|
||||||
);
|
);
|
||||||
return $this->description;
|
return $this->description;
|
||||||
|
@ -53,7 +53,7 @@ class Deprecation
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
private static bool $insideWithNoReplacement = false;
|
private static bool $insideNoticeSuppression = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -103,22 +103,32 @@ class Deprecation
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to wrap deprecated methods and deprecated config get()/set() that will be removed
|
* Used to wrap deprecated methods and deprecated config get()/set() called from the vendor
|
||||||
* in the next major version with no replacement. This is done to surpress deprecation notices
|
* dir that projects have no ability to change.
|
||||||
* by for calls from the vendor dir to deprecated code that projects have no ability to change
|
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
* @deprecated 5.4.0 Use withSuppressedNotice() instead
|
||||||
*/
|
*/
|
||||||
public static function withNoReplacement(callable $func)
|
public static function withNoReplacement(callable $func)
|
||||||
{
|
{
|
||||||
if (Deprecation::$insideWithNoReplacement) {
|
Deprecation::notice('5.4.0', 'Use withSuppressedNotice() instead');
|
||||||
|
return Deprecation::withSuppressedNotice($func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to wrap deprecated methods and deprecated config get()/set() called from the vendor
|
||||||
|
* dir that projects have no ability to change.
|
||||||
|
*/
|
||||||
|
public static function withSuppressedNotice(callable $func): mixed
|
||||||
|
{
|
||||||
|
if (Deprecation::$insideNoticeSuppression) {
|
||||||
return $func();
|
return $func();
|
||||||
}
|
}
|
||||||
Deprecation::$insideWithNoReplacement = true;
|
Deprecation::$insideNoticeSuppression = true;
|
||||||
try {
|
try {
|
||||||
return $func();
|
return $func();
|
||||||
} finally {
|
} finally {
|
||||||
Deprecation::$insideWithNoReplacement = false;
|
Deprecation::$insideNoticeSuppression = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +147,8 @@ class Deprecation
|
|||||||
$level = 1;
|
$level = 1;
|
||||||
}
|
}
|
||||||
$newLevel = $level;
|
$newLevel = $level;
|
||||||
// handle closures inside withNoReplacement()
|
// handle closures inside withSuppressedNotice()
|
||||||
if (Deprecation::$insideWithNoReplacement
|
if (Deprecation::$insideNoticeSuppression
|
||||||
&& substr($backtrace[$newLevel]['function'], -strlen('{closure}')) === '{closure}'
|
&& substr($backtrace[$newLevel]['function'], -strlen('{closure}')) === '{closure}'
|
||||||
) {
|
) {
|
||||||
$newLevel = $newLevel + 2;
|
$newLevel = $newLevel + 2;
|
||||||
@ -247,8 +257,8 @@ class Deprecation
|
|||||||
$count++;
|
$count++;
|
||||||
$arr = array_shift(Deprecation::$userErrorMessageBuffer);
|
$arr = array_shift(Deprecation::$userErrorMessageBuffer);
|
||||||
$message = $arr['message'];
|
$message = $arr['message'];
|
||||||
$calledInsideWithNoReplacement = $arr['calledInsideWithNoReplacement'];
|
$calledWithNoticeSuppression = $arr['calledWithNoticeSuppression'];
|
||||||
if ($calledInsideWithNoReplacement && !Deprecation::$showNoReplacementNotices) {
|
if ($calledWithNoticeSuppression && !Deprecation::$showNoReplacementNotices) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Deprecation::$isTriggeringError = true;
|
Deprecation::$isTriggeringError = true;
|
||||||
@ -284,7 +294,7 @@ class Deprecation
|
|||||||
$data = [
|
$data = [
|
||||||
'key' => sha1($string),
|
'key' => sha1($string),
|
||||||
'message' => $string,
|
'message' => $string,
|
||||||
'calledInsideWithNoReplacement' => Deprecation::$insideWithNoReplacement
|
'calledWithNoticeSuppression' => Deprecation::$insideNoticeSuppression
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
if (!Deprecation::isEnabled()) {
|
if (!Deprecation::isEnabled()) {
|
||||||
@ -310,7 +320,7 @@ class Deprecation
|
|||||||
$string .= ".";
|
$string .= ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
$level = Deprecation::$insideWithNoReplacement ? 4 : 2;
|
$level = Deprecation::$insideNoticeSuppression ? 4 : 2;
|
||||||
$string .= " Called from " . Deprecation::get_called_method_from_trace($backtrace, $level) . '.';
|
$string .= " Called from " . Deprecation::get_called_method_from_trace($backtrace, $level) . '.';
|
||||||
|
|
||||||
if ($caller) {
|
if ($caller) {
|
||||||
@ -319,7 +329,7 @@ class Deprecation
|
|||||||
$data = [
|
$data = [
|
||||||
'key' => sha1($string),
|
'key' => sha1($string),
|
||||||
'message' => $string,
|
'message' => $string,
|
||||||
'calledInsideWithNoReplacement' => Deprecation::$insideWithNoReplacement
|
'calledWithNoticeSuppression' => Deprecation::$insideNoticeSuppression
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if ($data && !array_key_exists($data['key'], Deprecation::$userErrorMessageBuffer)) {
|
if ($data && !array_key_exists($data['key'], Deprecation::$userErrorMessageBuffer)) {
|
||||||
|
@ -34,7 +34,7 @@ class DevBuildController extends Controller implements PermissionProvider
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be replaced with SilverStripe\Dev\Command\DbBuild',
|
'Will be replaced with SilverStripe\Dev\Command\DbBuild',
|
||||||
|
@ -47,7 +47,7 @@ class DevConfigController extends Controller implements PermissionProvider
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be replaced with SilverStripe\Dev\Command\ConfigDump',
|
'Will be replaced with SilverStripe\Dev\Command\ConfigDump',
|
||||||
|
@ -233,7 +233,7 @@ class DevelopmentAdmin extends Controller implements PermissionProvider
|
|||||||
*/
|
*/
|
||||||
public function buildDefaults()
|
public function buildDefaults()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be replaced with SilverStripe\Dev\Command\DbDefaults'
|
'Will be replaced with SilverStripe\Dev\Command\DbDefaults'
|
||||||
@ -266,7 +266,7 @@ class DevelopmentAdmin extends Controller implements PermissionProvider
|
|||||||
*/
|
*/
|
||||||
public function generatesecuretoken()
|
public function generatesecuretoken()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be replaced with SilverStripe\Dev\Command\GenerateSecureToken'
|
'Will be replaced with SilverStripe\Dev\Command\GenerateSecureToken'
|
||||||
|
@ -21,7 +21,7 @@ class SSListExporter extends Exporter implements TestOnly
|
|||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be removed without equivalent functionality to replace it',
|
'Will be removed without equivalent functionality to replace it',
|
||||||
|
@ -397,8 +397,7 @@ abstract class SapphireTest extends TestCase implements TestOnly
|
|||||||
public static function setUpBeforeClass(): void
|
public static function setUpBeforeClass(): void
|
||||||
{
|
{
|
||||||
// Disallow the use of DB replicas in tests
|
// Disallow the use of DB replicas in tests
|
||||||
// This prevents replicas from being used if there are environment variables set to use them
|
DB::setMustUsePrimary();
|
||||||
DB::setCanUseReplicas(false);
|
|
||||||
|
|
||||||
// Start tests
|
// Start tests
|
||||||
static::start();
|
static::start();
|
||||||
|
@ -36,7 +36,7 @@ class CleanupTestDatabasesTask extends BuildTask
|
|||||||
|
|
||||||
public function canView(): bool
|
public function canView(): bool
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be replaced with canRunInBrowser()'
|
'Will be replaced with canRunInBrowser()'
|
||||||
|
@ -25,7 +25,7 @@ class GridFieldConfig_Base extends GridFieldConfig
|
|||||||
$this->addComponent(GridFieldPageCount::create('toolbar-header-right'));
|
$this->addComponent(GridFieldPageCount::create('toolbar-header-right'));
|
||||||
$this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
|
$this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
|
||||||
|
|
||||||
Deprecation::withNoReplacement(function () use ($sort, $filter, $pagination) {
|
Deprecation::withSuppressedNotice(function () use ($sort, $filter, $pagination) {
|
||||||
$sort->setThrowExceptionOnBadDataType(false);
|
$sort->setThrowExceptionOnBadDataType(false);
|
||||||
$filter->setThrowExceptionOnBadDataType(false);
|
$filter->setThrowExceptionOnBadDataType(false);
|
||||||
$pagination->setThrowExceptionOnBadDataType(false);
|
$pagination->setThrowExceptionOnBadDataType(false);
|
||||||
|
@ -32,7 +32,7 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig
|
|||||||
$this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
|
$this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
|
||||||
$this->addComponent(GridFieldDetailForm::create(null, $showPagination, $showAdd));
|
$this->addComponent(GridFieldDetailForm::create(null, $showPagination, $showAdd));
|
||||||
|
|
||||||
Deprecation::withNoReplacement(function () use ($sort, $filter, $pagination) {
|
Deprecation::withSuppressedNotice(function () use ($sort, $filter, $pagination) {
|
||||||
$sort->setThrowExceptionOnBadDataType(false);
|
$sort->setThrowExceptionOnBadDataType(false);
|
||||||
$filter->setThrowExceptionOnBadDataType(false);
|
$filter->setThrowExceptionOnBadDataType(false);
|
||||||
$pagination->setThrowExceptionOnBadDataType(false);
|
$pagination->setThrowExceptionOnBadDataType(false);
|
||||||
|
@ -45,7 +45,7 @@ class GridFieldConfig_RelationEditor extends GridFieldConfig
|
|||||||
$this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
|
$this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
|
||||||
$this->addComponent(GridFieldDetailForm::create());
|
$this->addComponent(GridFieldDetailForm::create());
|
||||||
|
|
||||||
Deprecation::withNoReplacement(function () use ($sort, $filter, $pagination) {
|
Deprecation::withSuppressedNotice(function () use ($sort, $filter, $pagination) {
|
||||||
$sort->setThrowExceptionOnBadDataType(false);
|
$sort->setThrowExceptionOnBadDataType(false);
|
||||||
$filter->setThrowExceptionOnBadDataType(false);
|
$filter->setThrowExceptionOnBadDataType(false);
|
||||||
$pagination->setThrowExceptionOnBadDataType(false);
|
$pagination->setThrowExceptionOnBadDataType(false);
|
||||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\Logging;
|
|||||||
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Handler\AbstractProcessingHandler;
|
use Monolog\Handler\AbstractProcessingHandler;
|
||||||
|
use Monolog\Level;
|
||||||
use Monolog\LogRecord;
|
use Monolog\LogRecord;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
@ -34,10 +35,10 @@ class HTTPOutputHandler extends AbstractProcessingHandler
|
|||||||
*/
|
*/
|
||||||
private $cliFormatter = null;
|
private $cliFormatter = null;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct($level, $bubble);
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be renamed to ErrorOutputHandler',
|
'Will be renamed to ErrorOutputHandler',
|
||||||
|
@ -14,7 +14,7 @@ class ArrayLib
|
|||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function invert($arr)
|
public static function invert($arr)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::invert()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::invert()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function valuekey($arr)
|
public static function valuekey($arr)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::valuekey()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::valuekey()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function array_values_recursive($array)
|
public static function array_values_recursive($array)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::invearray_values_recursivert()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::invearray_values_recursivert()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function filter_keys($arr, $keys)
|
public static function filter_keys($arr, $keys)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::filter_keys()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::filter_keys()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function is_associative($array)
|
public static function is_associative($array)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::is_associative()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::is_associative()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function in_array_recursive($needle, $haystack, $strict = false)
|
public static function in_array_recursive($needle, $haystack, $strict = false)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::in_array_recursive()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::in_array_recursive()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function array_map_recursive($f, $array)
|
public static function array_map_recursive($f, $array)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::array_map_recursive()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::array_map_recursive()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function array_merge_recursive($array)
|
public static function array_merge_recursive($array)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::array_merge_recursive()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::array_merge_recursive()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function flatten($array, $preserveKeys = true, &$out = [])
|
public static function flatten($array, $preserveKeys = true, &$out = [])
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::flatten()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::flatten()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function iterateVolatile(array &$list)
|
public static function iterateVolatile(array &$list)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::iterateVolatile()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::iterateVolatile()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -341,7 +341,7 @@ class ArrayLib
|
|||||||
*/
|
*/
|
||||||
public static function shuffleAssociative(array &$array): void
|
public static function shuffleAssociative(array &$array): void
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::shuffleAssociative()');
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\ArrayLib::shuffleAssociative()');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
|
|||||||
*/
|
*/
|
||||||
public function __construct(array $items = [])
|
public function __construct(array $items = [])
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\ArrayList', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\ArrayList', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -731,7 +731,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
|
|||||||
|
|
||||||
// Apply default case sensitivity for backwards compatability
|
// Apply default case sensitivity for backwards compatability
|
||||||
if (!str_contains($filterKey, ':case') && !str_contains($filterKey, ':nocase')) {
|
if (!str_contains($filterKey, ':case') && !str_contains($filterKey, ':nocase')) {
|
||||||
$caseSensitive = Deprecation::withNoReplacement(fn() => static::config()->get('default_case_sensitive'));
|
$caseSensitive = Deprecation::withSuppressedNotice(fn() => static::config()->get('default_case_sensitive'));
|
||||||
if ($caseSensitive && in_array('case', $searchFilter->getSupportedModifiers())) {
|
if ($caseSensitive && in_array('case', $searchFilter->getSupportedModifiers())) {
|
||||||
$searchFilter->setModifiers($searchFilter->getModifiers() + ['case']);
|
$searchFilter->setModifiers($searchFilter->getModifiers() + ['case']);
|
||||||
} elseif (!$caseSensitive && in_array('nocase', $searchFilter->getSupportedModifiers())) {
|
} elseif (!$caseSensitive && in_array('nocase', $searchFilter->getSupportedModifiers())) {
|
||||||
|
232
src/ORM/DB.php
232
src/ORM/DB.php
@ -15,6 +15,7 @@ use SilverStripe\ORM\Connect\DBConnector;
|
|||||||
use SilverStripe\ORM\Connect\DBSchemaManager;
|
use SilverStripe\ORM\Connect\DBSchemaManager;
|
||||||
use SilverStripe\ORM\Connect\Query;
|
use SilverStripe\ORM\Connect\Query;
|
||||||
use SilverStripe\ORM\Queries\SQLExpression;
|
use SilverStripe\ORM\Queries\SQLExpression;
|
||||||
|
use SilverStripe\Core\CoreKernel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global database interface, complete with static methods.
|
* Global database interface, complete with static methods.
|
||||||
@ -22,6 +23,25 @@ use SilverStripe\ORM\Queries\SQLExpression;
|
|||||||
*/
|
*/
|
||||||
class DB
|
class DB
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The 'default' connection is a dynamic connection that will use either a replica connection if one is
|
||||||
|
* available and not forced to use the 'primary' connection, or the 'primary' connection
|
||||||
|
*
|
||||||
|
* Prior to CMS 5.4 when replica connections were introduced, the 'default' connection
|
||||||
|
* was not dynamic and instead represented what the 'primary' connection is now
|
||||||
|
*/
|
||||||
|
public const CONN_DEFAULT = 'default';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The 'primary' connection name, which is the main database connection and is used for all write
|
||||||
|
* operations and for read operations when the 'default' connection is forced to use the 'primary' connection
|
||||||
|
*/
|
||||||
|
public const CONN_PRIMARY = 'primary';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of replicas databases that can be configured
|
||||||
|
*/
|
||||||
|
public const MAX_REPLICAS = 99;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constant was added in SilverStripe 2.4 to indicate that SQL-queries
|
* This constant was added in SilverStripe 2.4 to indicate that SQL-queries
|
||||||
@ -76,6 +96,28 @@ class DB
|
|||||||
*/
|
*/
|
||||||
private static $connection_attempted = false;
|
private static $connection_attempted = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only use the primary database connection for the rest of the current request
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static bool $mustUsePrimary = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by DB::withPrimary() to count the number of times it has been called
|
||||||
|
* Uses an int instead of a bool to allow for nested calls
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static int $withPrimaryCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key of the replica config to use for this request
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static string $replicaConfigKey = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the global database connection.
|
* Set the global database connection.
|
||||||
* Pass an object that's a subclass of SS_Database. This object will be used when {@link DB::query()}
|
* Pass an object that's a subclass of SS_Database. This object will be used when {@link DB::query()}
|
||||||
@ -87,7 +129,7 @@ class DB
|
|||||||
* be accessed through DB::get_conn($name). This is useful when you have an application that
|
* be accessed through DB::get_conn($name). This is useful when you have an application that
|
||||||
* needs to connect to more than one database.
|
* needs to connect to more than one database.
|
||||||
*/
|
*/
|
||||||
public static function set_conn(Database $connection, $name = 'default')
|
public static function set_conn(Database $connection, $name = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
DB::$connections[$name] = $connection;
|
DB::$connections[$name] = $connection;
|
||||||
}
|
}
|
||||||
@ -99,10 +141,10 @@ class DB
|
|||||||
* the default connection is returned.
|
* the default connection is returned.
|
||||||
* @return Database
|
* @return Database
|
||||||
*/
|
*/
|
||||||
public static function get_conn($name = 'default')
|
public static function get_conn($name = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
// Allow default to connect to replica if configured
|
// Allow default to connect to replica if configured
|
||||||
if ($name === 'default') {
|
if ($name === DB::CONN_DEFAULT) {
|
||||||
$name = DB::getDefaultConnectionName();
|
$name = DB::getDefaultConnectionName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,12 +153,6 @@ class DB
|
|||||||
return DB::$connections[$name];
|
return DB::$connections[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that primary config is set, clone it from default config
|
|
||||||
if ($name === 'primary' && !static::hasConfig('primary')) {
|
|
||||||
$config = static::getConfig('default');
|
|
||||||
static::setConfig($config, 'primary');
|
|
||||||
}
|
|
||||||
|
|
||||||
// lazy connect
|
// lazy connect
|
||||||
$config = static::getConfig($name);
|
$config = static::getConfig($name);
|
||||||
if ($config) {
|
if ($config) {
|
||||||
@ -127,45 +163,16 @@ class DB
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only use the primary database connection for the rest of the current request
|
* Get whether the primary database connection must be used for the rest of the current request
|
||||||
*
|
* and not a replica connection
|
||||||
* @internal
|
|
||||||
*/
|
*/
|
||||||
private static bool $mustUsePrimary = false;
|
public static function getMustUsePrimary(): bool
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by DB::withPrimary() to count the number of times it has been called
|
|
||||||
* Uses an int instead of a bool to allow for nested calls *
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
private static int $withPrimaryCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The key of the previously choosen random replica config
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
private static string $randomReplicaConfigKey = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to determine if replicas can be used
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
private static bool $canUseReplicas = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the flag to allow or disallow the use of replica database connections
|
|
||||||
* For instance disable when running unit-tests
|
|
||||||
*/
|
|
||||||
public static function setCanUseReplicas(bool $canUseReplicas): void
|
|
||||||
{
|
{
|
||||||
DB::$canUseReplicas = $canUseReplicas;
|
return DB::$mustUsePrimary;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the flag to only use the primary database connection for the current request
|
* Set to use the primary database connection for rest of the current request
|
||||||
* meaning that replia connections will no longer be used
|
* meaning that replia connections will no longer be used
|
||||||
*
|
*
|
||||||
* This intentioally does not have a parameter to set this back to false, as this it to prevent
|
* This intentioally does not have a parameter to set this back to false, as this it to prevent
|
||||||
@ -192,58 +199,6 @@ class DB
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name of the database connection to use for the given SQL query
|
|
||||||
* The 'default' connection can be either the primary or a replica connection if configured
|
|
||||||
*/
|
|
||||||
private static function getDefaultConnectionName(string $sql = ''): string
|
|
||||||
{
|
|
||||||
if (DB::$mustUsePrimary || DB::$withPrimaryCount > 0 || !DB::$canUseReplicas || !DB::hasReplicaConfig()) {
|
|
||||||
return 'primary';
|
|
||||||
}
|
|
||||||
if ($sql) {
|
|
||||||
$dbClass = DB::getConfig('default')['type'];
|
|
||||||
// This must use getServiceSpec() and not Injector::get/create() followed by
|
|
||||||
// getConnector() as this can remove the dbConn from a different connection
|
|
||||||
// under edge case conditions
|
|
||||||
$dbSpec = Injector::inst()->getServiceSpec($dbClass);
|
|
||||||
$connectorService = $dbSpec['properties']['connector'];
|
|
||||||
$connector = Injector::inst()->convertServiceProperty($connectorService);
|
|
||||||
if ($connector->isQueryMutable($sql)) {
|
|
||||||
DB::$mustUsePrimary = true;
|
|
||||||
return 'primary';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (DB::$randomReplicaConfigKey) {
|
|
||||||
return DB::$randomReplicaConfigKey;
|
|
||||||
}
|
|
||||||
$name = DB::getRandomReplicaConfigKey();
|
|
||||||
DB::$randomReplicaConfigKey = $name;
|
|
||||||
return $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a random replica database configuration key from the available replica configurations
|
|
||||||
* The replica choosen will be used for the rest of the request, unless the primary connection
|
|
||||||
* is forced
|
|
||||||
*/
|
|
||||||
private static function getRandomReplicaConfigKey(): string
|
|
||||||
{
|
|
||||||
$replicaNumbers = [];
|
|
||||||
for ($i = 1; $i < 99; $i++) {
|
|
||||||
$replicaKey = DB::getReplicaConfigKey($i);
|
|
||||||
if (DB::hasConfig($replicaKey)) {
|
|
||||||
$replicaNumbers[] = $i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count($replicaNumbers) === 0) {
|
|
||||||
throw new RunTimeException('No replica configurations found');
|
|
||||||
}
|
|
||||||
// Choose a random replica
|
|
||||||
$index = rand(0, count($replicaNumbers) - 1);
|
|
||||||
return DB::getReplicaConfigKey($replicaNumbers[$index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the schema manager for the current database
|
* Retrieves the schema manager for the current database
|
||||||
*
|
*
|
||||||
@ -251,7 +206,7 @@ class DB
|
|||||||
* the default connection is returned.
|
* the default connection is returned.
|
||||||
* @return DBSchemaManager
|
* @return DBSchemaManager
|
||||||
*/
|
*/
|
||||||
public static function get_schema($name = 'default')
|
public static function get_schema($name = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
$connection = DB::get_conn($name);
|
$connection = DB::get_conn($name);
|
||||||
if ($connection) {
|
if ($connection) {
|
||||||
@ -269,7 +224,7 @@ class DB
|
|||||||
* the default connection is returned.
|
* the default connection is returned.
|
||||||
* @return string The resulting SQL as a string
|
* @return string The resulting SQL as a string
|
||||||
*/
|
*/
|
||||||
public static function build_sql(SQLExpression $expression, &$parameters, $name = 'default')
|
public static function build_sql(SQLExpression $expression, &$parameters, $name = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
$connection = DB::get_conn($name);
|
$connection = DB::get_conn($name);
|
||||||
if ($connection) {
|
if ($connection) {
|
||||||
@ -287,7 +242,7 @@ class DB
|
|||||||
* the default connection is returned.
|
* the default connection is returned.
|
||||||
* @return DBConnector
|
* @return DBConnector
|
||||||
*/
|
*/
|
||||||
public static function get_connector($name = 'default')
|
public static function get_connector($name = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
$connection = DB::get_conn($name);
|
$connection = DB::get_conn($name);
|
||||||
if ($connection) {
|
if ($connection) {
|
||||||
@ -403,8 +358,13 @@ class DB
|
|||||||
* @param string $label identifier for the connection
|
* @param string $label identifier for the connection
|
||||||
* @return Database
|
* @return Database
|
||||||
*/
|
*/
|
||||||
public static function connect($databaseConfig, $label = 'default')
|
public static function connect($databaseConfig, $label = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
|
// Allow default to connect to replica if configured
|
||||||
|
if ($label === DB::CONN_DEFAULT) {
|
||||||
|
$label = DB::getDefaultConnectionName();
|
||||||
|
}
|
||||||
|
|
||||||
// This is used by the "testsession" module to test up a test session using an alternative name
|
// This is used by the "testsession" module to test up a test session using an alternative name
|
||||||
if ($name = DB::get_alternative_database_name()) {
|
if ($name = DB::get_alternative_database_name()) {
|
||||||
$databaseConfig['database'] = $name;
|
$databaseConfig['database'] = $name;
|
||||||
@ -434,8 +394,11 @@ class DB
|
|||||||
* @param array $databaseConfig
|
* @param array $databaseConfig
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*/
|
*/
|
||||||
public static function setConfig($databaseConfig, $name = 'default')
|
public static function setConfig($databaseConfig, $name = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
|
if ($name === DB::CONN_DEFAULT) {
|
||||||
|
$name = DB::CONN_PRIMARY;
|
||||||
|
}
|
||||||
static::$configs[$name] = $databaseConfig;
|
static::$configs[$name] = $databaseConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,8 +408,11 @@ class DB
|
|||||||
* @param string $name
|
* @param string $name
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function getConfig($name = 'default')
|
public static function getConfig($name = DB::CONN_DEFAULT)
|
||||||
{
|
{
|
||||||
|
if ($name === DB::CONN_DEFAULT) {
|
||||||
|
$name = DB::CONN_PRIMARY;
|
||||||
|
}
|
||||||
if (static::hasConfig($name)) {
|
if (static::hasConfig($name)) {
|
||||||
return static::$configs[$name];
|
return static::$configs[$name];
|
||||||
}
|
}
|
||||||
@ -455,8 +421,11 @@ class DB
|
|||||||
/**
|
/**
|
||||||
* Check if a named connection config exists
|
* Check if a named connection config exists
|
||||||
*/
|
*/
|
||||||
public static function hasConfig($name = 'default'): bool
|
public static function hasConfig($name = DB::CONN_DEFAULT): bool
|
||||||
{
|
{
|
||||||
|
if ($name === DB::CONN_DEFAULT) {
|
||||||
|
$name = DB::CONN_PRIMARY;
|
||||||
|
}
|
||||||
return array_key_exists($name, static::$configs);
|
return array_key_exists($name, static::$configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -844,4 +813,63 @@ class DB
|
|||||||
{
|
{
|
||||||
DB::get_schema()->alterationMessage($message, $type);
|
DB::get_schema()->alterationMessage($message, $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the database connection to use for the given SQL query
|
||||||
|
* The 'default' connection can be either the primary or a replica connection if configured
|
||||||
|
*/
|
||||||
|
private static function getDefaultConnectionName(string $sql = ''): string
|
||||||
|
{
|
||||||
|
if (DB::$mustUsePrimary || DB::$withPrimaryCount > 0 || !DB::hasReplicaConfig()) {
|
||||||
|
return DB::CONN_PRIMARY;
|
||||||
|
}
|
||||||
|
if (DB::isMutableSql($sql)) {
|
||||||
|
DB::$mustUsePrimary = true;
|
||||||
|
return DB::CONN_PRIMARY;
|
||||||
|
}
|
||||||
|
if (DB::$replicaConfigKey) {
|
||||||
|
return DB::$replicaConfigKey;
|
||||||
|
}
|
||||||
|
$name = DB::getRandomReplicaConfigKey();
|
||||||
|
DB::$replicaConfigKey = $name;
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given SQL query is mutable
|
||||||
|
*/
|
||||||
|
private static function isMutableSql(string $sql): bool
|
||||||
|
{
|
||||||
|
$dbClass = DB::getConfig(DB::CONN_PRIMARY)['type'];
|
||||||
|
// This must use getServiceSpec() and not Injector::get/create() followed by
|
||||||
|
// getConnector() as this can remove the dbConn from a different connection
|
||||||
|
// under edge case conditions
|
||||||
|
$dbSpec = Injector::inst()->getServiceSpec($dbClass);
|
||||||
|
$connectorService = $dbSpec['properties']['connector'];
|
||||||
|
$connector = Injector::inst()->convertServiceProperty($connectorService);
|
||||||
|
return $connector->isQueryMutable($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a random replica database configuration key from the available replica configurations
|
||||||
|
* The replica choosen will be used for the rest of the request, unless the primary connection
|
||||||
|
* is forced
|
||||||
|
*/
|
||||||
|
private static function getRandomReplicaConfigKey(): string
|
||||||
|
{
|
||||||
|
$replicaNumbers = [];
|
||||||
|
for ($i = 1; $i < DB::MAX_REPLICAS; $i++) {
|
||||||
|
$replicaKey = DB::getReplicaConfigKey($i);
|
||||||
|
if (!DB::hasConfig($replicaKey)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$replicaNumbers[] = $i;
|
||||||
|
}
|
||||||
|
if (count($replicaNumbers) === 0) {
|
||||||
|
throw new RunTimeException('No replica configurations found');
|
||||||
|
}
|
||||||
|
// Choose a random replica
|
||||||
|
$index = rand(0, count($replicaNumbers) - 1);
|
||||||
|
return DB::getReplicaConfigKey($replicaNumbers[$index]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,10 @@ abstract class DataExtension extends Extension
|
|||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
// Wrapping with Deprecation::withNoReplacement() to avoid triggering deprecation notices
|
// Wrapping with Deprecation::withSuppressedNotice() to avoid triggering deprecation notices
|
||||||
// as we are unable to update existing subclasses of this class until a new major
|
// as we are unable to update existing subclasses of this class until a new major
|
||||||
// unless we add in the pointless empty methods that are in this class
|
// unless we add in the pointless empty methods that are in this class
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
$class = Extension::class;
|
$class = Extension::class;
|
||||||
Deprecation::notice('5.3.0', "Subclass $class instead", Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.3.0', "Subclass $class instead", Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
@ -142,10 +142,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
private static $default_classname = null;
|
private static $default_classname = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this DataObject class must only use the primary database and not a replica
|
* Whether this DataObject class must only use the primary database and not a read-only replica
|
||||||
* Note that this will be only be enforced when using DataQuery::execute() or
|
* Note that this will be only be enforced when using DataQuery::execute() or
|
||||||
* another method that uses calls DataQuery::execute() internally e.g. as DataObject::get()
|
* another method that uses calls DataQuery::execute() internally e.g. DataObject::get()
|
||||||
* This will not be enforced when using another method to query data e.g. SQLSelect or DB::query()
|
* This will not be enforced when using low-level ORM functionality to query data e.g. SQLSelect or DB::query()
|
||||||
*/
|
*/
|
||||||
private static bool $must_use_primary_db = false;
|
private static bool $must_use_primary_db = false;
|
||||||
|
|
||||||
@ -1994,7 +1994,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
if ($details['polymorphic']) {
|
if ($details['polymorphic']) {
|
||||||
$result = PolymorphicHasManyList::create($componentClass, $details['joinField'], static::class);
|
$result = PolymorphicHasManyList::create($componentClass, $details['joinField'], static::class);
|
||||||
if ($details['needsRelation']) {
|
if ($details['needsRelation']) {
|
||||||
Deprecation::withNoReplacement(fn () => $result->setForeignRelation($componentName));
|
Deprecation::withSuppressedNotice(fn () => $result->setForeignRelation($componentName));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$result = HasManyList::create($componentClass, $details['joinField']);
|
$result = HasManyList::create($componentClass, $details['joinField']);
|
||||||
|
@ -443,18 +443,6 @@ class DataQuery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls a callback on either the primary database or a replica, with respect to the configured
|
|
||||||
* value of `must_use_primary_db` on the current dataClass
|
|
||||||
*/
|
|
||||||
private function withCorrectDatabase(callable $callback): mixed
|
|
||||||
{
|
|
||||||
if (Config::inst()->get($this->dataClass(), 'must_use_primary_db')) {
|
|
||||||
return DB::withPrimary($callback);
|
|
||||||
}
|
|
||||||
return $callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the query and return the result as {@link SS_Query} object.
|
* Execute the query and return the result as {@link SS_Query} object.
|
||||||
*
|
*
|
||||||
@ -1522,4 +1510,16 @@ class DataQuery
|
|||||||
|
|
||||||
return $updated;
|
return $updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls a callback on either the primary database or a replica, with respect to the configured
|
||||||
|
* value of `must_use_primary_db` on the current dataClass
|
||||||
|
*/
|
||||||
|
private function withCorrectDatabase(callable $callback): mixed
|
||||||
|
{
|
||||||
|
if (Config::inst()->get($this->dataClass(), 'must_use_primary_db')) {
|
||||||
|
return DB::withPrimary($callback);
|
||||||
|
}
|
||||||
|
return $callback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ class DatabaseAdmin extends Controller
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be replaced with SilverStripe\Dev\Command\DbBuild',
|
'Will be replaced with SilverStripe\Dev\Command\DbBuild',
|
||||||
@ -213,7 +213,7 @@ class DatabaseAdmin extends Controller
|
|||||||
*/
|
*/
|
||||||
public static function lastBuilt()
|
public static function lastBuilt()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'5.4.0',
|
'5.4.0',
|
||||||
'Will be replaced with SilverStripe\Dev\Command\DbBuild::lastBuilt()'
|
'Will be replaced with SilverStripe\Dev\Command\DbBuild::lastBuilt()'
|
||||||
|
@ -19,7 +19,7 @@ class GroupedList extends ListDecorator
|
|||||||
|
|
||||||
public function __construct(SS_List&Sortable&Filterable&Limitable $list)
|
public function __construct(SS_List&Sortable&Filterable&Limitable $list)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\GroupedList', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\GroupedList', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
parent::__construct($list);
|
parent::__construct($list);
|
||||||
|
@ -32,7 +32,7 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
|
|||||||
*/
|
*/
|
||||||
public function __construct(SS_List&Sortable&Filterable&Limitable $list)
|
public function __construct(SS_List&Sortable&Filterable&Limitable $list)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\ListDecorator', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\ListDecorator', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class Map implements ArrayAccess, Countable, IteratorAggregate
|
|||||||
*/
|
*/
|
||||||
public function __construct(SS_List $list, $keyField = "ID", $valueField = "Title")
|
public function __construct(SS_List $list, $keyField = "ID", $valueField = "Title")
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\Map', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\Map', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class PaginatedList extends ListDecorator
|
|||||||
*/
|
*/
|
||||||
public function __construct(SS_List $list, $request = [])
|
public function __construct(SS_List $list, $request = [])
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\PaginatedList', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\List\PaginatedList', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class ValidationException extends Exception
|
|||||||
*/
|
*/
|
||||||
public function __construct($result = null, $code = 0)
|
public function __construct($result = null, $code = 0)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\Validation\ValidationException', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\Validation\ValidationException', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class ValidationResult
|
|||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\Validation\ValidationResult', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Core\Validation\ValidationResult', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ class CookieAuthenticationHandler implements AuthenticationHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Renew the token
|
// Renew the token
|
||||||
Deprecation::withNoReplacement(fn() => $rememberLoginHash->renew());
|
Deprecation::withSuppressedNotice(fn() => $rememberLoginHash->renew());
|
||||||
|
|
||||||
// Send the new token to the client if it was changed
|
// Send the new token to the client if it was changed
|
||||||
if ($rememberLoginHash->getToken()) {
|
if ($rememberLoginHash->getToken()) {
|
||||||
|
@ -34,7 +34,7 @@ class ArrayData extends ViewableData
|
|||||||
*/
|
*/
|
||||||
public function __construct($value = [])
|
public function __construct($value = [])
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ArrayData', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ArrayData', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ class ViewableData implements IteratorAggregate
|
|||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ModelData', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ModelData', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class ViewableData_Customised extends ViewableData
|
|||||||
*/
|
*/
|
||||||
public function __construct(ViewableData $originalObject, ViewableData $customisedObject)
|
public function __construct(ViewableData $originalObject, ViewableData $customisedObject)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ModelDataCustomised', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ModelDataCustomised', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class ViewableData_Debugger extends ViewableData
|
|||||||
*/
|
*/
|
||||||
public function __construct(ViewableData $object)
|
public function __construct(ViewableData $object)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ModelDataDebugger', Deprecation::SCOPE_CLASS);
|
Deprecation::notice('5.4.0', 'Will be renamed to SilverStripe\Model\ModelDataDebugger', Deprecation::SCOPE_CLASS);
|
||||||
});
|
});
|
||||||
$this->object = $object;
|
$this->object = $object;
|
||||||
|
@ -22,7 +22,7 @@ class IPUtilsTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
public function testIPv4($matches, $remoteAddr, $cidr)
|
public function testIPv4($matches, $remoteAddr, $cidr)
|
||||||
{
|
{
|
||||||
Deprecation::withNoReplacement(function () use ($matches, $remoteAddr, $cidr) {
|
Deprecation::withSuppressedNotice(function () use ($matches, $remoteAddr, $cidr) {
|
||||||
$this->assertSame($matches, IPUtils::checkIP($remoteAddr, $cidr));
|
$this->assertSame($matches, IPUtils::checkIP($remoteAddr, $cidr));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ class IPUtilsTest extends SapphireTest
|
|||||||
$this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
|
$this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
|
||||||
}
|
}
|
||||||
|
|
||||||
Deprecation::withNoReplacement(function () use ($matches, $remoteAddr, $cidr) {
|
Deprecation::withSuppressedNotice(function () use ($matches, $remoteAddr, $cidr) {
|
||||||
$this->assertSame($matches, IPUtils::checkIP($remoteAddr, $cidr));
|
$this->assertSame($matches, IPUtils::checkIP($remoteAddr, $cidr));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ class IPUtilsTest extends SapphireTest
|
|||||||
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
|
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
|
||||||
}
|
}
|
||||||
|
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
IPUtils::checkIP('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65');
|
IPUtils::checkIP('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -113,72 +113,72 @@ class DeprecationTest extends SapphireTest
|
|||||||
Deprecation::outputNotices();
|
Deprecation::outputNotices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWithNoReplacementDefault()
|
public function testwithSuppressedNoticeDefault()
|
||||||
{
|
{
|
||||||
Deprecation::enable();
|
Deprecation::enable();
|
||||||
$ret = Deprecation::withNoReplacement(function () {
|
$ret = Deprecation::withSuppressedNotice(function () {
|
||||||
return $this->myDeprecatedMethod();
|
return $this->myDeprecatedMethod();
|
||||||
});
|
});
|
||||||
$this->assertSame('abc', $ret);
|
$this->assertSame('abc', $ret);
|
||||||
Deprecation::outputNotices();
|
Deprecation::outputNotices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWithNoReplacementTrue()
|
public function testwithSuppressedNoticeTrue()
|
||||||
{
|
{
|
||||||
$message = implode(' ', [
|
$message = implode(' ', [
|
||||||
'SilverStripe\Dev\Tests\DeprecationTest->myDeprecatedMethod is deprecated.',
|
'SilverStripe\Dev\Tests\DeprecationTest->myDeprecatedMethod is deprecated.',
|
||||||
'My message.',
|
'My message.',
|
||||||
'Called from SilverStripe\Dev\Tests\DeprecationTest->testWithNoReplacementTrue.'
|
'Called from SilverStripe\Dev\Tests\DeprecationTest->testwithSuppressedNoticeTrue.'
|
||||||
]);
|
]);
|
||||||
$this->expectDeprecation();
|
$this->expectDeprecation();
|
||||||
$this->expectDeprecationMessage($message);
|
$this->expectDeprecationMessage($message);
|
||||||
Deprecation::enable(true);
|
Deprecation::enable(true);
|
||||||
$ret = Deprecation::withNoReplacement(function () {
|
$ret = Deprecation::withSuppressedNotice(function () {
|
||||||
return $this->myDeprecatedMethod();
|
return $this->myDeprecatedMethod();
|
||||||
});
|
});
|
||||||
$this->assertSame('abc', $ret);
|
$this->assertSame('abc', $ret);
|
||||||
Deprecation::outputNotices();
|
Deprecation::outputNotices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWithNoReplacementTrueCallUserFunc()
|
public function testwithSuppressedNoticeTrueCallUserFunc()
|
||||||
{
|
{
|
||||||
$message = implode(' ', [
|
$message = implode(' ', [
|
||||||
'SilverStripe\Dev\Tests\DeprecationTest->myDeprecatedMethod is deprecated.',
|
'SilverStripe\Dev\Tests\DeprecationTest->myDeprecatedMethod is deprecated.',
|
||||||
'My message.',
|
'My message.',
|
||||||
'Called from SilverStripe\Dev\Tests\DeprecationTest->testWithNoReplacementTrueCallUserFunc.'
|
'Called from SilverStripe\Dev\Tests\DeprecationTest->testwithSuppressedNoticeTrueCallUserFunc.'
|
||||||
]);
|
]);
|
||||||
$this->expectDeprecation();
|
$this->expectDeprecation();
|
||||||
$this->expectDeprecationMessage($message);
|
$this->expectDeprecationMessage($message);
|
||||||
Deprecation::enable(true);
|
Deprecation::enable(true);
|
||||||
$ret = Deprecation::withNoReplacement(function () {
|
$ret = Deprecation::withSuppressedNotice(function () {
|
||||||
return call_user_func([$this, 'myDeprecatedMethod']);
|
return call_user_func([$this, 'myDeprecatedMethod']);
|
||||||
});
|
});
|
||||||
$this->assertSame('abc', $ret);
|
$this->assertSame('abc', $ret);
|
||||||
Deprecation::outputNotices();
|
Deprecation::outputNotices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNoticeWithNoReplacementTrue()
|
public function testNoticewithSuppressedNoticeTrue()
|
||||||
{
|
{
|
||||||
$message = implode(' ', [
|
$message = implode(' ', [
|
||||||
'SilverStripe\Dev\Tests\DeprecationTest->testNoticeWithNoReplacementTrue is deprecated.',
|
'SilverStripe\Dev\Tests\DeprecationTest->testNoticewithSuppressedNoticeTrue is deprecated.',
|
||||||
'My message.',
|
'My message.',
|
||||||
'Called from PHPUnit\Framework\TestCase->runTest.'
|
'Called from PHPUnit\Framework\TestCase->runTest.'
|
||||||
]);
|
]);
|
||||||
$this->expectDeprecation();
|
$this->expectDeprecation();
|
||||||
$this->expectDeprecationMessage($message);
|
$this->expectDeprecationMessage($message);
|
||||||
Deprecation::enable(true);
|
Deprecation::enable(true);
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice('123', 'My message.');
|
Deprecation::notice('123', 'My message.');
|
||||||
});
|
});
|
||||||
Deprecation::outputNotices();
|
Deprecation::outputNotices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClassWithNoReplacement()
|
public function testClasswithSuppressedNotice()
|
||||||
{
|
{
|
||||||
$message = implode(' ', [
|
$message = implode(' ', [
|
||||||
'SilverStripe\Dev\Tests\DeprecationTest\DeprecationTestObject is deprecated.',
|
'SilverStripe\Dev\Tests\DeprecationTest\DeprecationTestObject is deprecated.',
|
||||||
'Some class message.',
|
'Some class message.',
|
||||||
'Called from SilverStripe\Dev\Tests\DeprecationTest->testClassWithNoReplacement.'
|
'Called from SilverStripe\Dev\Tests\DeprecationTest->testClasswithSuppressedNotice.'
|
||||||
]);
|
]);
|
||||||
$this->expectDeprecation();
|
$this->expectDeprecation();
|
||||||
$this->expectDeprecationMessage($message);
|
$this->expectDeprecationMessage($message);
|
||||||
@ -190,12 +190,12 @@ class DeprecationTest extends SapphireTest
|
|||||||
Deprecation::outputNotices();
|
Deprecation::outputNotices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClassWithInjectorWithNoReplacement()
|
public function testClassWithInjectorwithSuppressedNotice()
|
||||||
{
|
{
|
||||||
$message = implode(' ', [
|
$message = implode(' ', [
|
||||||
'SilverStripe\Dev\Tests\DeprecationTest\DeprecationTestObject is deprecated.',
|
'SilverStripe\Dev\Tests\DeprecationTest\DeprecationTestObject is deprecated.',
|
||||||
'Some class message.',
|
'Some class message.',
|
||||||
'Called from SilverStripe\Dev\Tests\DeprecationTest->testClassWithInjectorWithNoReplacement.'
|
'Called from SilverStripe\Dev\Tests\DeprecationTest->testClassWithInjectorwithSuppressedNotice.'
|
||||||
]);
|
]);
|
||||||
$this->expectDeprecation();
|
$this->expectDeprecation();
|
||||||
$this->expectDeprecationMessage($message);
|
$this->expectDeprecationMessage($message);
|
||||||
|
@ -11,7 +11,7 @@ class DeprecationTestObject extends DataObject implements TestOnly
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
Deprecation::withNoReplacement(function () {
|
Deprecation::withSuppressedNotice(function () {
|
||||||
Deprecation::notice(
|
Deprecation::notice(
|
||||||
'1.2.3',
|
'1.2.3',
|
||||||
'Some class message',
|
'Some class message',
|
||||||
|
@ -13,14 +13,7 @@ use SilverStripe\ORM\Tests\DBReplicaTest\TestObject;
|
|||||||
use SilverStripe\Security\Group;
|
use SilverStripe\Security\Group;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\ORM\DataQuery;
|
use SilverStripe\ORM\DataQuery;
|
||||||
use SilverStripe\Security\Permission;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for DB replica functionality
|
|
||||||
*
|
|
||||||
* Note that a lot of these methods test mutiple things at once. This is to increase the run speed of the tests
|
|
||||||
* because they're all quite slow due to the need to create and tear-down fixtures for every test
|
|
||||||
*/
|
|
||||||
class DBReplicaTest extends FunctionalTest
|
class DBReplicaTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
protected static $extra_dataobjects = [
|
protected static $extra_dataobjects = [
|
||||||
@ -33,9 +26,9 @@ class DBReplicaTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
$this->setupConfigsAndConnections(true);
|
$this->setupConfigsAndConnections(true);
|
||||||
// Allow using replicas - this is disabled by default in SapphireTest::setUpBeforeClass()
|
// Set DB:$mustUsePrimary to true to allow using replicas
|
||||||
DB::setCanUseReplicas(true);
|
// This is disabled by default in SapphireTest::setUpBeforeClass()
|
||||||
// Reset mustUsePrimary after using mutable sql to create yml fixtures
|
// Also reset mustUsePrimary after using mutable sql to create yml fixtures
|
||||||
// and also because by default an ADMIN user is logged in when using fixtures in SapphireTest::setUp()
|
// and also because by default an ADMIN user is logged in when using fixtures in SapphireTest::setUp()
|
||||||
// and also prevent tests from affecting subsequent tests
|
// and also prevent tests from affecting subsequent tests
|
||||||
(new ReflectionClass(DB::class))->setStaticPropertyValue('mustUsePrimary', false);
|
(new ReflectionClass(DB::class))->setStaticPropertyValue('mustUsePrimary', false);
|
||||||
@ -44,106 +37,70 @@ class DBReplicaTest extends FunctionalTest
|
|||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
{
|
{
|
||||||
$this->setupConfigsAndConnections(false);
|
$this->setupConfigsAndConnections(false);
|
||||||
DB::setCanUseReplicas(false);
|
// Reset DB:$mustUsePrimary to true which is the default set by SapphireTest::setUpBeforeClass()
|
||||||
|
(new ReflectionClass(DB::class))->setStaticPropertyValue('mustUsePrimary', true);
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function testUsesReplica(): void
|
||||||
* Using reflection, set DB::configs and DB::connections with a fake a replica connection
|
|
||||||
* that points to the same connection as the primary connection.
|
|
||||||
*/
|
|
||||||
private function setupConfigsAndConnections($includeReplica = true)
|
|
||||||
{
|
|
||||||
$reflector = new ReflectionClass(DB::class);
|
|
||||||
$defaultConfig = DB::getConfig('default');
|
|
||||||
$configs = ['default' => $defaultConfig];
|
|
||||||
if ($includeReplica) {
|
|
||||||
$configs['primary'] = $defaultConfig;
|
|
||||||
$configs['replica_01'] = $defaultConfig;
|
|
||||||
}
|
|
||||||
$reflector->setStaticPropertyValue('configs', $configs);
|
|
||||||
// Make a call to the database to ensure the connection is established
|
|
||||||
TestObject::get()->count();
|
|
||||||
// Create connections
|
|
||||||
$defaultConnection = DB::get_conn('default');
|
|
||||||
$connections = ['default' => $defaultConnection];
|
|
||||||
if ($includeReplica) {
|
|
||||||
$connections['primary'] = $defaultConnection;
|
|
||||||
$connections['replica_01'] = $defaultConnection;
|
|
||||||
}
|
|
||||||
$reflector->setStaticPropertyValue('connections', $connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the last connection name used by the DB class. This shows if a replica was used.
|
|
||||||
*/
|
|
||||||
private function getLastConnectionName()
|
|
||||||
{
|
|
||||||
return (new ReflectionClass(DB::class))->getStaticPropertyValue('lastConnectionName');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUsesReplica()
|
|
||||||
{
|
{
|
||||||
// Assert uses replica by default
|
// Assert uses replica by default
|
||||||
TestObject::get()->count();
|
TestObject::get()->count();
|
||||||
$this->assertSame('replica_01', $this->getLastConnectionName());
|
$this->assertSame('replica_01', $this->getLastConnectionName());
|
||||||
// Assert uses primary when using withPrimary()
|
// Assert uses primary when using withPrimary()
|
||||||
DB::withPrimary(fn() => TestObject::get()->count());
|
DB::withPrimary(fn() => TestObject::get()->count());
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
// Assert that withPrimary() was only temporary
|
// Assert that withPrimary() was only temporary
|
||||||
TestObject::get()->count();
|
TestObject::get()->count();
|
||||||
$this->assertSame('replica_01', $this->getLastConnectionName());
|
$this->assertSame('replica_01', $this->getLastConnectionName());
|
||||||
// Assert DB::setMustUsePrimary() forces primary from now on
|
// Assert DB::setMustUsePrimary() forces primary from now on
|
||||||
DB::setMustUsePrimary();
|
DB::setMustUsePrimary();
|
||||||
TestObject::get()->count();
|
TestObject::get()->count();
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMutableSql()
|
public function testMutableSql(): void
|
||||||
{
|
{
|
||||||
// Assert that using mutable sql in an ORM method with a dataclass uses primary
|
// Assert that using mutable sql in an ORM method with a dataclass uses primary
|
||||||
TestObject::create(['Title' => 'testing'])->write();
|
TestObject::create(['Title' => 'testing'])->write();
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
// Assert that now all subsequent queries use primary
|
// Assert that now all subsequent queries use primary
|
||||||
TestObject::get()->count();
|
TestObject::get()->count();
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMutableSqlDbQuery()
|
public function testMutableSqlDbQuery(): void
|
||||||
{
|
{
|
||||||
// Assert that using mutable sql in DB::query() uses primary
|
// Assert that using mutable sql in DB::query() uses primary
|
||||||
DB::query('INSERT INTO "DBReplicaTest_TestObject" ("Title") VALUES (\'testing\')');
|
DB::query('INSERT INTO "DBReplicaTest_TestObject" ("Title") VALUES (\'testing\')');
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
// Assert that now all subsequent queries use primary
|
// Assert that now all subsequent queries use primary
|
||||||
TestObject::get()->count();
|
TestObject::get()->count();
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMutableSqlDbPreparedQuery()
|
public function testMutableSqlDbPreparedQuery(): void
|
||||||
{
|
{
|
||||||
// Assert that using mutable sql in DB::prepared_query() uses primary
|
// Assert that using mutable sql in DB::prepared_query() uses primary
|
||||||
DB::prepared_query('INSERT INTO "DBReplicaTest_TestObject" ("Title") VALUES (?)', ['testing']);
|
DB::prepared_query('INSERT INTO "DBReplicaTest_TestObject" ("Title") VALUES (?)', ['testing']);
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
// Assert that now all subsequent queries use primary
|
// Assert that now all subsequent queries use primary
|
||||||
TestObject::get()->count();
|
TestObject::get()->count();
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetCurrentUser()
|
/**
|
||||||
|
* @dataProvider provideSetCurrentUser
|
||||||
|
*/
|
||||||
|
public function testSetCurrentUser(string $firstName, string $expected): void
|
||||||
{
|
{
|
||||||
// Assert non cms user logged in uses replica
|
$member = Member::get()->find('FirstName', $firstName);
|
||||||
$member = Member::get()->find('FirstName', 'random');
|
|
||||||
Security::setCurrentUser($member);
|
Security::setCurrentUser($member);
|
||||||
TestObject::get()->count();
|
TestObject::get()->count();
|
||||||
$this->assertSame('replica_01', $this->getLastConnectionName());
|
$this->assertSame($expected, $this->getLastConnectionName());
|
||||||
// Assert cms user logged in uses primary
|
|
||||||
$member = Member::get()->find('FirstName', 'cmsuser');
|
|
||||||
Security::setCurrentUser($member);
|
|
||||||
TestObject::get()->count();
|
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDataObjectMustUsePrimaryDb()
|
public function testDataObjectMustUsePrimaryDb(): void
|
||||||
{
|
{
|
||||||
// Assert that DataList::getIterator() respect DataObject.must_use_primary_db
|
// Assert that DataList::getIterator() respect DataObject.must_use_primary_db
|
||||||
foreach (TestObject::get() as $object) {
|
foreach (TestObject::get() as $object) {
|
||||||
@ -153,7 +110,7 @@ class DBReplicaTest extends FunctionalTest
|
|||||||
foreach (Group::get() as $group) {
|
foreach (Group::get() as $group) {
|
||||||
$group->Title = 'test2';
|
$group->Title = 'test2';
|
||||||
}
|
}
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName());
|
||||||
// Assert that DataQuery methods without params respect DataObject.must_use_primary_db
|
// Assert that DataQuery methods without params respect DataObject.must_use_primary_db
|
||||||
$methods = [
|
$methods = [
|
||||||
'count',
|
'count',
|
||||||
@ -165,7 +122,7 @@ class DBReplicaTest extends FunctionalTest
|
|||||||
(new DataQuery(TestObject::class))->$method();
|
(new DataQuery(TestObject::class))->$method();
|
||||||
$this->assertSame('replica_01', $this->getLastConnectionName(), "method is $method");
|
$this->assertSame('replica_01', $this->getLastConnectionName(), "method is $method");
|
||||||
(new DataQuery(Group::class))->$method();
|
(new DataQuery(Group::class))->$method();
|
||||||
$this->assertSame('primary', $this->getLastConnectionName(), "method is $method");
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName(), "method is $method");
|
||||||
}
|
}
|
||||||
// Assert that DataQuery methods with a param respect DataObject.must_use_primary_db
|
// Assert that DataQuery methods with a param respect DataObject.must_use_primary_db
|
||||||
$methods = [
|
$methods = [
|
||||||
@ -179,11 +136,50 @@ class DBReplicaTest extends FunctionalTest
|
|||||||
(new DataQuery(TestObject::class))->$method('ID');
|
(new DataQuery(TestObject::class))->$method('ID');
|
||||||
$this->assertSame('replica_01', $this->getLastConnectionName(), "method is $method");
|
$this->assertSame('replica_01', $this->getLastConnectionName(), "method is $method");
|
||||||
(new DataQuery(Group::class))->$method('ID');
|
(new DataQuery(Group::class))->$method('ID');
|
||||||
$this->assertSame('primary', $this->getLastConnectionName(), "method is $method");
|
$this->assertSame(DB::CONN_PRIMARY, $this->getLastConnectionName(), "method is $method");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRoutes()
|
public static function provideSetCurrentUser(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'non_cms_user' => [
|
||||||
|
'firstName' => 'random',
|
||||||
|
'expected' => 'replica_01'
|
||||||
|
],
|
||||||
|
'cms_user' => [
|
||||||
|
'firstName' => 'cmsuser',
|
||||||
|
'expected' => DB::CONN_PRIMARY
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function provideRoutes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'normal_route' => [
|
||||||
|
'path' => 'test',
|
||||||
|
'expected' => 'replica_01'
|
||||||
|
],
|
||||||
|
'security_route' => [
|
||||||
|
'path' => 'Security/login',
|
||||||
|
'expected' => DB::CONN_PRIMARY
|
||||||
|
],
|
||||||
|
'dev_route' => [
|
||||||
|
'path' => 'dev/tasks',
|
||||||
|
'expected' => DB::CONN_PRIMARY
|
||||||
|
],
|
||||||
|
'dev_in_path_but_not_dev_route' => [
|
||||||
|
'path' => 'test/dev',
|
||||||
|
'replica_01'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideRoutes
|
||||||
|
*/
|
||||||
|
public function testRoutes(string $path, string $expected): void
|
||||||
{
|
{
|
||||||
// Create a custom rule to test our controller that should default to using a replica
|
// Create a custom rule to test our controller that should default to using a replica
|
||||||
$rules = Config::inst()->get(Director::class, 'rules');
|
$rules = Config::inst()->get(Director::class, 'rules');
|
||||||
@ -191,40 +187,79 @@ class DBReplicaTest extends FunctionalTest
|
|||||||
// Ensure that routes staring with '$' are at the bottom of the assoc array index and don't override
|
// Ensure that routes staring with '$' are at the bottom of the assoc array index and don't override
|
||||||
// our new 'test' route
|
// our new 'test' route
|
||||||
uksort($rules, fn($a, $b) => str_starts_with($a, '$') ? 1 : (str_starts_with($b, '$') ? -1 : 0));
|
uksort($rules, fn($a, $b) => str_starts_with($a, '$') ? 1 : (str_starts_with($b, '$') ? -1 : 0));
|
||||||
// Assert that normal routes use replica
|
$this->get($path);
|
||||||
$this->get('/test');
|
$this->assertSame($expected, $this->getLastConnectionName());
|
||||||
$this->assertSame('replica_01', $this->getLastConnectionName());
|
|
||||||
// Assert that Security route is forced to use primary
|
|
||||||
$this->get('/Security/login');
|
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
|
||||||
// Assert that dev route is forced to use primary
|
|
||||||
$this->get('/dev/tasks');
|
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetCanUseReplicas()
|
public function provideHasReplicaConfig(): array
|
||||||
{
|
{
|
||||||
DB::setCanUseReplicas(false);
|
return [
|
||||||
TestObject::get()->count();
|
'no_replica' => [
|
||||||
$this->assertSame('primary', $this->getLastConnectionName());
|
'includeReplica' => false,
|
||||||
DB::setCanUseReplicas(true);
|
'expected' => false
|
||||||
TestObject::get()->count();
|
],
|
||||||
$this->assertSame('replica_01', $this->getLastConnectionName());
|
'with_replica' => [
|
||||||
|
'includeReplica' => true,
|
||||||
|
'expected' => true
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConfigMethods()
|
/**
|
||||||
|
* @dataProvider provideHasReplicaConfig
|
||||||
|
*/
|
||||||
|
public function testHasReplicaConfig(bool $includeReplica, bool $expected): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(DB::hasReplicaConfig());
|
||||||
|
$primaryConfig = DB::getConfig(DB::CONN_PRIMARY);
|
||||||
|
$config = [DB::CONN_PRIMARY => $primaryConfig];
|
||||||
|
if ($includeReplica) {
|
||||||
|
$config['replica_01'] = $primaryConfig;
|
||||||
|
}
|
||||||
|
(new ReflectionClass(DB::class))->setStaticPropertyValue('configs', $config);
|
||||||
|
$this->assertSame($expected, DB::hasReplicaConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasConfig(): void
|
||||||
{
|
{
|
||||||
// Test DB::hasConfig()
|
|
||||||
$this->assertFalse(DB::hasConfig('lorem'));
|
$this->assertFalse(DB::hasConfig('lorem'));
|
||||||
DB::setConfig(['type' => 'lorem'], 'lorem');
|
DB::setConfig(['type' => 'lorem'], 'lorem');
|
||||||
$this->assertTrue(DB::hasConfig('lorem'));
|
$this->assertTrue(DB::hasConfig('lorem'));
|
||||||
// Test DB::hasReplicaConfig()
|
}
|
||||||
$this->assertTrue(DB::hasReplicaConfig());
|
|
||||||
$default = DB::getConfig('default');
|
public function testGetReplicaConfigKey(): void
|
||||||
(new ReflectionClass(DB::class))->setStaticPropertyValue('configs', ['default' => $default]);
|
{
|
||||||
$this->assertFalse(DB::hasReplicaConfig());
|
|
||||||
// Test DB::getReplicaConfigKey()
|
|
||||||
$this->assertSame('replica_03', DB::getReplicaConfigKey(3));
|
$this->assertSame('replica_03', DB::getReplicaConfigKey(3));
|
||||||
$this->assertSame('replica_58', DB::getReplicaConfigKey(58));
|
$this->assertSame('replica_58', DB::getReplicaConfigKey(58));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using reflection, set DB::configs and DB::connections with a fake a replica connection
|
||||||
|
* that points to the same connection as the primary connection.
|
||||||
|
*/
|
||||||
|
private function setupConfigsAndConnections($includeReplica = true): void
|
||||||
|
{
|
||||||
|
$reflector = new ReflectionClass(DB::class);
|
||||||
|
$primaryConfig = DB::getConfig(DB::CONN_PRIMARY);
|
||||||
|
$configs = [DB::CONN_PRIMARY => $primaryConfig];
|
||||||
|
if ($includeReplica) {
|
||||||
|
$configs['replica_01'] = $primaryConfig;
|
||||||
|
}
|
||||||
|
$reflector->setStaticPropertyValue('configs', $configs);
|
||||||
|
// Create connections
|
||||||
|
$primaryConnection = DB::get_conn(DB::CONN_PRIMARY);
|
||||||
|
$connections = [DB::CONN_PRIMARY => $primaryConnection];
|
||||||
|
if ($includeReplica) {
|
||||||
|
$connections['replica_01'] = $primaryConnection;
|
||||||
|
}
|
||||||
|
$reflector->setStaticPropertyValue('connections', $connections);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last connection name used by the DB class. This shows if a replica was used.
|
||||||
|
*/
|
||||||
|
private function getLastConnectionName(): string
|
||||||
|
{
|
||||||
|
return (new ReflectionClass(DB::class))->getStaticPropertyValue('lastConnectionName');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ class PasswordEncryptorTest extends SapphireTest
|
|||||||
'encryptors',
|
'encryptors',
|
||||||
['test_sha1legacy' => [PasswordEncryptor_LegacyPHPHash::class => 'sha1']]
|
['test_sha1legacy' => [PasswordEncryptor_LegacyPHPHash::class => 'sha1']]
|
||||||
);
|
);
|
||||||
$e = Deprecation::withNoReplacement(fn() => PasswordEncryptor::create_for_algorithm('test_sha1legacy'));
|
$e = Deprecation::withSuppressedNotice(fn() => PasswordEncryptor::create_for_algorithm('test_sha1legacy'));
|
||||||
// precomputed hashes for 'mypassword' from different architectures
|
// precomputed hashes for 'mypassword' from different architectures
|
||||||
$amdHash = 'h1fj0a6m4o6k0sosks88oo08ko4gc4s';
|
$amdHash = 'h1fj0a6m4o6k0sosks88oo08ko4gc4s';
|
||||||
$intelHash = 'h1fj0a6m4o0g04ocg00o4kwoc4wowws';
|
$intelHash = 'h1fj0a6m4o0g04ocg00o4kwoc4wowws';
|
||||||
|
@ -110,7 +110,7 @@ class RememberLoginHashTest extends SapphireTest
|
|||||||
|
|
||||||
$member = $this->objFromFixture(Member::class, 'main');
|
$member = $this->objFromFixture(Member::class, 'main');
|
||||||
|
|
||||||
Deprecation::withNoReplacement(
|
Deprecation::withSuppressedNotice(
|
||||||
fn() => RememberLoginHash::config()->set('replace_token_during_session_renewal', $replaceToken)
|
fn() => RememberLoginHash::config()->set('replace_token_during_session_renewal', $replaceToken)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ class RememberLoginHashTest extends SapphireTest
|
|||||||
// Fetch the token from the DB - otherwise we still have the token from when this was originally created
|
// Fetch the token from the DB - otherwise we still have the token from when this was originally created
|
||||||
$storedHash = RememberLoginHash::get()->find('ID', $hash->ID);
|
$storedHash = RememberLoginHash::get()->find('ID', $hash->ID);
|
||||||
|
|
||||||
Deprecation::withNoReplacement(fn() => $storedHash->renew());
|
Deprecation::withSuppressedNotice(fn() => $storedHash->renew());
|
||||||
|
|
||||||
if ($replaceToken) {
|
if ($replaceToken) {
|
||||||
$this->assertNotEquals($oldToken, $storedHash->getToken());
|
$this->assertNotEquals($oldToken, $storedHash->getToken());
|
||||||
|
Loading…
Reference in New Issue
Block a user