Merge branch '3.4'
* 3.4: bumped Symfony version to 3.4.0 updated VERSION for 3.4.0-BETA1 updated CHANGELOG for 3.4.0-BETA1 Do not process bindings in AbstractRecursivePass don't bind scalar values to controller method arguments Add extra autowiring aliases adding AdapterInterface alias for cache.app Adding a new debug:autowiring command [HttpFoundation] Make sessions secure and lazy [Routing] Ensure uniqueness without repeated check [Console] Sync ConsoleLogger::interpolate with the one in HttpKernel
This commit is contained in:
commit
14de848cc4
|
@ -0,0 +1,217 @@
|
|||
CHANGELOG for 3.4.x
|
||||
===================
|
||||
|
||||
This changelog references the relevant changes (bug and security fixes) done
|
||||
in 3.4 minor versions.
|
||||
|
||||
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
|
||||
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.4.0...v3.4.1
|
||||
|
||||
* 3.4.0-BETA1 (2017-10-18)
|
||||
|
||||
* feature #24583 Adding a new debug:autowiring command (weaverryan)
|
||||
* feature #24523 [HttpFoundation] Make sessions secure and lazy (nicolas-grekas)
|
||||
* feature #22610 [Form] [TwigBridge] Added option to disable usage of default themes when rendering a form (emodric)
|
||||
* feature #23112 [OptionsResolver] Support array of types in allowed type (pierredup)
|
||||
* feature #24321 added ability to handle parent classes for PropertyNormalizer (ivoba)
|
||||
* feature #24505 [HttpKernel] implement reset() in DumpDataCollector (xabbuh)
|
||||
* feature #24425 [Console][HttpKernel] Handle new SHELL_VERBOSITY env var, also configures the default logger (nicolas-grekas)
|
||||
* feature #24387 [FORM] Prevent forms from extending itself as a parent (pierredup)
|
||||
* feature #24484 [DI] Throw accurate failures when accessing removed services (nicolas-grekas)
|
||||
* feature #24208 [Form] Display option definition from a given form type (yceruto, ogizanagi)
|
||||
* feature #23499 [Workflow] add guard is_valid() method support (alain-flaus, lyrixx)
|
||||
* feature #24388 [Security] Look at headers for switch_user username (chalasr)
|
||||
* feature #23708 Added deprecation to cwd not existing Fixes #18249 (alexbowers)
|
||||
* feature #24443 [Session] deprecate MemcacheSessionHandler (Tobion)
|
||||
* feature #24409 [Bridge\Doctrine][FrameworkBundle] Deprecate some remaining uses of ContainerAwareTrait (nicolas-grekas)
|
||||
* feature #24438 [Session][VarDumper] Deprecate accepting legacy mongo extension (Tobion)
|
||||
* feature #24389 [DoctrineBridge] Deprecate dbal session handler (Tobion)
|
||||
* feature #16835 [Security] Add Guard authenticator <supports> method (Amo, chalasr)
|
||||
* feature #24289 [FrameworkBundle][HttpKernel] Reset profiler (derrabus)
|
||||
* feature #24144 [FrameworkBundle] Expose dotenv in bin/console about (ro0NL)
|
||||
* feature #24403 [FrameworkBundle][Routing] Show welcome message if no routes are configured (yceruto)
|
||||
* feature #22679 [Form] Add tel and color types (apetitpa)
|
||||
* feature #23845 [Validator] Add unique entity violation cause (Ilya Vertakov)
|
||||
* feature #22132 [Lock] Automaticaly release lock when user forget it (jderusse)
|
||||
* feature #21751 Bootstrap4 support for Twig form theme (hiddewie, javiereguiluz)
|
||||
* feature #24383 [FrameworkBundle] Don't clear app pools on cache:clear (nicolas-grekas)
|
||||
* feature #24148 [Form] Hide label button when its setted to false (TeLiXj)
|
||||
* feature #24378 [SecurityBundle] Deprecate auto picking the first provider (ogizanagi)
|
||||
* feature #24260 [Security] Add impersonation support for stateless authentication (chalasr)
|
||||
* feature #24300 [HttpKernel][FrameworkBundle] Add a minimalist default PSR-3 logger (dunglas)
|
||||
* feature #21604 [Security] Argon2i Password Encoder (zanbaldwin)
|
||||
* feature #24372 [DowCrawler] Default to UTF-8 when possible (nicolas-grekas)
|
||||
* feature #24264 [TwigBundle] Improve the overriding of bundle templates (yceruto)
|
||||
* feature #24197 [Translation] Moved PhpExtractor and PhpStringTokenParser to Translation component (Nyholm)
|
||||
* feature #24362 [HttpKernel] Deprecate some compiler passes in favor of tagged iterator args (nicolas-grekas)
|
||||
* feature #21027 [Asset] Provide default context (ro0NL)
|
||||
* feature #22200 [DI] Reference tagged services in config (ro0NL)
|
||||
* feature #24337 Adding a shortcuts for the main security functionality (weaverryan, javiereguiluz)
|
||||
* feature #24358 [TwigBundle] register an identity translator as fallback (xabbuh)
|
||||
* feature #24357 [Yaml] include file and line no in deprecation message (xabbuh)
|
||||
* feature #24330 [FrameworkBundle] register class metadata factory alias (xabbuh)
|
||||
* feature #24349 [SecurityBundle] Add missing AclSchemaListener deprecation (ogizanagi)
|
||||
* feature #24202 [Filesystem] deprecate relative paths in makePathRelative() (xabbuh)
|
||||
* feature #21716 [Serializer] Add Support for `object_to_populate` in CustomNormalizer (chrisguitarguy)
|
||||
* feature #21960 Remove Validator\TypeTestCase and add validator logic to base TypeTestCase (pierredup)
|
||||
* feature #22113 [Lock] Include lock component in framework bundle (jderusse)
|
||||
* feature #24236 [WebProfilerBundle] Render file links for twig templates (ro0NL)
|
||||
* feature #21239 [Serializer] throw more specific exceptions (xabbuh)
|
||||
* feature #24256 CsvEncoder handling variable structures and custom header order (Oliver Hoff)
|
||||
* feature #23471 [Finder] Add a method to check if any results were found (duncan3dc)
|
||||
* feature #23149 [PhpUnitBridge] Added a CoverageListener to enhance the code coverage report (lyrixx)
|
||||
* feature #24318 [SecurityBundle] Deprecate ACL related code (chalasr)
|
||||
* feature #24335 [Security][SecurityBundle] Deprecate the HTTP digest auth (ogizanagi)
|
||||
* feature #21951 [Security][Firewall] Passing the newly generated security token to the event during user switching (klandaika)
|
||||
* feature #23485 [Config] extracted the xml parsing from XmlUtils::loadFile into XmlUtils::parse (Basster)
|
||||
* feature #22890 [HttpKernel] Add ability to configure catching exceptions for Client (kbond)
|
||||
* feature #24239 [HttpFoundation] Deprecate compatibility with PHP <5.4 sessions (afurculita)
|
||||
* feature #23882 [Security] Deprecated not being logged out after user change (iltar)
|
||||
* feature #24200 Added an alias for FlashBagInterface in config (tifabien)
|
||||
* feature #24295 [DI][DX] Throw exception on some ContainerBuilder methods used from extensions (ogizanagi)
|
||||
* feature #24253 [Yaml] support parsing files (xabbuh)
|
||||
* feature #24290 Adding Definition::addError() and a compiler pass to throw errors as exceptions (weaverryan)
|
||||
* feature #24301 [DI] Add AutowireRequiredMethodsPass to fix bindings for `@required` methods (nicolas-grekas)
|
||||
* feature #24226 [Cache] Add ResettableInterface to allow resetting any pool's local state (nicolas-grekas)
|
||||
* feature #24303 [FrameworkBundle] allow forms without translations and validator (xabbuh)
|
||||
* feature #24291 [SecurityBundle] Reset the authentication token between requests (derrabus)
|
||||
* feature #24280 [VarDumper] Make `dump()` a little bit more easier to use (freekmurze)
|
||||
* feature #24277 [Serializer] Getter for extra attributes in ExtraAttributesException (mdeboer)
|
||||
* feature #24257 [HttpKernel][DI] Enable Kernel to implement CompilerPassInterface (nicolas-grekas)
|
||||
* feature #23834 [DI] Add "PHP fluent format" for configuring the container (nicolas-grekas)
|
||||
* feature #24180 [Routing] Add PHP fluent DSL for configuring routes (nicolas-grekas)
|
||||
* feature #24232 [Bridge\Doctrine] Add "DoctrineType::reset()" method (nicolas-grekas)
|
||||
* feature #24238 [DI] Turn services and aliases private by default, with BC layer (nicolas-grekas)
|
||||
* feature #23648 [Form] Add input + regions options to TimezoneType (ro0NL)
|
||||
* feature #24185 [Form] Display general forms information on debug:form (yceruto)
|
||||
* feature #23747 [Serializer][FrameworkBundle] Add a DateInterval normalizer (Lctrs)
|
||||
* feature #24193 [FrameworkBundle] Reset stopwatch between requests (derrabus)
|
||||
* feature #24160 [HttpKernel] Deprecate bundle inheritance (fabpot)
|
||||
* feature #24155 [FrameworkBundle][HttpKernel] Add DI tag for resettable services (derrabus)
|
||||
* feature #23625 Feature #23583 Add current and fallback locales in WDT / Profiler (nemoneph)
|
||||
* feature #24179 [TwigBundle] Add default templates directory and option to configure it (yceruto)
|
||||
* feature #24104 Make as many services private as possible (nicolas-grekas)
|
||||
* feature #18314 [Translation] added support for adding custom message formatter (aitboudad)
|
||||
* feature #24158 deprecated profiler.matcher configuration (fabpot)
|
||||
* feature #24131 [Console] Do not display short exception trace for common console exceptions (yceruto)
|
||||
* feature #24080 Deprecated the web_profiler.position option (javiereguiluz)
|
||||
* feature #24114 [SecurityBundle] Throw a meaningful exception when an undefined user provider is used inside a firewall (chalasr)
|
||||
* feature #24122 [DI] rename ResolveDefinitionTemplatesPass to ResolveChildDefinitionsPass (nicolas-grekas)
|
||||
* feature #23901 [DI] Allow processing env vars (nicolas-grekas)
|
||||
* feature #24093 [FrameworkBundle] be able to enable workflow support explicitly (xabbuh)
|
||||
* feature #24064 [TwigBridge] Show Twig's loader paths on debug:twig command (yceruto)
|
||||
* feature #23978 [Cache] Use options from Memcached DSN (Bukashk0zzz)
|
||||
* feature #24075 Implemented PruneableInterface on TagAwareAdapter (Toflar)
|
||||
* feature #21414 [Console] Display file and line on Exception (arno14)
|
||||
* feature #24068 [HttpKernel] Deprecate EnvParametersResource (ro0NL)
|
||||
* feature #22542 [Lock] Check TTL expiration in lock acquisition (jderusse)
|
||||
* feature #24031 [Routing] Add the possibility to define a prefix for all routes of a controller (fabpot)
|
||||
* feature #23967 [VarDumper] add force-collapse/expand + use it for traces (nicolas-grekas)
|
||||
* feature #24033 [DI] Add ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE (nicolas-grekas)
|
||||
* feature #24026 [Security] add impersonator_user to "User was reloaded" log message (gharlan)
|
||||
* feature #23603 [Cache] Add (pdo|chain) cache (adapter|simple) prune method (robfrawley)
|
||||
* feature #23694 [Form] Add debug:form command (yceruto)
|
||||
* feature #24028 [Yaml] mark some classes as final (xabbuh)
|
||||
* feature #22543 [Lock] Expose an expiringDate and isExpired method in Lock (jderusse)
|
||||
* feature #23667 [Translation] Create an TranslationReaderInterface and move TranslationLoader to TranslationComponent (Nyholm)
|
||||
* feature #24024 [config] Add ability to deprecate a node (sanpii)
|
||||
* feature #23668 [VarDumper] Add period caster (maidmaid)
|
||||
* feature #23991 [DI] Improve psr4-based service discovery (alternative implementation) (kbond)
|
||||
* feature #23947 [Translation] Adding the ability do load <notes> in xliff2.0 (Nyholm)
|
||||
* feature #23887 [Console] Allow commands to provide a default name for compile time registration (chalasr, nicolas-grekas)
|
||||
* feature #23874 [DI] Case sensitive parameter names (ro0NL)
|
||||
* feature #23936 Remove some sf2 references (fabpot)
|
||||
* feature #23680 [Webprofiler] Added blocks that allows extension of the profiler page. (Nyholm)
|
||||
* feature #23665 Create an interface for TranslationWriter (Nyholm)
|
||||
* feature #23890 [Translation] Adding the ability do dump <notes> in xliff2.0 (Nyholm)
|
||||
* feature #23862 [SecurityBundle] resolve class name parameter inside AddSecurityVotersPass (pjarmalavicius)
|
||||
* feature #23915 [DI] Allow get available services from service locator (Koc)
|
||||
* feature #23792 [HttpKernel][FrameworkBundle] Add RebootableInterface, fix and un-deprecate cache:clear with warmup (nicolas-grekas)
|
||||
* feature #23227 Add support for "controller" keyword for configuring routes controllers (voronkovich)
|
||||
* feature #23869 [Console] Made console command shortcuts case insensitive (thanosp)
|
||||
* feature #23855 [DI] Allow dumping inline services in Yaml (nicolas-grekas)
|
||||
* feature #23836 [FrameworkBundle] Catch Fatal errors in commands registration (chalasr)
|
||||
* feature #23805 [HttpKernel] Deprecated commands auto-registration (GuilhemN)
|
||||
* feature #23816 [Debug] Detect internal and deprecated methods (GuilhemN)
|
||||
* feature #23812 [FrameworkBundle] Allow micro kernel to subscribe events easily (ogizanagi)
|
||||
* feature #22187 [DependencyInjection] Support local binding (GuilhemN)
|
||||
* feature #23741 [DI] Generate one file per service factory (nicolas-grekas)
|
||||
* feature #23807 [Debug] Trigger a deprecation when using an internal class/trait/interface (GuilhemN)
|
||||
* feature #22587 [Workflow] Add transition completed event (izzyp)
|
||||
* feature #23624 [FrameworkBundle] Commands as a service (ro0NL)
|
||||
* feature #21111 [Validator] add groups support to the Valid constraint (xabbuh)
|
||||
* feature #20361 [Config] Enable cannotBeEmpty along with requiresAtLeastOneElement (ro0NL)
|
||||
* feature #23712 [DependencyInjection] Deprecate autowiring service auto-registration (GuilhemN)
|
||||
* feature #23719 Autoconfigure instances of ArgumentValueResolverInterface (BPScott)
|
||||
* feature #23706 [Webprofiler] Improve sql explain table display (mimol91)
|
||||
* feature #23724 [Lock] Deprecate Filesystem/LockHandler (jderusse)
|
||||
* feature #23593 [Workflow] Adding workflow name to the announce event (Nyholm)
|
||||
* feature #20496 [Form] Allow pass filter callback to delete_empty option. (Koc)
|
||||
* feature #23451 [Cache] Add (filesystem|phpfiles) cache (adapter|simple) prune method and prune command (robfrawley)
|
||||
* feature #23519 [TwigBundle] Commands as a service (ro0NL)
|
||||
* feature #23591 [VarDumper] Add time zone caster (maidmaid)
|
||||
* feature #23510 [Console] Add a factory command loader for standalone application with lazy-loading needs (ogizanagi)
|
||||
* feature #23357 [VarDumper] Add interval caster (maidmaid)
|
||||
* feature #23550 [DebugBundle] Added min_depth to Configuration (james-johnston-thumbtack)
|
||||
* feature #23570 [FrameworkBundle] Make RouterCacheWarmer implement ServiceSubscriberInterface (nicolas-grekas)
|
||||
* feature #23437 [TwigBridge] deprecate TwigRenderer (Tobion)
|
||||
* feature #23515 [VarDumper] Added setMinDepth to VarCloner (james-johnston-thumbtack)
|
||||
* feature #23404 [Serializer] AbstractObjectNormalizer: Allow to disable type enforcement (ogizanagi)
|
||||
* feature #21086 [MonologBridge] Add TokenProcessor (maidmaid)
|
||||
* feature #22576 [Validator] Allow to use a property path to get value to compare in comparison constraints (ogizanagi)
|
||||
* feature #22689 [DoctrineBridge] Add support for doctrin/dbal v2.6 types (jvasseur)
|
||||
* feature #22734 [Console] Add support for command lazy-loading (chalasr)
|
||||
* feature #19034 [Security] make it possible to configure a custom access decision manager service (xabbuh)
|
||||
* feature #23037 [TwigBundle] Added a RuntimeExtensionInterface to take advantage of autoconfigure (lyrixx)
|
||||
* feature #22176 [DI] Allow imports in string format for YAML (ro0NL)
|
||||
* feature #23295 [Security] Lazy load user providers (chalasr)
|
||||
* feature #23440 [Routing] Add matched and default parameters to redirect responses (artursvonda, Tobion)
|
||||
* feature #22832 [Debug] Deprecate support for stacked errors (mbabker)
|
||||
* feature #21469 [HttpFoundation] Find the original request protocol version (thewilkybarkid)
|
||||
* feature #23431 [Validator] Add min/max amount of pixels to Image constraint (akeeman)
|
||||
* feature #23223 Add support for microseconds in Stopwatch (javiereguiluz)
|
||||
* feature #22341 [BrowserKit] Emulate back/forward browser navigation (e-moe)
|
||||
* feature #22619 [FrameworkBundle][Translation] Move translation compiler pass (lepiaf)
|
||||
* feature #22620 [FrameworkBundle][HttpKernel] Move httpkernel pass (lepiaf)
|
||||
* feature #23337 [Component][Serializer][Normalizer] : Deal it with Has Method for the Normalizer/Denormalizer (jordscream)
|
||||
* feature #22588 [VarDumper] Add filter in VarDumperTestTrait (maidmaid)
|
||||
* feature #23288 [Yaml] deprecate the !str tag (xabbuh)
|
||||
* feature #23039 [Validator] Support for parsing PHP constants in yaml loader (mimol91)
|
||||
* feature #22431 [VarDumper] Add date caster (maidmaid)
|
||||
* feature #23285 [Stopwatch] Add a reset method (jmgq)
|
||||
* feature #23320 [WebServer] Allow * to bind all interfaces (as INADDR_ANY) (jpauli, fabpot)
|
||||
* feature #23272 [FrameworkBundle] disable unusable fragment renderers (xabbuh)
|
||||
* feature #23332 [Yaml] fix the displayed line number (fabpot, xabbuh)
|
||||
* feature #23026 [SecurityBundle] Add user impersonation info and exit action to the profiler (yceruto)
|
||||
* feature #22932 [HttpFoundation] Adds support for the immutable directive in the cache-control header (twoleds)
|
||||
* feature #22554 [Profiler][Validator] Add a validator panel in profiler (ogizanagi)
|
||||
* feature #22124 Shift responsibility for keeping Date header to ResponseHeaderBag (mpdude)
|
||||
* feature #23122 Xml encoder optional type cast (ragboyjr)
|
||||
* feature #23207 [FrameworkBundle] Allow .yaml file extension everywhere (ogizanagi)
|
||||
* feature #23076 [Validator] Adds support to check specific DNS record type for URL (iisisrael)
|
||||
* feature #22629 [Security] Trigger a deprecation when a voter is missing the VoterInterface (iltar)
|
||||
* feature #22636 [Routing] Expose request in route conditions, if needed and possible (ro0NL)
|
||||
* feature #22909 [Yaml] Deprecate using the non-specific tag (GuilhemN)
|
||||
* feature #23042 Consistent error handling in remember me services (lstrojny)
|
||||
* feature #22444 [Serializer] DateTimeNormalizer: allow to provide timezone (ogizanagi)
|
||||
* feature #23143 [DI] Reference instead of inline for array-params (nicolas-grekas)
|
||||
* feature #23154 [WebProfilerBundle] Sticky ajax window (ro0NL)
|
||||
* feature #23161 [FrameworkBundle] Deprecate useless --no-prefix option (chalasr)
|
||||
* feature #23105 [SecurityBundle][Profiler] Give info about called security listeners in profiler (chalasr)
|
||||
* feature #23148 [FrameworkBundle] drop hard dependency on the Stopwatch component (xabbuh)
|
||||
* feature #23131 [FrameworkBundle] Remove dependency on Doctrine cache (fabpot)
|
||||
* feature #23114 [SecurityBundle] Lazy load security listeners (chalasr)
|
||||
* feature #23111 [Process] Deprecate ProcessBuilder (nicolas-grekas)
|
||||
* feature #22675 [FrameworkBundle] KernelTestCase: deprecate not using KERNEL_CLASS (ogizanagi)
|
||||
* feature #22917 [VarDumper] Cycle prev/next searching in HTML dumps (ro0NL)
|
||||
* feature #23044 Automatically enable the routing annotation loader (GuilhemN)
|
||||
* feature #22696 [PropertyInfo] Made ReflectionExtractor's prefix lists instance variables (neemzy)
|
||||
* feature #23035 Deprecate passing a concrete service in optional cache warmers (romainneutron)
|
||||
* feature #23036 Implement ServiceSubscriberInterface in optional cache warmers (romainneutron)
|
||||
* feature #23022 [Di] Remove closure-proxy arguments (nicolas-grekas)
|
||||
* feature #22903 [DI] Deprecate XML services without ID (ro0NL)
|
||||
* feature #22597 [Lock] Re-add the Lock component in 3.4 (jderusse)
|
||||
* feature #22803 [DI] Deprecate Container::initialized() for privates (ro0NL)
|
||||
* feature #22828 [Finder] Deprecate FilterIterator (ogizanagi)
|
||||
* feature #22826 [Validator] improve strict option value deprecation (xabbuh)
|
||||
|
|
@ -126,6 +126,8 @@ Form
|
|||
FrameworkBundle
|
||||
---------------
|
||||
|
||||
* The `session.use_strict_mode` option has been deprecated and is enabled by default.
|
||||
|
||||
* The `cache:clear` command doesn't clear "app" PSR-6 cache pools anymore,
|
||||
but still clears "system" ones.
|
||||
Use the `cache:pool:clear` command to clear "app" pools instead.
|
||||
|
@ -235,18 +237,13 @@ HttpFoundation
|
|||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`
|
||||
class has been deprecated and will be removed in 4.0. Use the `\SessionHandler` class instead.
|
||||
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy` class has been
|
||||
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler` class has been
|
||||
deprecated and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or
|
||||
extend `AbstractSessionHandler` instead.
|
||||
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` class has been
|
||||
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
|
||||
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy` class has been
|
||||
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
|
||||
|
||||
* `NativeSessionStorage::setSaveHandler()` now takes an instance of `\SessionHandlerInterface` as argument.
|
||||
Not passing it is deprecated and will throw a `TypeError` in 4.0.
|
||||
|
||||
* Using `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` with the legacy mongo extension
|
||||
has been deprecated and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.
|
||||
|
||||
|
|
|
@ -329,6 +329,8 @@ Form
|
|||
FrameworkBundle
|
||||
---------------
|
||||
|
||||
* The `session.use_strict_mode` option has been removed and strict mode is always enabled.
|
||||
|
||||
* The `validator.mapping.cache.doctrine.apc` service has been removed.
|
||||
|
||||
* The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter have been removed. Use the `Request::setTrustedProxies()` method in your front controller instead.
|
||||
|
@ -542,12 +544,11 @@ HttpFoundation
|
|||
* The ability to check only for cacheable HTTP methods using `Request::isMethodSafe()` is
|
||||
not supported anymore, use `Request::isMethodCacheable()` instead.
|
||||
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`,
|
||||
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy`,
|
||||
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` and
|
||||
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy` classes have been removed.
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler` class has been
|
||||
removed. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
|
||||
|
||||
* `NativeSessionStorage::setSaveHandler()` now requires an instance of `\SessionHandlerInterface` as argument.
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler` and
|
||||
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` classes have been removed.
|
||||
|
||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` does not work with the legacy
|
||||
mongo extension anymore. It requires mongodb/mongodb package and ext-mongodb.
|
||||
|
|
|
@ -22,10 +22,12 @@ CHANGELOG
|
|||
* Removed the "framework.validation.cache" configuration option. Configure the "cache.validator" service under "framework.cache.pools" instead.
|
||||
* Removed `PhpStringTokenParser`, use `Symfony\Component\Translation\Extractor\PhpStringTokenParser` instead.
|
||||
* Removed `PhpExtractor`, use `Symfony\Component\Translation\Extractor\PhpExtractor` instead.
|
||||
* Removed the `use_strict_mode` session option, it's is now enabled by default
|
||||
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* Session `use_strict_mode` is now enabled by default and the corresponding option has been deprecated
|
||||
* Made the `cache:clear` command to *not* clear "app" PSR-6 cache pools anymore,
|
||||
but to still clear "system" ones; use the `cache:pool:clear` command to clear "app" pools instead
|
||||
* Always register a minimalist logger that writes in `stderr`
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* A console command for autowiring information.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DebugAutowiringCommand extends ContainerDebugCommand
|
||||
{
|
||||
protected static $defaultName = 'debug:autowiring';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDefinition(array(
|
||||
new InputArgument('search', InputArgument::OPTIONAL, 'A search filter'),
|
||||
))
|
||||
->setDescription('Lists classes/interfaces you can use for autowiring')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command displays all classes and interfaces that
|
||||
you can use as type-hints for autowiring:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
You can also pass a search term to filter the list:
|
||||
|
||||
<info>php %command.full_name% log</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
|
||||
$builder = $this->getContainerBuilder();
|
||||
$serviceIds = $builder->getServiceIds();
|
||||
$serviceIds = array_filter($serviceIds, array($this, 'filterToServiceTypes'));
|
||||
|
||||
if ($search = $input->getArgument('search')) {
|
||||
$serviceIds = array_filter($serviceIds, function ($serviceId) use ($search) {
|
||||
return false !== stripos($serviceId, $search);
|
||||
});
|
||||
|
||||
if (empty($serviceIds)) {
|
||||
$errorIo->error(sprintf('No autowirable classes or interfaces found matching "%s"', $search));
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
asort($serviceIds);
|
||||
|
||||
$io->title('Autowirable Services');
|
||||
$io->text('The following classes & interfaces can be used as type-hints when autowiring:');
|
||||
if ($search) {
|
||||
$io->text(sprintf('(only showing classes/interfaces matching <comment>%s</comment>)', $search));
|
||||
}
|
||||
$io->newLine();
|
||||
$tableRows = array();
|
||||
foreach ($serviceIds as $serviceId) {
|
||||
$tableRows[] = array(sprintf('<fg=cyan>%s</fg=cyan>', $serviceId));
|
||||
if ($builder->hasAlias($serviceId)) {
|
||||
$tableRows[] = array(sprintf(' alias to %s', $builder->getAlias($serviceId)));
|
||||
}
|
||||
}
|
||||
|
||||
$io->table(array(), $tableRows);
|
||||
}
|
||||
}
|
|
@ -413,11 +413,10 @@ class Configuration implements ConfigurationInterface
|
|||
->scalarNode('gc_divisor')->end()
|
||||
->scalarNode('gc_probability')->defaultValue(1)->end()
|
||||
->scalarNode('gc_maxlifetime')->end()
|
||||
->booleanNode('use_strict_mode')->end()
|
||||
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
|
||||
->integerNode('metadata_update_threshold')
|
||||
->defaultValue('0')
|
||||
->info('seconds to wait between 2 session metadata updates, it will also prevent the session handler to write if the session has not changed')
|
||||
->info('seconds to wait between 2 session metadata updates')
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
|
|
|
@ -728,7 +728,7 @@ class FrameworkExtension extends Extension
|
|||
// session storage
|
||||
$container->setAlias('session.storage', $config['storage_id'])->setPrivate(true);
|
||||
$options = array();
|
||||
foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'use_strict_mode') as $key) {
|
||||
foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor') as $key) {
|
||||
if (isset($config[$key])) {
|
||||
$options[$key] = $config[$key];
|
||||
}
|
||||
|
@ -742,14 +742,7 @@ class FrameworkExtension extends Extension
|
|||
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
|
||||
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
|
||||
} else {
|
||||
$handlerId = $config['handler_id'];
|
||||
|
||||
if ($config['metadata_update_threshold'] > 0) {
|
||||
$container->getDefinition('session.handler.write_check')->addArgument(new Reference($handlerId));
|
||||
$handlerId = 'session.handler.write_check';
|
||||
}
|
||||
|
||||
$container->setAlias('session.handler', $handlerId)->setPrivate(true);
|
||||
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
|
||||
}
|
||||
|
||||
$container->setParameter('session.save_path', $config['save_path']);
|
||||
|
|
|
@ -111,5 +111,6 @@
|
|||
<service id="cache.global_clearer" parent="cache.default_clearer" public="true" />
|
||||
<service id="cache.app_clearer" alias="cache.default_clearer" public="true" />
|
||||
<service id="Psr\Cache\CacheItemPoolInterface" alias="cache.app" />
|
||||
<service id="Symfony\Component\Cache\Adapter\AdapterInterface" alias="cache.app" />
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
@ -55,6 +55,10 @@
|
|||
<tag name="console.command" command="debug:container" />
|
||||
</service>
|
||||
|
||||
<service id="Symfony\Bundle\FrameworkBundle\Command\DebugAutowiringCommand">
|
||||
<tag name="console.command" command="debug:autowiring" />
|
||||
</service>
|
||||
|
||||
<service id="Symfony\Bundle\FrameworkBundle\Command\EventDispatcherDebugCommand">
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
<tag name="console.command" command="debug:event-dispatcher" />
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
<service id="Symfony\Component\Routing\RouterInterface" alias="router" />
|
||||
<service id="Symfony\Component\Routing\Generator\UrlGeneratorInterface" alias="router" />
|
||||
<service id="Symfony\Component\Routing\Matcher\UrlMatcherInterface" alias="router" />
|
||||
<service id="Symfony\Component\Routing\RequestContextAwareInterface" alias="router" />
|
||||
|
||||
<service id="router.request_context" class="Symfony\Component\Routing\RequestContext">
|
||||
<argument>%router.request_context.base_url%</argument>
|
||||
|
|
|
@ -48,12 +48,14 @@
|
|||
<argument type="service" id="session.storage.metadata_bag" />
|
||||
</service>
|
||||
|
||||
<service id="session.handler.native_file" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler">
|
||||
<argument>%session.save_path%</argument>
|
||||
<service id="session.handler.native_file" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler">
|
||||
<argument type="service">
|
||||
<service class="Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler">
|
||||
<argument>%session.save_path%</argument>
|
||||
</service>
|
||||
</argument>
|
||||
</service>
|
||||
|
||||
<service id="session.handler.write_check" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler" />
|
||||
|
||||
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service">
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Console\Tester\ApplicationTester;
|
||||
|
||||
/**
|
||||
* @group functional
|
||||
*/
|
||||
class DebugAutowiringCommandTest extends WebTestCase
|
||||
{
|
||||
public function testBasicFunctionality()
|
||||
{
|
||||
static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml'));
|
||||
|
||||
$application = new Application(static::$kernel);
|
||||
$application->setAutoExit(false);
|
||||
|
||||
$tester = new ApplicationTester($application);
|
||||
$tester->run(array('command' => 'debug:autowiring'));
|
||||
|
||||
$this->assertContains('Symfony\Component\HttpKernel\HttpKernelInterface', $tester->getDisplay());
|
||||
$this->assertContains('alias to http_kernel', $tester->getDisplay());
|
||||
}
|
||||
|
||||
public function testSearchArgument()
|
||||
{
|
||||
static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml'));
|
||||
|
||||
$application = new Application(static::$kernel);
|
||||
$application->setAutoExit(false);
|
||||
|
||||
$tester = new ApplicationTester($application);
|
||||
$tester->run(array('command' => 'debug:autowiring', 'search' => 'kern'));
|
||||
|
||||
$this->assertContains('Symfony\Component\HttpKernel\HttpKernelInterface', $tester->getDisplay());
|
||||
$this->assertNotContains('Symfony\Component\Routing\RouterInterface', $tester->getDisplay());
|
||||
}
|
||||
|
||||
public function testSearchNoResults()
|
||||
{
|
||||
static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml'));
|
||||
|
||||
$application = new Application(static::$kernel);
|
||||
$application->setAutoExit(false);
|
||||
|
||||
$tester = new ApplicationTester($application);
|
||||
$tester->run(array('command' => 'debug:autowiring', 'search' => 'foo_fake'), array('capture_stderr_separately' => true));
|
||||
|
||||
$this->assertContains('No autowirable classes or interfaces found matching "foo_fake"', $tester->getErrorOutput());
|
||||
$this->assertEquals(1, $tester->getStatusCode());
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@
|
|||
<argument type="service" id="event_dispatcher" />
|
||||
</call>
|
||||
</service>
|
||||
<service id="Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface" alias="security.authentication.manager" />
|
||||
|
||||
<service id="security.authentication.trust_resolver" class="Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver">
|
||||
<argument>%security.authentication.trust_resolver.anonymous_class%</argument>
|
||||
|
|
|
@ -121,15 +121,23 @@ class ConsoleLogger extends AbstractLogger
|
|||
*/
|
||||
private function interpolate($message, array $context)
|
||||
{
|
||||
// build a replacement array with braces around the context keys
|
||||
$replace = array();
|
||||
if (false === strpos($message, '{')) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
$replacements = array();
|
||||
foreach ($context as $key => $val) {
|
||||
if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
|
||||
$replace[sprintf('{%s}', $key)] = $val;
|
||||
if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
|
||||
$replacements["{{$key}}"] = $val;
|
||||
} elseif ($val instanceof \DateTimeInterface) {
|
||||
$replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
|
||||
} elseif (\is_object($val)) {
|
||||
$replacements["{{$key}}"] = '[object '.\get_class($val).']';
|
||||
} else {
|
||||
$replacements["{{$key}}"] = '['.\gettype($val).']';
|
||||
}
|
||||
}
|
||||
|
||||
// interpolate replacement values into the message and return
|
||||
return strtr($message, $replace);
|
||||
return strtr($message, $replacements);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,9 +166,7 @@ class ConsoleLoggerTest extends TestCase
|
|||
} else {
|
||||
$dummy = $this->getMock('Symfony\Component\Console\Tests\Logger\DummyTest', array('__toString'));
|
||||
}
|
||||
$dummy->expects($this->once())
|
||||
->method('__toString')
|
||||
->will($this->returnValue('DUMMY'));
|
||||
$dummy->method('__toString')->will($this->returnValue('DUMMY'));
|
||||
|
||||
$this->getLogger()->warning($dummy);
|
||||
|
||||
|
|
|
@ -67,7 +67,6 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
|
|||
$value->setArguments($this->processValue($value->getArguments()));
|
||||
$value->setProperties($this->processValue($value->getProperties()));
|
||||
$value->setMethodCalls($this->processValue($value->getMethodCalls()));
|
||||
$value->setBindings($this->processValue($value->getBindings()));
|
||||
|
||||
$changes = $value->getChanges();
|
||||
if (isset($changes['factory'])) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
@ -88,6 +89,20 @@ class CheckExceptionOnInvalidReferenceBehaviorPassTest extends TestCase
|
|||
$this->process($container);
|
||||
}
|
||||
|
||||
public function testProcessDefinitionWithBindings()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container
|
||||
->register('b')
|
||||
->setBindings(array(new BoundArgument(new Reference('a'))))
|
||||
;
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
private function process(ContainerBuilder $container)
|
||||
{
|
||||
$pass = new CheckExceptionOnInvalidReferenceBehaviorPass();
|
||||
|
|
|
@ -17,8 +17,7 @@ CHANGELOG
|
|||
* checking for cacheable HTTP methods using the `Request::isMethodSafe()`
|
||||
method (by not passing `false` as its argument) is not supported anymore and
|
||||
throws a `\BadMethodCallException`
|
||||
* the `NativeSessionHandler` class has been removed
|
||||
* the `AbstractProxy`, `NativeProxy` and `SessionHandlerProxy` classes have been removed
|
||||
* the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes have been removed
|
||||
* setting session save handlers that do not implement `\SessionHandlerInterface` in
|
||||
`NativeSessionStorage::setSaveHandler()` is not supported anymore and throws a
|
||||
`\TypeError`
|
||||
|
@ -26,8 +25,9 @@ CHANGELOG
|
|||
3.4.0
|
||||
-----
|
||||
|
||||
* deprecated the `NativeSessionHandler` class,
|
||||
* deprecated the `AbstractProxy`, `NativeProxy` and `SessionHandlerProxy` classes,
|
||||
* implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new
|
||||
`AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper
|
||||
* deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes
|
||||
* deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
|
||||
* deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
|
||||
* deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* This abstract session handler provides a generic implementation
|
||||
* of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
|
||||
* enabling strict and lazy session handling.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
|
||||
{
|
||||
private $sessionName;
|
||||
private $prefetchId;
|
||||
private $prefetchData;
|
||||
private $newSessionId;
|
||||
private $igbinaryEmptyData;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$this->sessionName = $sessionName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function doRead($sessionId);
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
* @param string $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function doWrite($sessionId, $data);
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function doDestroy($sessionId);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
$this->prefetchData = $this->read($sessionId);
|
||||
$this->prefetchId = $sessionId;
|
||||
|
||||
return '' !== $this->prefetchData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
if (null !== $this->prefetchId) {
|
||||
$prefetchId = $this->prefetchId;
|
||||
$prefetchData = $this->prefetchData;
|
||||
$this->prefetchId = $this->prefetchData = null;
|
||||
|
||||
if ($prefetchId === $sessionId || '' === $prefetchData) {
|
||||
$this->newSessionId = '' === $prefetchData ? $sessionId : null;
|
||||
|
||||
return $prefetchData;
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->doRead($sessionId);
|
||||
$this->newSessionId = '' === $data ? $sessionId : null;
|
||||
if (\PHP_VERSION_ID < 70000) {
|
||||
$this->prefetchData = $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70000 && $this->prefetchData) {
|
||||
$readData = $this->prefetchData;
|
||||
$this->prefetchData = null;
|
||||
|
||||
if ($readData === $data) {
|
||||
return $this->updateTimestamp($sessionId, $data);
|
||||
}
|
||||
}
|
||||
if (null === $this->igbinaryEmptyData) {
|
||||
// see https://github.com/igbinary/igbinary/issues/146
|
||||
$this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize(array()) : '';
|
||||
}
|
||||
if ('' === $data || $this->igbinaryEmptyData === $data) {
|
||||
return $this->destroy($sessionId);
|
||||
}
|
||||
$this->newSessionId = null;
|
||||
|
||||
return $this->doWrite($sessionId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70000) {
|
||||
$this->prefetchData = null;
|
||||
}
|
||||
if (!headers_sent() && ini_get('session.use_cookies')) {
|
||||
if (!$this->sessionName) {
|
||||
throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', get_class($this)));
|
||||
}
|
||||
$sessionCookie = sprintf(' %s=', urlencode($this->sessionName));
|
||||
$sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId));
|
||||
$sessionCookieFound = false;
|
||||
$otherCookies = array();
|
||||
foreach (headers_list() as $h) {
|
||||
if (0 !== stripos($h, 'Set-Cookie:')) {
|
||||
continue;
|
||||
}
|
||||
if (11 === strpos($h, $sessionCookie, 11)) {
|
||||
$sessionCookieFound = true;
|
||||
|
||||
if (11 !== strpos($h, $sessionCookieWithId, 11)) {
|
||||
$otherCookies[] = $h;
|
||||
}
|
||||
} else {
|
||||
$otherCookies[] = $h;
|
||||
}
|
||||
}
|
||||
if ($sessionCookieFound) {
|
||||
header_remove('Set-Cookie');
|
||||
foreach ($otherCookies as $h) {
|
||||
header('Set-Cookie:'.$h, false);
|
||||
}
|
||||
} else {
|
||||
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class MemcachedSessionHandler implements \SessionHandlerInterface
|
||||
class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
/**
|
||||
* @var \Memcached Memcached driver
|
||||
|
@ -39,7 +39,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* List of available options:
|
||||
* * prefix: The prefix to use for the memcached keys in order to avoid collision
|
||||
* * expiretime: The time to live in seconds
|
||||
* * expiretime: The time to live in seconds.
|
||||
*
|
||||
* @param \Memcached $memcached A \Memcached instance
|
||||
* @param array $options An associative array of Memcached options
|
||||
|
@ -60,14 +60,6 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
|||
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -79,7 +71,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
return $this->memcached->get($this->prefix.$sessionId) ?: '';
|
||||
}
|
||||
|
@ -87,7 +79,15 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
$result = $this->memcached->delete($this->prefix.$sessionId);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
* @see https://packagist.org/packages/mongodb/mongodb
|
||||
* @see http://php.net/manual/en/set.mongodb.php
|
||||
*/
|
||||
class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
/**
|
||||
* @var \MongoDB\Client
|
||||
|
@ -43,7 +43,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||
* * id_field: The field name for storing the session id [default: _id]
|
||||
* * data_field: The field name for storing the session data [default: data]
|
||||
* * time_field: The field name for storing the timestamp [default: time]
|
||||
* * expiry_field: The field name for storing the expiry-timestamp [default: expires_at]
|
||||
* * expiry_field: The field name for storing the expiry-timestamp [default: expires_at].
|
||||
*
|
||||
* It is strongly recommended to put an index on the `expiry_field` for
|
||||
* garbage-collection. Alternatively it's possible to automatically expire
|
||||
|
@ -83,14 +83,6 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||
), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -102,7 +94,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
$this->getCollection()->deleteOne(array(
|
||||
$this->options['id_field'] => $sessionId,
|
||||
|
@ -126,7 +118,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
|
@ -148,7 +140,34 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
$methodName = 'updateOne';
|
||||
$options = array();
|
||||
} else {
|
||||
$methodName = 'update';
|
||||
$options = array('multiple' => false);
|
||||
}
|
||||
|
||||
$this->getCollection()->$methodName(
|
||||
array($this->options['id_field'] => $sessionId),
|
||||
array('$set' => array(
|
||||
$this->options['time_field'] => $this->createDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
)),
|
||||
$options
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
$dbData = $this->getCollection()->findOne(array(
|
||||
$this->options['id_field'] => $sessionId,
|
||||
|
|
|
@ -16,16 +16,8 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class NullSessionHandler implements \SessionHandlerInterface
|
||||
class NullSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -37,15 +29,7 @@ class NullSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -53,7 +37,31 @@ class NullSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
* @author Michael Williams <michael.williams@funsational.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class PdoSessionHandler implements \SessionHandlerInterface
|
||||
class PdoSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
/**
|
||||
* No locking is done. This means sessions are prone to loss of data due to
|
||||
|
@ -260,11 +260,13 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$this->sessionExpired = false;
|
||||
|
||||
if (null === $this->pdo) {
|
||||
$this->connect($this->dsn ?: $savePath);
|
||||
}
|
||||
|
||||
return true;
|
||||
return parent::open($savePath, $sessionName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,7 +275,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||
public function read($sessionId)
|
||||
{
|
||||
try {
|
||||
return $this->doRead($sessionId);
|
||||
return parent::read($sessionId);
|
||||
} catch (\PDOException $e) {
|
||||
$this->rollback();
|
||||
|
||||
|
@ -296,7 +298,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
// delete the record associated with this id
|
||||
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
|
||||
|
@ -317,7 +319,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
||||
|
||||
|
@ -372,6 +374,30 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
||||
|
||||
try {
|
||||
$updateStmt = $this->pdo->prepare(
|
||||
"UPDATE $this->table SET $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"
|
||||
);
|
||||
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
|
||||
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$updateStmt->execute();
|
||||
} catch (\PDOException $e) {
|
||||
$this->rollback();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -491,10 +517,8 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||
*
|
||||
* @return string The session data
|
||||
*/
|
||||
private function doRead($sessionId)
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
$this->sessionExpired = false;
|
||||
|
||||
if (self::LOCK_ADVISORY === $this->lockMode) {
|
||||
$this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
|
||||
}
|
||||
|
@ -517,7 +541,9 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
||||
}
|
||||
|
||||
if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
||||
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
||||
// In strict mode, session fixation is not possible: new sessions always start with a unique
|
||||
// random id, so that concurrency is not possible and this code path can be skipped.
|
||||
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
|
||||
// until other connections to the session are committed.
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* Adds basic `SessionUpdateTimestampHandlerInterface` behaviors to another `SessionHandlerInterface`.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class StrictSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function __construct(\SessionHandlerInterface $handler)
|
||||
{
|
||||
if ($handler instanceof \SessionUpdateTimestampHandlerInterface) {
|
||||
throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', get_class($handler), self::class));
|
||||
}
|
||||
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
parent::open($savePath, $sessionName);
|
||||
|
||||
return $this->handler->open($savePath, $sessionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
return $this->handler->read($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return $this->write($sessionId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
return $this->handler->write($sessionId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
return $this->handler->destroy($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return $this->handler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return $this->handler->gc($maxlifetime);
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* Wraps another SessionHandlerInterface to only write the session when it has been modified.
|
||||
*
|
||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||
*/
|
||||
class WriteCheckSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \SessionHandlerInterface
|
||||
*/
|
||||
private $wrappedSessionHandler;
|
||||
|
||||
/**
|
||||
* @var array sessionId => session
|
||||
*/
|
||||
private $readSessions;
|
||||
|
||||
public function __construct(\SessionHandlerInterface $wrappedSessionHandler)
|
||||
{
|
||||
$this->wrappedSessionHandler = $wrappedSessionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return $this->wrappedSessionHandler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return $this->wrappedSessionHandler->destroy($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return $this->wrappedSessionHandler->gc($maxlifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return $this->wrappedSessionHandler->open($savePath, $sessionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
$session = $this->wrappedSessionHandler->read($sessionId);
|
||||
|
||||
$this->readSessions[$sessionId] = $session;
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
if (isset($this->readSessions[$sessionId]) && $data === $this->readSessions[$sessionId]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->wrappedSessionHandler->write($sessionId, $data);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,9 @@
|
|||
namespace Symfony\Component\HttpFoundation\Session\Storage;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||
|
||||
/**
|
||||
* This provides a base class for session attribute storage.
|
||||
|
@ -23,7 +26,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
/**
|
||||
* @var SessionBagInterface[]
|
||||
*/
|
||||
protected $bags;
|
||||
protected $bags = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
@ -36,7 +39,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
protected $closed = false;
|
||||
|
||||
/**
|
||||
* @var \SessionHandlerInterface
|
||||
* @var AbstractProxy|\SessionHandlerInterface
|
||||
*/
|
||||
protected $saveHandler;
|
||||
|
||||
|
@ -85,13 +88,18 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
* sid_bits_per_character, "5"
|
||||
* trans_sid_hosts, $_SERVER['HTTP_HOST']
|
||||
* trans_sid_tags, "a=href,area=href,frame=src,form="
|
||||
*
|
||||
* @param array $options Session configuration options
|
||||
* @param \SessionHandlerInterface|null $handler
|
||||
* @param MetadataBag $metaBag MetadataBag
|
||||
*/
|
||||
public function __construct(array $options = array(), \SessionHandlerInterface $handler = null, MetadataBag $metaBag = null)
|
||||
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
|
||||
{
|
||||
$options += array(
|
||||
// disable by default because it's managed by HeaderBag (if used)
|
||||
'cache_limiter' => '',
|
||||
'cache_limiter' => 'private_no_expire',
|
||||
'use_cookies' => 1,
|
||||
'lazy_write' => 1,
|
||||
'use_strict_mode' => 1,
|
||||
);
|
||||
|
||||
session_register_shutdown();
|
||||
|
@ -104,7 +112,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
/**
|
||||
* Gets the save handler instance.
|
||||
*
|
||||
* @return \SessionHandlerInterface
|
||||
* @return AbstractProxy|\SessionHandlerInterface
|
||||
*/
|
||||
public function getSaveHandler()
|
||||
{
|
||||
|
@ -143,21 +151,15 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
*/
|
||||
public function getId()
|
||||
{
|
||||
return session_id();
|
||||
return $this->saveHandler->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \LogicException When the session is active
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
if (\PHP_SESSION_ACTIVE === session_status()) {
|
||||
throw new \LogicException('Cannot change the ID of an active session');
|
||||
}
|
||||
|
||||
session_id($id);
|
||||
$this->saveHandler->setId($id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,21 +167,15 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
*/
|
||||
public function getName()
|
||||
{
|
||||
return session_name();
|
||||
return $this->saveHandler->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \LogicException When the session is active
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
if (\PHP_SESSION_ACTIVE === session_status()) {
|
||||
throw new \LogicException('Cannot change the name of an active session');
|
||||
}
|
||||
|
||||
session_name($name);
|
||||
$this->saveHandler->setName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,20 +214,38 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
*/
|
||||
public function save()
|
||||
{
|
||||
$session = $_SESSION;
|
||||
|
||||
foreach ($this->bags as $bag) {
|
||||
if (empty($_SESSION[$key = $bag->getStorageKey()])) {
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
}
|
||||
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) {
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
|
||||
// Register custom error handler to catch a possible failure warning during session write
|
||||
set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline, $errcontext);
|
||||
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
|
||||
throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline);
|
||||
}, E_WARNING);
|
||||
|
||||
try {
|
||||
$e = null;
|
||||
session_write_close();
|
||||
restore_error_handler();
|
||||
} catch (\ErrorException $e) {
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
$_SESSION = $session;
|
||||
}
|
||||
if (null !== $e) {
|
||||
// The default PHP error message is not very helpful, as it does not give any information on the current save handler.
|
||||
// Therefore, we catch this error and trigger a warning with a better error message
|
||||
$handler = $this->getSaveHandler();
|
||||
if ($handler instanceof SessionHandlerProxy) {
|
||||
$handler = $handler->getHandler();
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', get_class($handler)), E_USER_WARNING);
|
||||
}
|
||||
|
||||
|
@ -258,8 +272,6 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \LogicException When the session is already started
|
||||
*/
|
||||
public function registerBag(SessionBagInterface $bag)
|
||||
{
|
||||
|
@ -279,7 +291,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
|
||||
}
|
||||
|
||||
if (!$this->started && \PHP_SESSION_ACTIVE === session_status()) {
|
||||
if (!$this->started && $this->saveHandler->isActive()) {
|
||||
$this->loadSession();
|
||||
} elseif (!$this->started) {
|
||||
$this->start();
|
||||
|
@ -372,16 +384,36 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
* @see http://php.net/sessionhandlerinterface
|
||||
* @see http://php.net/sessionhandler
|
||||
* @see http://github.com/drak/NativeSession
|
||||
*
|
||||
* @param \SessionHandlerInterface|null $saveHandler
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setSaveHandler(\SessionHandlerInterface $saveHandler = null)
|
||||
public function setSaveHandler($saveHandler = null)
|
||||
{
|
||||
$this->saveHandler = $saveHandler ?: new \SessionHandler();
|
||||
if (!$saveHandler instanceof AbstractProxy &&
|
||||
!$saveHandler instanceof \SessionHandlerInterface &&
|
||||
null !== $saveHandler) {
|
||||
throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
|
||||
}
|
||||
|
||||
if (headers_sent($file, $line)) {
|
||||
throw new \RuntimeException(sprintf('Failed to set the session handler because headers have already been sent by "%s" at line %d.', $file, $line));
|
||||
}
|
||||
|
||||
session_set_save_handler($this->saveHandler, false);
|
||||
// Wrap $saveHandler in proxy and prevent double wrapping of proxy
|
||||
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
|
||||
$saveHandler = new SessionHandlerProxy($saveHandler);
|
||||
} elseif (!$saveHandler instanceof AbstractProxy) {
|
||||
$saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
|
||||
}
|
||||
$this->saveHandler = $saveHandler;
|
||||
|
||||
if ($this->saveHandler instanceof SessionHandlerProxy) {
|
||||
session_set_save_handler($this->saveHandler->getHandler(), false);
|
||||
} elseif ($this->saveHandler instanceof \SessionHandlerInterface) {
|
||||
session_set_save_handler($this->saveHandler, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -400,12 +432,11 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
$session = &$_SESSION;
|
||||
}
|
||||
|
||||
$bags = $this->bags;
|
||||
$bags[] = $this->metadataBag;
|
||||
$bags = array_merge($this->bags, array($this->metadataBag));
|
||||
|
||||
foreach ($bags as $bag) {
|
||||
$key = $bag->getStorageKey();
|
||||
$session[$key] = $session[$key] ?? array();
|
||||
$session[$key] = isset($session[$key]) ? $session[$key] : array();
|
||||
$bag->initialize($session[$key]);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
||||
|
||||
/**
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
abstract class AbstractProxy
|
||||
{
|
||||
/**
|
||||
* Flag if handler wraps an internal PHP session handler (using \SessionHandler).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $wrapper = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $saveHandlerName;
|
||||
|
||||
/**
|
||||
* Gets the session.save_handler name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSaveHandlerName()
|
||||
{
|
||||
return $this->saveHandlerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this proxy handler and instance of \SessionHandlerInterface.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSessionHandlerInterface()
|
||||
{
|
||||
return $this instanceof \SessionHandlerInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWrapper()
|
||||
{
|
||||
return $this->wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has a session started?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return \PHP_SESSION_ACTIVE === session_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return session_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
if ($this->isActive()) {
|
||||
throw new \LogicException('Cannot change the ID of an active session');
|
||||
}
|
||||
|
||||
session_id($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return session_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
if ($this->isActive()) {
|
||||
throw new \LogicException('Cannot change the name of an active session');
|
||||
}
|
||||
|
||||
session_name($name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
||||
|
||||
/**
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \SessionHandlerInterface
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
public function __construct(\SessionHandlerInterface $handler)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->wrapper = ($handler instanceof \SessionHandler);
|
||||
$this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \SessionHandlerInterface
|
||||
*/
|
||||
public function getHandler()
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
// \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return (bool) $this->handler->open($savePath, $sessionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return (bool) $this->handler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
return (string) $this->handler->read($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
return (bool) $this->handler->write($sessionId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return (bool) $this->handler->destroy($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return (bool) $this->handler->gc($maxlifetime);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @requires PHP 7.0
|
||||
*/
|
||||
class AbstractSessionHandlerTest extends TestCase
|
||||
{
|
||||
private static $server;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
$spec = array(
|
||||
1 => array('file', '/dev/null', 'w'),
|
||||
2 => array('file', '/dev/null', 'w'),
|
||||
);
|
||||
if (!self::$server = @proc_open('exec php -S localhost:8053', $spec, $pipes, __DIR__.'/Fixtures')) {
|
||||
self::markTestSkipped('PHP server unable to start.');
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
if (self::$server) {
|
||||
proc_terminate(self::$server);
|
||||
proc_close(self::$server);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSession
|
||||
*/
|
||||
public function testSession($fixture)
|
||||
{
|
||||
$context = array('http' => array('header' => "Cookie: sid=123abc\r\n"));
|
||||
$context = stream_context_create($context);
|
||||
$result = file_get_contents(sprintf('http://localhost:8053/%s.php', $fixture), false, $context);
|
||||
|
||||
$this->assertStringEqualsFile(__DIR__.sprintf('/Fixtures/%s.expected', $fixture), $result);
|
||||
}
|
||||
|
||||
public function provideSession()
|
||||
{
|
||||
foreach (glob(__DIR__.'/Fixtures/*.php') as $file) {
|
||||
yield array(pathinfo($file, PATHINFO_FILENAME));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
|
||||
|
||||
$parent = __DIR__;
|
||||
while (!@file_exists($parent.'/vendor/autoload.php')) {
|
||||
if (!@file_exists($parent)) {
|
||||
// open_basedir restriction in effect
|
||||
break;
|
||||
}
|
||||
if ($parent === dirname($parent)) {
|
||||
echo "vendor/autoload.php not found\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$parent = dirname($parent);
|
||||
}
|
||||
|
||||
require $parent.'/vendor/autoload.php';
|
||||
|
||||
error_reporting(-1);
|
||||
ini_set('html_errors', 0);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('session.gc_probability', 0);
|
||||
ini_set('session.serialize_handler', 'php');
|
||||
ini_set('session.cookie_lifetime', 0);
|
||||
ini_set('session.cookie_domain', '');
|
||||
ini_set('session.cookie_secure', '');
|
||||
ini_set('session.cookie_httponly', '');
|
||||
ini_set('session.use_cookies', 1);
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
ini_set('session.cache_expire', 180);
|
||||
ini_set('session.cookie_path', '/');
|
||||
ini_set('session.cookie_domain', '');
|
||||
ini_set('session.cookie_secure', 1);
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
ini_set('session.use_strict_mode', 1);
|
||||
ini_set('session.lazy_write', 1);
|
||||
ini_set('session.name', 'sid');
|
||||
ini_set('session.save_path', __DIR__);
|
||||
ini_set('session.cache_limiter', 'private_no_expire');
|
||||
|
||||
header_remove('X-Powered-By');
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
register_shutdown_function(function () {
|
||||
echo "\n";
|
||||
header_remove('Last-Modified');
|
||||
session_write_close();
|
||||
print_r(headers_list());
|
||||
echo "shutdown\n";
|
||||
});
|
||||
ob_start();
|
||||
|
||||
class TestSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
private $data;
|
||||
|
||||
public function __construct($data = '')
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function open($path, $name)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return parent::open($path, $name);
|
||||
}
|
||||
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return parent::validateId($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return parent::read($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return parent::write($sessionId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return parent::destroy($sessionId);
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function gc($maxLifetime)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
echo __FUNCTION__.': ', $this->data, "\n";
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
echo __FUNCTION__.': ', $data, "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
echo __FUNCTION__, "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
open
|
||||
validateId
|
||||
read
|
||||
doRead: abc|i:123;
|
||||
read
|
||||
|
||||
write
|
||||
destroy
|
||||
doDestroy
|
||||
close
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: private, max-age=10800
|
||||
[2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
|
||||
)
|
||||
shutdown
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
require __DIR__.'/common.inc';
|
||||
|
||||
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||
session_start();
|
||||
|
||||
unset($_SESSION['abc']);
|
|
@ -0,0 +1,14 @@
|
|||
open
|
||||
validateId
|
||||
read
|
||||
doRead: abc|i:123;
|
||||
read
|
||||
123
|
||||
updateTimestamp
|
||||
close
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: private, max-age=10800
|
||||
)
|
||||
shutdown
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
require __DIR__.'/common.inc';
|
||||
|
||||
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||
session_start();
|
||||
|
||||
echo $_SESSION['abc'];
|
|
@ -0,0 +1,24 @@
|
|||
open
|
||||
validateId
|
||||
read
|
||||
doRead: abc|i:123;
|
||||
read
|
||||
destroy
|
||||
doDestroy
|
||||
close
|
||||
open
|
||||
validateId
|
||||
read
|
||||
doRead: abc|i:123;
|
||||
read
|
||||
|
||||
write
|
||||
doWrite: abc|i:123;
|
||||
close
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: private, max-age=10800
|
||||
[2] => Set-Cookie: sid=random_session_id; path=/; secure; HttpOnly
|
||||
)
|
||||
shutdown
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
require __DIR__.'/common.inc';
|
||||
|
||||
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||
session_start();
|
||||
|
||||
session_regenerate_id(true);
|
||||
|
||||
ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
|
|
@ -0,0 +1,20 @@
|
|||
open
|
||||
validateId
|
||||
read
|
||||
doRead:
|
||||
read
|
||||
Array
|
||||
(
|
||||
[0] => bar
|
||||
)
|
||||
$_SESSION is not empty
|
||||
write
|
||||
destroy
|
||||
close
|
||||
$_SESSION is not empty
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: private, max-age=10800
|
||||
)
|
||||
shutdown
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
require __DIR__.'/common.inc';
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||
|
||||
$storage = new NativeSessionStorage();
|
||||
$storage->setSaveHandler(new TestSessionHandler());
|
||||
$flash = new FlashBag();
|
||||
$storage->registerBag($flash);
|
||||
$storage->start();
|
||||
|
||||
$flash->add('foo', 'bar');
|
||||
|
||||
print_r($flash->get('foo'));
|
||||
echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
|
||||
echo "\n";
|
||||
|
||||
$storage->save();
|
||||
|
||||
echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
|
||||
|
||||
ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
|
|
@ -0,0 +1,15 @@
|
|||
open
|
||||
validateId
|
||||
read
|
||||
doRead: abc|i:123;
|
||||
read
|
||||
|
||||
updateTimestamp
|
||||
close
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: private, max-age=10800
|
||||
[2] => Set-Cookie: abc=def
|
||||
)
|
||||
shutdown
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
require __DIR__.'/common.inc';
|
||||
|
||||
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||
session_start();
|
||||
|
||||
setcookie('abc', 'def');
|
|
@ -153,6 +153,10 @@ class PdoSessionHandlerTest extends TestCase
|
|||
|
||||
public function testReadLockedConvertsStreamToString()
|
||||
{
|
||||
if (ini_get('session.use_strict_mode')) {
|
||||
$this->markTestSkipped('Strict mode needs no locking for new sessions.');
|
||||
}
|
||||
|
||||
$pdo = new MockPdo('pgsql');
|
||||
$selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
|
||||
$insertStmt = $this->getMockBuilder('PDOStatement')->getMock();
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
|
||||
|
||||
class StrictSessionHandlerTest extends TestCase
|
||||
{
|
||||
public function testOpen()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('open')
|
||||
->with('path', 'name')->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertInstanceof('SessionUpdateTimestampHandlerInterface', $proxy);
|
||||
$this->assertInstanceof(AbstractSessionHandler::class, $proxy);
|
||||
$this->assertTrue($proxy->open('path', 'name'));
|
||||
}
|
||||
|
||||
public function testCloseSession()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('close')
|
||||
->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->close());
|
||||
}
|
||||
|
||||
public function testValidateIdOK()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('data');
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->validateId('id'));
|
||||
}
|
||||
|
||||
public function testValidateIdKO()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('');
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertFalse($proxy->validateId('id'));
|
||||
}
|
||||
|
||||
public function testRead()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('data');
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertSame('data', $proxy->read('id'));
|
||||
}
|
||||
|
||||
public function testReadWithValidateIdOK()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('data');
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->validateId('id'));
|
||||
$this->assertSame('data', $proxy->read('id'));
|
||||
}
|
||||
|
||||
public function testReadWithValidateIdMismatch()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->exactly(2))->method('read')
|
||||
->withConsecutive(array('id1'), array('id2'))
|
||||
->will($this->onConsecutiveCalls('data1', 'data2'));
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->validateId('id1'));
|
||||
$this->assertSame('data2', $proxy->read('id2'));
|
||||
}
|
||||
|
||||
public function testUpdateTimestamp()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('write')
|
||||
->with('id', 'data')->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->updateTimestamp('id', 'data'));
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('write')
|
||||
->with('id', 'data')->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->write('id', 'data'));
|
||||
}
|
||||
|
||||
public function testWriteEmptyNewSession()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('');
|
||||
$handler->expects($this->never())->method('write');
|
||||
$handler->expects($this->never())->method('destroy');
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertFalse($proxy->validateId('id'));
|
||||
$this->assertSame('', $proxy->read('id'));
|
||||
$this->assertTrue($proxy->write('id', ''));
|
||||
}
|
||||
|
||||
public function testWriteEmptyExistingSession()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('data');
|
||||
$handler->expects($this->never())->method('write');
|
||||
$handler->expects($this->once())->method('destroy')->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertSame('data', $proxy->read('id'));
|
||||
$this->assertTrue($proxy->write('id', ''));
|
||||
}
|
||||
|
||||
public function testDestroy()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('destroy')
|
||||
->with('id')->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->destroy('id'));
|
||||
}
|
||||
|
||||
public function testDestroyNewSession()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('');
|
||||
$handler->expects($this->never())->method('destroy');
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertSame('', $proxy->read('id'));
|
||||
$this->assertTrue($proxy->destroy('id'));
|
||||
}
|
||||
|
||||
public function testDestroyNonEmptyNewSession()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('read')
|
||||
->with('id')->willReturn('');
|
||||
$handler->expects($this->once())->method('write')
|
||||
->with('id', 'data')->willReturn(true);
|
||||
$handler->expects($this->once())->method('destroy')
|
||||
->with('id')->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertSame('', $proxy->read('id'));
|
||||
$this->assertTrue($proxy->write('id', 'data'));
|
||||
$this->assertTrue($proxy->destroy('id'));
|
||||
}
|
||||
|
||||
public function testGc()
|
||||
{
|
||||
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$handler->expects($this->once())->method('gc')
|
||||
->with(123)->willReturn(true);
|
||||
$proxy = new StrictSessionHandler($handler);
|
||||
|
||||
$this->assertTrue($proxy->gc(123));
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
|
||||
|
||||
/**
|
||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||
*/
|
||||
class WriteCheckSessionHandlerTest extends TestCase
|
||||
{
|
||||
public function test()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('close')
|
||||
->with()
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($writeCheckSessionHandler->close());
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('foo', 'bar')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testSkippedWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('read')
|
||||
->with('foo')
|
||||
->will($this->returnValue('bar'))
|
||||
;
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->never())
|
||||
->method('write')
|
||||
;
|
||||
|
||||
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
|
||||
$this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testNonSkippedWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('read')
|
||||
->with('foo')
|
||||
->will($this->returnValue('bar'))
|
||||
;
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('foo', 'baZZZ')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
|
||||
$this->assertTrue($writeCheckSessionHandler->write('foo', 'baZZZ'));
|
||||
}
|
||||
}
|
|
@ -14,8 +14,10 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
|
|||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||
|
||||
/**
|
||||
* Test class for NativeSessionStorage.
|
||||
|
@ -98,7 +100,6 @@ class NativeSessionStorageTest extends TestCase
|
|||
|
||||
$storage->start();
|
||||
$id = $storage->getId();
|
||||
$this->assertEquals(session_id(), $id);
|
||||
$this->assertInternalType('string', $id);
|
||||
$this->assertNotSame('', $id);
|
||||
|
||||
|
@ -106,44 +107,6 @@ class NativeSessionStorageTest extends TestCase
|
|||
$this->assertSame($id, $storage->getId(), 'ID stays after saving session');
|
||||
}
|
||||
|
||||
public function testId()
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
|
||||
$this->assertEquals(session_id(), $storage->getId());
|
||||
$storage->setId('foo');
|
||||
$this->assertEquals('foo', $storage->getId());
|
||||
$this->assertEquals(session_id(), $storage->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testIdException()
|
||||
{
|
||||
session_start();
|
||||
$this->getStorage()->setId('foo');
|
||||
}
|
||||
|
||||
public function testName()
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
|
||||
$this->assertEquals(session_name(), $storage->getName());
|
||||
$storage->setName('foo');
|
||||
$this->assertEquals('foo', $storage->getName());
|
||||
$this->assertEquals(session_name(), $storage->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testNameException()
|
||||
{
|
||||
session_start();
|
||||
$this->getStorage()->setName('foo');
|
||||
}
|
||||
|
||||
public function testRegenerate()
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
|
@ -189,7 +152,7 @@ class NativeSessionStorageTest extends TestCase
|
|||
$this->iniSet('session.cache_limiter', 'nocache');
|
||||
|
||||
$storage = new NativeSessionStorage();
|
||||
$this->assertEquals('', ini_get('session.cache_limiter'));
|
||||
$this->assertEquals('private_no_expire', ini_get('session.cache_limiter'));
|
||||
}
|
||||
|
||||
public function testExplicitSessionCacheLimiter()
|
||||
|
@ -222,9 +185,9 @@ class NativeSessionStorageTest extends TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* @expectedException \TypeError
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testSetSaveHandlerError()
|
||||
public function testSetSaveHandlerException()
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
$storage->setSaveHandler(new \stdClass());
|
||||
|
@ -235,13 +198,17 @@ class NativeSessionStorageTest extends TestCase
|
|||
$this->iniSet('session.save_handler', 'files');
|
||||
$storage = $this->getStorage();
|
||||
$storage->setSaveHandler();
|
||||
$this->assertInstanceOf(\SessionHandler::class, $storage->getSaveHandler());
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||
$storage->setSaveHandler(null);
|
||||
$this->assertInstanceOf(\SessionHandler::class, $storage->getSaveHandler());
|
||||
$storage->setSaveHandler(new \SessionHandler());
|
||||
$this->assertInstanceOf(\SessionHandler::class, $storage->getSaveHandler());
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||
$storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler()));
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||
$storage->setSaveHandler(new NativeFileSessionHandler());
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||
$storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||
$storage->setSaveHandler(new NullSessionHandler());
|
||||
$this->assertInstanceOf(\SessionHandlerInterface::class, $storage->getSaveHandler());
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,12 +218,12 @@ class NativeSessionStorageTest extends TestCase
|
|||
{
|
||||
$storage = $this->getStorage();
|
||||
|
||||
$this->assertNotSame(\PHP_SESSION_ACTIVE, session_status());
|
||||
$this->assertFalse($storage->getSaveHandler()->isActive());
|
||||
$this->assertFalse($storage->isStarted());
|
||||
|
||||
session_start();
|
||||
$this->assertTrue(isset($_SESSION));
|
||||
$this->assertSame(\PHP_SESSION_ACTIVE, session_status());
|
||||
$this->assertTrue($storage->getSaveHandler()->isActive());
|
||||
|
||||
// PHP session might have started, but the storage driver has not, so false is correct here
|
||||
$this->assertFalse($storage->isStarted());
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||
|
||||
/**
|
||||
* Test class for AbstractProxy.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class AbstractProxyTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var AbstractProxy
|
||||
*/
|
||||
protected $proxy;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->proxy = $this->getMockForAbstractClass(AbstractProxy::class);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->proxy = null;
|
||||
}
|
||||
|
||||
public function testGetSaveHandlerName()
|
||||
{
|
||||
$this->assertNull($this->proxy->getSaveHandlerName());
|
||||
}
|
||||
|
||||
public function testIsSessionHandlerInterface()
|
||||
{
|
||||
$this->assertFalse($this->proxy->isSessionHandlerInterface());
|
||||
$sh = new SessionHandlerProxy(new \SessionHandler());
|
||||
$this->assertTrue($sh->isSessionHandlerInterface());
|
||||
}
|
||||
|
||||
public function testIsWrapper()
|
||||
{
|
||||
$this->assertFalse($this->proxy->isWrapper());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testIsActive()
|
||||
{
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
session_start();
|
||||
$this->assertTrue($this->proxy->isActive());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testName()
|
||||
{
|
||||
$this->assertEquals(session_name(), $this->proxy->getName());
|
||||
$this->proxy->setName('foo');
|
||||
$this->assertEquals('foo', $this->proxy->getName());
|
||||
$this->assertEquals(session_name(), $this->proxy->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testNameException()
|
||||
{
|
||||
session_start();
|
||||
$this->proxy->setName('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testId()
|
||||
{
|
||||
$this->assertEquals(session_id(), $this->proxy->getId());
|
||||
$this->proxy->setId('foo');
|
||||
$this->assertEquals('foo', $this->proxy->getId());
|
||||
$this->assertEquals(session_id(), $this->proxy->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testIdException()
|
||||
{
|
||||
session_start();
|
||||
$this->proxy->setId('foo');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||
|
||||
/**
|
||||
* Tests for SessionHandlerProxy class.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
class SessionHandlerProxyTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_Matcher
|
||||
*/
|
||||
private $mock;
|
||||
|
||||
/**
|
||||
* @var SessionHandlerProxy
|
||||
*/
|
||||
private $proxy;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->mock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$this->proxy = new SessionHandlerProxy($this->mock);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->mock = null;
|
||||
$this->proxy = null;
|
||||
}
|
||||
|
||||
public function testOpenTrue()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('open')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
$this->proxy->open('name', 'id');
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
}
|
||||
|
||||
public function testOpenFalse()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('open')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
$this->proxy->open('name', 'id');
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
}
|
||||
|
||||
public function testClose()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('close')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
$this->proxy->close();
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
}
|
||||
|
||||
public function testCloseFalse()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('close')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
$this->proxy->close();
|
||||
$this->assertFalse($this->proxy->isActive());
|
||||
}
|
||||
|
||||
public function testRead()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('read');
|
||||
|
||||
$this->proxy->read('id');
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('write');
|
||||
|
||||
$this->proxy->write('id', 'data');
|
||||
}
|
||||
|
||||
public function testDestroy()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('destroy');
|
||||
|
||||
$this->proxy->destroy('id');
|
||||
}
|
||||
|
||||
public function testGc()
|
||||
{
|
||||
$this->mock->expects($this->once())
|
||||
->method('gc');
|
||||
|
||||
$this->proxy->gc(86400);
|
||||
}
|
||||
}
|
|
@ -135,6 +135,11 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
|
|||
$binding = $bindings[$bindingName];
|
||||
|
||||
list($bindingValue, $bindingId) = $binding->getValues();
|
||||
|
||||
if (!$bindingValue instanceof Reference) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$binding->setValues(array($bindingValue, $bindingId, true));
|
||||
$args[$p->name] = $bindingValue;
|
||||
|
||||
|
|
|
@ -310,6 +310,22 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
|
|||
{
|
||||
return array(array(ControllerDummy::class), array('$bar'));
|
||||
}
|
||||
|
||||
public function testDoNotBindScalarValueToControllerArgument()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$resolver = $container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', ArgumentWithoutTypeController::class)
|
||||
->setBindings(array('$someArg' => '%foo%'))
|
||||
->addTag('controller.service_arguments');
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
|
||||
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
|
||||
$this->assertEmpty($locator);
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterTestController
|
||||
|
|
|
@ -128,7 +128,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
|||
$this->routes[$name] = $route;
|
||||
}
|
||||
|
||||
$this->resources = array_merge($this->resources, $collection->getResources());
|
||||
foreach ($collection->getResources() as $resource) {
|
||||
$this->addResource($resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,16 +264,21 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function getResources()
|
||||
{
|
||||
return array_unique($this->resources);
|
||||
return array_values($this->resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a resource for this collection.
|
||||
* Adds a resource for this collection. If the resource already exists
|
||||
* it is not added.
|
||||
*
|
||||
* @param ResourceInterface $resource A resource instance
|
||||
*/
|
||||
public function addResource(ResourceInterface $resource)
|
||||
{
|
||||
$this->resources[] = $resource;
|
||||
$key = (string) $resource;
|
||||
|
||||
if (!isset($this->resources[$key])) {
|
||||
$this->resources[$key] = $resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,12 +97,18 @@ class NativeSessionTokenStorage implements TokenStorageInterface
|
|||
$this->startSession();
|
||||
}
|
||||
|
||||
$token = isset($_SESSION[$this->namespace][$tokenId])
|
||||
? (string) $_SESSION[$this->namespace][$tokenId]
|
||||
: null;
|
||||
if (!isset($_SESSION[$this->namespace][$tokenId])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$token = (string) $_SESSION[$this->namespace][$tokenId];
|
||||
|
||||
unset($_SESSION[$this->namespace][$tokenId]);
|
||||
|
||||
if (!$_SESSION[$this->namespace]) {
|
||||
unset($_SESSION[$this->namespace]);
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue