diff --git a/application/Espo/Binding.php b/application/Espo/Binding.php index 328f55f720..f1ffcf18c6 100644 --- a/application/Espo/Binding.php +++ b/application/Espo/Binding.php @@ -53,6 +53,11 @@ class Binding implements BindingProcessor private function bindServices(Binder $binder): void { + $binder->bindService( + 'Espo\\Core\\Application\\ApplicationParams', + 'applicationParams' + ); + $binder->bindService( 'Espo\\Core\\InjectableFactory', 'injectableFactory' diff --git a/application/Espo/Core/Application.php b/application/Espo/Core/Application.php index d006ae07a7..cbc79185a9 100644 --- a/application/Espo/Core/Application.php +++ b/application/Espo/Core/Application.php @@ -29,6 +29,7 @@ namespace Espo\Core; +use Espo\Core\Application\ApplicationParams; use Espo\Core\Application\Runner; use Espo\Core\Application\RunnerParameterized; use Espo\Core\Container\ContainerBuilder; @@ -39,6 +40,7 @@ use Espo\Core\Utils\Autoload; use Espo\Core\Utils\Config; use Espo\Core\Utils\Metadata; use Espo\Core\Utils\ClientManager; +use RuntimeException; /** * A central access point of the application. @@ -47,19 +49,25 @@ class Application { protected Container $container; - public function __construct() - { + public function __construct( + ?ApplicationParams $params = null, + ) { date_default_timezone_set('UTC'); - $this->initContainer(); + $this->initContainer($params); $this->initAutoloads(); $this->initPreloads(); } - protected function initContainer(): void + protected function initContainer(?ApplicationParams $params): void { - /** @var Container $container */ - $container = (new ContainerBuilder())->build(); + $container = (new ContainerBuilder()) + ->withParams($params) + ->build(); + + if (!$container instanceof Container) { + throw new RuntimeException(); + } $this->container = $container; } diff --git a/application/Espo/Core/Application/ApplicationParams.php b/application/Espo/Core/Application/ApplicationParams.php new file mode 100644 index 0000000000..e1e92eea1a --- /dev/null +++ b/application/Espo/Core/Application/ApplicationParams.php @@ -0,0 +1,40 @@ +. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +namespace Espo\Core\Application; + +/** + * @since 9.2.0 + */ +readonly class ApplicationParams +{ + public function __construct( + public bool $noErrorHandler = false, + ) {} +} diff --git a/application/Espo/Core/Container/ContainerBuilder.php b/application/Espo/Core/Container/ContainerBuilder.php index b55d93acbe..233d4f992d 100644 --- a/application/Espo/Core/Container/ContainerBuilder.php +++ b/application/Espo/Core/Container/ContainerBuilder.php @@ -29,6 +29,7 @@ namespace Espo\Core\Container; +use Espo\Core\Application\ApplicationParams; use Espo\Core\Container; use Espo\Core\Container\Container as ContainerInterface; @@ -74,6 +75,14 @@ class ContainerBuilder 'metadata' => MetadataLoader::class, 'applicationState' => ApplicationStateLoader::class, ]; + private ?ApplicationParams $params = null; + + public function withParams(?ApplicationParams $params): self + { + $this->params = $params; + + return $this; + } public function withBindingLoader(BindingLoader $bindingLoader): self { @@ -161,6 +170,8 @@ class ContainerBuilder public function build(): ContainerInterface { + $this->services['applicationParams'] = $this->params ?? new ApplicationParams(); + /** @var Config $config */ $config = $this->services['config'] ?? ( new $this->configClassName( diff --git a/application/Espo/Core/Log/LogLoader.php b/application/Espo/Core/Log/LogLoader.php index f1d9ac3e45..8b89bc0aba 100644 --- a/application/Espo/Core/Log/LogLoader.php +++ b/application/Espo/Core/Log/LogLoader.php @@ -29,6 +29,7 @@ namespace Espo\Core\Log; +use Espo\Core\Application\ApplicationParams; use Espo\Core\ApplicationState; use Espo\Core\Log\Handler\DatabaseHandler; use Espo\Core\Log\Handler\EspoFileHandler; @@ -52,7 +53,8 @@ class LogLoader private readonly Config $config, private readonly HandlerListLoader $handlerListLoader, private readonly EntityManagerProxy $entityManagerProxy, - private readonly ApplicationState $applicationState + private readonly ApplicationState $applicationState, + private readonly ApplicationParams $applicationParams, ) {} public function load(): Log @@ -77,10 +79,12 @@ class LogLoader $log->pushHandler($handler); } - $errorHandler = new MonologErrorHandler($log); + if (!$this->applicationParams->noErrorHandler) { + $errorHandler = new MonologErrorHandler($log); - $errorHandler->registerExceptionHandler([], false); - $errorHandler->registerErrorHandler([], false); + $errorHandler->registerExceptionHandler([], false); + $errorHandler->registerErrorHandler([], false); + } return $log; } diff --git a/application/Espo/Core/Portal/Application.php b/application/Espo/Core/Portal/Application.php index 4054f8f5d8..cc761c1568 100644 --- a/application/Espo/Core/Portal/Application.php +++ b/application/Espo/Core/Portal/Application.php @@ -29,6 +29,7 @@ namespace Espo\Core\Portal; +use Espo\Core\Application\ApplicationParams; use Espo\Entities\Portal; use Espo\ORM\EntityManager; use Espo\Core\Exceptions\Forbidden; @@ -47,22 +48,25 @@ class Application extends BaseApplication * @throws NotFound * @noinspection PhpMissingParentConstructorInspection */ - public function __construct(?string $portalId) - { + public function __construct( + ?string $portalId, + ?ApplicationParams $params = null, + ) { date_default_timezone_set('UTC'); - $this->initContainer(); + $this->initContainer($params); $this->initPortal($portalId); $this->initAutoloads(); $this->initPreloads(); } - protected function initContainer(): void + protected function initContainer(?ApplicationParams $params): void { $container = (new ContainerBuilder()) ->withConfigClassName(Config::class) ->withContainerClassName(PortalContainer::class) ->withContainerConfigurationClassName(PortalContainerConfiguration::class) + ->withParams($params) ->build(); if (!$container instanceof PortalContainer) { diff --git a/install/core/Installer.php b/install/core/Installer.php index 7becb17241..0dd685ab53 100644 --- a/install/core/Installer.php +++ b/install/core/Installer.php @@ -84,8 +84,9 @@ class Installer 'theme', ]; - public function __construct() - { + public function __construct( + private Application\ApplicationParams $applicationParams = new Application\ApplicationParams(), + ) { $this->initialize(); require_once('install/core/InstallerConfig.php'); @@ -130,7 +131,7 @@ class Installer $config->get('defaultPermissions') ?? null ); - $injectableFactory = (new Application()) + $injectableFactory = (new Application($this->applicationParams)) ->getContainer() ->getByClass(InjectableFactory::class); @@ -150,7 +151,7 @@ class Installer $configWriter->save(); } - $this->app = new Application(); + $this->app = new Application($this->applicationParams); } private function getContainer(): Container diff --git a/tests/integration/Core/BaseTestCase.php b/tests/integration/Core/BaseTestCase.php index 31091704b2..84d45a9f90 100644 --- a/tests/integration/Core/BaseTestCase.php +++ b/tests/integration/Core/BaseTestCase.php @@ -62,6 +62,7 @@ abstract class BaseTestCase extends TestCase protected ?string $userName = null; /** Password used for authentication. */ protected ?string $password = null; + /** * @var ?array{ * entities?: array>, @@ -186,9 +187,6 @@ abstract class BaseTestCase extends TestCase $this->espoTester->terminate(); $this->espoTester = null; $this->espoApplication = null; - - //restore_error_handler(); - //restore_exception_handler(); } /** diff --git a/tests/integration/Core/Tester.php b/tests/integration/Core/Tester.php index a99c501ff2..dcfc2df8a9 100644 --- a/tests/integration/Core/Tester.php +++ b/tests/integration/Core/Tester.php @@ -53,6 +53,8 @@ use Espo\Entities\User; use Espo\ORM\Entity; use Espo\ORM\EntityManager; +use Installer; +use RuntimeException; use Slim\Psr7\Factory\RequestFactory; use Slim\Psr7\Response; @@ -194,12 +196,15 @@ class Tester $portalId = $portalId ?? $this->portalId ?? null; if (!isset($this->application) || $reload) { - if ($clearCache) { $this->clearCache(); } - $this->application = !$portalId ? new Application() : new PortalApplication($portalId); + $applicationParams = new Application\ApplicationParams(noErrorHandler: true); + + $this->application = !$portalId ? + new Application($applicationParams) : + new PortalApplication($portalId, $applicationParams); $auth = $this->application ->getContainer() @@ -309,7 +314,9 @@ class Tester require_once('install/core/Installer.php'); - $installer = new \Installer(); + $applicationParams = new Application\ApplicationParams(noErrorHandler: true); + + $installer = new Installer($applicationParams); $installer->saveData(array_merge($configData, [ 'language' => 'en_US' @@ -317,12 +324,12 @@ class Tester $installer->saveConfig($configData); - $app = new Application(); + $app = new Application($applicationParams); $this->createDatabase($app); $this->dropTables($app); - $installer = new \Installer(); // reload installer to have all config data + $installer = new Installer($applicationParams); // reload installer to have all config data $installer->rebuild(); $installer->setSuccess(); } @@ -341,7 +348,7 @@ class Tester $dbname = $params->getName(); if (!$dbname) { - throw new \RuntimeException('No "dbname" in database config.'); + throw new RuntimeException('No "dbname" in database config.'); } $params = $params->withName(null); diff --git a/tests/integration/Espo/User/CreateUserTest.php b/tests/integration/Espo/User/CreateUserTest.php index f7fdf76e00..8007643d8a 100644 --- a/tests/integration/Espo/User/CreateUserTest.php +++ b/tests/integration/Espo/User/CreateUserTest.php @@ -29,7 +29,9 @@ namespace tests\integration\Espo\User; -class CreateUserTest extends \tests\integration\Core\BaseTestCase +use tests\integration\Core\BaseTestCase; + +class CreateUserTest extends BaseTestCase { protected ?string $dataFile = 'User/Login.php';