Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## [11.2.0](https://github.com/anzusystems/common-bundle/compare/11.1.1...11.2.0) (2026-07-03)

### Features
* New `HttpExceptionHandler` — standardized JSON error response (`error`, `detail`, `contextId`) for any unhandled `HttpException`, preserving the exception's original status code (429, 503, ...) instead of falling through to the default handler as 500. Registered after the more specific handlers (`AccessDeniedExceptionHandler`, `NotFoundExceptionHandler`, ...).
* `ExceptionHandlerCompilerPass` now sorts exception handlers by the `priority` attribute of the `anzu_systems_common.logs.exception_handler` tag (high → low). Handlers without an explicit priority keep the default `0`; the built-in `HttpExceptionHandler` registers with `-100`, so more specific handlers always take precedence:
```php
$definition->addTag(AnzuSystemsCommonBundle::TAG_EXCEPTION_HANDLER, ['priority' => -100]);
```

## [10.0.0](https://github.com/anzusystems/common-bundle/compare/9.4.0...10.0.0) (2024-10-30)

### Features
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ anzu_common:
- AnzuSystems\CommonBundle\Exception\Handler\ValidationExceptionHandler
- AnzuSystems\CommonBundle\Exception\Handler\AppReadOnlyModeExceptionHandler
- AnzuSystems\CommonBundle\Exception\Handler\AccessDeniedExceptionHandler
- AnzuSystems\CommonBundle\Exception\Handler\HttpExceptionHandler
logs:
enabled: true
# Logs are sent through Symfony Messenger.
Expand Down
2 changes: 2 additions & 0 deletions ecs.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use SlevomatCodingStandard\Sniffs\PHP\UselessParenthesesSniff;
use Symplify\CodingStandard\Fixer\ArrayNotation\ArrayListItemNewlineFixer;
use Symplify\CodingStandard\Fixer\ArrayNotation\ArrayOpenerAndCloserNewlineFixer;
use Symplify\CodingStandard\Fixer\Spacing\MethodChainingNewlineFixer;
use Symplify\EasyCodingStandard\Config\ECSConfig;

return ECSConfig::configure()
Expand All @@ -62,6 +63,7 @@
NotOperatorWithSuccessorSpaceFixer::class => null,
UselessParenthesesSniff::class => null,
MethodChainingIndentationFixer::class => ['src/DependencyInjection/*Configuration.php'],
MethodChainingNewlineFixer::class => ['src/DependencyInjection/*Configuration.php'],
'SlevomatCodingStandard\Sniffs\Classes\UnusedPrivateElementsSniff.WriteOnlyProperty' => ['src/Entity/User.php'],
'SlevomatCodingStandard\Sniffs\Whitespaces\DuplicateSpacesSniff.DuplicateSpaces' => null,
'SlevomatCodingStandard\Sniffs\Commenting\DisallowCommentAfterCodeSniff.DisallowedCommentAfterCode' => null,
Expand Down
9 changes: 5 additions & 4 deletions src/AnzuTap/AnzuTapEditor.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public function transform(string $data): AnzuTapBody

$document = new DOMDocument();
$document->loadHTML($data);
$bodyNode = $document->getElementsByTagName('body')->item(0);
$bodyNode = $document->getElementsByTagName('body')
->item(0);

$body = new AnzuTapDocNode();
if (false === (null === $bodyNode)) {
Expand All @@ -70,7 +71,7 @@ public function transform(string $data): AnzuTapBody
);
}

public function getMarkTransformer(DOMElement | DOMText $element): ?AnzuMarkTransformerInterface
public function getMarkTransformer(DOMElement|DOMText $element): ?AnzuMarkTransformerInterface
{
$key = $this->markTransformerProvider->getMarkTransformerKey($element);
if ($this->resolvedMarkTransformers->has($key)) {
Expand All @@ -80,7 +81,7 @@ public function getMarkTransformer(DOMElement | DOMText $element): ?AnzuMarkTran
return null;
}

public function getNodeTransformer(DOMElement | DOMText $element): AnzuNodeTransformerInterface
public function getNodeTransformer(DOMElement|DOMText $element): AnzuNodeTransformerInterface
{
$key = $this->transformerProvider->getNodeTransformerKey($element);

Expand Down Expand Up @@ -173,7 +174,7 @@ private function getUniqueMarks(): array
}

private function processNode(
DOMElement | DOMText $node,
DOMElement|DOMText $node,
AnzuNodeTransformerInterface $nodeTransformer,
AnzuTapNodeInterface $anzuTapParentNode,
): ?AnzuTapNodeInterface {
Expand Down
2 changes: 1 addition & 1 deletion src/AnzuTap/Transformer/Node/TextNodeTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static function getSupportedNodeNames(): array
];
}

public function transform(DOMElement | DOMText $element, EmbedContainer $embedContainer, ?AnzuTapNodeInterface $parent = null): ?AnzuTapNodeInterface
public function transform(DOMElement|DOMText $element, EmbedContainer $embedContainer, ?AnzuTapNodeInterface $parent = null): ?AnzuTapNodeInterface
{
$text = $this->getText(
$element,
Expand Down
2 changes: 1 addition & 1 deletion src/AnzuTap/Transformer/Traits/TextNodeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

trait TextNodeTrait
{
protected function getText(DOMText | DOMElement $element, bool $allowEmpty = false): ?string
protected function getText(DOMText|DOMElement $element, bool $allowEmpty = false): ?string
{
$text = $element->textContent;
$textToTrim = $text;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

final readonly class AnzuTapNodeTransformerProvider implements NodeTransformerProviderInterface
{
public function getNodeTransformerKey(DOMElement | DOMText $element): string
public function getNodeTransformerKey(DOMElement|DOMText $element): string
{
return $element->nodeName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@

interface MarkTransformerProviderInterface
{
public function getMarkTransformerKey(DOMElement | DOMText $element): string;
public function getMarkTransformerKey(DOMElement|DOMText $element): string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@

interface NodeTransformerProviderInterface
{
public function getNodeTransformerKey(DOMElement | DOMText $element): string;
public function getNodeTransformerKey(DOMElement|DOMText $element): string;
}
15 changes: 9 additions & 6 deletions src/ApiFilter/ApiQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ public function getTotalCount(): int

return (int) $this->dqb
->select('count(t)')
->getQuery()->getSingleScalarResult()
->getQuery()
->getSingleScalarResult()
;
}

Expand All @@ -100,7 +101,8 @@ public function getData(): array
->select('t')
->setFirstResult($this->apiParams->getOffset())
->setMaxResults($limit)
->getQuery()->getResult()
->getQuery()
->getResult()
;
}

Expand Down Expand Up @@ -168,10 +170,11 @@ private function applyFilters(): void
break;
default:
$this->dqb->andWhere(
$this->dqb->expr()->{$filterVariant}(
't.' . $field,
':' . $paramName
)
$this->dqb->expr()
->{$filterVariant}(
't.' . $field,
':' . $paramName
)
);
$this->dqb->setParameter(
$paramName,
Expand Down
9 changes: 6 additions & 3 deletions src/ApiFilter/ApiQueryMongo.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ public function getOptions(): array

private function getPersistedName(string $field): string
{
$fieldAttributes = $this->getPropertyByField($field)->getAttributes(PersistedName::class);
$fieldAttributes = $this->getPropertyByField($field)
->getAttributes(PersistedName::class);
if (array_key_exists(0, $fieldAttributes)) {
$persistedNameAttribute = $fieldAttributes[0]->newInstance();
/** @psalm-suppress RedundantConditionGivenDocblockType */
Expand Down Expand Up @@ -142,7 +143,8 @@ private function getPropertyType(string $field): string
return 'oid';
}

return (string) $this->getPropertyByField($field)->getType();
return (string) $this->getPropertyByField($field)
->getType();
}

/**
Expand All @@ -156,7 +158,8 @@ private function getPropertyByField(string $field): ReflectionProperty
$class = $this->classReflection;
foreach ($propertyPath as $curPropName) {
/** @psalm-var class-string $curClassType */
$curClassType = (string) $class->getProperty($curPropName)->getType();
$curClassType = (string) $class->getProperty($curPropName)
->getType();
$class = new ReflectionClass($curClassType);
}

Expand Down
2 changes: 1 addition & 1 deletion src/ApiFilter/CustomFilterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@

interface CustomFilterInterface
{
public function apply(QueryBuilder $dqb, string $field, string | int $value): QueryBuilder;
public function apply(QueryBuilder $dqb, string $field, string|int $value): QueryBuilder;
}
2 changes: 1 addition & 1 deletion src/ApiFilter/FieldCallbackInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

interface FieldCallbackInterface
{
public function __invoke(string | int $value): void;
public function __invoke(string|int $value): void;
public function field(): string;
}
3 changes: 2 additions & 1 deletion src/CodingStandard/Fixer/MigrationPrimaryKeyFirstFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ private function fixColumns(string $fullMatch, string $columnsString): string
array_diff_key($columns, [$pkIndex => true])
)];

return (new UnicodeString($fullMatch))->replace($columnsString, implode(', ', $reordered))->toString();
return (new UnicodeString($fullMatch))->replace($columnsString, implode(', ', $reordered))
->toString();
}

/**
Expand Down
7 changes: 4 additions & 3 deletions src/Controller/AbstractAnzuApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected function okResponse(
/**
* Get newly created entity.
*/
protected function createdResponse(array | object $data): JsonResponse
protected function createdResponse(array|object $data): JsonResponse
{
return $this->getResponse($data, JsonResponse::HTTP_CREATED);
}
Expand All @@ -59,7 +59,7 @@ protected function noContentResponse(): JsonResponse
}

protected function getResponse(
array | object $data,
array|object $data,
int $statusCode = JsonResponse::HTTP_OK,
): JsonResponse {
return new JsonResponse(
Expand All @@ -73,7 +73,8 @@ protected function getResponse(
protected function lockApi(bool $blocking = false): void
{
$this->resourceLocker->lock(
(string) $this->container->get('request_stack')->getCurrentRequest()?->get('_route'),
(string) $this->container->get('request_stack')
->getCurrentRequest()?->get('_route'),
$blocking
);
}
Expand Down
3 changes: 0 additions & 3 deletions src/Controller/DebugController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
#[OA\Tag('Debug')]
final class DebugController extends AbstractAnzuApiController
{
/**
* Get lead time.
*/
#[OAResponse(description: 'Lead time')]
public function getLeadTime(): JsonResponse
{
Expand Down
2 changes: 0 additions & 2 deletions src/Controller/HealthCheckController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ public function __construct(
}

/**
* Health check.
*
* @throws SerializerException
*/
#[OAResponse(description: 'Health check')]
Expand Down
2 changes: 0 additions & 2 deletions src/Controller/LogController.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ public function getOneJournalLog(string $id): JsonResponse
}

/**
* Get one audit log.
*
* @throws SerializerException
*/
#[OAParameterPath('id'), OAResponse(Log::class)]
Expand Down
17 changes: 11 additions & 6 deletions src/DataFixtures/Fixtures/AbstractFixtures.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ public function getRegistry(): ArrayCollection
*
* @throws InvalidArgumentException
*/
public function getOneFromRegistry(string | int $key): object
public function getOneFromRegistry(string|int $key): object
{
$object = $this->getRegistry()->get($key);
$object = $this->getRegistry()
->get($key);
if (null === $object) {
throw new InvalidArgumentException(sprintf('Object key "%s" not found in registry!', $key));
}
Expand All @@ -80,14 +81,16 @@ public function getOneFromRegistry(string | int $key): object
/**
* @param E $object
*/
public function addToRegistry(object $object, string | int | null $key = null): self
public function addToRegistry(object $object, string|int|null $key = null): self
{
if ($key) {
$this->getRegistry()->set($key, $object);
$this->getRegistry()
->set($key, $object);

return $this;
}
$this->getRegistry()->add($object);
$this->getRegistry()
->add($object);

return $this;
}
Expand All @@ -99,7 +102,9 @@ public function addToRegistry(object $object, string | int | null $key = null):
*/
public function findOneRegistryRecord(Closure $filter): ?object
{
return $this->getRegistry()->filter($filter)->first() ?: null;
return $this->getRegistry()
->filter($filter)
->first() ?: null;
}

public function useCustomId(): bool
Expand Down
8 changes: 8 additions & 0 deletions src/DependencyInjection/AnzuSystemsCommonExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
use AnzuSystems\CommonBundle\Exception\Handler\AppReadOnlyModeExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\DefaultExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\ExceptionHandlerInterface;
use AnzuSystems\CommonBundle\Exception\Handler\HttpExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\NotFoundExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\ValidationExceptionHandler;
use AnzuSystems\CommonBundle\HealthCheck\HealthChecker;
Expand Down Expand Up @@ -372,6 +373,13 @@ private function loadErrors(ContainerBuilder $container): void
$container->setDefinition(SerializerExceptionHandler::class, $definition);
}

if ($hasHandler(HttpExceptionHandler::class)) {
$definition = new Definition(HttpExceptionHandler::class);
$definition->addArgument($debug);
$definition->addTag(AnzuSystemsCommonBundle::TAG_EXCEPTION_HANDLER, ['priority' => -100]);
$container->setDefinition(HttpExceptionHandler::class, $definition);
}

$container
->getDefinition(ExceptionListener::class)
->replaceArgument('$defaultExceptionHandler', new Reference($errors['default_exception_handler']))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@
use AnzuSystems\CommonBundle\Event\Listener\ExceptionListener;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

final class ExceptionHandlerCompilerPass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;

public function process(ContainerBuilder $container): void
{
if (false === $container->hasDefinition(ExceptionListener::class)) {
return;
}

$references = [];
foreach (array_keys($container->findTaggedServiceIds(AnzuSystemsCommonBundle::TAG_EXCEPTION_HANDLER)) as $service) {
$references[] = new Reference($service);
}

$container
->getDefinition(ExceptionListener::class)
->replaceArgument('$exceptionHandlers', new IteratorArgument($references))
->replaceArgument(
'$exceptionHandlers',
new IteratorArgument($this->findAndSortTaggedServices(AnzuSystemsCommonBundle::TAG_EXCEPTION_HANDLER, $container))
)
;
}
}
2 changes: 2 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use AnzuSystems\CommonBundle\Exception\Handler\AppReadOnlyModeExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\DefaultExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\ExceptionHandlerInterface;
use AnzuSystems\CommonBundle\Exception\Handler\HttpExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\NotFoundExceptionHandler;
use AnzuSystems\CommonBundle\Exception\Handler\ValidationExceptionHandler;
use AnzuSystems\CommonBundle\HealthCheck\Module\DataMountModule;
Expand Down Expand Up @@ -67,6 +68,7 @@ final class Configuration implements ConfigurationInterface
AppReadOnlyModeExceptionHandler::class,
AccessDeniedExceptionHandler::class,
SerializerExceptionHandler::class,
HttpExceptionHandler::class,
];
private const array HEALTH_CHECK_MODULES = [
MysqlModule::class,
Expand Down
6 changes: 4 additions & 2 deletions src/Doctrine/Walker/ForceIndexWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ public function walkFromClause(FromClause $fromClause): string
{
$result = parent::walkFromClause($fromClause);

$index = $this->getQuery()->getHint(self::HINT_FORCE_INDEX_FOR_FROM);
$index = $this->getQuery()
->getHint(self::HINT_FORCE_INDEX_FOR_FROM);
if ($index) {
/** @var string $result */
$result = preg_replace('~(\bFROM\s*\w+\s*\w+)~', sprintf('$1 FORCE INDEX (%s)', $index), $result);
}

$indexJoin = $this->getQuery()->getHint(self::HINT_FORCE_INDEX_FOR_JOIN);
$indexJoin = $this->getQuery()
->getHint(self::HINT_FORCE_INDEX_FOR_JOIN);
foreach ($indexJoin ?: [] as $joinName => $indexName) {
/** @var string $result */
$result = preg_replace("~(\bJOIN\s*{$joinName}\s*\w+)~", sprintf('$1 FORCE INDEX (%s)', $indexName), $result);
Expand Down
Loading
Loading