This commit is contained in:
Sebastian Molenda
2026-05-12 21:10:38 +02:00
commit ab96d82fcf
2544 changed files with 721700 additions and 0 deletions
@@ -0,0 +1,197 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @since 4.2.0
* @license https://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Core\TestSuite;
use Cake\Core\Configure;
use Cake\Core\ConsoleApplicationInterface;
use Cake\Core\ContainerInterface;
use Cake\Core\HttpApplicationInterface;
use Cake\Event\EventInterface;
use Cake\Routing\Router;
use Closure;
use League\Container\Exception\NotFoundException;
use LogicException;
use PHPUnit\Framework\Attributes\After;
/**
* A set of methods used for defining container services
* in test cases.
*
* This trait leverages the `Application.buildContainer` event
* to inject the mocked services into the container that the
* application uses.
*/
trait ContainerStubTrait
{
/**
* The customized application class name.
*
* @phpstan-var class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface>|null
* @var string|null
*/
protected ?string $_appClass = null;
/**
* The customized application constructor arguments.
*
* @var array|null
*/
protected ?array $_appArgs = null;
/**
* The collection of container services.
*
* @var array<string, mixed>
*/
private array $containerServices = [];
/**
* Configure the application class to use in integration tests.
*
* @param string $class The application class name.
* @param array|null $constructorArgs The constructor arguments for your application class.
* @return void
* @phpstan-param class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface> $class
*/
public function configApplication(string $class, ?array $constructorArgs): void
{
$this->_appClass = $class;
$this->_appArgs = $constructorArgs;
}
/**
* Create an application instance.
*
* Uses the configuration set in `configApplication()`.
*
* @return \Cake\Core\HttpApplicationInterface|\Cake\Core\ConsoleApplicationInterface
*/
protected function createApp(): HttpApplicationInterface|ConsoleApplicationInterface
{
if (class_exists(Router::class)) {
Router::resetRoutes();
}
if ($this->_appClass) {
$appClass = $this->_appClass;
} else {
/** @var class-string<\Cake\Http\BaseApplication> $appClass */
$appClass = Configure::read('App.namespace') . '\Application';
}
if (!class_exists($appClass)) {
throw new LogicException(sprintf('Cannot load `%s` for use in integration testing.', $appClass));
}
$appArgs = $this->_appArgs ?: [CONFIG];
$app = new $appClass(...$appArgs);
if ($this->containerServices && method_exists($app, 'getEventManager')) {
$app->getEventManager()->on('Application.buildContainer', [$this, 'modifyContainer']);
}
foreach ($this->appPluginsToLoad as $pluginName => $config) {
if (is_array($config)) {
$app->addPlugin($pluginName, $config);
} else {
$app->addPlugin($config);
}
}
return $app;
}
/**
* Add a mocked service to the container.
*
* When the container is created the provided classname
* will be mapped to the factory function. The factory
* function will be used to create mocked services.
*
* @param string $class The class or interface you want to define.
* @param \Closure $factory The factory function for mocked services.
* @return $this
*/
public function mockService(string $class, Closure $factory)
{
$this->containerServices[$class] = $factory;
return $this;
}
/**
* Remove a mocked service to the container.
*
* @param string $class The class or interface you want to remove.
* @return $this
*/
public function removeMockService(string $class)
{
unset($this->containerServices[$class]);
return $this;
}
/**
* Wrap the application's container with one containing mocks.
*
* If any mocked services are defined, the application's container
* will be replaced with one containing mocks. The original
* container will be set as a delegate to the mock container.
*
* @param \Cake\Event\EventInterface $event The event
* @param \Cake\Core\ContainerInterface $container The container to wrap.
* @return void
*/
public function modifyContainer(EventInterface $event, ContainerInterface $container): void
{
if (!$this->containerServices) {
return;
}
foreach ($this->containerServices as $key => $factory) {
if ($container->has($key)) {
try {
$container->extend($key)->setConcrete($factory);
} catch (NotFoundException) {
$container->add($key, $factory);
}
} else {
$container->add($key, $factory);
}
}
$event->setResult($container);
}
/**
* Clears any mocks that were defined and cleans
* up application class configuration.
*
* @return void
*/
#[After]
public function cleanupContainer(): void
{
$this->_appArgs = null;
$this->_appClass = null;
$this->containerServices = [];
}
}
// phpcs:disable
class_alias(
'Cake\Core\TestSuite\ContainerStubTrait',
'Cake\TestSuite\ContainerStubTrait'
);
// phpcs:enable