Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/8.1' into 8.2
Browse files Browse the repository at this point in the history
  • Loading branch information
dlubitz committed Sep 15, 2023
2 parents d5c9215 + 52a536d commit 5c63048
Show file tree
Hide file tree
Showing 18 changed files with 181 additions and 30 deletions.
3 changes: 2 additions & 1 deletion Neos.Cache/Classes/Backend/PdoBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ protected function createCacheTables(): void
{
$this->connect();
try {
PdoHelper::importSql($this->databaseHandle, $this->pdoDriver, __DIR__ . '/../../Resources/Private/DDL.sql');
PdoHelper::importSql($this->databaseHandle, $this->pdoDriver, __DIR__ . '/../../Resources/Private/DDL.sql', ['###CACHE_TABLE_NAME###' => $this->cacheTableName, '###TAGS_TABLE_NAME###' => $this->tagsTableName]);
} catch (\PDOException $exception) {
throw new Exception('Could not create cache tables with DSN "' . $this->dataSourceName . '". PDO error: ' . $exception->getMessage(), 1259576985);
}
Expand Down Expand Up @@ -648,6 +648,7 @@ public function setup(): Result
$this->connect();
} catch (Exception $exception) {
$result->addError(new Error($exception->getMessage(), (int)$exception->getCode(), [], 'Failed'));
return $result;
}
if ($this->pdoDriver === 'sqlite') {
$result->addNotice(new Notice('SQLite database tables are created automatically and don\'t need to be set up'));
Expand Down
6 changes: 3 additions & 3 deletions Neos.Cache/Classes/Backend/TaggableMultiBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ protected function buildSubBackend(string $backendClassName, array $backendOptio
public function flushByTag(string $tag): int
{
$this->prepareBackends();
$count = 0;
$flushed = 0;
foreach ($this->backends as $backend) {
try {
$count |= $backend->flushByTag($tag);
$flushed += $backend->flushByTag($tag);
} catch (Throwable $throwable) {
$this->logger?->error('Failed flushing cache by tag using backend ' . get_class($backend) . ' in ' . get_class($this) . ': ' . $this->throwableStorage?->logThrowable($throwable), LogEnvironment::fromMethodName(__METHOD__));
$this->handleError($throwable);
$this->removeUnhealthyBackend($backend);
}
}
return $count;
return $flushed;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion Neos.Cache/Classes/Psr/Cache/CachePool.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class CachePool implements CacheItemPoolInterface
/**
* Pattern an entry identifier must match.
*/
const PATTERN_ENTRYIDENTIFIER = '/^[a-zA-Z0-9_%\-&]{1,250}$/';
const PATTERN_ENTRYIDENTIFIER = '/^[a-zA-Z0-9_%\-&\.]{1,250}$/';

/**
* @var BackendInterface
Expand Down
8 changes: 4 additions & 4 deletions Neos.Cache/Resources/Private/DDL.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BEGIN;

CREATE TABLE "cache" (
CREATE TABLE "###CACHE_TABLE_NAME###" (
"identifier" VARCHAR(250) NOT NULL,
"cache" VARCHAR(250) NOT NULL,
"context" VARCHAR(150) NOT NULL,
Expand All @@ -10,13 +10,13 @@ CREATE TABLE "cache" (
PRIMARY KEY ("identifier", "cache", "context")
);

CREATE TABLE "tags" (
CREATE TABLE "###TAGS_TABLE_NAME###" (
"identifier" VARCHAR(250) NOT NULL,
"cache" VARCHAR(250) NOT NULL,
"context" VARCHAR(150) NOT NULL,
"tag" VARCHAR(250) NOT NULL
);
CREATE INDEX "identifier" ON "tags" ("identifier", "cache", "context");
CREATE INDEX "tag" ON "tags" ("tag");
CREATE INDEX "identifier" ON "###TAGS_TABLE_NAME###" ("identifier", "cache", "context");
CREATE INDEX "tag" ON "###TAGS_TABLE_NAME###" ("tag");

COMMIT;
8 changes: 4 additions & 4 deletions Neos.Cache/Resources/Private/mysql.DDL.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BEGIN;

CREATE TABLE "cache" (
CREATE TABLE "###CACHE_TABLE_NAME###" (
"identifier" VARCHAR(250) NOT NULL,
"cache" VARCHAR(250) NOT NULL,
"context" VARCHAR(150) NOT NULL,
Expand All @@ -10,15 +10,15 @@ CREATE TABLE "cache" (
PRIMARY KEY ("identifier", "cache", "context")
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE "tags" (
CREATE TABLE "###TAGS_TABLE_NAME###" (
"pk" INT NOT NULL AUTO_INCREMENT,
"identifier" VARCHAR(250) NOT NULL,
"cache" VARCHAR(250) NOT NULL,
"context" VARCHAR(150) NOT NULL,
"tag" VARCHAR(250) NOT NULL,
PRIMARY KEY ("pk")
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE INDEX "identifier" ON tags ("identifier", "cache", "context");
CREATE INDEX "tag" ON "tags" ("tag");
CREATE INDEX "identifier" ON ###TAGS_TABLE_NAME### ("identifier", "cache", "context");
CREATE INDEX "tag" ON "###TAGS_TABLE_NAME###" ("tag");

COMMIT;
8 changes: 4 additions & 4 deletions Neos.Cache/Resources/Private/pgsql.DDL.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BEGIN;

CREATE TABLE "cache" (
CREATE TABLE "###CACHE_TABLE_NAME###" (
"identifier" VARCHAR(250) NOT NULL,
"cache" VARCHAR(250) NOT NULL,
"context" VARCHAR(150) NOT NULL,
Expand All @@ -10,13 +10,13 @@ CREATE TABLE "cache" (
PRIMARY KEY ("identifier", "cache", "context")
);

CREATE TABLE "tags" (
CREATE TABLE "###TAGS_TABLE_NAME###" (
"identifier" VARCHAR(250) NOT NULL,
"cache" VARCHAR(250) NOT NULL,
"context" VARCHAR(150) NOT NULL,
"tag" VARCHAR(250) NOT NULL
);
CREATE INDEX "identifier" ON "tags" ("identifier", "cache", "context");
CREATE INDEX "tag" ON "tags" ("tag");
CREATE INDEX "identifier" ON "###TAGS_TABLE_NAME###" ("identifier", "cache", "context");
CREATE INDEX "tag" ON "###TAGS_TABLE_NAME###" ("tag");

COMMIT;
48 changes: 48 additions & 0 deletions Neos.Cache/Tests/Unit/Backend/TaggableMultiBackendTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);

namespace Neos\Cache\Tests\Unit\Backend;

include_once(__DIR__ . '/../../BaseTestCase.php');

use Neos\Cache\Backend\NullBackend;
use Neos\Cache\Backend\TaggableMultiBackend;
use Neos\Cache\EnvironmentConfiguration;
use Neos\Cache\Tests\BaseTestCase;

class TaggableMultiBackendTest extends BaseTestCase
{
/**
* @test
*/
public function flushByTagReturnsCountOfFlushedEntries(): void
{
$mockBuilder = $this->getMockBuilder(NullBackend::class);
$firstNullBackendMock = $mockBuilder->getMock();
$secondNullBackendMock = $mockBuilder->getMock();
$thirdNullBackendMock = $mockBuilder->getMock();

$firstNullBackendMock->expects(self::once())->method('flushByTag')->with('foo')->willReturn(2);
$secondNullBackendMock->expects(self::once())->method('flushByTag')->with('foo')->willThrowException(new \RuntimeException());
$thirdNullBackendMock->expects(self::once())->method('flushByTag')->with('foo')->willReturn(3);

$multiBackend = new TaggableMultiBackend($this->getEnvironmentConfiguration(), []);
$this->inject($multiBackend, 'backends', [$firstNullBackendMock, $secondNullBackendMock, $thirdNullBackendMock]);
$this->inject($multiBackend, 'initialized', true);

$result = $multiBackend->flushByTag('foo');
self::assertSame(5, $result);
}

/**
* @return EnvironmentConfiguration
*/
public function getEnvironmentConfiguration(): EnvironmentConfiguration
{
return new EnvironmentConfiguration(
__DIR__ . '~Testing',
'vfs://Foo/',
255
);
}
}
52 changes: 52 additions & 0 deletions Neos.Cache/Tests/Unit/Psr/Cache/CachePoolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

use Neos\Cache\Backend\AbstractBackend;
use Neos\Cache\Backend\BackendInterface;
use Neos\Cache\Psr\Cache\CachePool;
use Neos\Cache\Psr\Cache\CacheItem;
use Neos\Cache\Psr\InvalidArgumentException;
Expand All @@ -23,6 +24,57 @@
*/
class CachePoolTest extends BaseTestCase
{
public function validIdentifiersDataProvider(): array
{
return [
['short'],
['SomeValidIdentifier'],
['withNumbers0123456789'],
['withUnder_score'],
['with.dot'],

// The following tests exceed the minimum requirements of the PSR-6 keys (@see https://www.php-fig.org/psr/psr-6/#definitions)
['dashes-are-allowed'],
['percent%sign'],
['amper&sand'],
['a-string-that-exceeds-the-psr-minimum-maxlength-of-sixtyfour-but-is-shorter-than-twohundredandfifty-characters'],
];
}

/**
* @test
* @dataProvider validIdentifiersDataProvider
*/
public function validIdentifiers(string $identifier): void
{
$mockBackend = $this->getMockBuilder(BackendInterface::class)->getMock();
$cachePool = new CachePool($identifier, $mockBackend);
self::assertInstanceOf(CachePool::class, $cachePool);
}

public function invalidIdentifiersDataProvider(): array
{
return [
[''],
['späcialcharacters'],
['a-string-that-exceeds-the-maximum-allowed-length-of-twohundredandfifty-characters-which-is-pretty-large-as-it-turns-out-so-i-repeat-a-string-that-exceeds-the-maximum-allowed-length-of-twohundredandfifty-characters-still-not-there-wow-crazy-flow-rocks-though'],
];
}

/**
* @test
* @dataProvider invalidIdentifiersDataProvider
*/
public function invalidIdentifiers(string $identifier): void
{
$mockBackend = $this->getMockBuilder(BackendInterface::class)->getMock();

$this->expectException(\InvalidArgumentException::class);
new CachePool($identifier, $mockBackend);
}



/**
* @test
*/
Expand Down
18 changes: 15 additions & 3 deletions Neos.Flow/Classes/Core/Booting/Scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
*/
class Scripts
{
/** @var string */
protected static $builtPhpCommand = null;

/**
* Initializes the Class Loader
*
Expand Down Expand Up @@ -773,6 +776,10 @@ protected static function buildSubprocessCommand(string $commandIdentifier, arra
*/
public static function buildPhpCommand(array $settings): string
{
if (isset(static::$builtPhpCommand)) {
return static::$builtPhpCommand;
}

$subRequestEnvironmentVariables = [
'FLOW_ROOTPATH' => FLOW_PATH_ROOT,
'FLOW_PATH_TEMPORARY_BASE' => FLOW_PATH_TEMPORARY_BASE,
Expand Down Expand Up @@ -810,7 +817,7 @@ public static function buildPhpCommand(array $settings): string

static::ensureWebSubrequestsUseCurrentlyRunningPhpVersion($command);

return $command;
return static::$builtPhpCommand = $command;
}

/**
Expand Down Expand Up @@ -867,8 +874,13 @@ protected static function ensureCLISubrequestsUseCurrentlyRunningPhpBinary($phpB
);
}

exec(PHP_BINARY . ' -r "echo realpath(PHP_BINARY);"', $output);
$realPhpBinary = $output[0];
// stfu to avoid possible open_basedir restriction https://github.com/neos/flow-development-collection/pull/2491
$realPhpBinary = @realpath(PHP_BINARY);
if ($realPhpBinary === false) {
// bypass with exec open_basedir restriction
exec(PHP_BINARY . ' -r "echo realpath(PHP_BINARY);"', $output);
$realPhpBinary = $output[0];
}
if (strcmp($realPhpBinary, $configuredPhpBinaryPathAndFilename) !== 0) {
throw new Exception\SubProcessException(sprintf(
'You are running the Flow CLI with a PHP binary different from the one Flow is configured to use internally. ' .
Expand Down
3 changes: 2 additions & 1 deletion Neos.Flow/Classes/Mvc/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Neos\Flow\Security\Exception\AccessDeniedException;
use Neos\Flow\Security\Exception\AuthenticationRequiredException;
use Neos\Flow\Security\Exception\MissingConfigurationException;
use Psr\Log\LoggerInterface;

/**
* Dispatches requests to the controller which was specified by the request and
Expand Down Expand Up @@ -105,7 +106,7 @@ public function dispatch(ActionRequest $request, ActionResponse $response)
// Rethrow as the SecurityEntryPoint middleware will take care of the rest
throw $exception->attachInterceptedRequest($request);
} catch (AccessDeniedException $exception) {
/** @var PsrLoggerFactoryInterface $securityLogger */
/** @var LoggerInterface $securityLogger */
$securityLogger = $this->objectManager->get(PsrLoggerFactoryInterface::class)->get('securityLogger');
$securityLogger->warning('Access denied', LogEnvironment::fromMethodName(__METHOD__));
throw $exception;
Expand Down
4 changes: 3 additions & 1 deletion Neos.Flow/Classes/Mvc/Routing/Dto/RouteTags.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ public static function createFromTag(string $tag): self
*/
public static function createFromArray(array $tags): self
{
array_walk($tags, 'static::validateTag');
foreach ($tags as $tag) {
self::validateTag($tag);
}
return new static($tags);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ protected function buildConstructorInjectionCode(Configuration $objectConfigurat
} else {
if (strpos($argumentValue, '.') !== false) {
$settingPath = explode('.', $argumentValue);
$settings = Arrays::getValueByPath($this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS), array_shift($settingPath));
$settings = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, array_shift($settingPath));
$argumentValue = Arrays::getValueByPath($settings, $settingPath);
}
if (!isset($this->objectConfigurations[$argumentValue])) {
Expand Down Expand Up @@ -629,7 +629,7 @@ protected function buildMethodParametersCode(array $argumentConfigurations)
} else {
if (strpos($argumentValue, '.') !== false) {
$settingPath = explode('.', $argumentValue);
$settings = Arrays::getValueByPath($this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS), array_shift($settingPath));
$settings = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, array_shift($settingPath));
$argumentValue = Arrays::getValueByPath($settings, $settingPath);
}
$preparedArguments[] = '\Neos\Flow\Core\Bootstrap::$staticObjectManager->get(\'' . $argumentValue . '\')';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace Neos\Flow\Persistence\Doctrine;

/*
Expand All @@ -20,6 +21,7 @@
use Neos\Flow\Validation\ValidatorResolver;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;
use Doctrine\ORM\Event\LifecycleEventArgs;

/**
* An onFlush listener for Flow's Doctrine PersistenceManager.
Expand Down Expand Up @@ -54,6 +56,36 @@ class ObjectValidationAndDeDuplicationListener
*/
protected $entityManager;

/**
* An prePersist event listener to deduplicate value objects.
*
* This removes all existing value objects in doctrines identity map. This is needed as doctrine handles their
* identity based on the object and not based on the
*
* @param LifecycleEventArgs $eventArgs
* @return void
*/
public function prePersist(LifecycleEventArgs $eventArgs)
{
$entityManager = $eventArgs->getObjectManager();
$unitOfWork = $entityManager->getUnitOfWork();
$objectToPersist = $eventArgs->getObject();

$classMetadata = $entityManager->getClassMetadata(get_class($objectToPersist));
$className = $classMetadata->rootEntityName;

$classSchema = $this->reflectionService->getClassSchema($className);
$identityMapOfClassName = $unitOfWork->getIdentityMap()[$className] ?? [];

if ($classSchema !== null && $classSchema->getModelType() === ClassSchema::MODELTYPE_VALUEOBJECT) {
foreach ($identityMapOfClassName as $objectInIdentityMap) {
if ($this->persistenceManager->getIdentifierByObject($objectInIdentityMap) === $this->persistenceManager->getIdentifierByObject($objectToPersist)) {
$unitOfWork->removeFromIdentityMap($objectInIdentityMap);
}
}
}
}

/**
* An onFlush event listener used to act upon persistence.
*
Expand Down
2 changes: 1 addition & 1 deletion Neos.Flow/Configuration/Settings.Persistence.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Neos:
events: ['onFlush']
listener: 'Neos\Flow\Persistence\Doctrine\AllowedObjectsListener'
'Neos\Flow\Persistence\Doctrine\ObjectValidationAndDeDuplicationListener':
events: ['onFlush']
events: ['onFlush', 'prePersist']
listener: 'Neos\Flow\Persistence\Doctrine\ObjectValidationAndDeDuplicationListener'

# Doctrine ORM Second Level Cache configuration.
Expand Down
2 changes: 2 additions & 0 deletions Neos.Flow/Tests/Unit/Core/Booting/ScriptsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ protected static function ensureWebSubrequestsUseCurrentlyRunningPhpVersion($php

public static function buildSubprocessCommand(string $commandIdentifier, array $settings, array $commandArguments = []): string
{
// clear cache for testing
static::$builtPhpCommand = null;
return parent::buildSubprocessCommand($commandIdentifier, $settings, $commandArguments);
}
}
Expand Down

0 comments on commit 5c63048

Please sign in to comment.