diff --git a/cli-script.php b/cli-script.php index 879b2de65..c21778710 100755 --- a/cli-script.php +++ b/cli-script.php @@ -6,7 +6,6 @@ use SilverStripe\Control\HTTPApplication; use SilverStripe\Core\CoreKernel; use SilverStripe\ORM\DB; use SilverStripe\ORM\Connect\NullDatabase; -use SilverStripe\Core\DatabaselessKernel; require __DIR__ . '/src/includes/autoload.php'; @@ -25,9 +24,10 @@ if ($skipDatabase) { DB::set_conn(new NullDatabase()); } // Default application -$kernel = $skipDatabase - ? new DatabaselessKernel(BASE_PATH) - : new CoreKernel(BASE_PATH); +$kernel = new CoreKernel(BASE_PATH); +if ($skipDatabase) { + $kernel->setBootDatabase(false); +} $app = new HTTPApplication($kernel); $response = $app->handle($request); diff --git a/src/Control/CliController.php b/src/Control/CliController.php index 2377ebab7..30d66250b 100644 --- a/src/Control/CliController.php +++ b/src/Control/CliController.php @@ -4,6 +4,7 @@ namespace SilverStripe\Control; use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Injector\Injector; +use SilverStripe\Dev\Deprecation; use SilverStripe\Security\Permission; use SilverStripe\Security\Security; @@ -13,9 +14,16 @@ use SilverStripe\Security\Security; * call to {@link process()} on every sub-subclass. For instance, calling * "sake DailyTask" from the commandline will call {@link process()} on every subclass * of DailyTask. + * + * @deprecated 5.4.0 Will be replaced with symfony/console commands */ abstract class CliController extends Controller { + public function __construct() + { + parent::__construct(); + Deprecation::notice('5.4.0', 'Will be replaced with symfony/console commands', Deprecation::SCOPE_CLASS); + } private static $allowed_actions = [ 'index' diff --git a/src/Control/Middleware/ConfirmationMiddleware/CliBypass.php b/src/Control/Middleware/ConfirmationMiddleware/CliBypass.php index f834cfb4e..1a16be0c6 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/CliBypass.php +++ b/src/Control/Middleware/ConfirmationMiddleware/CliBypass.php @@ -5,12 +5,26 @@ namespace SilverStripe\Control\Middleware\ConfirmationMiddleware; use SilverStripe\Control\Director; use SilverStripe\Control\HTTPRequest; use SilverStripe\Core\Kernel; +use SilverStripe\Dev\Deprecation; /** * Allows a bypass when the request has been run in CLI mode + * + * @deprecated 5.4.0 Will be removed without equivalent functionality to replace it */ class CliBypass implements Bypass { + public function __construct() + { + Deprecation::withNoReplacement(function () { + Deprecation::notice( + '5.4.0', + 'Will be removed without equivalent functionality to replace it', + Deprecation::SCOPE_CLASS + ); + }); + } + /** * Returns true if the current process is running in CLI mode * diff --git a/src/Core/BaseKernel.php b/src/Core/BaseKernel.php index ee6889602..a1dbc503b 100644 --- a/src/Core/BaseKernel.php +++ b/src/Core/BaseKernel.php @@ -357,6 +357,14 @@ abstract class BaseKernel implements Kernel $this->booted = $bool; } + /** + * Check whether the kernel has booted or not + */ + public function getBooted(): bool + { + return $this->booted; + } + public function shutdown() { } diff --git a/src/Core/CoreKernel.php b/src/Core/CoreKernel.php index 7f968c8a3..acf43ddfc 100644 --- a/src/Core/CoreKernel.php +++ b/src/Core/CoreKernel.php @@ -8,18 +8,29 @@ use SilverStripe\ORM\DB; use Exception; use LogicException; use SilverStripe\Dev\Deprecation; +use SilverStripe\ORM\Connect\NullDatabase; /** * Simple Kernel container */ class CoreKernel extends BaseKernel { + protected bool $bootDatabase = true; /** * Indicates whether the Kernel has been flushed on boot */ private ?bool $flush = null; + /** + * Set whether the database should boot or not. + */ + public function setBootDatabase(bool $bool): static + { + $this->bootDatabase = $bool; + return $this; + } + /** * @param false $flush * @throws HTTPResponse_Exception @@ -29,6 +40,10 @@ class CoreKernel extends BaseKernel { $this->flush = $flush; + if (!$this->bootDatabase) { + DB::set_conn(new NullDatabase()); + } + $this->bootPHP(); $this->bootManifests($flush); $this->bootErrorHandling(); @@ -47,6 +62,9 @@ class CoreKernel extends BaseKernel */ protected function validateDatabase() { + if (!$this->bootDatabase) { + return; + } $databaseConfig = DB::getConfig(); // Gracefully fail if no DB is configured if (empty($databaseConfig['database'])) { @@ -62,6 +80,9 @@ class CoreKernel extends BaseKernel */ protected function bootDatabaseGlobals() { + if (!$this->bootDatabase) { + return; + } // Now that configs have been loaded, we can check global for database config global $databaseConfig; global $database; @@ -94,6 +115,9 @@ class CoreKernel extends BaseKernel */ protected function bootDatabaseEnvVars() { + if (!$this->bootDatabase) { + return; + } // Set default database config $databaseConfig = $this->getDatabaseConfig(); $databaseConfig['database'] = $this->getDatabaseName(); diff --git a/src/Core/DatabaselessKernel.php b/src/Core/DatabaselessKernel.php index c21ec3681..c3c809dc3 100644 --- a/src/Core/DatabaselessKernel.php +++ b/src/Core/DatabaselessKernel.php @@ -3,6 +3,7 @@ namespace SilverStripe\Core; use Exception; +use SilverStripe\Dev\Deprecation; /** * Boot a kernel without requiring a database connection. @@ -11,6 +12,7 @@ use Exception; * around the availability of a database for every execution path. * * @internal + * @deprecated 5.4.0 Use SilverStripe\Core\CoreKernel::setBootDatabase() instead */ class DatabaselessKernel extends BaseKernel { @@ -29,6 +31,16 @@ class DatabaselessKernel extends BaseKernel */ protected $bootErrorHandling = true; + public function __construct($basePath) + { + parent::__construct($basePath); + Deprecation::notice( + '5.4.0', + 'Use ' . CoreKernel::class . '::setBootDatabase() instead', + Deprecation::SCOPE_CLASS + ); + } + public function setBootErrorHandling(bool $bool) { $this->bootErrorHandling = $bool; diff --git a/src/Dev/BuildTask.php b/src/Dev/BuildTask.php index 9b2659c53..5497cc3f0 100644 --- a/src/Dev/BuildTask.php +++ b/src/Dev/BuildTask.php @@ -30,6 +30,7 @@ abstract class BuildTask * * @config * @var string + * @deprecated 5.4.0 Will be replaced with $commandName */ private static $segment = null; @@ -55,6 +56,7 @@ abstract class BuildTask /** * @var string $description Describe the implications the task has, * and the changes it makes. Accepts HTML formatting. + * @deprecated 5.4.0 Will be replaced with a static property with the same name */ protected $description = 'No description available'; @@ -90,9 +92,13 @@ abstract class BuildTask /** * @return string HTML formatted description + * @deprecated 5.4.0 Will be replaced with a static method with the same name */ public function getDescription() { + Deprecation::withNoReplacement( + fn() => Deprecation::notice('5.4.0', 'Will be replaced with a static method with the same name') + ); return $this->description; } } diff --git a/src/Dev/DevBuildController.php b/src/Dev/DevBuildController.php index 6dc791d42..155929686 100644 --- a/src/Dev/DevBuildController.php +++ b/src/Dev/DevBuildController.php @@ -11,6 +11,9 @@ use SilverStripe\Security\Permission; use SilverStripe\Security\PermissionProvider; use SilverStripe\Security\Security; +/** + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Command\DbBuild + */ class DevBuildController extends Controller implements PermissionProvider { @@ -28,6 +31,18 @@ class DevBuildController extends Controller implements PermissionProvider 'CAN_DEV_BUILD', ]; + public function __construct() + { + parent::__construct(); + Deprecation::withNoReplacement(function () { + Deprecation::notice( + '5.4.0', + 'Will be replaced with SilverStripe\Dev\Command\DbBuild', + Deprecation::SCOPE_CLASS + ); + }); + } + protected function init(): void { parent::init(); @@ -68,7 +83,7 @@ class DevBuildController extends Controller implements PermissionProvider || Permission::check(static::config()->get('init_permissions')) ); } - + public function providePermissions(): array { return [ diff --git a/src/Dev/DevConfigController.php b/src/Dev/DevConfigController.php index 03c532810..056f0ee04 100644 --- a/src/Dev/DevConfigController.php +++ b/src/Dev/DevConfigController.php @@ -15,6 +15,8 @@ use Symfony\Component\Yaml\Yaml; /** * Outputs the full configuration. + * + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Command\ConfigDump */ class DevConfigController extends Controller implements PermissionProvider { @@ -41,6 +43,19 @@ class DevConfigController extends Controller implements PermissionProvider 'CAN_DEV_CONFIG', ]; + + public function __construct() + { + parent::__construct(); + Deprecation::withNoReplacement(function () { + Deprecation::notice( + '5.4.0', + 'Will be replaced with SilverStripe\Dev\Command\ConfigDump', + Deprecation::SCOPE_CLASS + ); + }); + } + protected function init(): void { parent::init(); @@ -157,7 +172,7 @@ class DevConfigController extends Controller implements PermissionProvider || Permission::check(static::config()->get('init_permissions')) ); } - + public function providePermissions(): array { return [ diff --git a/src/Dev/DevelopmentAdmin.php b/src/Dev/DevelopmentAdmin.php index 752ae82dd..ccf279c30 100644 --- a/src/Dev/DevelopmentAdmin.php +++ b/src/Dev/DevelopmentAdmin.php @@ -54,6 +54,7 @@ class DevelopmentAdmin extends Controller implements PermissionProvider * ] * * @var array + * @deprecated 5.4.0 Will be replaced with "controllers" and "commands" configuration properties */ private static $registered_controllers = []; @@ -82,7 +83,7 @@ class DevelopmentAdmin extends Controller implements PermissionProvider if (static::config()->get('deny_non_cli') && !Director::is_cli()) { return $this->httpError(404); } - + if (!$this->canViewAll() && empty($this->getLinks())) { Security::permissionFailure($this); return; @@ -201,8 +202,12 @@ class DevelopmentAdmin extends Controller implements PermissionProvider return $links; } + /** + * @deprecated 5.4.0 Will be removed without equivalent functionality to replace it + */ protected function getRegisteredController($baseUrlPart) { + Deprecation::notice('5.4.0', 'Will be removed without equivalent functionality to replace it'); $reg = Config::inst()->get(static::class, 'registered_controllers'); if (isset($reg[$baseUrlPart])) { @@ -223,9 +228,18 @@ class DevelopmentAdmin extends Controller implements PermissionProvider * DataObject classes * Should match the $url_handlers rule: * 'build/defaults' => 'buildDefaults', + * + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Commands\DbDefaults */ public function buildDefaults() { + Deprecation::withNoReplacement(function () { + Deprecation::notice( + '5.4.0', + 'Will be replaced with SilverStripe\Dev\Command\DbDefaults' + ); + }); + $da = DatabaseAdmin::create(); $renderer = null; @@ -247,9 +261,18 @@ class DevelopmentAdmin extends Controller implements PermissionProvider /** * Generate a secure token which can be used as a crypto key. * Returns the token and suggests PHP configuration to set it. + * + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Commands\GenerateSecureToken */ public function generatesecuretoken() { + Deprecation::withNoReplacement(function () { + Deprecation::notice( + '5.4.0', + 'Will be replaced with SilverStripe\Dev\Command\GenerateSecureToken' + ); + }); + $generator = Injector::inst()->create('SilverStripe\\Security\\RandomGenerator'); $token = $generator->randomToken('sha1'); $body = <<isCli() ? Deprecation::shouldShowForCli() : Deprecation::shouldShowForHttp()); + || (Director::is_cli() ? Deprecation::shouldShowForCli() : Deprecation::shouldShowForHttp()); } /** @@ -185,10 +199,12 @@ class HTTPOutputHandler extends AbstractProcessingHandler } /** - * This method is required and must be protected for unit testing, since we can't mock static or private methods + * This method used to be used for unit testing but is no longer required. + * @deprecated 5.4.0 Use SilverStripe\Control\Director::is_cli() instead */ protected function isCli(): bool { + Deprecation::notice('5.4.0', 'Use ' . Director::class . '::is_cli() instead'); return Director::is_cli(); } } diff --git a/src/ORM/ArrayLib.php b/src/ORM/ArrayLib.php index d3c4815e6..de2f610a6 100644 --- a/src/ORM/ArrayLib.php +++ b/src/ORM/ArrayLib.php @@ -4,6 +4,7 @@ namespace SilverStripe\ORM; use Generator; use SilverStripe\Dev\Deprecation; +use InvalidArgumentException; /** * Library of static methods for manipulating arrays. @@ -354,4 +355,65 @@ class ArrayLib $array = $shuffledArray; } + + /** + * Insert a value into an array before another given value. + * Does not preserve keys. + * + * @param mixed $before The value to check for. If this value isn't in the source array, $insert will be put at the end. + * @param boolean $strict If true then this will perform a strict type comparison to look for the $before value in the source array. + * @param boolean $splatInsertArray If true, $insert must be an array. + * Its values will be splatted into the source array. + */ + public static function insertBefore(array $array, mixed $insert, mixed $before, bool $strict = false, bool $splatInsertArray = false): array + { + if ($splatInsertArray && !is_array($insert)) { + throw new InvalidArgumentException('$insert must be an array when $splatInsertArray is true. Got ' . gettype($insert)); + } + $array = array_values($array); + $pos = array_search($before, $array, $strict); + if ($pos === false) { + return static::insertIntoArray($array, $insert, $splatInsertArray); + } + return static::insertAtPosition($array, $insert, $pos, $splatInsertArray); + } + + /** + * Insert a value into an array after another given value. + * Does not preserve keys. + * + * @param mixed $after The value to check for. If this value isn't in the source array, $insert will be put at the end. + * @param boolean $strict If true then this will perform a strict type comparison to look for the $before value in the source array. + * @param boolean $splatInsertArray If true, $insert must be an array. + * Its values will be splatted into the source array. + */ + public static function insertAfter(array $array, mixed $insert, mixed $after, bool $strict = false, bool $splatInsertArray = false): array + { + if ($splatInsertArray && !is_array($insert)) { + throw new InvalidArgumentException('$insert must be an array when $splatInsertArray is true. Got ' . gettype($insert)); + } + $array = array_values($array); + $pos = array_search($after, $array, $strict); + if ($pos === false) { + return static::insertIntoArray($array, $insert, $splatInsertArray); + } + return static::insertAtPosition($array, $insert, $pos + 1, $splatInsertArray); + } + + private static function insertAtPosition(array $array, mixed $insert, int $pos, bool $splatInsertArray): array + { + $result = array_slice($array, 0, $pos); + $result = static::insertIntoArray($result, $insert, $splatInsertArray); + return array_merge($result, array_slice($array, $pos)); + } + + private static function insertIntoArray(array $array, mixed $insert, bool $splatInsertArray): array + { + if ($splatInsertArray) { + $array = array_merge($array, $insert); + } else { + $array[] = $insert; + } + return $array; + } } diff --git a/src/ORM/DatabaseAdmin.php b/src/ORM/DatabaseAdmin.php index 5b222599b..29f56474a 100644 --- a/src/ORM/DatabaseAdmin.php +++ b/src/ORM/DatabaseAdmin.php @@ -10,6 +10,7 @@ use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Environment; use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Manifest\ClassLoader; +use SilverStripe\Dev\Deprecation; use SilverStripe\Dev\DevBuildController; use SilverStripe\Dev\DevelopmentAdmin; use SilverStripe\ORM\Connect\DatabaseException; @@ -25,6 +26,8 @@ use SilverStripe\Versioned\Versioned; * * Utility functions for administrating the database. These can be accessed * via URL, e.g. http://www.yourdomain.com/db/build. + * + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Command\DbBuild */ class DatabaseAdmin extends Controller { @@ -39,6 +42,7 @@ class DatabaseAdmin extends Controller /** * Obsolete classname values that should be remapped in dev/build + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Command\DbBuild.classname_value_remapping */ private static $classname_value_remapping = [ 'File' => 'SilverStripe\\Assets\\File', @@ -56,9 +60,22 @@ class DatabaseAdmin extends Controller /** * Config setting to enabled/disable the display of record counts on the dev/build output + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Command\DbBuild.show_record_counts */ private static $show_record_counts = true; + public function __construct() + { + parent::__construct(); + Deprecation::withNoReplacement(function () { + Deprecation::notice( + '5.4.0', + 'Will be replaced with SilverStripe\Dev\Command\DbBuild', + Deprecation::SCOPE_CLASS + ); + }); + } + protected function init() { parent::init(); @@ -191,9 +208,18 @@ class DatabaseAdmin extends Controller * * @return string Returns the timestamp of the time that the database was * last built + * + * @deprecated 5.4.0 Will be replaced with SilverStripe\Dev\Command\DbBuild::lastBuilt() */ public static function lastBuilt() { + Deprecation::withNoReplacement(function () { + Deprecation::notice( + '5.4.0', + 'Will be replaced with SilverStripe\Dev\Command\DbBuild::lastBuilt()' + ); + }); + $file = TEMP_PATH . DIRECTORY_SEPARATOR . 'database-last-generated-' diff --git a/src/ORM/FieldType/DBDatetime.php b/src/ORM/FieldType/DBDatetime.php index 877c707f2..cbc3b8e86 100644 --- a/src/ORM/FieldType/DBDatetime.php +++ b/src/ORM/FieldType/DBDatetime.php @@ -2,6 +2,7 @@ namespace SilverStripe\ORM\FieldType; +use DateTime; use Exception; use IntlDateFormatter; use InvalidArgumentException; @@ -195,6 +196,69 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider return $field; } + /** + * Get the amount of time inbetween two datetimes. + */ + public static function getTimeBetween(DBDateTime $from, DBDateTime $to): string + { + $fromRaw = new DateTime(); + $fromRaw->setTimestamp((int) $from->getTimestamp()); + $toRaw = new DateTime(); + $toRaw->setTimestamp((int) $to->getTimestamp()); + $diff = $fromRaw->diff($toRaw); + $result = []; + if ($diff->y) { + $result[] = _t( + __CLASS__ . '.nYears', + 'one year|{count} years', + ['count' => $diff->y] + ); + } + if ($diff->m) { + $result[] = _t( + __CLASS__ . '.nMonths', + 'one month|{count} months', + ['count' => $diff->m] + ); + } + if ($diff->d) { + $result[] = _t( + __CLASS__ . '.nDays', + 'one day|{count} days', + ['count' => $diff->d] + ); + } + if ($diff->h) { + $result[] = _t( + __CLASS__ . '.nHours', + 'one hour|{count} hours', + ['count' => $diff->h] + ); + } + if ($diff->i) { + $result[] = _t( + __CLASS__ . '.nMinutes', + 'one minute|{count} minutes', + ['count' => $diff->i] + ); + } + if ($diff->s) { + $result[] = _t( + __CLASS__ . '.nSeconds', + 'one second|{count} seconds', + ['count' => $diff->s] + ); + } + if (empty($result)) { + return _t( + __CLASS__ . '.nSeconds', + '{count} seconds', + ['count' => 0] + ); + } + return implode(', ', $result); + } + /** * */ diff --git a/tests/php/Logging/HTTPOutputHandlerTest.php b/tests/php/Logging/HTTPOutputHandlerTest.php index 2f21f118f..065d0794c 100644 --- a/tests/php/Logging/HTTPOutputHandlerTest.php +++ b/tests/php/Logging/HTTPOutputHandlerTest.php @@ -6,6 +6,7 @@ use Monolog\Handler\HandlerInterface; use ReflectionClass; use ReflectionMethod; use SilverStripe\Control\Director; +use SilverStripe\Core\Environment; use SilverStripe\Core\Injector\Injector; use SilverStripe\Dev\Deprecation; use SilverStripe\Dev\SapphireTest; @@ -172,14 +173,20 @@ class HTTPOutputHandlerTest extends SapphireTest } $reflectionDeprecation->setStaticPropertyValue('isTriggeringError', $triggeringError); - $mockHandler = $this->getMockBuilder(HTTPOutputHandler::class)->onlyMethods(['isCli'])->getMock(); - $mockHandler->method('isCli')->willReturn($isCli); + $reflectionDirector = new ReflectionClass(Environment::class); + $origIsCli = $reflectionDirector->getStaticPropertyValue('isCliOverride'); + $reflectionDirector->setStaticPropertyValue('isCliOverride', $isCli); - $result = $reflectionShouldShow->invoke($mockHandler, $errorCode); - $this->assertSame($expected, $result); + try { + $handler = new HTTPOutputHandler(); + $result = $reflectionShouldShow->invoke($handler, $errorCode); + $this->assertSame($expected, $result); - Deprecation::setShouldShowForCli($cliShouldShowOrig); - Deprecation::setShouldShowForHttp($httpShouldShowOrig); - $reflectionDeprecation->setStaticPropertyValue('isTriggeringError', $triggeringErrorOrig); + Deprecation::setShouldShowForCli($cliShouldShowOrig); + Deprecation::setShouldShowForHttp($httpShouldShowOrig); + $reflectionDeprecation->setStaticPropertyValue('isTriggeringError', $triggeringErrorOrig); + } finally { + $reflectionDirector->setStaticPropertyValue('isCliOverride', $origIsCli); + } } } diff --git a/tests/php/ORM/ArrayLibTest.php b/tests/php/ORM/ArrayLibTest.php index d22e871e7..39615f316 100644 --- a/tests/php/ORM/ArrayLibTest.php +++ b/tests/php/ORM/ArrayLibTest.php @@ -368,4 +368,176 @@ class ArrayLibTest extends SapphireTest } } } + + public function provideInsertBefore(): array + { + return [ + 'simple insertion' => [ + 'insert' => 'new', + 'before' => 'def', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'new', 'def', '0', null, true, 0, 'last'] + ], + 'insert before first' => [ + 'insert' => 'new', + 'before' => 'abc', + 'strict' => true, + 'splat' => false, + 'expected' => ['new', 'abc', '', [1,2,3], 'def', '0', null, true, 0, 'last'] + ], + 'insert before last' => [ + 'insert' => 'new', + 'before' => 'last', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'new', 'last'] + ], + 'insert before missing' => [ + 'insert' => 'new', + 'before' => 'this value isnt there', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'last', 'new'] + ], + 'strict' => [ + 'insert' => 'new', + 'before' => 0, + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 'new', 0, 'last'] + ], + 'not strict' => [ + 'insert' => 'new', + 'before' => 0, + 'strict' => false, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', 'new', '0', null, true, 0, 'last'] + ], + 'before array' => [ + 'insert' => 'new', + 'before' => [1,2,3], + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', 'new', [1,2,3], 'def', '0', null, true, 0, 'last'] + ], + 'before missing array' => [ + 'insert' => 'new', + 'before' => ['a', 'b', 'c'], + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'last', 'new'] + ], + 'splat array' => [ + 'insert' => ['a', 'b', 'c'], + 'before' => 'def', + 'strict' => true, + 'splat' => true, + 'expected' => ['abc', '', [1,2,3], 'a', 'b', 'c', 'def', '0', null, true, 0, 'last'] + ], + 'no splat array' => [ + 'insert' => ['a', 'b', 'c'], + 'before' => 'def', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], ['a', 'b', 'c'], 'def', '0', null, true, 0, 'last'] + ], + ]; + } + + /** + * @dataProvider provideInsertBefore + */ + public function testInsertBefore(mixed $insert, mixed $before, bool $strict, bool $splat, array $expected): void + { + $array = ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'last']; + $final = ArrayLib::insertBefore($array, $insert, $before, $strict, $splat); + $this->assertSame($expected, $final); + } + + public function provideInsertAfter(): array + { + return [ + 'simple insertion' => [ + 'insert' => 'new', + 'before' => 'def', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', 'new', '0', null, true, 0, 'last'] + ], + 'insert after first' => [ + 'insert' => 'new', + 'before' => 'abc', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', 'new', '', [1,2,3], 'def', '0', null, true, 0, 'last'] + ], + 'insert after last' => [ + 'insert' => 'new', + 'before' => 'last', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'last', 'new'] + ], + 'insert after missing' => [ + 'insert' => 'new', + 'before' => 'this value isnt there', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'last', 'new'] + ], + 'strict' => [ + 'insert' => 'new', + 'before' => 0, + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'new', 'last'] + ], + 'not strict' => [ + 'insert' => 'new', + 'before' => 0, + 'strict' => false, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', 'new', null, true, 0, 'last'] + ], + 'after array' => [ + 'insert' => 'new', + 'before' => [1,2,3], + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'new', 'def', '0', null, true, 0, 'last'] + ], + 'after missing array' => [ + 'insert' => 'new', + 'before' => ['a', 'b', 'c'], + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'last', 'new'] + ], + 'splat array' => [ + 'insert' => ['a', 'b', 'c'], + 'before' => 'def', + 'strict' => true, + 'splat' => true, + 'expected' => ['abc', '', [1,2,3], 'def', 'a', 'b', 'c', '0', null, true, 0, 'last'] + ], + 'no splat array' => [ + 'insert' => ['a', 'b', 'c'], + 'before' => 'def', + 'strict' => true, + 'splat' => false, + 'expected' => ['abc', '', [1,2,3], 'def', ['a', 'b', 'c'], '0', null, true, 0, 'last'] + ], + ]; + } + + /** + * @dataProvider provideInsertAfter + */ + public function testInsertAfter(mixed $insert, mixed $after, bool $strict, bool $splat, array $expected): void + { + $array = ['abc', '', [1,2,3], 'def', '0', null, true, 0, 'last']; + $final = ArrayLib::insertAfter($array, $insert, $after, $strict, $splat); + $this->assertSame($expected, $final); + } } diff --git a/tests/php/ORM/DBDatetimeTest.php b/tests/php/ORM/DBDatetimeTest.php index 71b24be22..9196016d8 100644 --- a/tests/php/ORM/DBDatetimeTest.php +++ b/tests/php/ORM/DBDatetimeTest.php @@ -302,4 +302,50 @@ class DBDatetimeTest extends SapphireTest ['-59 seconds', '2019-03-03 11:59:01'], ]; } + + public function provideGetTimeBetween(): array + { + return [ + 'no time between' => [ + 'timeBefore' => '2019-03-03 12:00:00', + 'timeAfter' => '2019-03-03 12:00:00', + 'expected' => '0 seconds', + ], + 'one second between' => [ + 'timeBefore' => '2019-03-03 12:00:00', + 'timeAfter' => '2019-03-03 12:00:01', + 'expected' => 'one second', + ], + 'some seconds between' => [ + 'timeBefore' => '2019-03-03 12:00:00', + 'timeAfter' => '2019-03-03 12:00:15', + 'expected' => '15 seconds', + ], + 'days and minutes between' => [ + 'timeBefore' => '2019-03-03 12:00:00', + 'timeAfter' => '2019-03-15 12:05:00', + 'expected' => '12 days, 5 minutes', + ], + 'years, months, and hours between' => [ + 'timeBefore' => '2019-03-03 12:00:00', + 'timeAfter' => '2028-01-03 17:00:00', + 'expected' => '8 years, 10 months, 5 hours', + ], + 'backwards in time doesnt say "negative" or "-"' => [ + 'timeBefore' => '2019-03-03 12:00:00', + 'timeAfter' => '2018-01-06 12:01:12', + 'expected' => 'one year, one month, 27 days, 23 hours, 58 minutes, 48 seconds', + ], + ]; + } + + /** + * @dataProvider provideGetTimeBetween + */ + public function testGetTimeBetween(string $timeBefore, string $timeAfter, string $expected): void + { + $before = (new DBDateTime())->setValue($timeBefore); + $after = (new DBDateTime())->setValue($timeAfter); + $this->assertSame($expected, DBDatetime::getTimeBetween($before, $after)); + } }