Repository navigation
Conversation
There was a problem hiding this comment.
Pull request overview
Centralizes current SiteAccess and scope-change management in SiteAccessService, migrating consumers away from SiteAccessAware.
Changes:
- Adds request-aware SiteAccess stack operations and scope events.
- Migrates routing, preview, console, fragment, security, and image services.
- Updates dependency injection and tests.
Reviewed changes
Copilot reviewed 43 out of 43 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php |
Tests stack and request semantics. |
tests/lib/MVC/Symfony/Security/HttpUtilsTest.php |
Tests service-based SiteAccess lookup. |
tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php |
Supplies the new service dependency. |
tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php |
Updates generator setup and mocks. |
tests/lib/MVC/Symfony/Routing/GeneratorTest.php |
Tests dynamic current SiteAccess lookup. |
tests/lib/Helper/ContentPreviewHelperTest.php |
Tests delegated scope changes. |
tests/integration/Core/Resources/settings/common.yml |
Seeds integration-test SiteAccess state. |
tests/integration/Core/LegacyTestContainerBuilder.php |
Uses the service interface. |
tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php |
Initializes SiteAccess for IO tests. |
tests/bundle/Core/SiteAccess/Config/IOConfigResolverTest.php |
Mocks the service interface. |
tests/bundle/Core/Routing/DefaultRouterTest.php |
Tests constructor-injected service usage. |
tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php |
Tests current-scope fragment rewriting. |
tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php |
Tests CLI scope changes. |
tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php |
Verifies HttpUtils injection. |
tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php |
Verifies fragment dependencies. |
tests/bundle/Core/DependencyInjection/Compiler/ChainRoutingPassTest.php |
Verifies router injection. |
src/lib/Resources/settings/roles.yml |
Injects the service interface. |
src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php |
Exposes change and restore APIs. |
src/lib/MVC/Symfony/SiteAccess/SiteAccessService.php |
Implements stack ownership and events. |
src/lib/MVC/Symfony/SiteAccess/SiteAccessAware.php |
Deprecates the legacy interface. |
src/lib/MVC/Symfony/Security/HttpUtils.php |
Reads SiteAccess dynamically. |
src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php |
Injects SiteAccessService. |
src/lib/MVC/Symfony/Routing/Generator.php |
Uses the current SiteAccess for URLs. |
src/lib/MVC/Symfony/Controller/Content/PreviewController.php |
Guards missing SiteAccess state. |
src/lib/Helper/ContentPreviewHelper.php |
Delegates scope stack operations. |
src/contracts/Test/IbexaTestKernel.php |
Seeds test-kernel SiteAccess state. |
src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php |
Handles absent current SiteAccess. |
src/bundle/Core/Routing/DefaultRouter.php |
Uses dynamic SiteAccess lookup. |
src/bundle/Core/Resources/config/services.yml |
Rewires console and fragment services. |
src/bundle/Core/Resources/config/routing.yml |
Configures generator and service dependencies. |
src/bundle/Core/Resources/config/image.yml |
Rewires image alias generation. |
src/bundle/Core/Resources/config/helpers.yml |
Rewires preview and config helpers. |
src/bundle/Core/Resources/config/commands.yml |
Updates debug-command dependency. |
src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php |
Uses current SiteAccess in cache metadata. |
src/bundle/Core/Fragment/InlineFragmentRenderer.php |
Removes obsolete SiteAccess storage. |
src/bundle/Core/Fragment/DecoratedFragmentRenderer.php |
Rewrites paths using current SiteAccess. |
src/bundle/Core/EventListener/ConsoleCommandListener.php |
Establishes fresh CLI SiteAccess state. |
src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php |
Constructor-injects HttpUtils dependency. |
src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php |
Injects fragment renderer dependency. |
src/bundle/Core/DependencyInjection/Compiler/ChainRoutingPass.php |
Injects default-router dependency. |
src/bundle/Core/Command/DebugConfigResolverCommand.php |
Reads CLI SiteAccess from the service. |
phpstan-baseline.neon |
Removes obsolete suppressions. |
dependencies.json |
Temporarily pins the companion Behat branch. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+61
to
+63
| public function onKernelFinishRequest(FinishRequestEvent $event): void | ||
| { | ||
| $this->popSiteAccessStack(); |
Comment on lines
+1
to
+11
| { | ||
| "recipesEndpoint": "", | ||
| "packages": [ | ||
| { | ||
| "requirement": "dev-IBX-12204-siteaccessservice-scope-stack as 6.0.x-dev", | ||
| "repositoryUrl": "https://github.com/ibexa/behat.git", | ||
| "package": "ibexa/behat", | ||
| "shouldBeAddedAsVCS": false | ||
| } | ||
| ] | ||
| } |
This was referenced Aug 10, 2026
Steveb-p
added a commit
that referenced
this pull request
Aug 12, 2026
previewContentAction() only called restoreConfigScope()/setPreviewActive(false) on the success path; both catch blocks for the forwarded sub-request returned early, skipping them. This pre-existing gap didn't matter before, since SiteAccessService never tracked scope changes at all — but now that changeConfigScope() pushes onto its stack, skipping the restore leaves SiteAccessService and ConfigResolver disagreeing on the current scope: the stack silently unwinds on request finish (no CONFIG_SCOPE_RESTORE dispatch), so SiteAccessService::getCurrent() reverts while ConfigResolver, updated by ConfigScopeListener reacting to the earlier CONFIG_SCOPE_CHANGE, stays on the preview scope. Wrapped the sub-request handling in try/finally so both calls always run, regardless of how the method exits. Calling restoreConfigScope() even when the scope was never changed is safe: restoreSiteAccess()'s floor guard never pops past the request-matched entry, so it's a no-op in that case (matching the pre-existing test expectations, which already assumed an unconditional call). Flagged by GitHub Copilot's review on PR #798.
|
This was referenced Aug 12, 2026
Contributor
Author
Contributor
…ss() the supported way to switch SiteAccess scope Manually dispatching ScopeChangeEvent under MVCEvents::CONFIG_SCOPE_CHANGE/ CONFIG_SCOPE_RESTORE was previously the only documented way to switch the current SiteAccess for content preview, CLI --siteaccess, and sub-requests. SiteAccessService now tracks the current SiteAccess as a stack and exposes changeSiteAccess()/restoreSiteAccess() as the primary API; every internal caller (SiteAccessMatchListener, SiteAccessRestoreListener, DefaultRouter, ContentPreviewHelper, ConsoleCommandListener, route/fragment/image generators, HttpUtils) now goes through it instead of constructing SiteAccess\Aware setters or dispatching the event by hand. Dispatching the event manually still works, but is deprecated: SiteAccessService keeps reacting to it for not-yet-migrated/third-party listeners, emitting a runtime deprecation notice whenever the event wasn't dispatched internally by changeSiteAccess()/restoreSiteAccess() themselves. Reconciled against the 79 commits that landed on 6.0 since this branch was opened, including three PRs that independently touched the same area (SiteAccess stack handling, SiteAccess\Aware removal, DefaultRouter's decorator rewrite) - this is a from-scratch re-implementation against that new baseline rather than a mechanical rebase.
Steveb-p
force-pushed
the
IBX-12204-siteaccessservice-scope-stack
branch
from
October 2, 2026 12:28
d536264 to
ef50d06
Compare
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Warning
This branch includes a temporary commit (
dependencies.json) that points the Behat CI job atibexa/behat#191 so the "Commands use the default
siteaccess if not specified" scenario can run (and pass) before that companion PR merges.
Must be removed before merging this PR.
Description:
A
ConfigScopeListeneralready reacts toMVCEvents::CONFIG_SCOPE_CHANGE/CONFIG_SCOPE_RESTOREand broadcasts the new SiteAccess to everyVersatileScopeInterfaceconfig resolver and everySiteAccessAwareview manager/view provider — butSiteAccessServiceitself was never one of those broadcast targets. It kept a single value, injected once viaSiteAccessAware::setSiteAccess()from request matching, that never updated when a scope change was dispatched (see IBX-8074). Every scope-change call site (ContentPreviewHelper,ConsoleCommandListener) also had to hand-build aScopeChangeEventand dispatch it itself, with no supported API for it.This PR makes
SiteAccessServicethe single owner of "what is the current SiteAccess right now":SiteAccessService::changeSiteAccess(SiteAccess $siteAccess): SiteAccessandrestoreSiteAccess(): ?SiteAccess, which wrap the existingScopeChangeEventdispatch underMVCEvents::CONFIG_SCOPE_CHANGE/CONFIG_SCOPE_RESTORE— no new event types, just a supported API instead of hand-dispatching.SiteAccessServicenow maintains a real LIFO stack ofSiteAccesschanges instead of a single injected value:EventSubscriberInterfaceand subscribes toMVCEvents::SITEACCESS: on aMAIN_REQUESTmatch it resets the stack to[$siteAccess]; on aSUB_REQUESTmatch (fragments, ESI, content-preview's internal sub-request) it pushes onto the stack.KernelEvents::FINISH_REQUEST, which pops the stack — but never below 1 remaining entry. That single "floor guard" is shared withrestoreSiteAccess(), so a sub-request's own push is always safely undone by its own finish, and an unbalancedrestoreSiteAccess()call (or CLI's one-timechangeSiteAccess()floor) can never strip the stack down to empty.getCurrent()returns the top of the stack, ornullif it's empty — it does not fall back to the shared, container-wideSiteAccesssingleton.SiteAccessService(andConsoleCommandListener, see below) intentionally stop relying on that singleton altogether; a SiteAccess with an "uninitialized" matcher (SiteAccess::MATCHING_TYPE_UNINITIALIZED) is treated as null defensively even if one somehow ends up on the stack.SiteAccessAwareis marked@deprecated(no runtime deprecation notice — it's still legitimately used by consumers not migrated here) in favor of callingSiteAccessService::getCurrent()directly.SiteAccessAwareconsumers to constructor-injectSiteAccessServiceInterfaceand callgetCurrent()instead of storing a value viasetSiteAccess():ContentPreviewHelper,ConsoleCommandListener,HttpUtils,DefaultRouter,Generator(andUrlAliasGenerator),AliasGeneratorDecorator,DecoratedFragmentRenderer/InlineFragmentRenderer.ConsoleCommandListenerno longer mutates the sharedSiteAccesssingleton at all — it constructs a freshSiteAccessfor the CLI invocation and hands it tochangeSiteAccess().ConfigResolverand friends still read that singleton directly for theMATCHING_TYPE_UNINITIALIZEDsentinel (see IBX-12192) and are intentionally not migrated in this PR (would require a circular DI dependency back ontoSiteAccessService, plus non-trivial caching/broadcast semantics). Same reasoning excludesSiteAccess\Router,View\Manager/Provider\Configured.IbexaTestKernelandLegacyTestContainerBuilder's test settings now explicitly seedSiteAccessServiceviachangeSiteAccess()with a freshly-constructed defaultSiteAccess, the same mechanism CLI uses.ComplexConfigProcessorwhile migrating it: it read$this->siteAccessService->getCurrent()->namewith no null check. Added agetCurrentSiteAccessName()guard that throwsInvalidArgumentException, matching the precedent already set inSiteAccessService::getSiteAccessesRelation().DecoratedFragmentRenderer/InlineFragmentRendererpreviously only ever saw the original request's SiteAccess when rewriting fragment paths (they weren't part ofConfigScopeListener's scope-change broadcast). They now correctly reflect whateverSiteAccessService::getCurrent()says, including during an active content-preview scope change — this is a correctness fix, not a regression.routing.yml,services.yml,helpers.yml,image.yml, and theSecurityPass/ChainRoutingPass/FragmentPasscompiler passes now passSiteAccessServiceInterfaceas a constructor argument instead of the oldsetSiteAccess()calls;SiteAccessServiceitself gained anEventDispatcherInterfaceconstructor argument and akernel.event_subscribertag. ForHttpUtilsandDefaultRouter— which extend Symfony framework base classes with their own evolving constructors — the new parameter is appended as a nullable, named-argument-bound constructor parameter ($definition->setArgument('$siteAccessService', ...)), so the existing positional arguments coming from Symfony's own service definitions are left untouched.phpstan-baseline.neonentries left behind by removingSiteAccessAware/setSiteAccess()from the migrated classes.Known BC break (intentional): any custom Symfony console command that type-hints the shared
Ibexa\Core\MVC\Symfony\SiteAccessservice directly to read the CLI-resolved siteaccess will no longer see it updated —ConsoleCommandListenerused to mutate that shared singleton in place specifically for such consumers, but no longer does (see above). Commands relying on this must switch to injectingSiteAccessServiceInterfaceand callinggetCurrent()instead. Audited the rest of the publicibexa/*org for the same pattern and found exactly two affected spots, both now fixed:Ibexa\Bundle\Core\Command\DebugConfigResolverCommand(ibexa:debug:config-resolver) in this repo — fixed in this PR.ibexa/behat'sTestSiteaccessCommandfixture command (used bysrc/bundle/Core/Features/Console/console.feature's "Commands use the default siteaccess if not specified" scenario) — fixed in companion PR ibexa/behat#191, cross-tested here via the temporarydependencies.json(see warning at the top).Verified:
phpstan analyseclean, and theunit_core/bundle_core/bundle_ioPHPUnit suites pass (6567/864/27 tests respectively, no failures — only pre-existing PHPUnit-deprecation warnings unrelated to this change).For QA:
Two scenarios worth exercising manually:
--siteaccess=<name>and confirmSiteAccessService::getCurrent()(andConfigResolver) reflect that siteaccess for the whole command run.New unit test coverage lives in
SiteAccessServiceTestfor the stack semantics specifically: nestedchangeSiteAccess()/restoreSiteAccess()pairs behaving as LIFO,MAIN_REQUESTresetting vs.SUB_REQUESTpushing, the finish_request floor guard never popping below 1 entry, and a full sub-request-nesting scenario.Documentation:
SiteAccessAwareis now@deprecated— worth a mention in the deprecation notes for this release, pointing integrators atSiteAccessServiceInterface::getCurrent()/changeSiteAccess()/restoreSiteAccess()instead of the oldsetSiteAccess()+ hand-dispatchedScopeChangeEventpattern.The BC break above should also be called out explicitly in the release/upgrade notes: custom console commands reading the shared
SiteAccessservice to determine the CLI-resolved siteaccess must switch toSiteAccessServiceInterface::getCurrent().