diff --git a/.travis.yml b/.travis.yml index 8cbcc71c82525..7ed8c84c6e56d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,21 +15,17 @@ addons: env: global: - - MIN_PHP=5.5.9 - - SYMFONY_PROCESS_PHP_TEST_BINARY=~/.phpenv/versions/5.6/bin/php + - MIN_PHP=7.1.3 + - SYMFONY_PROCESS_PHP_TEST_BINARY=~/.phpenv/shims/php matrix: include: - - php: hhvm-3.18 - sudo: required - group: edge - - php: 5.5 - - php: 5.6 - - php: 7.0 + - php: 7.1.3 - php: 7.1 env: deps=high - php: 7.2 env: deps=low + fast_finish: true cache: @@ -92,24 +88,15 @@ before_install: export -f tfold # php.ini configuration - if [[ $PHP = hhvm* ]]; then - INI=/etc/hhvm/php.ini - else - INI=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - phpenv config-rm xdebug.ini || echo "xdebug not available" - fi + INI=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini + phpenv config-rm xdebug.ini || echo "xdebug not available" echo date.timezone = Europe/Paris >> $INI echo memory_limit = -1 >> $INI echo session.gc_probability = 0 >> $INI echo opcache.enable_cli = 1 >> $INI - echo hhvm.jit = 0 >> $INI echo apc.enable_cli = 1 >> $INI echo extension = redis.so >> $INI echo extension = memcached.so >> $INI - [[ $PHP = 5.* ]] && echo extension = memcache.so >> $INI - if [[ $PHP = 5.* ]]; then - echo extension = mongo.so >> $INI - fi # tpecl is a helper to compile and cache php extensions tpecl () { @@ -129,7 +116,7 @@ before_install: export -f tpecl # Matrix lines for intermediate PHP versions are skipped for pull requests - if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && ! $PHP = hhvm* && $TRAVIS_PULL_REQUEST != false ]]; then + if [[ ! $deps && ! $PHP = $MIN_PHP && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip skip=1 else @@ -138,17 +125,14 @@ before_install: - | # Install sigchild-enabled PHP to test the Process component on the lowest PHP matrix line - if [[ ! $deps && $PHP = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then - wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj && + if [[ ! $deps && $PHP = $MIN_PHP && ! -d php-$MIN_PHP/sapi ]]; then + wget http://php.net/get/php-$MIN_PHP.tar.bz2/from/this/mirror -O - | tar -xj && (cd php-$MIN_PHP && ./configure --enable-sigchild --enable-pcntl && make -j2) fi - | # Install extra PHP extensions - if [[ ! $skip && $PHP = 5.* ]]; then - ([[ $deps ]] || tfold ext.symfony_debug 'cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> '"$INI") - tfold ext.apcu tpecl apcu-4.0.11 apcu.so - elif [[ ! $skip && $PHP = 7.* ]]; then + if [[ ! $skip ]]; then # install libsodium if [[ ! -e ~/php-ext/$(php -r "echo basename(ini_get('extension_dir'));")/libsodium/sodium.so ]]; then sudo add-apt-repository ppa:ondrej/php -y @@ -200,15 +184,13 @@ install: export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev if [[ ! $skip && $deps ]]; then mv composer.json.phpunit composer.json; fi - if [[ ! $skip && $PHP = 7.* ]]; then + if [[ ! $skip ]]; then ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb) fi - if [[ ! $skip ]]; then $COMPOSER_UP; fi - if [[ ! $skip ]]; then ./phpunit install; fi - - | - # phpinfo - if [[ ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi + - php -i - | run_tests () { @@ -219,13 +201,11 @@ install: echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP && $PHPUNIT_X$LEGACY'" elif [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP --prefer-lowest --prefer-stable && $PHPUNIT_X'" - elif [[ $PHP = hhvm* ]]; then - $PHPUNIT --exclude-group no-hhvm,benchmark,intl-data else echo "$COMPONENTS" | parallel --gnu "tfold {} $PHPUNIT_X {}" tfold tty-group $PHPUNIT --group tty - if [[ $PHP = ${MIN_PHP%.*} ]]; then - echo -e "1\\n0" | xargs -I{} bash -c "tfold src/Symfony/Component/Process.sigchild{} SYMFONY_DEPRECATIONS_HELPER=weak ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/" + if [[ $PHP = $MIN_PHP ]]; then + tfold src/Symfony/Component/Process.sigchild SYMFONY_DEPRECATIONS_HELPER=weak php-$MIN_PHP/sapi/cli/php ./phpunit --colors=always src/Symfony/Component/Process/ fi fi } diff --git a/CHANGELOG-3.0.md b/CHANGELOG-3.0.md deleted file mode 100644 index 6269bd60d37aa..0000000000000 --- a/CHANGELOG-3.0.md +++ /dev/null @@ -1,566 +0,0 @@ -CHANGELOG for 3.0.x -=================== - -This changelog references the relevant changes (bug and security fixes) done -in 3.0 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.0.0...v3.0.1 - -* 3.0.9 (2016-07-30) - - * bug #19470 undefined offset fix (#19406) (ReenExe) - * bug #19300 [HttpKernel] Use flock() for HttpCache's lock files (mpdude) - * bug #19428 [Process] Fix write access check for pipes on Windows (nicolas-grekas) - * bug #19439 [DependencyInjection] Fixed deprecated default message template with XML (jeremyFreeAgent) - * bug #19397 [HttpFoundation] HttpCache refresh stale responses containing an ETag (maennchen) - * bug #19426 [Form] Fix the money form type render with Bootstrap3 (Th3Mouk) - * bug #19422 [DomCrawler] Inherit the namespace cache in subcrawlers (stof) - * bug #19425 [BrowserKit] Uppercase the "GET" method in redirects (jakzal) - * bug #19384 Fix PHP 7.1 related failures (nicolas-grekas) - * bug #19379 [VarDumper] Fix for PHP 7.1 (nicolas-grekas) - * bug #19342 Added class existence check if is_subclass_of() fails in compiler passes (SCIF) - * bug #19369 Fix the DBAL session handler version check for Postgresql (stof) - * bug #19368 [VarDumper] Fix dumping jsons casted as arrays (nicolas-grekas) - * bug #19334 [Security] Fix the retrieval of the last username when using forwarding (stof) - * bug #19321 [HttpFoundation] Add OPTIONS and TRACE to the list of safe methods (dunglas) - * bug #19317 [BrowserKit] Update Client::getAbsoluteUri() for query string only URIs (georaldc) - * bug #19298 [ClassLoader] Fix declared classes being computed when not needed (nicolas-grekas) - * bug #19316 [Validator] Added additional MasterCard range to the CardSchemeValidator (Dennis Væversted) - * bug #19290 [HttpKernel] fixed internal subrequests having an if-modified-since-header (MalteWunsch) - * bug #19307 [Security] Fix deprecated usage of DigestAuthenticationEntryPoint::getKey() in DigestAuthenticationListener (Maxime STEINHAUSSER) - * bug #19309 [DoctrineBridge] added missing error code for constraint. (Koc) - * bug #19306 [Form] fixed bug - name in ButtonBuilder (cheprasov) - * bug #19292 [varDumper] Fix missing usage of ExceptionCaster::$traceArgs (nicolas-grekas) - * bug #19288 [VarDumper] Fix indentation trimming in ExceptionCaster (nicolas-grekas) - * bug #19267 [Validator] UuidValidator must accept a Uuid constraint. (hhamon) - * bug #19186 Fix for #19183 to add support for new PHP MongoDB extension in sessions. (omanizer) - * bug #19253 [Console] Fix block() padding formatting after #19189 (chalasr) - * bug #19218 [Security][Guard] check if session exist before using it (pasdeloup) - -* 3.0.8 (2016-06-30) - - * bug #19217 [HttpKernel] Inline ValidateRequestListener logic into HttpKernel (nicolas-grekas) - * bug #18688 [HttpFoundation] Warning when request has both Forwarded and X-Forwarded-For (magnusnordlander) - * bug #19173 [Console] Decouple SymfonyStyle from TableCell (ro0NL) - * bug #19189 [Console] Fix formatting of SymfonyStyle::comment() (chalasr) - * bug #19211 [Form] fix post max size translation type extension for >= 2.8 (Tobion) - * bug #17822 [WIP] [Form] fix `empty_data` option in expanded `ChoiceType` (HeahDude) - * bug #19134 Distinguish between first and subsequent progress bar displays (rquadling) - * bug #19061 [FORM] fix post_max_size_message translation (alt. 2) (David Badura) - * bug #19100 [Console] Fixed SymfonyQuestionHelper multi-choice with defaults (sstok) - * bug #18924 [DoctrineBridge] Don't use object IDs in DoctrineChoiceLoader when passing a value closure (webmozart) - * bug #19138 [DomCrawler] No more exception on field name with strange format (guiled, fabpot) - * bug #18935 [Form] Consider a violation even if the form is not submitted (egeloen) - * bug #19127 [Form] Add exception to FormRenderer about non-unique block names (enumag) - * bug #19118 [Process] Fix pipes cleaning on Windows (nicolas-grekas) - * bug #19128 Avoid phpunit 5.4 warnings on getMock (2.7+) (iltar) - * bug #19114 [HttpKernel] Dont close the reponse stream in debug (nicolas-grekas) - * bug #19101 [Session] fix PDO transaction aborted under PostgreSQL (Tobion) - * bug #18501 [HttpFoundation] changed MERGE queries (hjkl) - * bug #19062 [HttpFoundation] Fix UPSERT for PgSql >= 9.5 (nicolas-grekas) - * bug #18548 [Form] minor fixes in DateTime transformers (HeahDude) - * bug #18732 [PropertyAccess][DX] Enhance exception that say that some methods are missing if they don't (nykopol) - * bug #19048 [HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5 (nicolas-grekas) - * bug #19038 Fix feature detection for IE (Alsciende) - * bug #18915 [DependencyInjection] force enabling the external XML entity loaders (xabbuh) - * bug #19020 [Form] Fixed collapsed choice attributes (HeahDude) - * bug #19028 [Yaml] properly count skipped comment lines (xabbuh) - * bug #19009 [WebProfilerBundle] Fix invalid CSS style (romainneutron) - * bug #17733 [Yaml] Fix wrong line number when comments are inserted in the middle of a block. (paradajozsef) - * bug #18911 Fixed singular of committee (peterrehm) - * bug #18971 Do not inject web debug toolbar on attachments (peterrehm) - -* 3.0.7 (2016-06-06) - - * bug #18908 [DependencyInjection] force enabling the external XML entity loaders (xabbuh) - * bug #18893 [DependencyInjection] Skip deep reference check for 'service_container' (RobertMe) - * bug #18812 Catch \Throwable (fprochazka) - * bug #18821 [Form] Removed UTC specification with timestamp (francisbesset) - * bug #18861 Fix for #18843 (inso) - * bug #18889 [Console] SymfonyStyle: Fix alignment/prefixing of multi-line comments (chalasr) - * bug #18907 [Routing] Fix the annotation loader taking a class constant as a beginning of a class name (jakzal, nicolas-grekas) - * bug #18879 [Console] SymfonyStyle: Align multi-line/very-long-line blocks (chalasr) - * bug #18864 [Console][DX] Fixed ambiguous error message when using a duplicate option shortcut (peterrehm) - * bug #18883 Fix js comment in profiler (linnaea) - * bug #18844 [Yaml] fix exception contexts (xabbuh) - * bug #18840 [Yaml] properly handle unindented collections (xabbuh) - * bug #18765 Catch \Throwable (fprochazka) - * bug #18813 Catch \Throwable (fprochazka) - * bug #18839 People - person singularization (Keeo) - * bug #18828 [Yaml] chomp newlines only at the end of YAML documents (xabbuh) - * bug #18814 Fixed server status command when port has been omitted (peterrehm) - * bug #18759 [Validator] Support for DateTimeImmutable (krzysiekpiasecki) - * bug #18799 Use levenshtein level for better Bundle matching (j0k3r) - * bug #18413 [WebProfilerBundle] Fix CORS ajax security issues (romainneutron) - * bug #18774 [console][table] adjust width of colspanned cell. (aitboudad) - * bug #18507 [BUG] Delete class 'control-group' in bootstrap 3 (Philippe Degeeter) - * bug #18747 [Form] Modified iterator_to_array's 2nd parameter to false in ViolationMapper (issei-m) - * bug #18635 [Console] Prevent fatal error when calling Command::getHelper without helperSet (chalasr) - * bug #18686 [console][table] adjust width of colspanned cell. (aitboudad) - * bug #18761 [Form] Modified iterator_to_array's 2nd parameter to false in ViolationMapper (issei-m) - * bug #18745 [MonologBridge] Uninstallable together with symfony/http-kernel in 3.0.6 (ymc-dabe) - * bug #18737 [Debug] Fix fatal error handlers on PHP 7 (nicolas-grekas) - -* 3.0.6 (2016-05-10) - - * security #18736 Fixed issue with blank password with Ldap (csarrazi) - * security #18733 limited the maximum length of a submitted username (fabpot) - * bug #18730 [FrameworkBundle] prevent calling get() for service_container service (xabbuh) - * bug #18705 added a conflict between Monolog bridge 2.8 and HTTP Kernel 3.0+ (fabpot) - * bug #18709 [DependencyInjection] top-level anonymous services must be public (xabbuh) - * bug #18388 [EventDispatcher] check for method to exist (xabbuh) - * bug #18699 [DependencyInjection] Use the priority of service decoration on service with parent (hason) - * bug #18692 add @Event annotation for KernelEvents (Haehnchen) - * bug #18246 [DependencyInjection] fix ambiguous services schema (backbone87) - -* 3.0.5 (2016-05-03) - - * bug #18180 [Form] fixed BC break with pre selection of choices with `ChoiceType` and its children (HeahDude) - * bug #18645 [Console] Fix wrong exceptions being thrown (JhonnyL) - * bug #18562 [WebProfilerBunde] Give an absolute url in case the request occured from another domain (romainneutron) - * bug #18600 [DI] Fix AutowirePass fatal error with classes that have non-existing parents (hason, nicolas-grekas) - * bug #18556 [FrameworkBundle] Better output for user in ContainerDebugCommand (JhonnyL) - * bug #18603 [PropertyAccess] ->getValue() should be read-only (nicolas-grekas) - * bug #18593 [VarDumper] Fix dumping type hints for non-existing parent classes (nicolas-grekas) - * bug #18596 [DI] Fix internal caching in AutowirePass (nicolas-grekas) - * bug #18581 [Console] [TableHelper] make it work with SymfonyStyle. (aitboudad) - * bug #18280 [Routing] add query param if value is different from default (Tobion) - * bug #18540 Replace iconv_*() uses by mb_*(), add mbstring polyfill when required (nicolas-grekas) - * bug #18496 [Console] use ANSI escape sequences in ProgressBar overwrite method (alekitto) - * bug #18490 [LDAP] Free the search result after a search to free memory (hiddewie) - * bug #18491 [DependencyInjection] anonymous services are always private (xabbuh) - * bug #18515 [Filesystem] Better error handling in remove() (nicolas-grekas) - * bug #18081 [Form] FormValidator removed code related to removed `cascade_validation` option (peterrehm) - * bug #18360 [PropertyInfo] Extract nullable and collection key type for Doctrine associations (teohhanhui) - * bug #18449 [PropertyAccess] Fix regression (nicolas-grekas) - * bug #18429 [Console] Correct time formatting. (camporter) - * bug #18457 [WebProfilerBundle] Fixed error from unset twig variable (simonsargeant) - * bug #18467 [DependencyInjection] Resolve aliases before removing abstract services + add tests (nicolas-grekas) - * bug #18469 Force profiler toolbar svg display (pyrech) - * bug #18460 [DomCrawler] Fix select option with empty value (Matt Wells) - * bug #18425 [Security] Fixed SwitchUserListener when exiting an impersonation with AnonymousToken (lyrixx) - * bug #18317 [Form] fix "prototype" not required when parent form is not required (HeahDude) - * bug #18439 [Logging] Add support for Firefox (43+) in ChromePhpHandler (arjenm) - * bug #18385 Detect CLI color support for Windows 10 build 10586 (mlocati) - * bug #18426 [EventDispatcher] Try first if the event is Stopped (lyrixx) - * bug #18407 Fixed the "hover" state of the profiler sidebar menu (javiereguiluz) - * bug #18399 [Intl] Fix int32 min boundary check (nicolas-grekas) - * bug #18394 [FrameworkBundle] Return the invokable service if its name is the class name (dunglas) - * bug #18347 Fixed the styles of the Symfony icon in the web debug toolbar (javiereguiluz) - * bug #18265 Optimize ReplaceAliasByActualDefinitionPass (ajb-in) - * bug #18349 [Process] Fix stream_select priority when writing to stdin (nicolas-grekas) - * bug #18358 [Form] NumberToLocalizedStringTransformer should return floats when possible (nicolas-grekas) - * bug #17926 [DependencyInjection] Enable alias for service_container (hason) - -* 3.0.4 (2016-03-30) - - * bug #18352 [Debug] Fix case sensitivity checks (nicolas-grekas) - * bug #18336 [Debug] Fix handling of php7 throwables (nicolas-grekas) - * bug #18354 [FrameworkBundle][TwigBridge] fix high deps tests (xabbuh) - * bug #18312 [ClassLoader] Fix storing not-found classes in APC cache (nicolas-grekas) - * bug #18298 [Validator] do not treat payload as callback (xabbuh) - * bug #18275 [Form] Fix BC break introduced in #14403 (HeahDude) - * bug #18271 [FileSystem] Google app engine filesystem (swordbeta) - * bug #18255 [HttpFoundation] Fix support of custom mime types with parameters (Ener-Getick) - * bug #18272 [Bridge\PhpUnit] Workaround old phpunit bug, no colors in weak mode, add tests (nicolas-grekas) - * bug #18259 [PropertyAccess] Backport fixes from 2.7 (nicolas-grekas) - * bug #18261 [PropertyAccess] Fix isPropertyWritable not using the reflection cache (nicolas-grekas) - * bug #18224 [PropertyAccess] Remove most ref mismatches to improve perf (nicolas-grekas) - * bug #18237 [WebProfilerBundle] Added table-layout property to AJAX toolbar css (kevintweber) - * bug #18209 [PropertyInfo] Support Doctrine custom mapping type in DoctrineExtractor (teohhanhui) - * bug #18210 [PropertyAccess] Throw an UnexpectedTypeException when the type do not match (dunglas, nicolas-grekas) - * bug #18216 [Intl] Fix invalid numeric literal on PHP 7 (nicolas-grekas) - * bug #18147 [Validator] EmailValidator cannot extract hostname if email contains multiple @ symbols (natechicago) - * bug #18023 [Process] getIncrementalOutput should work without calling getOutput (romainneutron) - * bug #18175 [Translation] Add support for fuzzy tags in PoFileLoader (nud) - * bug #18179 [Form] Fix NumberToLocalizedStringTransformer::reverseTransform with big integers (ovrflo, nicolas-grekas) - * bug #18164 [HttpKernel] set s-maxage only if all responses are cacheable (xabbuh) - * bug #18150 [Process] Wait a bit less on Windows (nicolas-grekas) - * bug #18130 [Debug] Replaced logic for detecting filesystem case sensitivity (Dan Blows) - * bug #18137 Autowiring the concrete class too - consistent with behavior of other services (weaverryan) - * bug #18087 [WebProfiler] Sidebar button padding (rvanlaak) - * bug #18080 [HttpFoundation] Set the Content-Range header if the requested Range is unsatisfied (jakzal) - * bug #18084 [HttpFoundation] Avoid warnings when checking malicious IPs (jakzal) - * bug #18066 [Process] Fix pipes handling (nicolas-grekas) - * bug #18078 [Console] Fix an autocompletion question helper issue with non-sequentially indexed choices (jakzal) - * bug #18048 [HttpKernel] Fix mem usage when stripping the prod container (nicolas-grekas) - * bug #18065 [Finder] Partially revert #17134 to fix a regression (jakzal) - * bug #18018 [HttpFoundation] exception when registering bags for started sessions (xabbuh) - * bug #18054 [Filesystem] Fix false positive in ->remove() (nicolas-grekas) - * bug #18049 [Validator] Fix the locale validator so it treats a locale alias as a valid locale (jakzal) - * bug #18019 [Intl] Update ICU to version 55 (jakzal) - * bug #18015 [Process] Fix memory issue when using large input streams (romainneutron) - * bug #16656 [HttpFoundation] automatically generate safe fallback filename (xabbuh) - * bug #15794 [Console] default to stderr in the console helpers (alcohol) - * bug #17984 Allow to normalize \Traversable when serializing xml (Ener-Getick) - * bug #17434 Improved the error message when a template is not found (rvanginneken, javiereguiluz) - * bug #17687 Improved the error message when using "@" in a decorated service (javiereguiluz) - * bug #17744 Improve error reporting in router panel of web profiler (javiereguiluz) - * bug #17894 [FrameworkBundle] Fix a regression in handling absolute template paths (jakzal) - * bug #17990 [DoctrineBridge][Form] Fix performance regression in EntityType (kimlai) - * bug #17595 [HttpKernel] Remove _path from query parameters when fragment is a subrequest (cmenning) - * bug #17986 [DomCrawler] Dont use LIBXML_PARSEHUGE by default (nicolas-grekas) - * bug #17668 add 'guid' to list of exception to filter out (garak) - * bug #17615 Ensure backend slashes for symlinks on Windows systems (cpsitgmbh) - * bug #17626 Try to delete broken symlinks (IchHabRecht) - * bug #17978 [Yaml] ensure dump indentation to be greather than zero (xabbuh) - * bug #16886 [Form] [ChoiceType] Prefer placeholder to empty_value (boite) - * bug #17976 [WebProfilerBundle] fix debug toolbar rendering by removing inadvertently added links (craue) - * bug #17971 Variadic controller params (NiR-, fabpot) - * bug #17876 [DependencyInjection] Fixing autowiring bug when some args are set (weaverryan) - * bug #17568 Improved Bootstrap form theme for hidden fields (javiereguiluz) - * bug #17561 [WebProfilerBundle] Fix design issue in profiler when having errors in forms (Pierstoval) - * bug #17925 [Bridge] The WebProcessor now forwards the client IP (magnetik) - -* 3.0.3 (2016-02-28) - - * bug #17919 #17676 - making the proxy instantiation compatible with ProxyManager 2.x by detecting proxy features (Ocramius) - * bug #17947 Fix - #17676 (backport #17919 to 2.3) (Ocramius) - * bug #17942 Fix bug when using an private aliased factory service (WouterJ) - * bug #17798 [Form] Fix BC break by allowing 'choice_label' option to be 'false' in ChoiceType (HeahDude) - * bug #17542 ChoiceFormField of type "select" could be "disabled" (bouland) - * bug #17602 [HttpFoundation] Fix BinaryFileResponse incorrect behavior with if-range header (bburnichon) - * bug #17760 [Form] fix choice value "false" in ChoiceType (HeahDude) - * bug #17914 [Console] Fix escaping of trailing backslashes (nicolas-grekas) - * bug #17074 Fix constraint validator alias being required (Triiistan) - * bug #17866 [DependencyInjection] replace alias in factories (xabbuh) - * bug #17867 [DependencyInjection] replace alias in factory services (xabbuh) - * bug #17865 [FrameworkBundle] disable the assets helper when assets are disabled (xabbuh) - * bug #17860 Fixed the antialiasing of the toolbar text (javiereguiluz) - * bug #17569 [FrameworkBundle] read commands from bundles when accessing list (havvg) - * bug #16987 [FileSystem] Windows fix (flip111) - * bug #17787 [Form] Fix choice placeholder edge cases (Tobion) - * bug #17835 [Yaml] fix default timezone to be UTC (xabbuh) - * bug #17823 [DependencyInjection] fix dumped YAML string (xabbuh) - * bug #17818 [Console] InvalidArgumentException is thrown under wrong condition (robinkanters) - * bug #17819 [HttpKernel] Prevent a fatal error when DebugHandlersListener is used with a kernel with no terminateWithException() method (jakzal) - * bug #17814 [DependencyInjection] fix dumped YAML snytax (xabbuh) - * bug #17099 [Form] Fixed violation mapping if multiple forms are using the same (or part of the same) property path (alekitto) - * bug #17694 [DoctrineBridge] [Form] fix choice_value in EntityType (HeahDude) - * bug #17790 [Config] Fix EnumNodeDefinition to allow building enum nodes with one element (ogizanagi) - * bug #17729 [Yaml] properly parse lists in object maps (xabbuh) - * bug #17719 [DependencyInjection] fixed exceptions thrown by get method of ContainerBuilder (lukaszmakuch) - * bug #17742 [DependencyInjection] Fix #16461 Container::set() replace aliases (mnapoli) - * bug #17745 Added more exceptions to singularify method (javiereguiluz) - * bug #17691 Fixed (string) catchable fatal error for PHP Incomplete Class instances (yceruto) - * bug #17766 Fixed (string) catchable fatal error for PHP Incomplete Class instances (yceruto) - * bug #17757 [HttpFoundation] BinaryFileResponse sendContent return as parent. (2.3) (SpacePossum) - * bug #17748 [DomCrawler] Remove the overridden getHash() method to prevent problems when cloning the crawler (jakzal) - * bug #17725 [WebProfilerBundle] Add width attribute on SVG - Fix toolbar profiler on microsoft edge (AlexandrePavy) - * bug #17703 [FrameworkBundle] Support autowiring for TranslationInterface (dunglas) - * bug #17613 [WebProfiler] Fixed logo and menu profiler for Microsoft Edge (WhiteEagle88) - * bug #17702 [TwigBridge] forward compatibility with Yaml 3.1 (xabbuh) - * bug #17673 [Routing] add files used in FileResource objects (xabbuh) - * bug #17672 [DependencyInjection][Routing] add files used in FileResource objects (xabbuh) - * bug #17669 [Console] remove readline support (xabbuh) - * bug #17600 Fixed the Bootstrap form theme for inlined checkbox/radio (javiereguiluz) - -* 3.0.2 (2016-02-03) - - * bug #17658 [FrameworkBundle] fix assets and templating tests (xabbuh) - * bug #17596 [Translation] Add resources from fallback locale to parent catalogue (c960657) - * bug #17605 [FrameworkBundle] remove default null value for asset version (xabbuh) - * bug #17606 [DependencyInjection] pass triggerDeprecationError arg to parent class (xabbuh) - * bug #16956 [DependencyInjection] XmlFileLoader: enforce tags to have a name (xabbuh) - * bug #16265 [BrowserKit] Corrected HTTP_HOST logic (Naktibalda) - * bug #17559 [SecurityBundle] Fix HTTP Digest auth not being passed user checker (SamFleming) - * bug #17554 [DependencyInjection] resolve aliases in factories (xabbuh) - * bug #17555 [DependencyInjection] resolve aliases in factory services (xabbuh) - * bug #17511 [Form] ArrayChoiceList can now deal with a null in choices (issei-m) - * bug #17430 [Serializer] Ensure that groups are strings (dunglas) - * bug #16795 [FrameworkBundle][Validator] Fix apc cache service & config (ogizanagi) - * bug #15272 [FrameworkBundle] Fix template location for PHP templates (jakzal) - * bug #11232 [Routing] Fixes fatal errors with object resources in AnnotationDirectoryLoader::supports (Tischoi) - * bug #17526 Escape the delimiter in Glob::toRegex (javiereguiluz) - * bug #17527 fixed undefined variable (fabpot) - * bug #15706 [framework-bundle] Added support for the `0.0.0.0/0` trusted proxy (zerkms) - * bug #16274 [HttpKernel] Lookup the response even if the lock was released after two second wait (jakzal) - * bug #16954 [TranslationUpdateCommand] fixed undefined resultMessage var. (aitboudad) - * bug #17355 [DoctrineBridge][Validator] >= 2.3 Pass association instead of ID as argument (xavismeh) - * bug #17330 Limit the max height/width of icons in the profiler menu (javiereguiluz) - * bug #17454 Allow absolute URLs to be displayed in the debug toolbar (javiereguiluz) - * bug #16736 [Request] Ignore invalid IP addresses sent by proxies (GromNaN) - * bug #17459 [EventDispatcher] TraceableEventDispatcher resets event listener priorities (c960657) - * bug #17486 [FrameworkBundle] Throw for missing container extensions (kix) - * bug #16961 Overriding profiler position in CSS breaks JS positioning (aschempp) - * bug #16873 Able to load big xml files with DomCrawler (zorn-v) - * bug #16897 [Form] Fix constraints could be null if not set (DZunke) - * bug #16912 [Translation][Writer] avoid calling setBackup if the dumper is not FileDumper (aitboudad) - * bug #17505 sort bundles in config:dump-reference command (xabbuh) - * bug #17506 [FrameworkBundle] enable assets when templates are enabled (xabbuh) - * bug #17514 [Asset] Add defaultNull to version configuration (ewgRa) - * bug #16511 [Asset] Ability to set empty version strategy in packages (ewgRa) - * bug #17457 Display Ajax requests from newest to oldest in the toolbar (javiereguiluz) - * bug #17503 [Asset] CLI: use request context to generate absolute URLs (xabbuh) - * bug #17478 [HttpFoundation] Do not overwrite the Authorization header if it is already set (jakzal) - * bug #17461 [Yaml] tag for dumped PHP objects must be a local one (xabbuh) - * bug #16822 [FrameworkBundle][Validator] Fix apc cache service deprecation (ogizanagi) - * bug #17463 [Form] make tests compatible with Symfony 2.8 and 3.0 (xabbuh) - * bug #17456 [DX] Remove default match from AbstractConfigCommand::findExtension (kix) - * bug #17455 Fixed form types in profiler (javiereguiluz) - * bug #17424 [Process] Update in 2.7 for stream-based output storage (romainneutron) - * bug #17417 Fixed the form profiler when using long form types (javiereguiluz) - * bug #17423 [Process] Use stream based storage to avoid memory issues (romainneutron) - * bug #17041 [FrameworkBundle] Added the assets helper again (dosten) - * bug #17406 [Form] ChoiceType: Fix a notice when 'choices' normalizer is replaced (paradajozsef) - * bug #17433 [FrameworkBundle] Don't log twice with the error handler (nicolas-grekas) - * bug #17418 Fixed Bootstrap form theme form "reset" buttons (javiereguiluz) - * bug #17416 [PropertyInfo] PhpDocExtractor: Fix a notice when the property doesn'… (dunglas) - * bug #17404 fix merge 2.3 into 2.7 for SecureRandom dependency (Tobion) - * bug #17373 [SecurityBundle] fix SecureRandom service constructor args (Tobion) - * bug #17397 Remove remaining calls to non-existing method (paradajozsef) - * bug #17382 [TwigBridge] Use label_format option for checkbox and radio labels (enumag) - * bug #17380 [TwigBridge] Use label_format option for checkbox and radio labels (enumag) - * bug #17377 Fix performance (PHP5) and memory (PHP7) issues when using token_get_all (nicolas-grekas, peteward) - * bug #17389 [Routing] Fixed correct class name in thrown exception (fixes #17388) (robinvdvleuten) - * bug #17358 [ClassLoader] Use symfony/polyfill-apcu (nicolas-grekas) - * bug #17370 [HttpFoundation][Cookie] Cookie DateTimeInterface fix (wildewouter) - * security #17359 do not ship with a custom rng implementation (xabbuh, fabpot) - * bug #17253 [Console] HHVM read input stream bug (mbutkereit) - * bug #17314 Fix max width for multibyte keys in choice question (mheki) - * bug #17326 [Console] Display console application name even when no version set (polc) - * bug #17328 [Serializer] Allow to use proxies in object_to_populate (dunglas) - * bug #17202 [FrameworkBundle] Don't log twice with the error handler (nicolas-grekas) - * bug #17347 Workaround https://bugs.php.net/63206 (nicolas-grekas) - * bug #17340 [HttpFoundation] Fixed Request HTTP_USER_AGENT on 3.X versions (davelima) - * bug #17199 [Serializer] Allow context to contain not serializable data (dunglas, nicolas-grekas) - * bug #17334 [WebProfiler] Fixed sf-minitoolbar height (yceruto) - * bug #17140 [Serializer] Remove normalizer cache in Serializer class (jvasseur) - * bug #17320 [Debug] Fixed erroneous deprecation notice for extended Interfaces (peterrehm) - * bug #17307 [FrameworkBundle] Fix paths with % in it (like urlencoded) (scaytrase) - * bug #17078 [Bridge] [Doctrine] [Validator] Added support \IteratorAggregate for UniqueEntityValidator (Disparity) - * bug #17298 [FrameworkBundle] Use proper class to fetch $versionStrategy property (dosten) - * bug #17287 [HttpKernel] Forcing string comparison on query parameters sort in UriSigner (Tim van Densen) - * bug #17279 [FrameworkBundle] Add case in Kernel directory guess for PHPUnit (tgalopin) - * bug #17278 [FrameworkBundle] Add case in Kernel directory guess for PHPUnit (tgalopin) - * bug #17063 bug #14246 [Filesystem] dumpFile() negates default file permissions (Hidde Boomsma) - * bug #17283 [WebProfilerBundle] Remove loading status from AJAX toolbar after error (kucharovic) - * bug #17275 [PhpUnitBridge] Re-enable the garbage collector (nicolas-grekas) - * bug #17276 [Process] Fix potential race condition (nicolas-grekas) - * bug #17261 [FrameworkBundle] Allow to autowire service_container (dunglas) - * bug #17183 [FrameworkBundle] Set the kernel.name properly after a cache warmup (jakzal) - * bug #17197 [Yaml] cast arrays to objects after parsing has finished (xabbuh) - * bug #17247 Fix toolbar display when nvd3 is loaded on page (Seldaek) - * bug #17159 [Yaml] recognize when a block scalar is left (xabbuh) - * bug #17195 bug #14246 [Filesystem] dumpFile() non atomic (Hidde Boomsma) - * feature #16747 [Form] Improved performance of ChoiceType and its subtypes (webmozart) - * bug #17179 [WebProfiler] Removed an object as route generator argument (iltar) - * bug #17177 [Process] Fix potential race condition leading to transient tests (nicolas-grekas) - * bug #17163 [Form] fix Catchable Fatal Error if choices is not an array (Gladhon, nicolas-grekas) - * bug #17152 [DoctrineBridge] [PropertyInfo] Catch Doctrine\ORM\Mapping\MappingException (dunglas) - * bug #17119 [Form] improve deprecation message for "empty_value" and "choice_list" options. (hhamon) - * bug #17156 [HttpFoundation] add missing symfony/polyfill-php55 dependency (xabbuh) - * bug #17162 [Form] Fix regression on Collection type (hason) - -* 3.0.1 (2015-12-26) - - * bug #16864 [Yaml] fix indented line handling in folded blocks (xabbuh) - * bug #17052 Fixed flatten exception recursion with errors (GrahamCampbell) - * bug #16826 Embedded identifier support (mihai-stancu) - * bug #17079 Also transform inline mappings to objects (WouterJ) - * bug #17129 [Config] Fix array sort on normalization in edge case (romainneutron) - * feature #17035 [DomCrawler] Revert previous restriction, allow selection of every DOMNode object (EdgarPE) - * bug #17094 [Process] More robustness and deterministic tests (nicolas-grekas) - * bug #17112 [PropertyAccess] Reorder elements array after PropertyPathBuilder::replace (alekitto) - * bug #17109 Improved the design of the web debug toolbar (javiereguiluz) - * bug #16797 [Filesystem] Recursively widen non-executable directories (Slamdunk) - * bug #16926 [DependencyInjection] fixed definition loosing property shared when decorated by a parent definition (wahler) - * bug #17040 [Console] Avoid extra blank lines when rendering exceptions (ogizanagi) - * bug #17044 [Form] fix BC break introduced with prototype_data option (memphys) - * bug #17055 [Security] Verify if a password encoded with bcrypt is no longer than 72 characters (jakzal) - * bug #16959 [Form] fix #15544 when a collection type attribute "required" is false, "prototype" should too (HeahDude) - * bug #16806 [Validator] BicValidator - fixed raising violations to a maximum of one (mvhirsch) - * bug #16842 [Ldap] Escape carriage returns in LDAP DNs. (ChadSikorra) - * bug #16860 [Yaml] do not remove "comments" in scalar blocks (xabbuh) - * bug #17002 [Console][Table] fixed render row that contains multiple cells. (aitboudad) - * bug #16964 CSS min-height and min-width should not be "auto" (aschempp) - * bug #16971 [HttpFoundation] Added the ability of using BinaryFileResponse with stream wrappers (jakzal, Sander-Toonen) - * bug #17048 Fix the logout path when not using the router (stof) - * bug #17049 Fix the logout path when not using the router (stof) - * bug #17057 [FrameworkBundle][HttpKernel] the finder is required to discover bundle commands (xabbuh) - * bug #17059 [HttpFoundation] fix error level for deprecation (xabbuh) - * bug #17006 [Form] Fix casting regression in DoctrineChoiceLoader (bendavies) - * bug #16911 [PropertyInfo] Update List Information from ReflectionExtractor (zanderbaldwin) - * bug #16955 [FrameworkBundle] ContainerDebugCommand: pass the right object to the descriptors (xabbuh) - * bug #16970 [HttpKernel] HttpCache: remove an ESI instance checking (voronkovich) - * feature #16760 Show silenced errors in separate tab (peterrehm) - * feature #16937 [PhpUnitBridge] Replace "weak-verbose" by "deprecations upper bound" mode (nicolas-grekas) - * bug #16953 return ajax collector to collectors.php (NothingWeAre) - * bug #16915 [Process] Enhance compatiblity with --enable-sigchild (nicolas-grekas) - * bug #16829 [FrameworkBundle] prevent cache:clear creating too long paths (Tobion) - * bug #16922 [FrameworkBundle] [Bug] Fixes new InputStyle bug #16920 (AlmogBaku) - * bug #16921 Fix short array syntax for php 5.3 (ewgRa) - * bug #16450 [Serializer] Fixed `array_unique` on array of objects in `getAllowedAttributes`. (CornyPhoenix) - * bug #16757 [FrameworkBundle] [Translation] Fixed translations not written when no translations directory in update command (jeremyFreeAgent) - * bug #16902 [Security] Fix a Polyfill import statement in StringUtils (magnetik) - * bug #16871 [FrameworkBundle] Disable built-in server commands when Process component is missing (gnugat, xabbuh) - * bug #16870 [FrameworkBundle] Disable the server:run command when Process component is missing (gnugat, xabbuh) - * feature #16789 [PhpUnitBridge] Add weak-verbose mode and match against message instead of test name (nicolas-grekas) - * minor #16850 [MonologBridge] Added a test case for the Logger class (derrabus) - * bug #16796 [Form] Fix choices defined as Traversable (nicolas-grekas) - * bug #16742 [Console][ProgressBar] redrawFrequency should never be 0 (dritter) - * bug #16846 [MonologBridge] Monolog Bridge 2.8 is incompatible with HttpKernel 3.0 (derrabus) - * bug #16816 [Config] Throw an exception when using cannotBeEmpty() with numeric or boolean nodes (Ener-Getick) - * bug #16799 Improve error message for undefined DIC aliases (mpdude) - * bug #16825 [VarDumper] fix .sf-dump z-index (debug bar conflict) (Antoine LA) - * bug #16772 Refactoring EntityUserProvider::__construct() to not do work, cause cache warm error (weaverryan) - * bug #16788 Reapply the Yaml bugfix of #16745 (stof) - -* 3.0.0 (2015-11-30) - - * bug #16758 Fix BC for the default root form name (stof) - * feature #16754 [Security] allow arbitrary types in VoterInterface::vote() (xabbuh) - * bug #16753 [Process] Fix signaling/stopping logic on Windows (nicolas-grekas) - * feature #16755 [Security] add subject variable to expression context (xabbuh) - * bug #16642 [DI][autowiring] throw exception when many services use the same class. (aitboudad) - * bug #16745 [Yaml] look for colon in parsed inline string (xabbuh) - * bug #16733 [Console] do not encode backslashes in console default description (Tobion) - * feature #16735 [WIP] [Ldap] Marked the Ldap component as internal (csarrazi) - * bug #16734 Make sure security.role_hierarchy.roles always exists (WouterJ) - * feature #16723 [Form] remove deprecated CSRF options (xabbuh) - * feature #16725 [Form] Removed useless code (webmozart) - * feature #16724 Added getBlockPrefix to FormTypeInterface (WouterJ) - * feature #16722 [Security][SecurityBundle] Use csrf_token_id instead of deprecated intention (jakzal) - * feature #16727 [Form] remove deprecated getTimezones() method (xabbuh) - * bug #16312 [HttpKernel] clearstatcache() so the Cache sees when a .lck file has been released (mpdude) - * bug #16351 [WIP] [Form] [TwigBridge] Bootstrap horizontal theme missing tests (pieter2627) - * feature #16715 [Form] Remove choices_as_values option on ChoiceType (nicolas-grekas) - * feature #16692 [Form] Drop remaining CsrfProviderAdapter/Interface mentions (nicolas-grekas) - * feature #16719 [Security] remove deprecated HTTP digest auth key (xabbuh) - * bug #16685 [Form] Fixed: Duplicate choice labels are remembered when using "choices_as_values" = false (webmozart) - * feature #16709 [Bridge\PhpUnit] Display the stack trace of a deprecation on-demand (nicolas-grekas) - * bug #16704 [Form+SecurityBundle] Trigger deprecation for csrf_provider+intention options (nicolas-grekas) - * feature #16629 [HttpFoundation] Remove deprecated class method parameter (belka-ew) - * feature #16706 [HttpFoundation] Deprecate $deep parameter on ParameterBag (nicolas-grekas) - * bug #16705 [Form] Deprecated setting "choices_as_values" to "false" (webmozart) - * feature #16690 [Form] Deprecated ArrayKeyChoiceList (webmozart) - * feature #16687 [Form] Deprecated TimezoneType::getTimezones() (webmozart) - * bug #16681 [Form] Deprecated setting "choices_as_values" to "false" (webmozart) - * feature #16694 [SecurityBundle] make ACL an optional dependency (Tobion) - * bug #16695 [SecurityBundle] disable the init:acl command if ACL is not used (Tobion) - * bug #16677 [Form] Fixed wrong usages of the "text" type (webmozart) - * bug #16679 [Form] Disabled view data validation if "data_class" is set to null (webmozart) - * bug #16621 [Console] Fix bug with $output overloading (WouterJ) - * feature #16601 [Security] Deprecate "AbstractVoter" in favor of "Voter" (nicolas-grekas, lyrixx) - * bug #16676 [HttpFoundation] Workaround HHVM rewriting HTTP response line (nicolas-grekas) - * bug #16668 [ClassLoader] Fix parsing namespace when token_get_all() is missing (nicolas-grekas) - * bug #16615 fix type assignement (rande) - * bug #16386 Bug #16343 [Router] Too many Routes ? (jelte) - * bug #16498 fix unused variable warning (eventhorizonpl) - * feature #16031 [Translation][Form] Do not translate form labels and placeholders when 'translation_domain' is false (Restless-ET) - * bug #16651 [Debug] Ensure class declarations are loaded only once (nicolas-grekas) - * feature #16637 Removed unneeded polyfill (GrahamCampbell) - * security #16631 n/a (xabbuh) - * security #16630 n/a (xabbuh) - * bug #16633 [Filesystem] Fixed failing test due to tempdir symlink (toretto460) - * bug #16607 [HttpFoundation] Delete not existing session handler proxy member (belka-ew) - * bug #16609 [HttpKernel] Don't reset on shutdown but in FrameworkBundle/Test/KernelTestCase (nicolas-grekas) - * bug #16477 [Routing] Changing RouteCollectionBuilder::import() behavior to add to the builder (weaverryan) - * bug #16588 Sent out a status text for unknown HTTP headers. (dawehner) - * bug #16295 [DependencyInjection] Unescape parameters for all types of injection (Nicofuma) - * bug #16377 [WebProfilerBundle] Fix minitoolbar height (rvanlaak) - * bug #16585 Add support for HTTP status code 418 back (dawehner) - * bug #16574 [Process] Fix PhpProcess with phpdbg runtime (nicolas-grekas) - * bug #16581 Fix call to undefined function json_last_error_message (dawehner) - * bug #16573 [FrameworkBundle] Fix PropertyInfo extractor namespace in framework bundle (jvasseur) - * bug #16578 [Console] Fix bug in windows detection (kbond) - * bug #16546 [Serializer] ObjectNormalizer: don't serialize static methods and props (dunglas) - * bug #16352 Fix the server variables in the router_*.php files (leofeyer) - * bug #16537 [Validator] Allow an empty path with a non empty fragment or a query (jakzal) - * bug #16528 [Translation] Add support for Armenian pluralization. (marcosdsanchez) - * bug #16510 [Process] fix Proccess run with pts enabled (ewgRa) - -* 3.0.0-BETA1 (2015-11-16) - - * feature #16516 Remove some more legacy code (nicolas-grekas) - * feature #11431 [Console] End of options (--) signal support (Seldaek) - * feature #16411 [3.0] use ContainerAwareTrait (blanchonvincent, Tobion) - * feature #16400 [3.0] [Templating] remove deprecated method (TomasVotruba) - * feature #16381 remove polyfills for unsupported php versions (Tobion) - * feature #16392 [3.0] [Security] remove deprecated SrtingUtils class (TomasVotruba) - * feature #16390 [3.0] [Serializer] JsonEncoder: remove deprecated method (TomasVotruba) - * feature #16301 [EventDispatcher] add method getListenerPriority() to interface (xabbuh) - * feature #12119 [Console] Add progress indicator helper (kbond) - * feature #16203 [Yaml] removed YAML parser \ escaping in double-quotes (fabpot) - * feature #16107 [3.0] Various deprecations cleanups (nicolas-grekas) - * feature #16125 Replace is_callable checks with type hints (mpajunen, nicolas-grekas) - * feature #16076 [HttpFoundation] change precedence of parameters in Request::get (Tobion) - * feature #16075 [3.0] Clean Form, Validator, DowCrawler and some more (nicolas-grekas) - * feature #16035 [3.0][Security] Remove deprecated features (follow up of #15899) (Koc) - * feature #8967 [HttpFoundation] Request->getRequestFormat should only rely on the request attributes (pvandommelen) - * feature #16067 [3.0] Remove more deprecated interfaces in Form and Validator (nicolas-grekas) - * feature #16020 [CssSelector] removed the deprecated CssSelector class (fabpot) - * feature #15196 [HttpKernel] make RequestStack parameter required (Tobion) - * feature #16024 [Validator] removed deprecated features in Validator and Form (fabpot) - * feature #15900 [3.0][DoctrineBridge] Removed deprecated features (WouterJ) - * feature #15904 [3.0][FrameworkBundle] Removed deprecated features (WouterJ) - * feature #16019 [HttpFoundation] removed the ParameterBag::get() deep argument (fabpot) - * feature #16018 [HttpKernel] removed deprecated profiler storages (fabpot) - * feature #15899 [3.0][Security] Remove deprecated features (WouterJ) - * feature #15929 [3.0][Config] Remove ResourceInterface::getResource() which was deprecated in 2.8 (mpdude) - * feature #15965 [Finder] removed obsolete code (fabpot) - * feature #15905 [3.0][Config] Removed isFresh() related functionality (WouterJ) - * feature #15936 [Console] remove deprecated shell (Tobion) - * feature #10788 [HttpKernel] Add better error message when controller action isn't callable (pierredup) - * feature #15868 [Finder] Remove deprecated classes (nicolas-grekas) - * feature #15869 [Translation][file dump] remove deprecated format method. (aitboudad) - * feature #15801 [WebProfilerBundle][HttpKernel] removed import/export commands (jakzal) - * feature #15759 [3.0][Translator] remove deprecated DiffOperation class. (aitboudad) - * feature #15684 [Security] Remove deprecated interfaces (nicolas-grekas) - * feature #15685 [3.0] Various deprecation removal (nicolas-grekas) - * feature #15693 [DI] Remove deprecated scope concept (nicolas-grekas) - * feature #15695 [FrameworkBundle] Removed deprecated code paths (nicolas-grekas) - * feature #15430 [SecurityBundle] Remove deprecated code (dosten) - * feature #15422 [Debug] Remove deprecated ExceptionHandler::createResponse (nicolas-grekas) - * feature #15347 [DependencyInjection][3.0] Add initialized to container interface (znerol) - * feature #15219 [DependencyInjection] Added ParameterBagInterface::remove (lyrixx) - * feature #14927 Removed deprecated stuff from the Config component (dosten) - * feature #14693 [3.0][Translator] changed the visibility of the locale from protected to private. (aitboudad) - * feature #14979 [Security] removed obsolete translations (fabpot) - * feature #14928 Removed deprecated stuff from the Console component (dosten) - * feature #14155 Removed deprecations in DependencyInjection component (dosten) - * feature #14145 Removed deprecations in Filesystem component (dosten) - * feature #14153 Remove the legacy PDO session handler (dosten) - * feature #14634 [3.0][HttpKernel] remove deprecated functions and classes (vincentaubert) - * feature #14317 [3.0][FrameworkBundle][lint commands ] remove deprecated alias. (vincentaubert) - * feature #14220 [3.0][Console][OutputStyle] Implements verbosity levels methods (ogizanagi) - * feature #14169 [Debug] Removed deprecated interfaces (nicolas-grekas) - * feature #14147 Removed deprecations in Process component (dosten) - * feature #14150 Removed deprecations in Templating component (dosten) - * feature #14156 Remove deprecated Locale component (stloyd) - * feature #14120 Removed deprecation in translation component (saro0h) - * feature #14118 Removed deprecations in Console component (saro0h) - * feature #14119 Removed deprecation in YAML component (saro0h) - * feature #14091 [3.0] [FrameworkBundle] Drop backward compatibility for debug commands (matthieuauger) - * feature #14070 removed all *.class parameters (fabpot) - * feature #13808 [OptionsResolver] removed deprecated functionality (Tobion) - * feature #13756 [3.0][Console] Added type hint (francisbesset) - * feature #13752 [PropertyAccess] remove deprecations for 3.0 (Tobion) - * feature #13666 removed deprecated asset feature (fabpot) - * feature #13407 [Form] Remove deprecated setDefaultOptions and OptionsResolverInterface (peterrehm) - * feature #13396 [Routing] remove deprecations for 3.0 (Tobion) - * feature #13444 [Serializer] Remove deprecated JSON error methods (dunglas) - * feature #13258 [HttpFoundation] remove deprecated : FlashBag don't implement anymore IteratorAggregate (FlorianLB) - * feature #13086 [Console] Define isVerbose(), etc. methods in OutputInterface (frne) - * feature #13348 [3.0][Monolog] Remove deprecated interface and deprecated methods (rosier) - * feature #13260 [3.0][EventDispatcher][Event] removed deprecated methods (aitboudad) - * feature #13203 [3.0] [ClassLoader] removed deprecated UniversalClassLoader and DebugClassLoader classes. (hhamon) - * feature #13409 removed deprecated Twig features (fabpot) - * feature #13233 [TwigBundle] removed deprecated ActionsExtension (fabpot) - * feature #12663 [FrameworkBundle] remove deprecated method 'createEsi' (FlorianLB) - * feature #13122 [3.0][Form] Removed depracted events PRE_BIND, BIND and POST_BIND (saro0h) - * feature #13216 [3.0] [Config] removed deprecated ReferenceDumper class. (hhamon) - * feature #12457 [FrameworkBundle] REFS #11294 Controller class become abstract (mickaelandrieu) - * feature #12460 [3.0] [FrameworkBundle] removed request service occurrences. (hhamon) - * feature #13121 [Console] Removed DialogHelper, ProgressHelper and TableHelper (saro0h) - * feature #13127 [FrameworkBundle] Removed the deprecated RouterApacheDumperCommand (saro0h) - * feature #13128 [Form] Removed deprecated form_enctype() (saro0h) - * feature #13130 [HttpKernel] Removed deprecated ErrorHandler and ExceptionHandler classes (saro0h) - * feature #13129 [Yaml] Removed the ability to parse a file in Yaml::parse() (saro0h) - * feature #12994 Add LegacyPdoSessionHandler class (jeremylivingston) - * feature #13046 [3.0] [Bridge] [Swiftmailer] removed Swiftmailer bridge namespace. (hhamon) - * feature #12854 [3.0][HttpKernel] Remove unused method Kernel::isClassInActiveBundle (hacfi) - * feature #12697 [3.0][Process] Remove deprecated methods (romainneutron) - * feature #12731 [Monolog Bridge] Remove deprecated log methods + add unit tests (FlorianLB) - * feature #12461 [HttpKernel] Removed deprecated Kernel::init() method (saro0h) diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md deleted file mode 100644 index 36efff588f4d8..0000000000000 --- a/CHANGELOG-3.1.md +++ /dev/null @@ -1,478 +0,0 @@ -CHANGELOG for 3.1.x -=================== - -This changelog references the relevant changes (bug and security fixes) done -in 3.1 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.1.0...v3.1.1 - -* 3.1.9 (2017-01-12) - - * bug #21218 [Form] DateTimeToLocalizedStringTransformer does not use timezone when using date only (magnetik) - * bug #20605 [Ldap] Always have a valid connection when using the EntryManager (bobvandevijver) - * bug #21104 [FrameworkBundle] fix IPv6 address handling in server commands (xabbuh) - * bug #20793 [Validator] Fix caching of constraints derived from non-serializable parents (uwej711) - * bug #19586 [TwigBundle] Fix bug where namespaced paths don't take parent bundles in account (wesleylancel) - * bug #21237 [FrameworkBundle] Fix relative paths used as cache keys (nicolas-grekas) - * bug #21183 [Validator] respect groups when merging constraints (xabbuh) - * bug #21179 [TwigBundle] Fixing regression in TwigEngine exception handling (Bertalan Attila) - * bug #21220 [DI] Fix missing new line after private alias (ogizanagi) - * bug #21211 Classloader tmpname (lyrixx) - * bug #21205 [TwigBundle] fixed usage when Templating is not installed (fabpot) - * bug #21155 [Validator] Check cascasdedGroups for being countable (scaytrase) - * bug #21200 [Filesystem] Check that directory is writable after created it in dumpFile() (chalasr) - * bug #21165 [Serializer] int is valid when float is expected when deserializing JSON (dunglas) - * bug #21166 [Cache] Fix order of writes in ChainAdapter (nicolas-grekas) - * bug #21113 [FrameworkBundle][HttpKernel] Fix resources loading for bundles with custom structure (chalasr) - * bug #21084 [Yaml] handle empty lines inside unindented collection (xabbuh) - * bug #20925 [HttpFoundation] Validate/cast cookie expire time (ro0NL) - * bug #21032 [SecurityBundle] Made collection of user provider unique when injecting them to the RemberMeService (lyrixx) - * bug #21078 [Console] Escape default value when dumping help (lyrixx) - * bug #21076 [Console] OS X Can't call cli_set_process_title php without superuser (ogizanagi) - * bug #20900 [Console] Descriptors should use Helper::strlen (ogizanagi) - * bug #21025 [Cache] remove is_writable check on filesystem cache (4rthem) - * bug #21064 [Debug] Wrap call to ->log in a try catch block (lyrixx) - * bug #21010 [Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes (SpacePossum) - * bug #20991 [cache] Bump RedisAdapter default timeout to 5s (Nicofuma) - * bug #20859 Avoid warning in PHP 7.2 because of non-countable data (wouterj) - * bug #21053 [Validator] override property constraints in child class (xabbuh) - * bug #21034 [FrameworkBundle] Make TemplateController working without the Templating component (dunglas) - * bug #20970 [Console] Fix question formatting using SymfonyStyle::ask() (chalasr, ogizanagi) - * bug #20999 [HttpKernel] Continuation of #20599 for 3.1 (ro0NL) - * bug #20975 [Form] fix group sequence based validation (xabbuh) - * bug #20599 [WebProfilerBundle] Display multiple HTTP headers in WDT (ro0NL) - * bug #20799 [TwigBundle] do not try to register incomplete definitions (xabbuh) - * bug #20961 [Validator] phpize default option values (xabbuh) - * bug #20934 [FrameworkBundle] Fix PHP form templates on translatable attributes (ro0NL) - * bug #20957 [FrameworkBundle] test for the Validator component to be present (xabbuh) - * bug #20936 [DependencyInjection] Fix on-invalid attribute type in xsd (ogizanagi) - * bug #20931 [VarDumper] Fix dumping by-ref variadics (nicolas-grekas) - * bug #20734 [Security] AbstractVoter->supportsAttribute gives false positive if attribute is zero (0) (martynas-foodpanda) - * bug #14082 [config] Fix issue when key removed and left value only (zerustech) - * bug #20910 [HttpFoundation] Fix cookie to string conversion for raw cookies (ro0NL) - * bug #20847 [Console] fixed BC issue with static closures (araines) - -* 3.1.8 (2016-12-13) - - * bug #20714 [FrameworkBundle] Fix unresolved parameters from default configs in debug:config (chalasr) - * bug #20442 [FrameworkBundle] Bundle commands are not available via find() (julienfalque) - * bug #20840 [WebProfilerBundle] add dependency on Twig (xabbuh) - * bug #20828 [Validator] Fix init of YamlFileLoader::$classes for empty files (nicolas-grekas) - * bug #20745 [Validator] add class name to the cache key (Simperfit) - * bug #20530 [Serializer] Remove AbstractObjectNormalizer::isAttributeToNormalize (dunglas) - * bug #19141 Throw less misleading exception when property access not found (bramtweedegolf) - * bug #20539 Cast result to int before adding to it (alcaeus) - * bug #20831 [Twig] Fix deprecations with Twig 1.29 (nicolas-grekas) - * bug #20816 [FrameworkBundle] Removed kernel.debug from the cache pool namespace seed (Sander Toonen) - * bug #20646 Maintain the selected panel when redirecting to another profile (javiereguiluz) - * bug #20767 [Cache] Fix dumping SplDoublyLinkedList iter mode (nicolas-grekas) - * bug #20736 [Console] fixed PHP7 Errors when not using Dispatcher (keradus) - * bug #20756 [HttpKernel] Regression test for missing controller arguments (iltar) - * bug #20755 [HttpKernel] Regression test for missing controller arguments (iltar) - * bug #20732 fix the inline level for dumped multi-line strings (xabbuh) - * bug #20418 [Form][DX] FileType "multiple" fixes (yceruto) - * bug #19902 [DependencyInjection] PhpDumper.php: hasReference() shouldn't search references in lazy service. (antanas-arvasevicius) - * bug #20704 [Console] Fix wrong handling of multiline arg/opt descriptions (ogizanagi) - * bug #20712 [TwigBundle] Fix twig loader registered twice (ogizanagi) - * bug #20716 [WebProfilerBundle] Fix dump block is unfairly restrained (ogizanagi) - * bug #20671 [Config] ConfigCache::isFresh() should return false when unserialize() fails (nicolas-grekas) - * bug #20676 [ClassLoader] Use only forward slashes in generated class map (nicolas-grekas) - * bug #20664 [Validator] ensure the proper context for nested validations (xabbuh) - * bug #20661 bug #20653 [WebProfilerBundle] Profiler includes ghost panels (jzawadzki) - * bug #20374 [FrameworkBundle] Improve performance of ControllerNameParser (enumag) - * bug #20474 [Routing] Fail properly when a route parameter name cannot be used as a PCRE subpattern name (fancyweb) - * bug #20566 [DI] Initialize properties before method calls (ro0NL) - * bug #20609 [DI] Fixed custom services definition BC break introduced in ec7e70fb… (kiler129) - * bug #20598 [DI] Aliases should preserve the aliased invalid behavior (nicolas-grekas) - * bug #20600 [Process] Fix process continuing after reached timeout using getIterator() (chalasr) - * bug #20602 [HttpKernel] Revert BC breaking change of Request::isMethodSafe() (nicolas-grekas) - * bug #20499 [Doctrine][Form] support large integers (xabbuh) - * bug #20576 [Process] Do feat test before enabling TTY mode (nicolas-grekas) - * bug #20577 [FrameworkBundle] Mark cache.default_*_provider services private (nicolas-grekas) - -* 3.1.7 (2016-11-21) - - * bug #20550 [YAML] Fix processing timestamp strings with timezone (myesain) - * bug #20543 [DI] Fix error when trying to resolve a DefinitionDecorator (nicolas-grekas) - * bug #20544 [PhpUnitBridge] Fix time-sensitive tests that use data providers (julienfalque) - * bug #20484 bumped min version of Twig to 1.28 (fabpot) - * bug #20519 [Debug] Remove GLOBALS from exception context to avoid endless recursion (Seldaek) - * bug #20455 [ClassLoader] Fix ClassCollectionLoader inlining with __halt_compiler (giosh94mhz) - * bug #20307 [Form] Fix Date\TimeType marked as invalid on request with single_text and zero seconds (LuisDeimos) - * bug #20480 [FrameworkBundle] Register the ArrayDenormalizer (dunglas) - * bug #20286 [Serializer] Fix DataUriNormalizer's regex (dunglas) - * bug #20466 [Translation] fixed nested fallback catalogue using multiple locales. (aitboudad) - * bug #20465 [#18637][TranslationDebug] workaround for getFallbackLocales. (aitboudad) - * bug #20453 [Cache] Make directory hashing case insensitive (nicolas-grekas) - * bug #20440 [TwigBridge][TwigBundle][HttpKernel] prefer getSourceContext() over getSource() (xabbuh) - * bug #20287 Properly format value in UniqueEntityValidator (alcaeus) - * bug #20422 [Translation][fallback] add missing resources in parent catalogues. (aitboudad) - * bug #20378 [Form] Fixed show float values as choice value in ChoiceType (yceruto) - * bug #20294 Improved the design of the metrics in the profiler (javiereguiluz) - * bug #20375 [HttpFoundation][Session] Fix memcache session handler (klandaika) - * bug #20377 [Console] Fix infinite loop on missing input (chalasr) - * bug #20372 [Console] simplified code (fabpot) - * bug #20342 [Form] Fix UrlType transforms valid protocols (ogizanagi) - * bug #20292 Enhance GAE compat by removing some realpath() (nicolas-grekas) - * bug #20326 [VarDumper] Fix dumping Twig source in stack traces (nicolas-grekas) - * bug #20321 Compatibility with Twig 1.27 (xkobal) - -* 3.1.6 (2016-10-27) - - * bug #20291 [Yaml] Fix 7.1 compat (nicolas-grekas) - * bug #20289 Fix edge case with StreamedResponse where headers are sent twice (Nicofuma) - * bug #20267 [DependencyInjection] A decorated service should not keep the autowiring types (chalasr) - * bug #20278 [DependencyInjection] merge tags instead of completely replacing them (xabbuh) - * bug #20271 Changes related to Twig 1.27 (fabpot) - * bug #20252 Trim constant values in XmlFileLoader (lstrojny) - * bug #20239 [HttpKernel] Fix a regression in the RequestDataCollector (jakzal) - * bug #20253 [TwigBridge] Use non-deprecated Twig_Node::getTemplateLine() (fabpot) - * bug #20243 [WebProfilerBundle][btn-link] add `cursor: pointer` (aitboudad) - * bug #20175 [VarDumper] Fix source links with latests Twig versions (nicolas-grekas) - * bug #20235 [DomCrawler] Allow pipe (|) character in link tags when using Xpath expressions (klausi, nicolas-grekas) - * bug #20224 [Twig] removed deprecations added in Twig 1.27 (fabpot) - * bug #19478 fixed Filesystem:makePathRelative and added 2 more testcases (muhammedeminakbulut) - * bug #20218 [HttpFoundation] no 304 response if method is not cacheable (xabbuh) - * bug #20207 [DependencyInjection] move tags from decorated to decorating service (xabbuh) - * bug #20205 [HttpCache] fix: do not cache OPTIONS request (dmaicher) - * bug #20146 [Validator] Prevent infinite loop in PropertyMetadata (wesleylancel) - * bug #20184 [FrameworkBundle] Convert null prefix to an empty string in translation:update (chalasr) - * bug #20154 [PropertyInfo] Fix edge cases in ReflectionExtractor (nicolas-grekas) - * bug #19725 [Security] $attributes can be anything, but RoleVoter assumes strings (Jonatan Männchen) - * bug #20127 [HttpFoundation] JSONP callback validation (ro0NL) - * bug #20163 add missing use statement (xabbuh) - * bug #19961 [Console] Escape question text and default value in SymfonyStyle::ask() (chalasr) - * bug #20141 [Console] Fix validation of empty values using SymfonyQuestionHelper::ask() (chalasr) - * bug #20147 [FrameworkBundle] Alter container class instead of kernel name in cache:clear command (nicolas-grekas) - * bug #20156 Fix event annotation for arguments resolving event (Koc) - * bug #20152 [HttpKernel] Fix nullable types handling (nicolas-grekas) - -* 3.1.5 (2016-10-03) - - * bug #20102 [Validator] Url validator not validating hosts ending in a number (gwkunze) - * bug #20132 Use "more entropy" option for uniqid() (javiereguiluz) - * bug #20122 [Validator] Reset constraint options (ro0NL) - * bug #20116 fixed AddConstraintValidatorsPass config (fabpot) - * bug #20078 Fix #19943 Make sure to process each interface metadata only once (lemoinem) - * bug #20080 [Form] compound forms without children should be considered rendered implicitly (backbone87) - * bug #20087 [VarDumper] Fix PHP 7.1 compat (nicolas-grekas) - * bug #20086 [VarDumper] Fix PHP 7.1 compat (nicolas-grekas) - * bug #20077 [Process] silent file operation to avoid open basedir issues (xabbuh) - * bug #20079 fixed Twig support for 1.26 and 2.0 (fabpot) - * bug #20051 Fix indexBy type extraction (lemoinem) - * bug #19951 [Finder] Trim trailing directory slash in ExcludeDirectoryFilterIterator (ro0NL) - * bug #19980 [Ldap] Fixed issue with legacy find() method not working as expected (csarrazi) - * bug #20026 [Cache] Fixed password used to make the redis connection. (ErikSaunier) - * bug #20018 [VarDumper] Fix test (nicolas-grekas) - * bug #20011 Use UUID for error codes for Form validator. (Koc) - * bug #20010 [DX] Fixed regression when exception message swallowed when logging it. (Koc) - * bug #19983 [TwigBridge] removed Twig null nodes (deprecated as of Twig 1.25) (fabpot) - * bug #19946 [Console] Fix parsing optionnal options with empty value in argv (chalasr) - * bug #19636 [Finder] no PHP warning on empty directory iteration (ggottwald) - * bug #19784 [HttpKernel] Fixed the nullable support for php 7.1 and below (iltar) - * bug #19923 [bugfix] [Console] Set `Input::$interactive` to `false` when command is executed with `--quiet` as verbosity level (phansys) - * bug #19811 Fixed the nullable support for php 7.1 and below (2.7, 2.8, 3.0) (iltar) - * bug #19853 [PropertyInfo] Make ReflectionExtractor compatible with ReflectionType changes in PHP 7.1 (teohhanhui) - * bug #19904 [Form] Fixed collapsed ChoiceType options attributes (HeahDude) - * bug #19872 [Filesystem] Consider the umask setting when dumping a file (leofeyer) - * bug #19908 [Config] Handle open_basedir restrictions in FileLocator (Nicofuma) - * bug #19893 [FrameworkBundle] Remove cache clearer default value in config (nicolas-grekas) - * bug #19924 [DoctrineBridge][PropertyInfo] Treat Doctrine decimal type as string (teohhanhui) - * bug #19932 Fixed bad merge (GrahamCampbell) - * bug #19922 [Yaml][TwigBridge] Use JSON_UNESCAPED_SLASHES for lint commands output (chalasr) - * bug #19928 [Validator] Update IpValidatorTest data set with a valid reserved IP (jakzal) - * bug #19813 [Console] fixed PHP7 Errors are now handled and converted to Exceptions (fonsecas72) - * bug #19879 [Form] Incorrect timezone with DateTimeLocalizedStringTransformer (mbeccati) - * bug #19878 Fix translation:update command count (tgalopin) - * bug #19859 [ClassLoader] Fix ClassCollectionLoader inlining with declare(strict_types=1) (nicolas-grekas) - * bug #19780 [FrameworkBundle] Incorrect line break in exception message (500 debug page) (pedroresende) - * bug #19595 [form] lazy trans `post_max_size_message`. (aitboudad) - * bug #19870 [DI] Fix setting synthetic services on ContainerBuilder (nicolas-grekas) - * bug #19848 Revert "minor #19689 [DI] Cleanup array_key_exists (ro0NL)" (nicolas-grekas) - * bug #19842 [FrameworkBundle] Check for class existence before is_subclass_of (chalasr) - * bug #19827 [BrowserKit] Fix cookie expiration on 32 bit systems (jameshalsall) - -* 3.1.4 (2016-09-03) - - * bug #19812 [WebProfilerBundle] Fix margin on toolbar route panel when no route is found in the request (jameshalsall) - * bug #19786 Update profiler's layout to use flexbox (javiereguiluz) - * bug #19794 [VarDumper] Various minor fixes & cleanups (nicolas-grekas) - * bug #19751 Fixes the calendar in constructor to handle null (wakqasahmed) - * bug #19743 [symfony/symfony] add "provides" for psr/cache-implementation (alcohol) - * bug #19388 [Validator][GroupSequence] fixed GroupSequence validation ignores PropetyMetadata of parent classes (Sandro Hopf) - * bug #19729 Add symfony/inflector to composer.json "replaces" (teohhanhui) - * bug #19601 [FrameworkBundle] Added friendly exception when constraint validator class does not exist (yceruto) - * bug #19580 [Validator] fixed duplicate constraints with parent class interfaces (dmaicher) - * bug #19647 [Debug] Swap dumper services at bootstrap (lyrixx) - * bug #19685 [DI] Include dynamic services in alternatives (ro0NL) - * bug #19702 [Debug][HttpKernel][VarDumper] Prepare for committed 7.2 changes (aka "small-bc-breaks") (nicolas-grekas) - * bug #19704 [DependencyInjection] PhpDumper::isFrozen inconsistency (allflame) - * bug #19643 [DependencyInjection] Fix service autowiring inheritance (chalasr) - * bug #19649 [Serializer] Fix denormalization of arrays (dunglas) - * bug #19667 [SecurityBundle] Add missing deprecation notice for form_login.intention (ro0NL) - * bug #19666 Verify explicitly that the request IP is a valid IPv4 address (nesk) - * bug #19660 Disable CLI color for Windows 10 greater than 10.0.10586 (mlocati) - * bug #19663 Exception details break the layout (Dionysis Arvanitis) - * bug #19651 [HttpKernel] Fix HttpCache validation HTTP method (tgalopin) - * bug #19650 [FrameworkBundle] Fix default lifetime of cache pools (nicolas-grekas) - * bug #19623 [VarDumper] Fix dumping continuations (nicolas-grekas) - * bug #19437 [PropertyInfo] Fix an error in PropertyInfoCacheExtractor (Ener-Getick) - * bug #19549 [HttpFoundation] fixed Request::getContent() reusage bug (1ma) - * bug #19373 [Form] Skip CSRF validation on form when POST max size is exceeded (jameshalsall) - * bug #19541 Fix #19531 [Form] DateType fails parsing when midnight is not a valid time (mbeccati) - * bug #19567 [Cache] Handle unserialize() failures gracefully (nicolas-grekas) - * bug #19579 [Process] Strengthen Windows pipe files opening (again...) (nicolas-grekas) - * bug #19564 Added class existence check if is_subclass_of() fails in compiler passes (SCIF) - * bug #19551 [Cache] Use SCAN instead of KEYS with Redis >= 2.8 (nicolas-grekas) - * bug #19522 [SwiftMailerBridge] Fix flawed deprecation message (chalasr) - * bug #19510 [Process] Fix double-fread() when reading unix pipes (nicolas-grekas) - * bug #19508 [Process] Fix AbstractPipes::write() for a situation seen on HHVM (at least) (nicolas-grekas) - * bug #19470 undefined offset fix (#19406) (ReenExe) - -* 3.1.3 (2016-07-30) - - * bug #19300 [HttpKernel] Use flock() for HttpCache's lock files (mpdude) - * bug #19428 [Process] Fix write access check for pipes on Windows (nicolas-grekas) - * bug #19442 [Cache] Fix default lifetime being ignored (nicolas-grekas) - * bug #19435 [Cache] Fix incorrect timestamps generated by FilesystemAdapter (nicolas-grekas) - * bug #19434 [Serializer] enable property info in framework bundle (David Badura) - * bug #19439 [DependencyInjection] Fixed deprecated default message template with XML (jeremyFreeAgent) - * bug #19397 [HttpFoundation] HttpCache refresh stale responses containing an ETag (maennchen) - * bug #19426 [Form] Fix the money form type render with Bootstrap3 (Th3Mouk) - * bug #19422 [DomCrawler] Inherit the namespace cache in subcrawlers (stof) - * bug #19425 [BrowserKit] Uppercase the "GET" method in redirects (jakzal) - * bug #19384 Fix PHP 7.1 related failures (nicolas-grekas) - * bug #19379 [VarDumper] Fix for PHP 7.1 (nicolas-grekas) - * bug #19342 Added class existence check if is_subclass_of() fails in compiler passes (SCIF) - * bug #19369 Fix the DBAL session handler version check for Postgresql (stof) - * bug #19368 [VarDumper] Fix dumping jsons casted as arrays (nicolas-grekas) - * bug #19334 [Security] Fix the retrieval of the last username when using forwarding (stof) - * bug #19352 [Serializer] Include the format in the cache key (dunglas) - * bug #19321 [HttpFoundation] Add OPTIONS and TRACE to the list of safe methods (dunglas) - * bug #19317 [BrowserKit] Update Client::getAbsoluteUri() for query string only URIs (georaldc) - * bug #19298 [ClassLoader] Fix declared classes being computed when not needed (nicolas-grekas) - * bug #19316 [Validator] Added additional MasterCard range to the CardSchemeValidator (Dennis Væversted) - * bug #19290 [HttpKernel] fixed internal subrequests having an if-modified-since-header (MalteWunsch) - * bug #19307 [Security] Fix deprecated usage of DigestAuthenticationEntryPoint::getKey() in DigestAuthenticationListener (Maxime STEINHAUSSER) - * bug #19309 [DoctrineBridge] added missing error code for constraint. (Koc) - * bug #19306 [Form] fixed bug - name in ButtonBuilder (cheprasov) - * bug #19292 [varDumper] Fix missing usage of ExceptionCaster::$traceArgs (nicolas-grekas) - * bug #19288 [VarDumper] Fix indentation trimming in ExceptionCaster (nicolas-grekas) - * bug #19267 [Validator] UuidValidator must accept a Uuid constraint. (hhamon) - * bug #19272 [Security] fixed DebugAccessDecisionManager::setVoters() (HeahDude) - * bug #19260 [Form] Fix depreciation triggers (tgalopin) - * bug #19186 Fix for #19183 to add support for new PHP MongoDB extension in sessions. (omanizer) - * bug #19253 [Console] Fix block() padding formatting after #19189 (chalasr) - * bug #19218 [Security][Guard] check if session exist before using it (pasdeloup) - -* 3.1.2 (2016-06-30) - - * bug #19227 [DoctrineBridge] fixed default parameter value in UniqueEntityValidator (HeahDude) - * bug #18934 Fixed some issues of the AccessDecisionManager profiler (javiereguiluz) - * bug #19217 [HttpKernel] Inline ValidateRequestListener logic into HttpKernel (nicolas-grekas) - * bug #18688 [HttpFoundation] Warning when request has both Forwarded and X-Forwarded-For (magnusnordlander) - * bug #19173 [Console] Decouple SymfonyStyle from TableCell (ro0NL) - * bug #19204 [Security] Allow LDAP loadUser override (tucksaun) - * bug #19189 [Console] Fix formatting of SymfonyStyle::comment() (chalasr) - * bug #19211 [Form] fix post max size translation type extension for >= 2.8 (Tobion) - * bug #17822 [WIP] [Form] fix `empty_data` option in expanded `ChoiceType` (HeahDude) - * bug #19159 [WebProfilerBundle] Added a conflict for Http-Kernel < 3.1 (HeahDude) - * bug #19134 Distinguish between first and subsequent progress bar displays (rquadling) - * bug #19061 [FORM] fix post_max_size_message translation (alt. 2) (David Badura) - * bug #19100 [Console] Fixed SymfonyQuestionHelper multi-choice with defaults (sstok) - * bug #18924 [DoctrineBridge] Don't use object IDs in DoctrineChoiceLoader when passing a value closure (webmozart) - * bug #19138 [DomCrawler] No more exception on field name with strange format (guiled, fabpot) - * bug #18935 [Form] Consider a violation even if the form is not submitted (egeloen) - * bug #19127 [Form] Add exception to FormRenderer about non-unique block names (enumag) - * bug #19118 [Process] Fix pipes cleaning on Windows (nicolas-grekas) - * bug #19128 Avoid phpunit 5.4 warnings on getMock (2.7+) (iltar) - * bug #19120 [FrameworkBundle] templating can be fully disabled (xabbuh) - * bug #19114 [HttpKernel] Dont close the reponse stream in debug (nicolas-grekas) - * bug #19101 [Session] fix PDO transaction aborted under PostgreSQL (Tobion) - * bug #18501 [HttpFoundation] changed MERGE queries (hjkl) - * bug #19081 [YAML] Fixed parsing problem with nested DateTime lists (jkphl, xabbuh) - * bug #19062 [HttpFoundation] Fix UPSERT for PgSql >= 9.5 (nicolas-grekas) - * bug #18548 [Form] minor fixes in DateTime transformers (HeahDude) - * bug #18732 [PropertyAccess][DX] Enhance exception that say that some methods are missing if they don't (nykopol) - -* 3.1.1 (2016-06-15) - - * bug #19048 [HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5 (nicolas-grekas) - * bug #19042 [Cache] Fix double fetch in ProxyAdapter (nicolas-grekas) - * bug #19038 Fix feature detection for IE (Alsciende) - * bug #18915 [DependencyInjection] force enabling the external XML entity loaders (xabbuh) - * bug #19020 [Form] Fixed collapsed choice attributes (HeahDude) - * bug #19028 [Yaml] properly count skipped comment lines (xabbuh) - * bug #19009 [WebProfilerBundle] Fix invalid CSS style (romainneutron) - * bug #17733 [Yaml] Fix wrong line number when comments are inserted in the middle of a block. (paradajozsef) - * bug #18909 Fixed singular of committee (peterrehm) - * bug #18911 Fixed singular of committee (peterrehm) - * bug #18971 Do not inject web debug toolbar on attachments (peterrehm) - * bug #18944 [Ldap] Fixed issue with legacy client initialisation (csarrazi) - * bug #18974 Added missing APCU CacheProvider of doctrine/cache 1.6.x (Carsten Eilers) - * bug #18954 [HttpKernel] Fix RequestDataCollector starting the session (romainneutron) - * bug #18949 [Security] Fix DebugAccessDecisionManager when object is not a scalar (romainneutron) - * bug #18959 [WebProfilerBundle] Fixed forwarded request data in templates (HeahDude) - * bug #18943 [Ldap][Security] The Ldap user provider abstract service now has the correct number of arguments (csarrazi) - * bug #18925 [Console] Fix BC break introduced by #18101 (dunglas) - * bug #18908 [DependencyInjection] force enabling the external XML entity loaders (xabbuh) - * bug #18893 [DependencyInjection] Skip deep reference check for 'service_container' (RobertMe) - * bug #18812 Catch \Throwable (fprochazka) - * bug #18821 [Form] Removed UTC specification with timestamp (francisbesset) - * bug #18861 Fix for #18843 (inso) - -* 3.1.0 (2016-05-30) - - * bug #18889 [Console] SymfonyStyle: Fix alignment/prefixing of multi-line comments (chalasr) - * bug #18907 [Routing] Fix the annotation loader taking a class constant as a beginning of a class name (jakzal, nicolas-grekas) - * bug #18899 [Yaml] search for colons in strings only (xabbuh) - -* 3.1.0-RC1 (2016-05-26) - - * bug #18879 [Console] SymfonyStyle: Align multi-line/very-long-line blocks (chalasr) - * bug #18881 [Security][Ldap] Fixed issue with password attribute containing an array of values. (csarrazi) - * bug #18864 [Console][DX] Fixed ambiguous error message when using a duplicate option shortcut (peterrehm) - * bug #18883 Fix js comment in profiler (linnaea) - * feature #18867 [Cache] Drop counting hit/miss in ProxyAdapter (nicolas-grekas) - * bug #18837 [Serializer] AbstractObjectNormalizer: be sure that isAllowedAttribute is called (dunglas) - * bug #18838 [Serializer] ObjectNormalizer: add missing parameters (dunglas) - * bug #18844 [Yaml] fix exception contexts (xabbuh) - * bug #18840 [Yaml] properly handle unindented collections (xabbuh) - * bug #18765 Catch \Throwable (fprochazka) - * bug #18813 Catch \Throwable (fprochazka) - * bug #18839 People - person singularization (Keeo) - * bug #18820 [Config] Allow schemed paths in FileResource (nicolas-grekas) - * bug #18828 [Yaml] chomp newlines only at the end of YAML documents (xabbuh) - * bug #18814 Fixed server status command when port has been omitted (peterrehm) - * bug #18759 [Validator] Support for DateTimeImmutable (krzysiekpiasecki) - * bug #18799 Use levenshtein level for better Bundle matching (j0k3r) - * bug #18413 [WebProfilerBundle] Fix CORS ajax security issues (romainneutron) - -* 3.1.0-BETA1 (2016-05-13) - - * feature #18725 [Ldap] Added the possibility to configure all available Ldap options for connection (csarrazi) - * feature #18715 [FrameworkBundle] Default to Apcu+Filesystem cache chain (nicolas-grekas) - * feature #18184 [DomCrawler] Expose getter for uri (hason) - * feature #18654 [Bridge/Doctrine] Use better exception in the register mapping pass (dantleech) - * feature #18676 [HttpKernel] Add request method to logger messages (gnat42) - * feature #18716 [Cache] Add nonce based cache invalidation to ApcuAdapter (nicolas-grekas) - * feature #18762 [Translation] XLIFF Add `id` to meta data. (SpacePossum) - * feature #18689 [Cache] Add support for Predis, RedisArray and RedisCluster (nicolas-grekas) - * feature #18667 [FrameworkBundle] Semantic config for app/system/pool caches (tgalopin, nicolas-grekas) - * feature #18685 move event listener method type hint docs to @Event annotations defau… (Haehnchen) - * feature #18681 [Cache] Add DSN based Redis connection factory (nicolas-grekas) - * feature #18656 Updating the error message of an AuthenticationEntryPointInterface (weaverryan) - * feature #18069 [DoctrineBridge] deprecate `MergeDoctrineCollectionListener::onBind()` (HeahDude) - * feature #18492 [LDAP] Check whether an entry attribute exists (hiddewie) - * feature #18359 [Form] [DoctrineBridge] optimized LazyChoiceList and DoctrineChoiceLoader (HeahDude) - * feature #18357 [Form] Let `TextType` implement `DataTransformerInterface` (HeahDude) - * feature #18631 [FrameworkBundle] Add optional logger to cache pools (nicolas-grekas) - * feature #18597 [Cache] Add CacheItem::validateKey utility method (nicolas-grekas) - * feature #17660 [Serializer] Integrate the PropertyInfo Component (recursive denormalization and hardening) (mihai-stancu, dunglas) - * feature #18561 [FrameworkBundle] Fallback to default cache system in production for serializer (tgalopin) - * feature #18567 [FrameworkBundle][Serializer] Fix APC cache service name (tgalopin) - * feature #17959 [Serializer] Harden the ObjectNormalizer (dunglas) - * feature #18547 DX: better error message if factory class is empty (dbu) - * feature #18020 fix #17993 - Deprecated callable strings (hamza) - * feature #18487 [Cache] Add DoctrineProvider, for using PSR-6 pools in Doctrine Cache (nicolas-grekas) - * feature #18544 [FrameworkBundle] Fallback to default cache system in production for validation (tgalopin) - * feature #18416 [FrameworkBundle] Calls support for debug:container (JhonnyL) - * feature #18513 [Process] Turn getIterator() args to flags & add ITER_SKIP_OUT/ERR modes (nicolas-grekas) - * feature #18371 [FrameworkBundle] integrate the Cache component (xabbuh, nicolas-grekas) - * feature #18440 Add the kernel.controller_arguments event (stof) - * feature #18308 Added an ArgumentResolver with clean extension point (iltar, HeahDude) - * feature #18414 [Process] Implement IteratorAggregate to stream output (nicolas-grekas) - * feature #18144 [DI] Only rebuild autowiring cache when actually needed (weaverryan) - * feature #18386 [Process] Add InputStream to seamlessly feed running processes (nicolas-grekas) - * feature #18167 [DependencyInjection] Fix a limitation of the PhpDumper (Ener-Getick) - * feature #18387 [DX] [LDAP] Added default service name for the Security component's Ldap providers (csarrazi) - * feature #18290 [Translation] deprecate the backup feature (xabbuh) - * feature #18036 [Serializer] XmlEncoder: Make load flags configurable (dunglas) - * feature #17589 [WebProfilerBundle] [DX] Feature allow forward and redirection detection in wdt (HeahDude) - * feature #18260 Add Inflector component (from StringUtil of PropertyAccess) (teohhanhui) - * feature #18356 [FrameworkBundle] Deprecated form types as services (HeahDude) - * feature #17458 Add strict image validation (Koc) - * feature #18350 [Process] Accept Traversable input (nicolas-grekas) - * feature #18135 [Security] Deprecate onAuthenticationSuccess() (weaverryan) - * feature #18294 [Yaml] dump non UTF-8 encoded strings as binary data (xabbuh) - * feature #18215 [Cache] Add a Chain adapter (dunglas, nicolas-grekas) - * feature #18242 [FrameworkBundle][TwigBundle] Make EngineInterface autowirable (dunglas) - * feature #18197 Make Request::isFromTrustedProxy() public. (Peter Bex) - * feature #18211 [Security] Use auth trust resolver to determine anonymous in ContextListener (WouterJ) - * feature #18232 [Bridge\PhpUnit] Add "disabled" mode to SYMFONY_DEPRECATIONS_HELPER (nicolas-grekas) - * feature #18181 [PhpUnitBridge] Mock DNS functions (nicolas-grekas) - * feature #18176 [Cache] Restrict flushes to namespace scopes (nicolas-grekas) - * feature #18172 [Cache] Redis adapter (gcds, nicolas-grekas) - * feature #18101 [Console] Allow to register commands privately (Ener-Getick) - * feature #18143 [DomCrawler] Exposed getter for baseHref (AAstakhov) - * feature #18034 [FrameworkBundle] Deprecate absolute template paths (jakzal) - * feature #18105 [HttpFoundation] Add support for sending raw cookies in the response (jakzal) - * feature #17255 [Console] ApplicationTester - test stdout and stderr (SpacePossum) - * feature #18024 [Cache] Add namespace handling to all adapters (nicolas-grekas) - * feature #17734 [Cache] Count cache hits/misses in ProxyAdapter (nicolas-grekas) - * feature #17887 Show more information in the security profiler (javiereguiluz) - * feature #17642 [FrameworkBundle] [DX] Add `Controller::json` method to make it easy to send json (mcfedr) - * feature #17484 [FrameworkBundle][DX] Add Levenshtein suggesters to AbstractConfigCommand (kix) - * feature #17690 [FrameworkBundle] Use canBeEnabled() instead of canBeUnset() for consistency (Ener-Getick) - * feature #17714 Adding new TargetPathTrait to get/set the authentication "target_path" (weaverryan) - * feature #17852 Improved the logger panel when the log context is very long (javiereguiluz) - * feature #17761 [Console] Add non-auto column width functionality (akeeman) - * feature #17943 [Yaml] option to dump multi line strings as scalar blocks (xabbuh) - * feature #17553 [Validator] Added a format option to the DateTime constraint. (dosten) - * feature #17728 [Yaml] add option to dump objects as maps (xabbuh) - * feature #17863 [Yaml] add support for parsing the !!binary tag (xabbuh) - * feature #17738 [PropertyAccess] Throw an InvalidArgumentException when the type do not match (dunglas) - * feature #17531 [PropertyInfo] Use last version of reflection docblock (joelwurtz) - * feature #17782 Support autowiring for Doctrine\Common\Annotations\Reader (maryo) - * feature #17603 [Serializer] Add a normalizer that support JsonSerializable objects (mcfedr) - * feature #17630 [FrameworkBundle] Register the DateTimeNormalizer (dunglas) - * feature #17631 [FrameworkBundle] Register the DataUriNormalizer (dunglas) - * feature #17545 [Serializer] Add normalizer / denormalizer awarness (joelwurtz) - * feature #17877 [DependencyInjection] Improving autowiring error messages (weaverryan) - * feature #17732 [DEPRECATION] : deprecated support for Traversable in method ResizeFormListener::PreSubmit (ybensacq) - * feature #17721 [Cache] Add FilesystemAdapter (nicolas-grekas) - * feature #17836 [Yaml] support to parse and dump DateTime objects (xabbuh) - * feature #17809 [Yaml] deprecate starting plain scalars with characters (xabbuh) - * feature #17817 [Ldap] Add write support for the Ldap component (csarrazi) - * feature #17560 [Ldap] Improving the LDAP component (csarrazi) - * feature #17726 [FrameworkBundle] Improve debug:container command (voronkovich) - * feature #17743 [Yaml] dumper flag for enabling exceptions on invalid type (xabbuh) - * feature #17746 [Yaml] deprecate the Dumper::setIndentation() method (xabbuh) - * feature #17730 [Yaml] introduce flags to customize the parser behavior (xabbuh) - * feature #17125 Webprofiler add status code to search form (oktapodia) - * feature #17705 [TwigBridge] deprecate the boolean object support trigger (xabbuh) - * feature #17578 [Yaml] dump customization option with dumper flags (xabbuh) - * feature #17585 [DomCrawler] Abstract URI logic and crawl images (valeriangalliat) - * feature #17654 [Cache] Don't clone, serialize (nicolas-grekas) - * feature #16947 [FrameworkBundle] PropertyInfo: register the SerializerExtractor (dunglas) - * feature #17611 [HttpKernel] Deprecate passing objects as URI attributes to the ESI and SSI renderers (jakzal) - * feature #14288 [Console] Add getters for Application::$autoExit and $catchExceptions (VasekPurchart) - * feature #17504 [Console] Show code when an exception is thrown (maidmaid) - * feature #17540 [WebProfilerBundle] Add HTTP return code in the Ajax request list table (kucharovic) - * feature #17446 [Serializer] Add PSR-6 adapter (dunglas) - * feature #16917 [PropertyInfo] Cache support (dunglas) - * feature #17532 [Asset] Version as service (ewgRa) - * feature #17440 [Validator] Add a PSR-6 adapter (dunglas) - * feature #17113 [Serializer] Add a MaxDepth option (dunglas) - * feature #17530 [Cache] Handle and log errors properly (nicolas-grekas) - * feature #17522 [Cache] Use generator in ArrayAdapter (gcds) - * feature #16164 [Serializer] Add a data: URI normalizer (dunglas) - * feature #15279 Added {{ value }} message placeholder to UniqueEntityValidator (jperovic) - * feature #16652 [console] Add truncate method to FormatterHelper (mheki) - * feature #17438 [Cache] Allow and use generators in AbstractAdapter (nicolas-grekas) - * feature #17111 [HttpKernel] added a setter for the headers property in the HttpException (smatyas) - * feature #17132 [DependencyInjection] Properly ignore invalid reference arguments in collection arguments (ogizanagi) - * feature #17427 [Process] Allow a callback whenever the output is disabled (romainneutron) - * feature #17327 Added support links to exception and toolbar (peterrehm) - * feature #16909 Allows access to payload in callback validator (conradkleinespel) - * feature #17402 [Profiler] make it possible to omit the link var (xabbuh) - * feature #17411 [Serializer] Add a new DateTime normalizer (dunglas) - * feature #17462 [Yaml] deprecate parsing the !!php/object tag (xabbuh) - * feature #17408 [Cache] Symfony PSR-6 implementation (nicolas-grekas) - * feature #17323 [DependencyInjection] Deprecate unsupported attributes/elements for alias (Ener-Getick) - * feature #17305 [VarDumper] Add flags to allow fine tuning dumps representation (nicolas-grekas) - * feature #17318 [HttpFoundation] Allow to get all the mime types associated to a format in the Request (Ener-Getick) - * feature #17133 [DependencyInjection] Make YamlFileLoader raise a deprecation notice if a service definition contains unsupported keywords. (hhamon) - * feature #17191 [Serializer] Move the normalization logic in an abstract class (dunglas) - * feature #16994 [Form] Deprecate the "choices_as_values" option of ChoiceType (nicolas-grekas) diff --git a/CHANGELOG-3.2.md b/CHANGELOG-3.2.md deleted file mode 100644 index 544ae4b071c06..0000000000000 --- a/CHANGELOG-3.2.md +++ /dev/null @@ -1,643 +0,0 @@ -CHANGELOG for 3.2.x -=================== - -This changelog references the relevant changes (bug and security fixes) done -in 3.2 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.2.0...v3.2.1 - -* 3.2.13 (2017-08-01) - - * bug #22244 [Console] Fix passing options with defaultCommand (Jakub Sacha) - * bug #23684 [Debug] Missing escape in debug output (c960657) - * bug #23654 [DI] Fix using private services in expressions (nicolas-grekas) - * bug #23662 [VarDumper] Adapt to php 7.2 changes (nicolas-grekas) - * bug #23649 [Form][TwigBridge] Don't render _method in form_rest() for a child form (fmarchalemisys) - * bug #23023 [DoctrineBridge][PropertyInfo] Added support for Doctrine Embeddables (vudaltsov) - * bug #23619 [Validator] Fix IbanValidator for ukrainian IBANs (paroe) - * bug #23586 Fix case sensitive sameSite cookie (mikefrancis) - * bug #23238 [Security] ensure the 'route' index is set before attempting to use it (gsdevme) - * bug #23330 [WebProfilerBundle] Fix full sized dump hovering in toolbar (ogizanagi) - * bug #23580 Fix login redirect when referer contains a query string (fabpot) - * bug #23558 [FrameworkBundle] fix ValidatorCacheWarmer: use serializing ArrayAdapter (dmaicher) - * bug #23574 [VarDumper] Move locale sniffing to dump() time (nicolas-grekas) - -* 3.2.12 (2017-07-17) - - * bug #23549 [PropertyInfo] conflict for phpdocumentor/reflection-docblock 3.2 (xabbuh) - * security #23507 [Security] validate empty passwords again (xabbuh) - * bug #23526 [HttpFoundation] Set meta refresh time to 0 in RedirectResponse content (jnvsor) - * bug #23540 Disable inlining deprecated services (alekitto) - * bug #23468 [DI] Handle root namespace in service definitions (ro0NL) - * bug #23256 [Security] Fix authentication.failure event not dispatched on AccountStatusException (chalasr) - * bug #23461 Use rawurlencode() to transform the Cookie into a string (javiereguiluz) - * bug #23459 [TwigBundle] allow to configure custom formats in XML configs (xabbuh) - * bug #23460 Don't display the Symfony debug toolbar when printing the page (javiereguiluz) - * bug #23469 [FrameworkBundle] do not wire namespaces for the ArrayAdapter (xabbuh) - * bug #23417 [DI][Security] Prevent unwanted deprecation notices when using Expression Languages (dunglas) - * bug #23261 Fixed absolute url generation for query strings and hash urls (alexander-schranz) - * bug #23398 [Filesystem] Dont copy perms when origin is remote (nicolas-grekas) - -* 3.2.11 (2017-07-05) - - * bug #23390 [Cache] Handle APCu failures gracefully (nicolas-grekas) - * bug #23378 [FrameworkBundle] Do not remove files from assets dir (1ed) - -* 3.2.10 (2017-07-04) - - * bug #23366 [FrameworkBundle] Don't get() private services from debug:router (chalasr) - * bug #23341 [DoctrineBridge][Security][Validator] do not validate empty values (xabbuh) - * bug #23274 Display a better error design when the toolbar cannot be displayed (yceruto) - * bug #23296 [WebProfilerBundle] Fix css trick used for offsetting html anchor from fixed header (ogizanagi) - * bug #23333 [PropertyAccess] Fix TypeError discard (dunglas) - * bug #23326 [Cache] fix cleanup of expired items for PdoAdapter (dmaicher) - * bug #23345 [Console] fix description of INF default values (xabbuh) - * bug #23299 [Workflow] Added more events to the announce function (Nyholm) - * bug #23279 Don't call count on non countable object (pierredup) - * bug #23283 [TwigBundle] add back exception check (xabbuh) - * bug #23268 Show exception is checked twice in ExceptionController of twig (gmponos) - * bug #23266 Display a better error message when the toolbar cannot be displayed (javiereguiluz) - * bug #23271 [FrameworkBundle] allow SSI fragments configuration in XML files (xabbuh) - * bug #23254 [Form][TwigBridge] render hidden _method field in form_rest() (xabbuh) - * bug #23250 [Translation] return fallback locales whenever possible (xabbuh) - * bug #23240 [Console] Fix catching exception type in QuestionHelper (voronkovich) - * bug #23229 [WebProfilerBundle] Eliminate line wrap on count column (routing) (e-moe) - * bug #22732 [Security] fix switch user _exit without having current token (dmaicher) - * bug #22730 [FrameworkBundle] Sessions: configurable "use_strict_mode" option for NativeSessionStorage (MacDada) - * bug #23195 [FrameworkBundle] [Command] Clean bundle directory, fixes #23177 (NicolasPion) - * bug #23052 [TwigBundle] Add Content-Type header for exception response (rchoquet) - * bug #23199 Reset redirectCount when throwing exception (hvanoch) - * bug #23186 [TwigBundle] Move template.xml loading to a compiler pass (ogizanagi) - * bug #23130 Keep s-maxage when expiry and validation are used in combination (mpdude) - * bug #23129 Fix two edge cases in ResponseCacheStrategy (mpdude) - * feature #22636 [Routing] Expose request in route conditions, if needed and possible (ro0NL) - * bug #22636 [Routing] Expose request in route conditions, if needed and possible (ro0NL) - * bug #22943 [SecurityBundle] Move cache of the firewall context into the request parameters (GromNaN) - * bug #23057 [Translation][FrameworkBundle] Fix resource loading order inconsistency reported in #23034 (mpdude) - * bug #23092 [Filesystem] added workaround in Filesystem::rename for PHP bug (VolCh) - * bug #23128 [HttpFoundation] fix for Support for new 7.1 session options (vincentaubert) - * bug #23176 [VarDumper] fixes (nicolas-grekas) - * bug #23100 [PropertyAccess] Do not silence TypeErrors from client code. (tsufeki) - * bug #23156 [PropertyAccess] Fix Usage with anonymous classes (mablae) - * bug #23091 [Cache] ApcuAdapter::isSupported() should return true when apc.enable_cli=Off (nicolas-grekas) - * bug #22953 #22839 - changed debug toolbar dump section to relative and use full window width (mkurzeja) - * bug #23086 [FrameworkBundle] Fix perf issue in CacheClearCommand::warmup() (nicolas-grekas) - * bug #23098 Cache ipCheck (2.7) (gonzalovilaseca) - * bug #23069 [SecurityBundle] Show unique Inherited roles in profile panel (yceruto) - * bug #23073 [TwigBridge] Fix namespaced classes (ogizanagi) - * bug #23063 [Cache] Fix extensibility of TagAwareAdapter::TAGS_PREFIX (wucdbm) - * bug #22936 [Form] Mix attr option between guessed options and user options (yceruto) - * bug #22976 [DependencyInjection] Use more clear message when unused environment variables detected (voronkovich) - * bug #23045 [Cache] fix Redis scheme detection (xabbuh) - * bug #22988 [PropertyInfo][DoctrineBridge] The bigint Doctrine's type must be converted to string (dunglas) - * bug #23014 Fix optional cache warmers are always instantiated whereas they should be lazy-loaded (romainneutron) - * bug #23024 [EventDispatcher] Fix ContainerAwareEventDispatcher::hasListeners(null) (nicolas-grekas) - * bug #22996 [Form] Fix \IntlDateFormatter timezone parameter usage to bypass PHP bug #66323 (romainneutron) - * bug #22994 Harden the debugging of Twig filters and functions (stof) - -* 3.2.9 (2017-05-29) - - * bug #22847 [Console] ChoiceQuestion must have choices (ro0NL) - * bug #22900 [FrameworkBundle][Console] Fix the override of a command registered by the kernel (aaa2000) - * bug #22910 [Filesystem] improve error handling in lock() (xabbuh) - * bug #22924 [Cache] Dont use pipelining with RedisCluster (nicolas-grekas) - * bug #22718 [Console] Fixed different behaviour of key and value user inputs in multiple choice question (borNfreee) - * bug #22829 [Yaml] fix colon without space deprecation (xabbuh) - * bug #22901 Fix missing abstract key in XmlDumper (weaverryan) - * bug #22912 [DI] Avoid private call to Container::has() (ro0NL) - * bug #22866 [DI] Check for privates before shared services (ro0NL) - * bug #22874 [WebProfilerBundle] Fix sub-requests display in time profiler panel (nicolas-grekas) - * bug #22817 [PhpUnitBridge] optional error handler arguments (xabbuh) - * bug #22752 Improved how profiler errors are displayed on small screens (javiereguiluz) - * bug #22715 [FrameworkBundle] remove Security deps from the require section (xabbuh) - * bug #22647 [VarDumper] Fix dumping of non-nested stubs (nicolas-grekas) - * bug #22409 [Yaml] respect inline level when dumping objects as maps (goetas, xabbuh) - * bug #22584 [Security] Avoid unnecessary route lookup for empty logout path (ro0NL) - * bug #22690 [Console] Fix errors not rethrown even if not handled by console.error listeners (chalasr) - * bug #22669 [FrameworkBundle] AbstractConfigCommand: do not try registering bundles twice (ogizanagi) - * bug #22676 [FrameworkBundle] Adding the extension XML (flug) - * bug #22652 [Workflow] Move twig extension registration to twig bundle (ogizanagi) - -* 3.2.8 (2017-05-01) - - * bug #22550 Allow Upper Case property names in ObjectNormalizer (insekticid) - * bug #22528 [Asset] Starting slash should indicate no basePath wanted (weaverryan) - * bug #22568 [EventDispatcher] fix getting priorities of listeners during dispatch (dmaicher) - * bug #22541 [EventDispatcher] fix: unwrap listeners for correct info (dmaicher) - * bug #22526 [Asset] Preventing the base path or absolute URL from being prefixed incorrectly (weaverryan) - * bug #22523 [WebProfilerBundle] Fixed the flickering when loading complex profiler panels (javiereguiluz) - * bug #21958 [Console] Fix bar width with multilines ProgressBar's format (maidmaid) - * bug #22435 [Console] Fix dispatching throwables from ConsoleEvents::COMMAND (nicolas-grekas) - * bug #22478 [Serializer] XmlEncoder: fix negative int and large numbers handling (dunglas) - * bug #22424 [Debug] Set exit status to 255 on error (nicolas-grekas) - * bug #22426 [PropertyInfo] Prevent returning int values in some cases (dunglas) - * bug #22401 Prevent double registrations related to tag priorities (nicolas-grekas) - * bug #22399 Prevent double registrations related to tag priorities (nicolas-grekas) - * bug #22396 Prevent double registrations related to tag priorities (nicolas-grekas) - * bug #22374 [Cache] Remove exception false-positive from FilesystemAdapterTrait (nicolas-grekas) - * bug #22377 [Console] Allow terminal dimensions to be set to 0 (unbounded) (duncan3dc) - * bug #22352 [HttpFoundation] Add `use_strict_mode` in validOptions for session (sstok) - * bug #22351 [Yaml] don't keep internal state between parser runs (xabbuh) - * bug #22304 Moved $this->setDate() before the deprecation handling. (mpdonadio) - * bug #22307 [Debug] Fix php notice (enumag) - * bug #22311 [DI] Fix second auto-registration (nicolas-grekas) - * bug #22109 [Validator] check for empty host when calling checkdnsrr() (apetitpa) - * bug #22280 [DI] Fix the xml schema (GuilhemN) - * bug #22282 [DI] Prevent AutowirePass from triggering irrelevant deprecations (chalasr) - * bug #22255 [Translation] avoid creating cache files for fallback locales. (aitboudad) - * bug #22292 Fixes #22264 - add support for Chrome headless (redthor) - -* 3.2.7 (2017-04-05) - - * bug #22285 [HttpKernel] Fix forward compat with Request::setTrustedProxies() (nicolas-grekas) - * bug #22265 Allow Upper Case property names (insekticid) - * bug #22258 [DI] Autowiring and factories are incompatible with each others (nicolas-grekas) - * bug #22254 [DI] Don't use auto-registered services to populate type-candidates (nicolas-grekas) - * bug #22229 [ExpressionLanguage] Provide the expression in syntax errors (k0pernikus, stof) - * bug #22251 [PropertyInfo] Support nullable array or collection (4rthem) - * bug #22240 [DI] Fix fatal error at ContainerBuilder::compile() if config is not installed (chalasr) - * bug #22140 [Form] Improve the exceptions when trying to get the data in a PRE_SET_DATA listener and the data has not already been set (fancyweb) - * bug #22217 [Console] Fix table cell styling (ro0NL) - * bug #22194 [Console] CommandTester: disable color support detection (julienfalque) - * bug #22188 [Console] Revised exception rendering (ro0NL) - * bug #22154 [WebProfilerBundle] Normalize whitespace in exceptions passed in headers (curry684) - * bug #22183 [Process] Fix bug which wiped or mangled env vars (pjcdawkins) - * bug #22142 [Console] Escape exception messages in renderException (chalasr) - * bug #22172 Fix port usage in server:status command (alcaeus) - * bug #22164 [Bridge\Doctrine] Fix change breaking doctrine-bundle test suite (nicolas-grekas) - * bug #22159 [FrameworkBundle] Cache pool clear command requires at least 1 pool (ro0NL) - * bug #22133 [Filesystem] normalize paths before making them relative (xabbuh) - * bug #22138 [HttpFoundation][bugfix] $bags should always be initialized (MacDada) - * bug #21810 #21809 [SecurityBundle] bugfix: if security provider's name contains upper cases then container didn't compile (Antanas Arvasevicius) - * bug #22123 [WebProfilerBundle] Fix for CSS attribute at Profiler Translation Page (e-moe) - * bug #19778 [Security] Fixed roles serialization on token from user object (eko) - * bug #22036 Set Date header in Response constructor already (mpdude) - * bug #22022 [Validator] fix URL validator to detect non supported chars according to RFC 3986 (e-moe) - * bug #21849 [HttpFoundation] Fix missing handling of for/host/proto info from "Forwarded" header (nicolas-grekas) - * bug #21968 Fixed pathinfo calculation for requests starting with a question mark. (syzygymsu) - * bug #22027 Revert "bug #21841 [Console] Do not squash input changes made from console.command event (chalasr)" (chalasr) - * bug #21846 [HttpFoundation] Fix Request::getHost() when having several hosts in X_FORWARDED_HOST (nicolas-grekas) - * bug #21208 [Validator] Add object handling of invalid constraints in Composite (SenseException) - * bug #22044 [Serializer] [XML] Ignore Process Instruction (jordscream) - * bug #22090 [WebProfilerBundle] Fix Content-Security-Policy compatibility in case of a `style-src 'self'` policy (romainneutron) - * bug #22079 [HttpKernel] Fixed bug with purging of HTTPS URLs (ausi) - * bug #22045 [WebProfilerBundle] Handle Content-Security-Policy-Report-Only header correctly (romainneutron) - * bug #21523 #20411 fix Yaml parsing for very long quoted strings (RichardBradley) - * bug #22001 [Doctrine Bridge] fix priority for doctrine event listeners (dmaicher) - * bug #22040 [FrameworkBundle] improve message when workflows are missing (xabbuh) - * bug #22032 [FrameworkBundle] Fix translation dep constraint (chalasr) - * bug #21996 [Cache] Enhance error reporting for FilesystemAdapter (nicolas-grekas) - * bug #21981 [Console] Use proper line endings in BufferedOutput (julienfalque) - * bug #21976 [VarDumper] Add missing isset() checks in some casters (nicolas-grekas) - * bug #21973 [VarDumper] Add missing isset() checks in some casters (nicolas-grekas) - * bug #21957 [Form] Choice type int values (BC Fix) (mcfedr) - -* 3.2.6 (2017-03-10) - - * bug #21930 [Cache] Cached files rely on umask (4rthem) - * bug #21946 Use PHPUnit 5.4 instead of 5.3 (j0k3r) - * bug #21936 [PropertyAccess] Use ArrayAdapter in debug mode (chalasr) - -* 3.2.5 (2017-03-09) - - * bug #21923 [travis] Test with hhvm 3.18 (nicolas-grekas) - * bug #21793 [Workflow] Fixed marking state on leave and enter events (HeahDude) - * bug #21912 [Yaml] dump escape sequences when possible (xabbuh) - * bug #21908 [Cache] Fix Redis pipelining/multi-ops (nicolas-grekas) - * bug #21823 dumpFile(), preserve existing file permissions (chs2) - * bug #21880 [Form] Fixed overridden choices option in extended choice types (HeahDude) - * bug #21896 [PHPunitBridge] Count @expectedDeprecation as an assertion (wouterj) - * bug #21865 [Security] context listener: hardening user provider handling (xabbuh) - * bug #21883 [HttpKernel] fix Kernel name when stored in a directory starting with a number (fabpot) - * bug #21841 [Console] Do not squash input changes made from console.command event (chalasr) - * bug #21481 [Form] Fixed empty conversion of Intl types (HeahDude) - * bug #21671 [Serializer] Xml encoder throws exception for valid data (gr1ev0us) - * bug #21805 Provide less state in getRequestFormat (dawehner) - * bug #21851 Adding use statement for InvalidArgumentException (Nyholm) - * bug #21832 [Routing] Ignore hidden directories when loading routes from annotations (jakzal) - * bug #21769 [Form] Improve rounding precision (foaly-nr1) - * bug #21825 [PhpUnitBridge] disable global test listener when not registered (xabbuh) - * bug #21267 [Form] Fix ChoiceType to ensure submitted data is not nested unnecessarily (issei-m) - * bug #21813 Update phpstorm helper to the official format (pierredup) - * bug #21731 Fix emacs link (rubenrua) - * bug #21802 Fix issues reported by static analyse (romainneutron) - * bug #21800 Fix issues reported by static analyze (romainneutron) - * bug #21782 [DependencyInjection] add missing dumped private services list in a container frozen constructor. (hhamon) - * bug #21798 Revert "bug #21791 [SecurityBundle] only pass relevant user provider (xabbuh)" (xabbuh) - * bug #21791 [SecurityBundle] only pass relevant user provider (xabbuh) - * bug #21776 [Process] Fix ignoring of bad env var names (nicolas-grekas) - * bug #21787 [PhpUnitBridge] do not register the test listener twice (xabbuh) - * bug #21756 [Yaml] Stop replacing NULLs when merging (gadelat) - * bug #21689 [WebServerBundle] fixed html attribute escape (Seb33300) - * bug #21722 [ExpressionLanguage] Registering functions after calling evaluate(), compile() or parse() is not supported (maidmaid) - * bug #21679 [SecurityBundle] fix priority ordering of security voters (xabbuh) - * bug #21656 [DoctrineBridge] Fixed validating custom doctrine type columns (dmaicher) - * bug #21115 [Validator] do not guess getter method names (xabbuh) - * bug #21670 [DependencyInjection] Fix autowiring types when there are more than 2 services colliding (GuilhemN) - * bug #21665 [DependencyInjection] Fix autowiring collisions detection (nicolas-grekas, GuilhemN) - * bug #21661 Fix Composer constraints (fabpot) - * bug #21582 [HttpCache] purge both http and https from http cache (dbu) - * bug #21637 [FrameworkBundle] remove translation data collector when not usable (xabbuh) - * bug #21647 [Yaml] consistently parse omitted keys as the colon (xabbuh) - -* 3.2.4 (2017-02-16) - - * bug #21634 [VarDumper] Added missing persistent stream cast (lyrixx) - * bug #21436 [DependencyInjection] check for circular refs caused by method calls (xabbuh) - * bug #21400 [Serializer] fix upper camel case conversion (see #21399) (markusu49) - * bug #21599 [Console][Table] fixed render when using multiple rowspans. (aitboudad) - * bug #21613 [Process] Permit empty suffix on Windows (Bilge) - * bug #21057 [DI] Auto register extension configuration classes as a resource (ro0NL) - * bug #21607 Improve tracking of environment variables in the case of private services (tgalopin) - * bug #21592 [Validator] property constraints can be added in child classes (angelk, xabbuh) - * bug #21458 [Config] Early return for DirectoryResource (robfrawley) - * bug #21562 [DoctrineBridge] make sure that null can be the invalid value (xabbuh) - * bug #21556 [FrameworkBundle] Wire ArrayCache for annotation reader at bootstrap (nicolas-grekas) - * bug #21584 [WebProfilerBundle] Readd Symfony version status in the toolbar (wouterj) - * bug #21557 [VarDumper] Improve dump of AMQP* Object (lyrixx) - * bug #21579 [Security] LdapUserProvider should not throw an exception if the UID key does not exist in an LDAP entry (csarrazi) - * bug #21552 [FrameworkBundle] Fix annotations cache folder path (akeeman) - * bug #21542 [VarDumper] Fixed dumping of terminated generator (lyrixx) - * bug #21292 Ignore missing 'debug.file_link_formatter' service in Debug bundle (core23) - -* 3.2.3 (2017-02-06) - - * bug #21528 [Cache] Fix class exists checks in PhpArrayAdapter (nicolas-grekas) - * bug #20844 [Config] Fix checking cache for non existing meta file (hason) - * bug #21063 [Form] Fixed DateType format option for single text widget (HeahDude) - * bug #21430 Casting TableCell value to string. (jaydiablo) - * bug #21359 [FrameworkBundle] fixed custom domain for translations in php templates (robinlehrmann) - * bug #21485 [Process] Non ASCII characters disappearing during the escapeshellarg (GuillaumeVerdon) - * bug #21370 [FrameworkBundle] Execute the PhpDocExtractor earlier (GuilhemN) - * bug #21462 [BrowserKit] ignore invalid cookies expires date format (xabbuh) - * bug #21438 [Console] Fix TableCell issues with decoration (ogizanagi) - * bug #21431 [DoctrineBridge] always check for all fields to be mapped (xabbuh) - * bug #21360 [PropertyAccess] Handle interfaces in the invalid argument exception (fancyweb) - * bug #21403 [DI] Fix defaults overriding empty strings in AutowirePass (nicolas-grekas) - * bug #21401 [Debug] Workaround "null" $context (nicolas-grekas) - * bug #21381 [FrameworkBundle] Dont wire "annotations.cached_reader" before removing passes (nicolas-grekas) - * bug #21387 Fix double escaping of the decision attributes in the profiler (stof) - * bug #21372 [DependencyInjection] Fixed variadic method parameter in autowired classes (brainexe) - * bug #21338 [Cache] Fix tags expiration (nicolas-grekas) - * bug #21333 [HttpKernel] Fix ArgumentValueResolver for arguments default null (chalasr) - * bug #20871 [HttpKernel] Give higher priority to adding request formats (akeeman) - * bug #21332 [PropertyInfo] Don't try to access a property thru a static method (dunglas) - * bug #21336 [PhpUnit] Blacklist DeprecationErrorHandler in stack traces (nicolas-grekas) - * bug #21331 [PropertyInfo] Exclude static methods form properties guessing (dunglas) - * bug #21280 [Workflow] Fixed support of multiple transitions with the same name. (lyrixx) - * bug #21271 [Workflow] Added new validator to make sure each place has unique translation names (Nyholm) - * bug #21323 [Cache] [PdoAdapter] Fix MySQL 1170 error (blob as primary key) (akeeman) - * bug #21318 Don't add csp-headers if none are required (arjenm) - * bug #21291 [Ldap] Ldap username case fix (quentinus95) - * bug #21311 [Debug] Fix fatal error when changing ErrorHandler loggers if an exception is buffered (skalpa) - * bug #21288 [Doctrine Bridge] fix UniqueEntityValidator for composite object primary keys (dmaicher, HeahDude) - * bug #21285 [TwigBundle] do not lose already set method calls (xabbuh) - * bug #21279 #20411 fix Yaml parsing for very long quoted strings (RichardBradley) - * bug #21276 [Cache] Fix missing use statement in FilesystemAdapter (Lctrs) - * bug #21269 [Cache] Using strpbrk() instead of strcspn() is faster (nicolas-grekas) - -* 3.2.2 (2017-01-12) - - * bug #21257 [Profiler][Form] Fix form profiler errors profiler_dump (ogizanagi) - * bug #21243 [FrameworkBundle] Fix class_exists() checks in PhpArrayAdapter-related cache warmers (nicolas-grekas, mpajunen) - * bug #21218 [Form] DateTimeToLocalizedStringTransformer does not use timezone when using date only (magnetik) - * bug #20605 [Ldap] Always have a valid connection when using the EntryManager (bobvandevijver) - * bug #21104 [FrameworkBundle] fix IPv6 address handling in server commands (xabbuh) - * bug #20793 [Validator] Fix caching of constraints derived from non-serializable parents (uwej711) - * bug #19586 [TwigBundle] Fix bug where namespaced paths don't take parent bundles in account (wesleylancel) - * bug #21237 [FrameworkBundle] Fix relative paths used as cache keys (nicolas-grekas) - * bug #21183 [Validator] respect groups when merging constraints (xabbuh) - * bug #21179 [TwigBundle] Fixing regression in TwigEngine exception handling (Bertalan Attila) - * bug #21220 [DI] Fix missing new line after private alias (ogizanagi) - * bug #21211 Classloader tmpname (lyrixx) - * bug #21205 [TwigBundle] fixed usage when Templating is not installed (fabpot) - * bug #21155 [Validator] Check cascasdedGroups for being countable (scaytrase) - * bug #21200 [Filesystem] Check that directory is writable after created it in dumpFile() (chalasr) - * bug #21186 [Bridge/PhpUnit] Relax expectedDeprecation for forward compat (nicolas-grekas) - * bug #21184 [FrameworkBundle] Remove Response* from classes to compile (nicolas-grekas) - * bug #21165 [Serializer] int is valid when float is expected when deserializing JSON (dunglas) - * bug #21167 [Cache] Remove silenced warning tiggered by PhpArrayAdapter (nicolas-grekas) - * bug #21166 [Cache] Fix order of writes in ChainAdapter (nicolas-grekas) - * bug #21113 [FrameworkBundle][HttpKernel] Fix resources loading for bundles with custom structure (chalasr) - * bug #20995 [DependencyInjection] Fix the priority order of compiler pass trait (francoispluchino) - * bug #21084 [Yaml] handle empty lines inside unindented collection (xabbuh) - * bug #21143 [PhpUnitBridge] Set COMPOSER_ROOT_VERSION while installing (nicolas-grekas) - * bug #20925 [HttpFoundation] Validate/cast cookie expire time (ro0NL) - * bug #21138 [PhpUnitBridge] skip tests with failure and error states too (xabbuh) - * bug #21135 [PhpUnitBridge] hide stack trace of expected deprecation failures (xabbuh) - * bug #21117 [Yaml] add missing indicator character (xabbuh) - * bug #21121 [PhpUnitBridge] respect skipped and incomplete tests (xabbuh) - * bug #21032 [SecurityBundle] Made collection of user provider unique when injecting them to the RemberMeService (lyrixx) - * bug #21078 [Console] Escape default value when dumping help (lyrixx) - * bug #21076 [Console] OS X Can't call cli_set_process_title php without superuser (ogizanagi) - * bug #20900 [Console] Descriptors should use Helper::strlen (ogizanagi) - * bug #21025 [Cache] remove is_writable check on filesystem cache (4rthem) - * bug #21064 [Debug] Wrap call to ->log in a try catch block (lyrixx) - * bug #21069 [Debug] Fixed cast of stream (lyrixx) - * bug #21010 [Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes (SpacePossum) - * bug #20991 [cache] Bump RedisAdapter default timeout to 5s (Nicofuma) - * bug #20959 [FrameworkBundle] Ignore AnnotationException exceptions in the AnnotationsCacheWarmer (fancyweb) - * bug #20795 [FrameworkBundle] Allow multiple transitions with the same name (Padam87) - * bug #20859 Avoid warning in PHP 7.2 because of non-countable data (wouterj) - * bug #21053 [Validator] override property constraints in child class (xabbuh) - * bug #21034 [FrameworkBundle] Make TemplateController working without the Templating component (dunglas) - * bug #20970 [Console] Fix question formatting using SymfonyStyle::ask() (chalasr, ogizanagi) - * bug #20999 [HttpKernel] Continuation of #20599 for 3.1 (ro0NL) - * bug #20975 [Form] fix group sequence based validation (xabbuh) - * bug #20599 [WebProfilerBundle] Display multiple HTTP headers in WDT (ro0NL) - * bug #20799 [TwigBundle] do not try to register incomplete definitions (xabbuh) - * bug #20961 [Validator] phpize default option values (xabbuh) - * bug #20934 [FrameworkBundle] Fix PHP form templates on translatable attributes (ro0NL) - * bug #20957 [FrameworkBundle] test for the Validator component to be present (xabbuh) - * bug #20936 [DependencyInjection] Fix on-invalid attribute type in xsd (ogizanagi) - * bug #20931 [VarDumper] Fix dumping by-ref variadics (nicolas-grekas) - * bug #20749 [FrameworkBundle] Smarter default for framework.annotations (ogizanagi) - * bug #20734 [Security] AbstractVoter->supportsAttribute gives false positive if attribute is zero (0) (martynas-foodpanda) - * bug #14082 [config] Fix issue when key removed and left value only (zerustech) - * bug #20910 [HttpFoundation] Fix cookie to string conversion for raw cookies (ro0NL) - * bug #20909 Fix misresolved parameters in debug:config on 3.2 (chalasr) - * bug #20904 [TwigBundle] Config is now a hard dependency (dunglas) - * bug #20847 [Console] fixed BC issue with static closures (araines) - -* 3.2.1 (2016-12-13) - - * bug #20891 Add support for REDIS_URL environment variables. (robinvdvleuten) - * bug #20724 [WebProfilerBundle] Fix AJAX panel with fetch requests (OnekO) - * bug #20883 Don’t compile when Opcache is not enabled on CLI (ruudk) - * bug #20877 DateIntervalType: 'invert' should not inherit the 'required' option (galeaspablo) - * bug #20886 [Form] DateIntervalType: Do not try to translate choices (ogizanagi) - * bug #20855 [Yaml] do not trigger deprecations for valid YAML (xabbuh) - * bug #20714 [FrameworkBundle] Fix unresolved parameters from default configs in debug:config (chalasr) - * bug #20862 Allow simple-phpunit to be used with an HTTP proxy (Cydonia7) - * bug #20882 [TwigBridge] fix constructor args check (xabbuh) - * bug #20860 [WebProfilerBundle] Fix a web profiler form issue with fields added to the form after the form was built (tgalopin) - * bug #20442 [FrameworkBundle] Bundle commands are not available via find() (julienfalque) - * bug #20840 [WebProfilerBundle] add dependency on Twig (xabbuh) - * bug #20833 [HttpKernel] Fix open_basedir compat in DataCollector (nicolas-grekas) - * bug #20828 [Validator] Fix init of YamlFileLoader::$classes for empty files (nicolas-grekas) - * bug #20688 [FrameworkBundle] Resolve env params in debug:config command (nicolas-grekas) - * bug #20725 [HttpKernel] Fix annotation cache warmer with failing or missing classes (nicolas-grekas) - * bug #20830 [FrameworkBundle] Fix validation cache warmer with failing or missing classes (nicolas-grekas) - * bug #20760 [FrameworkBundle] [Workflow] Fix service marking store configuration (fduch) - * bug #20745 [Validator] add class name to the cache key (Simperfit) - * bug #20530 [Serializer] Remove AbstractObjectNormalizer::isAttributeToNormalize (dunglas) - * bug #19141 Throw less misleading exception when property access not found (bramtweedegolf) - * bug #20539 Cast result to int before adding to it (alcaeus) - * bug #20831 [Twig] Fix deprecations with Twig 1.29 (nicolas-grekas) - * bug #20701 Ignore missing 'debug.file_link_formatter' service in Debug and Twig bundles (mbabker) - * bug #20816 [FrameworkBundle] Removed kernel.debug from the cache pool namespace seed (Sander Toonen) - * bug #20769 [Bridge\Twig] Trigger deprecation when using FormExtension::$renderer (nicolas-grekas) - * bug #20646 Maintain the selected panel when redirecting to another profile (javiereguiluz) - * bug #20767 [Cache] Fix dumping SplDoublyLinkedList iter mode (nicolas-grekas) - * bug #20690 [Serializer] Fix argument object denormalization (ogizanagi) - * bug #20762 [Form] Fix FormDataCollector (nicolas-grekas, Padam87) - * bug #20747 [HttpKernel] Fixed RequestDataCollector handling of null header values. (Gabriel Moreira) - * bug #20727 [TwigBundle] Inject project root path into twig filesystem loader (4rthem) - * bug #20736 [Console] fixed PHP7 Errors when not using Dispatcher (keradus) - * bug #20756 [HttpKernel] Regression test for missing controller arguments (iltar) - * bug #20755 [HttpKernel] Regression test for missing controller arguments (iltar) - * bug #20732 fix the inline level for dumped multi-line strings (xabbuh) - * bug #20418 [Form][DX] FileType "multiple" fixes (yceruto) - * bug #19902 [DependencyInjection] PhpDumper.php: hasReference() shouldn't search references in lazy service. (antanas-arvasevicius) - * bug #20704 [Console] Fix wrong handling of multiline arg/opt descriptions (ogizanagi) - * bug #20700 [WebProfilerBundle][Translator] Fix TranslationDataCollector should use cloneVar (ogizanagi) - * bug #20712 [TwigBundle] Fix twig loader registered twice (ogizanagi) - * bug #20716 [WebProfilerBundle] Fix dump block is unfairly restrained (ogizanagi) - * bug #20717 Fix hide button in toolbar (nicolasdewez) - -* 3.2.0 (2016-11-30) - - * bug #20687 [FrameworkBundle] Forbid env parameters in routing configuration (nicolas-grekas) - * bug #20607 [Validator] Bring egulias/email-validator ~2.0 to parity with ~1.2 (Lctrs) - * bug #20671 [Config] ConfigCache::isFresh() should return false when unserialize() fails (nicolas-grekas) - * bug #20679 [VarDumper] Use default color for ellipsed namespaces/paths (nicolas-grekas) - * bug #20676 [ClassLoader] Use only forward slashes in generated class map (nicolas-grekas) - * bug #20664 [Validator] ensure the proper context for nested validations (xabbuh) - * bug #20661 bug #20653 [WebProfilerBundle] Profiler includes ghost panels (jzawadzki) - * bug #20652 Fixed getRouteParams() when no parameters are available (wouterj) - -* 3.2.0-RC2 (2016-11-27) - - * bug #20601 [FrameworkBundle] Don't rely on any parent definition for "cache.annotations" (nicolas-grekas) - * bug #20638 Fix legacy tests that do not trigger any depreciation (julienfalque) - * bug #20374 [FrameworkBundle] Improve performance of ControllerNameParser (enumag) - * bug #20474 [Routing] Fail properly when a route parameter name cannot be used as a PCRE subpattern name (fancyweb) - * bug #20616 [Bridge/Doctrine] Use cache.prefix.seed parameter for generating cache namespace (nicolas-grekas) - * bug #20566 [DI] Initialize properties before method calls (ro0NL) - * bug #20583 [Workflow] Fixed graphviz dumper for state machine (lyrixx) - * bug #20621 [HttpKernel] Fix exception when serializing request attributes (nicolas-grekas) - * bug #20609 [DI] Fixed custom services definition BC break introduced in ec7e70fb… (kiler129) - * bug #20598 [DI] Aliases should preserve the aliased invalid behavior (nicolas-grekas) - * bug #20600 [Process] Fix process continuing after reached timeout using getIterator() (chalasr) - * bug #20603 [HttpKernel] Deprecate checking for cacheable HTTP methods in Request::isMethodSafe() (nicolas-grekas) - * bug #20602 [HttpKernel] Revert BC breaking change of Request::isMethodSafe() (nicolas-grekas) - * bug #20610 [FrameworkBundle] Add framework.cache.prefix_seed for predictible cache key prefixes (nicolas-grekas) - * bug #20595 [WebProfilerBundle] Fix deprecated uses of profiler_dump (nicolas-grekas) - * bug #20589 [SecurityBundle] Fix FirewallConfig nullable arguments (ogizanagi) - * bug #20590 [DI] Allow null as default env value (sroze) - * bug #20499 [Doctrine][Form] support large integers (xabbuh) - * bug #20559 [FrameworkBundle] Avoid warming up the validator cache for non-existent class (Seldaek) - * bug #20576 [Process] Do feat test before enabling TTY mode (nicolas-grekas) - * bug #20577 [FrameworkBundle] Mark cache.default_*_provider services private (nicolas-grekas) - * bug #20550 [YAML] Fix processing timestamp strings with timezone (myesain) - * bug #20543 [DI] Fix error when trying to resolve a DefinitionDecorator (nicolas-grekas) - * bug #20544 [PhpUnitBridge] Fix time-sensitive tests that use data providers (julienfalque) - -* 3.2.0-RC1 (2016-11-17) - - * feature #20533 [DI] Revert "deprecate get() for uncompiled container builders" (nicolas-grekas) - * bug #20525 [TwigBundle] Give some love to exception pages (nicolas-grekas) - * bug #20484 bumped min version of Twig to 1.28 (fabpot) - * bug #20512 [DI] Fix accepting null as default env param value (nicolas-grekas) - * bug #20519 [Debug] Remove GLOBALS from exception context to avoid endless recursion (Seldaek) - * bug #20455 [ClassLoader] Fix ClassCollectionLoader inlining with __halt_compiler (giosh94mhz) - * bug #20307 [Form] Fix Date\TimeType marked as invalid on request with single_text and zero seconds (LuisDeimos) - * bug #20432 [FrameworkBundle] Add --no-prefix option to translation:update (chalasr) - * bug #20480 [FrameworkBundle] Register the ArrayDenormalizer (dunglas) - * bug #20286 [Serializer] Fix DataUriNormalizer's regex (dunglas) - * bug #20466 [Translation] fixed nested fallback catalogue using multiple locales. (aitboudad) - * bug #20465 [#18637][TranslationDebug] workaround for getFallbackLocales. (aitboudad) - * bug #20453 [Cache] Make directory hashing case insensitive (nicolas-grekas) - * bug #20428 [TwigBundle] fixed template root path (fabpot) - * feature #20447 [DI] Force env params to be string|null (nicolas-grekas) - * feature #20451 [Workflow] Added Definition builder (Nyholm) - * bug #20460 [FrameworkBundle] Fixed WorkflowCommand to support state machines (HeahDude) - * bug #20440 [TwigBridge][TwigBundle][HttpKernel] prefer getSourceContext() over getSource() (xabbuh) - * feature #19629 [Workflow] Make the Workflow support State Machines (Nyholm, lyrixx) - * bug #20287 Properly format value in UniqueEntityValidator (alcaeus) - * bug #20422 [Translation][fallback] add missing resources in parent catalogues. (aitboudad) - * bug #20378 [Form] Fixed show float values as choice value in ChoiceType (yceruto) - * feature #20416 [Bridge\Monolog][FrameworkBundle] Add & wire a DebugProcessor (nicolas-grekas) - * bug #20415 [DI][Serializer] Add missing deprecations (nicolas-grekas) - * bug #20294 Improved the design of the metrics in the profiler (javiereguiluz) - * bug #20375 [HttpFoundation][Session] Fix memcache session handler (klandaika) - * bug #20377 [Console] Fix infinite loop on missing input (chalasr) - * feature #20232 [DependencyInjection] fixed ini file values conversion (fabpot) - * feature #19490 [SecurityBundle] Integrate current firewall in Profiler (chalasr) - * feature #19398 [DX][SecurityBundle] Introduce a FirewallConfig class accessible from FirewallContext (chalasr) - * bug #20336 [HttpKernel] Base DataCollector throws warning on unsupported scheme strings (ogizanagi) - * bug #20335 [Yaml] Fix String offset cast error in Inline parser (romainneutron) - * bug #20372 [Console] simplified code (fabpot) - * bug #20342 [Form] Fix UrlType transforms valid protocols (ogizanagi) - * bug #20341 Fix YamlReferenceDumper unnamed nested prototypes (ogizanagi) - * bug #20292 Enhance GAE compat by removing some realpath() (nicolas-grekas) - * bug #20325 [VarDumper] Fix source links to Twig files (nicolas-grekas) - * bug #20328 [Console] Fix empty COLUMNS/LINES env vars (nicolas-grekas) - * bug #20326 [VarDumper] Fix dumping Twig source in stack traces (nicolas-grekas) - * bug #20321 Compatibility with Twig 1.27 (xkobal) - -* 3.2.0-BETA1 (2016-10-27) - - * feature #19973 Added a default ide file link web view (jeremyFreeAgent) - * feature #20285 [TwigBundle] made Twig cache independent of the project root directory (fabpot) - * feature #20266 [Console] rename Command::private to Command::hidden (xabbuh) - * feature #20270 [PhpUnitBridge] Drop ErrorAssert (nicolas-grekas) - * feature #20256 [PhpUnitBridge] Allow configuring removed deps and phpunit versions (nicolas-grekas) - * feature #20255 [PhpUnitBridge] Replace ErrorAssert by `@expectedDeprecation` (nicolas-grekas) - * feature #20047 [Form] Change FormTypeGuesserChain to accept Traversable (enumag) - * feature #19982 [Validator] Allow validating multiple groups in one GroupSequence step (enumag) - * feature #19741 [ExpressionLanguage] Making cache PSR6 compliant (Alexandre GESLIN) - * feature #20217 [Serializer] Support specifying format for DateTimeNormalizer::denormalize (teohhanhui) - * feature #19452 Remove the new SecurityUserValueResolver (weaverryan) - * feature #15002 [DoctrineBridge] Add a way to select the repository used by the UniqueEntity validator (ogizanagi) - * feature #20113 Use the method map as authoritative list of factories for dumped containers (stof) - * feature #19576 [WebProfiler] added support for window.fetch calls in ajax section (ivoba) - * feature #19991 [TwigBridge] Added access to token from twig AppVariable (HeahDude) - * feature #20029 Hide commands from ApplicationDescriptor, but allow invoking (jwdeitch, Jordan Deitch) - * feature #20121 Class existence resource (fabpot) - * feature #20119 [TwigBundle] changed the runtime loader to return null if there is no match (fabpot) - * feature #20093 Twig extensions refatoring to decouple definitions from implementations (fabpot) - * feature #20094 added Twig runtimes for "critical" Twig extensions (fabpot) - * feature #20097 [FrameworkBundle] removed the Doctrine Annotations lib dependency on FrameworkBundle (fabpot) - * feature #20019 [FrameworkBundle] Add phpstorm ide (hason) - * feature #20092 added a Twig runtime loader (fabpot) - * feature #20075 [FrameworkBundle] removed the Security Core and Security CSRF component dependencies on FrameworkBundle (fabpot) - * feature #20072 [FrameworkBundle] removed the Templating component dependency on FrameworkBundle (fabpot) - * feature #20070 [FrameworkBundle] removed the Translation component dependency on FrameworkBundle (fabpot) - * feature #20067 [FrameworkBundle] removed the Asset component dependency on FrameworkBundle (fabpot) - * feature #20037 [Cache] Handle arbitrary key length when the backend cant using hashing (nicolas-grekas) - * feature #20040 [Bridge/PhpUnit] Handle native E_DEPRECATED (nicolas-grekas) - * feature #19987 [VarDumper] Use ClassStub for reflected types (nicolas-grekas) - * feature #20012 [Translation] added Base Exception for the component. (aitboudad) - * feature #19996 removed obsolete images (since 2.2) (fabpot) - * feature #19997 inlined some CSS (fabpot) - * feature #19304 [Yaml] fix parsing multi-line mapping values (xabbuh) - * feature #19191 [DependencyInjection] Automatically detect the definitions class when possible (Ener-Getick) - * feature #19745 [Validator] Added context object method callback to choice validator (Peter Bouwdewijn) - * feature #19614 [HttpKernel] Use VarDumper in the profiler (wouterj, nicolas-grekas) - * feature #19480 [Config] Fix (Yaml|Xml)ReferenceDumper for nested prototypes (ogizanagi) - * feature #19681 [DI] Allow injecting ENV parameters at runtime using env(MY_ENV_VAR) (nicolas-grekas) - * feature #19197 [Serializer][FrameworkBundle] Add a CSV encoder (dunglas) - * feature #19257 [Validator][Choice] Make strict the default option for choice validation (peterrehm) - * feature #19326 [Serializer][FrameworkBundle] Add a YAML encoder (dunglas) - * feature #19484 [PropertyInfo] Extract the logic converting a php doc to a Type (Ener-Getick) - * feature #19495 [master][console] Allow multiple options to be set. (SpacePossum) - * feature #19584 [DependencyInjection] Improve ParameterNotFoundException when accessing a nested parameter (wouterj) - * feature #19485 [FrameworkBundle] Introduce a cache warmer for Validator based on PhpArrayAdapter (tgalopin) - * feature #19790 [FrameworkBundle] add support for prioritizing form type extension tags (dmaicher) - * feature #19507 [FrameworkBundle] Introduce a cache warmer for Serializer based on PhpArrayAdapter (tgalopin) - * feature #19734 [HttpFoundation] Deprecate extending some methods (Ener-Getick) - * feature #19795 Replace count with a given number out of the box (bocharsky-bw) - * feature #19807 [FrameworkBundle] Add debug.file_link_format with remapping for IDE links (nicolas-grekas) - * feature #19891 [FrameworkBundle] Add cache:pool:clear command (nicolas-grekas) - * feature #19900 [FrameworkBundle] Add CachePoolClearerPass for weak cache pool refs in cache clearers (nicolas-grekas) - * feature #19570 [Config] Fix YamlReferenceDumper prototyped array support (ogizanagi) - * feature #19824 [Console] Add ability to regress the ProgressBar (jameshalsall) - * feature #19892 [DI] Add corresponding service id in some exception messages (nicolas-grekas) - * feature #19843 [Security] Allow run-time configuration of hash algo (nicolas-grekas) - * feature #19894 [Cache] Add "persistent_id" option to RedisAdapter::createConnection() (nicolas-grekas) - * feature #19915 [Bridge/PhpUnit] Add bin/simple-phpunit wrapper (=phpunit - yaml - prophecy) (nicolas-grekas) - * feature #19826 [VarDumper] Add ClassStub for clickable & shorter PHP identifiers (nicolas-grekas) - * feature #19816 [VarDumper] Add LinkStub to create links in HTML dumps (nicolas-grekas) - * feature #19768 [VarDumper] Enhance dumping arguments in stack traces (nicolas-grekas) - * feature #19796 [VarDumper] Make the line clickable to toggle dumps (nicolas-grekas) - * feature #19764 [Config] Add ExprBuilder::ifEmpty() (ogizanagi) - * feature #19797 [VarDumper] Handle attributes in Data clones for more semantic dumps (nicolas-grekas) - * feature #19755 [VarDumper] Get dump as string with `$dumper->dump(..., true);` (nicolas-grekas) - * feature #19604 [Routing] Add seamless support for unicode requirements (nicolas-grekas) - * feature #19714 [VarDumper] Handle "title" attribute on virtual properties (nicolas-grekas) - * feature #19687 [FrameworkBundle] Use relative paths in templates paths cache (tgalopin) - * feature #19339 [WebProfilerBundle][Form][DX] To expand the form nodes that contains children with errors (yceruto) - * feature #19519 [Cache] Add PDO + Doctrine DBAL adapter (nicolas-grekas) - * feature #19672 [VarDumper] Allow dumping subparts of cloned Data structures (nicolas-grekas) - * feature #19657 [VarDumper] Add line in trace indexes (nicolas-grekas) - * feature #19639 [Routing] Generate URLs in compliance with PHP_QUERY_RFC3986 (jameshalsall) - * feature #19568 [Debug] Better error handling (lyrixx) - * feature #19552 [HttpFoundation] Add named constructor on JsonResponse (tyx) - * feature #19630 [VarDumper] Enhance dumping __PHP_Incomplete_Class objects (nicolas-grekas) - * feature #19515 [Cache] Drop TaggedCacheItemInterface (nicolas-grekas) - * feature #19511 Use a dedicated exception in the file locator (leofeyer) - * feature #19529 Add Yaml::PARSE_EXCEPTION_ON_DUPLICATE to throw exceptions on duplicates (Alex Pott) - * feature #19473 [Security] Expose the required roles in AccessDeniedException (Nicofuma) - * feature #19524 [Cache] Add generic TagAwareAdapter wrapper (replaces TagAwareRedisAdapter) (nicolas-grekas) - * feature #19504 [Yaml] deprecate missing space after mapping key colon (xabbuh) - * feature #19430 [DomCrawler] Add support for XPath expression evaluation (jakzal) - * feature #19205 [HttpKernel] Allow bundles to declare classes and annotated classes to compile using patterns (tgalopin) - * feature #18533 [FrameworkBundle] Wire PhpArrayAdapter with a new cache warmer for annotations (tgalopin) - * feature #17498 [Filesystem] Add a cross-platform readlink method (tgalopin) - * feature #19289 [VarDumper] Dumping exceptions is now more compact (nicolas-grekas) - * feature #19276 [ClassLoader] Add ClassCollectionLoader::inline() to generate inlined-classes files (nicolas-grekas) - * feature #19325 [FrameworkBundle] Allow to specify a domain when updating translations (antograssiot) - * feature #19277 [Serializer] Argument objects (theofidry, dunglas) - * feature #19322 [HttpFoundation] Add Request::isMethodIdempotent method (dunglas) - * feature #18510 Added a SecurityUserValueResolver for controllers (iltar) - * feature #19203 [Bridge/Doctrine] Reset the EM lazy-proxy instead of the EM service (nicolas-grekas) - * feature #19236 [FrameworkBundle] Deprecate the service serializer.mapping.cache.doctrine.apc (Ener-Getick) - * feature #19174 [FrameworkBundle] Show server:run logs by default (nicolas-grekas) - * feature #19137 [Serializer] Allow to use easily static constructors (Ener-Getick) - * feature #19146 [DependencyInjection] deprecate access to private shared services. (hhamon) - * feature #19190 [DependencyInjection] Add support for short services configurators syntax (voronkovich) - * feature #18823 [Cache] Add PhpArrayAdapter to use shared memory on PHP 7.0 (tgalopin) - * feature #18948 [VarDumper] Add maxDepth & maxStringLength display options (MGDSoft, nicolas-grekas) - * feature #18626 [Yaml] Added support for parsing PHP constants (HeahDude) - * feature #19104 Adds support for the SameSite attribute in cookies. (iangcarroll) - * feature #19153 [Validator] support egulias/email-validator 2.x (xabbuh) - * feature #11394 [Routing] support for array values in route defaults (xabbuh) - * feature #11882 [Workflow] Introducing the workflow component (fabpot, lyrixx) - * feature #19151 [VarDumper] Add support for XmlReader objects (Taluu) - * feature #18471 [Console] Add Lockable trait (geoffrey-brier) - * feature #19139 [FrameworkBundle][Yaml] Move YamlLintCommand to the Yaml component (chalasr) - * feature #19143 Response headers fix (fabpot) - * feature #16809 [Form][FrameworkBundle][Bridge] Add a DateInterval form type (MisatoTremor) - * feature #18466 [Bridge][Twig] Optionally pass dumper into DumpExtension (CarsonF) - * feature #18022 [DependencyInjection] Sort the CompilerPass by priority (Ener-Getick) - * feature #19090 [Console] Add ConsoleLogger::hasErrored() (nicolas-grekas) - * feature #19079 [Debug] Do not quote numbers in stack trace (c960657) - * feature #19012 [Console] progress bar fix (TomasVotruba, fabpot) - * feature #19047 [Cache] Add tags based invalidation + TagAwareRedisAdapter (nicolas-grekas) - * feature #17644 Deprecate using Form::isValid() with an unsubmitted form (Ener-Getick) - * feature #12979 [Router] added appending of new optional document fragment (rodnaph) - * feature #18710 [Console] Simplify simulation of user inputs in CommandTester (chalasr) - * feature #18999 [Console] Centralize input stream in base Input class (chalasr) - * feature #19060 [ExpressionLanguage] Add a way to hook on each node when dumping the AST (nicolas-grekas) - * feature #18880 [PhpUnitBridge] add a triggered errors assertion helper (xabbuh) - * feature #16906 [Console] Better support for one command app (lyrixx) - * feature #17203 Move Constraint validator test case to Test namespace (WouterJ) - * feature #18502 [FrameworkBundle] Add file helper to Controller (dfridrich) - * feature #19053 [Process] Allow inheriting env vars instead of replacing them (nicolas-grekas) - * feature #18833 [HttpKernel] Move duplicated logic from Esi/Ssi to an AbstractSurrogate (chalasr) - * feature #18220 Don't send default cache header for 301 redirects (e-moe) - * feature #17662 [Translation][transChoice] allows escaping the pipe character. (aitboudad) - * feature #18322 [DomCrawler] Attach label to form fields (carlosV2) - * feature #18482 Created a trait to sort tagged services (iltar) - * feature #15458 [Filesystem] Add feature to create hardlinks for files (andrerom) - * feature #18940 [Console] Add path argument to dump a specific option in debug:config (chalasr) - * feature #19013 [ExpressionLanguage] Added a way to dump the AST (lyrixx) - * feature #18332 [Form] added `CallbackChoiceLoader` and refactored ChoiceType's children (HeahDude) - * feature #18869 [Routing] Throw exception when PHP start tag is missing (WouterJ) - * feature #18781 [Console] Display errors in quiet mode (multi-io) - * feature #19011 [HttpKernel] Add convenient method ArgumentResolver:: getDefaultArgumentValueResolvers (romainneutron) - * feature #18568 [WebProfilerBundle] Fix bundle usage in Content-Security-Policy context without unsafe-inline (romainneutron) - * feature #16838 [PropertyAccess] Add PSR-6 cache (dunglas) - * feature #18790 [Console] Show aliases in command description instead of in different lines in application description (juanmirod) - * feature #18728 deprecate get() for uncompiled container builders (xabbuh) - * feature #18483 [Serializer] Deprecate SerializerAwareEncoder (JhonnyL) - * feature #18337 [PropertyInfo] Support singular adder and remover (dunglas) - * feature #18894 [Cache] Added PhpFilesAdapter (trakos, nicolas-grekas) - * feature #18964 [PhpUnitBridge] Make DnsMock match namespaces that begin with Tests\\ (teohhanhui) - * feature #18726 [PhpUnitBridge] Make ClockMock match namespaces that begin with Tests\\ (teohhanhui) - * feature #18825 [Cache] Create NullAdapter to disable cache if needed (tgalopin) - * feature #18675 [VarDumper] Add Redis caster (nicolas-grekas) - * feature #18785 [Yaml] deprecate comma separators in floats (xabbuh) - * feature #18486 [Yaml] Allow using _ in some numeric notations (Taluu) diff --git a/CHANGELOG-3.3.md b/CHANGELOG-3.3.md deleted file mode 100644 index fe698c1e6dc39..0000000000000 --- a/CHANGELOG-3.3.md +++ /dev/null @@ -1,741 +0,0 @@ -CHANGELOG for 3.3.x -=================== - -This changelog references the relevant changes (bug and security fixes) done -in 3.3 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.3.0...v3.3.1 - -* 3.3.15 (2018-01-05) - - * bug #25532 [HttpKernel] Disable CSP header on exception pages (ostrolucky) - * bug #25491 [Routing] Use the default host even if context is empty (sroze) - * bug #25672 [WebServerBundle] use interface_exists instead of class_exists (kbond) - * bug #25662 Dumper shouldn't use html format for phpdbg / cli-server (jhoff) - * bug #25529 [Validator] Fix access to root object when using composite constraint (ostrolucky) - * bug #25430 Fixes for Oracle in PdoSessionHandler (elislenio) - * bug #25599 Add application/ld+json format associated to json (vincentchalamon) - * bug #25623 [HttpFoundation] Fix false-positive ConflictingHeadersException (nicolas-grekas) - * bug #25624 [WebServerBundle] Fix escaping of php binary with arguments (nicolas-grekas) - * bug #25567 [Process] Fix setting empty env vars (nicolas-grekas) - * bug #25407 [Console] Commands with an alias should not be recognized as ambiguous (Simperfit) - * bug #25523 [WebServerBundle] fix a bug where require would not require the good file because of env (Simperfit) - * bug #25559 [Process] Dont use getenv(), it returns arrays and can introduce subtle breaks accros PHP versions (nicolas-grekas) - * bug #25552 [WebProfilerBundle] Let fetch() cast URL to string (ro0NL) - * bug #25521 [Console] fix a bug when you are passing a default value and passing -n would output the index (Simperfit) - * bug #25489 [FrameworkBundle] remove esi/ssi renderers if inactive (dmaicher) - * bug #25427 Preserve percent-encoding in URLs when performing redirects in the UrlMatcher (mpdude) - * bug #25480 [FrameworkBundle] add missing validation options to XSD file (xabbuh) - * bug #25487 [Console] Fix a bug when passing a letter that could be an alias (Simperfit) - * bug #25425 When available use AnnotationRegistry::registerUniqueLoader (jrjohnson) - * bug #24594 [Translation] Fix InvalidArgumentException when using untranslated plural forms from .po files (BjornTwachtmann) - * bug #25233 [TwigBridge][Form] Fix hidden currency element with Bootstrap 3 theme (julienfalque) - * bug #25413 [HttpKernel] detect deprecations thrown by container initialization during tests (nicolas-grekas) - * bug #25408 [Debug] Fix catching fatal errors in case of nested error handlers (nicolas-grekas) - * bug #25330 [HttpFoundation] Support 0 bit netmask in IPv6 (`::/0`) (stephank) - * bug #25378 [VarDumper] Fixed file links leave blank pages when ide is configured (antalaron) - * bug #25410 [HttpKernel] Fix logging of post-terminate errors/exceptions (nicolas-grekas) - * bug #25417 [Process] Dont rely on putenv(), it fails on ZTS PHP (nicolas-grekas) - * bug #25333 [DI] Impossible to set an environment variable and then an array as container parameter (Phantas0s) - * bug #25438 [Yaml] empty lines don't count for indent detection (xabbuh) - * bug #25389 [Yaml] fix some edge cases with indented blocks (xabbuh) - * bug #25340 [Serializer] Unset attributes when creating child context (dunglas) - * bug #25323 [ExpressionLanguage] throw an SyntaxError instead of an undefined index notice (Simperfit) - * bug #25364 [DependencyInjection] Prevent a loop in aliases within the `findDefinition` method (sroze) - -* 3.3.14 (2017-12-04) - - * bug #25304 [Bridge/PhpUnit] Prefer $_SERVER['argv'] over $argv (ricknox) - * bug #25308 [FrameworkBundle] Fix a bug where a color tag will be shown when passing an antislash (Simperfit) - * bug #25278 Fix for missing whitespace control modifier in form layout (kubawerlos) - * bug #25305 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) - * bug #25236 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) - * bug #25312 [DI] Fix deep-inlining of non-shared refs (nicolas-grekas) - * bug #25309 [Yaml] parse newlines in quoted multiline strings (xabbuh) - * bug #25241 [Yaml] do not eagerly filter comment lines (xabbuh) - * bug #25258 [link] Prevent warnings when running link with 2.7 (dunglas) - * bug #24750 [Validator] ExpressionValidator should use OBJECT_TO_STRING (Simperfit) - * bug #25232 [WebProfilerBundle] [TwigBundle] Fix Profiler breaking XHTML pages (tistre) - * bug #25209 [VarDumper] Dont use empty(), it chokes on eg GMP objects (nicolas-grekas) - * bug #25200 [HttpKernel] Arrays with scalar values passed to ESI fragment renderer throw deprecation notice (Simperfit) - * bug #25217 [Dotenv] Changed preg_match flags from null to 0 (deekthesqueak) - * bug #25203 [DI] Fix infinite loop in InlineServiceDefinitionsPass (nicolas-grekas) - * bug #25185 [Serializer] Do not cache attributes if `attributes` in context (sroze) - * bug #25182 [HttpFoundation] AutExpireFlashBag should not clear new flashes (Simperfit, sroze) - * bug #25179 [FrameworkBundle][Serializer] Remove YamlEncoder definition if Yaml component isn't installed (ogizanagi) - * bug #25163 [DI] Fix tracking of env vars in exceptions (nicolas-grekas) - * bug #25152 [Form] Don't rely on `Symfony\Component\HttpFoundation\File\File` if http-foundation isn't in FileType (issei-m) - * bug #24987 [Console] Fix global console flag when used in chain (Simperfit) - * bug #25146 [DI] Dont resolve envs in service ids (nicolas-grekas) - * bug #25113 [Routing] Fix "config-file-relative" annotation loader resources (nicolas-grekas, sroze) - * bug #25109 Make debug:container search command case-insensitive (jzawadzki) - * bug #25043 [Yaml] added ability for substitute aliases when mapping is on single line (Michał Strzelecki, xabbuh) - * bug #25102 [Form] Fixed ContextErrorException in FileType (chihiro-adachi) - * bug #25130 [DI] Fix handling of inlined definitions by ContainerBuilder (nicolas-grekas) - * bug #25094 [FrameworkBundle][DX] Display a nice error message if an enabled component is missing (derrabus) - * bug #25097 [Bridge\PhpUnit] Turn "preserveGlobalState" to false by default, revert "Blacklist" removal (nicolas-grekas) - * bug #25072 [Bridge/PhpUnit] Remove trailing "\n" from ClockMock::microtime(false) (joky) - * bug #25032 [Bridge\PhpUnit] Disable broken auto-require mechanism of phpunit (nicolas-grekas) - * bug #24956 Fix ambiguous pattern (weltling) - -* 3.3.13 (2017-11-16) - - * security #24995 Validate redirect targets using the session cookie domain (nicolas-grekas) - * security #24994 Prevent bundle readers from breaking out of paths (xabbuh) - * security #24993 Ensure that submitted data are uploaded files (xabbuh) - * security #24992 Namespace generated CSRF tokens depending of the current scheme (dunglas) - -* 3.3.12 (2017-11-13) - - * bug #24954 [DI] Fix dumping with custom base class (nicolas-grekas) - * bug #24952 [HttpFoundation] Fix session-related BC break (nicolas-grekas, sroze) - * bug #24929 [Console] Fix traversable autocomplete values (ro0NL) - -* 3.3.11 (2017-11-10) - - * bug #24888 [FrameworkBundle] Specifically inject the debug dispatcher in the collector (ogizanagi) - * bug #24909 [Intl] Update ICU data to 60.1 (jakzal) - * bug #24870 [YAML] Allow to parse custom tags when linting yaml files (pierredup) - * bug #24910 [HttpKernel][Debug] Remove noise from stack frames of deprecations (nicolas-grekas) - * bug #24906 [Bridge/ProxyManager] Remove direct reference to value holder property (nicolas-grekas) - * bug #24900 [Validator] Fix Costa Rica IBAN format (Bozhidar Hristov) - * bug #24904 [Validator] Add Belarus IBAN format (Bozhidar Hristov) - * bug #24881 [WebserverBundle] fixed the bug that caused that the webserver would … (Serkan Yildiz) - * bug #24531 [HttpFoundation] Fix forward-compat of NativeSessionStorage with PHP 7.2 (sroze) - * bug #24665 Fix dump panel hidden when closing a dump (julienfalque) - * bug #24816 [Serializer] Fix extra attributes when no group specified (ogizanagi) - * bug #24814 [Intl] Make intl-data tests pass and save language aliases again (jakzal) - * bug #24810 [Serializer] readd default argument value (xabbuh) - * bug #24735 [VarDumper] fix trailling comma when dumping an exception (Simperfit) - * bug #24764 [HttpFoundation] add Early Hints to Reponse to fix test (Simperfit) - * bug #24759 Removes \n or space when $context/$extra are empty (kirkmadera) - * bug #24758 Throwing exception if redis and predis unavailable (aequasi) - * bug #24744 debug:container --types: Fix bug with non-existent classes (weaverryan) - * bug #24605 [FrameworkBundle] Do not load property_access.xml if the component isn't installed (ogizanagi) - * bug #24706 [DependencyInjection] Add the possibility to disable assets via xml (renatomefi) - * bug #24696 Ensure DeprecationErrorHandler::collectDeprecations() is triggered (alexpott) - * bug #24686 Fix $_ENV/$_SERVER precedence in test framework (fabpot) - * bug #24606 [HttpFoundation] Fix FileBag issue with associative arrays (enumag) - * bug #24673 [DI] Throw when a service name or an alias contains dynamic values (prevent an infinite loop) (dunglas) - * bug #24681 Fix isolated error handling (alexpott) - * bug #24575 Ensure that PHPUnit's error handler is still working in isolated tests (alexpott) - * bug #24597 [PhpUnitBridge] fix deprecation triggering test detection (xabbuh) - * bug #24660 Escape trailing \ in QuestionHelper autocompletion (kamazee) - * bug #24598 Prefer line formatter on missing cli dumper (greg0ire) - * bug #24644 [Security] Fixed auth provider authenticate() cannot return void (glye) - * bug #24642 [Routing] Fix resource miss (dunglas) - * bug #24608 Adding the Form default theme files to be warmed up in Twig's cache (weaverryan) - * bug #24626 streamed response should return $this (DQNEO) - * bug #24589 Username and password in basic auth are allowed to contain '.' (Richard Quadling) - * bug #24566 Fixed unsetting from loosely equal keys OrderedHashMap (maryo) - * bug #24570 [Debug] Fix same vendor detection in class loader (Jean-Beru) - * bug #24573 Fixed pathinfo calculation for requests starting with a question mark. (syzygymsu) - * bug #24565 [Serializer] YamlEncoder: throw if the Yaml component isn't installed (dunglas) - * bug #24563 [Serializer] ObjectNormalizer: throw if PropertyAccess isn't installed (dunglas) - * bug #24571 [PropertyInfo] Add support for the iterable type (dunglas) - * bug #24579 pdo session fix (mxp100) - * bug #24536 [Security] Reject remember-me token if UserCheckerInterface::checkPostAuth() fails (kbond) - * bug #24548 [Bridge\PhpUnit] Handle deprecations triggered in separate processes (paul-m) - * bug #24519 [Validator] [Twig] added magic method __isset() to File Constraint class (loru88) - * bug #24532 [DI] Fix possible incorrect php-code when dumped strings contains newlines (Strate) - * bug #24502 [HttpFoundation] never match invalid IP addresses (xabbuh) - * bug #24460 [Form] fix parsing invalid floating point numbers (xabbuh) - * bug #24490 [HttpFoundation] Combine Cache-Control headers (c960657) - * bug #23711 Fix support for PHP 7.2 (Simperfit, nicolas-grekas) - * bug #24494 [HttpFoundation] Add missing session.lazy_write config option (nicolas-grekas) - * bug #24498 [Bridge\PhpUnit] Fix infinite loop when running isolated method (nicolas-grekas) - * bug #24434 [Form] Use for=ID on radio/checkbox label. (Nyholm) - * bug #24455 [Console] Escape command usage (sroze) - * bug #24462 [Yaml] parse references on merge keys with objects (xabbuh) - -* 3.3.10 (2017-10-05) - - * bug #23906 Added support for guards when advancing workflow from a command (GDIBass) - * bug #24448 [Session] fix MongoDb session handler to gc all expired sessions (Tobion) - * bug #24431 [FrameworkBundle] Fix bad interface hint in AbstractController (nicolas-grekas) - * bug #24419 [Cache] Fix race condition in TagAwareAdapter (nicolas-grekas) - * bug #24417 [Yaml] parse references on merge keys (xabbuh) - * bug #24416 [Yaml] treat trailing backslashes in multi-line strings (xabbuh) - * bug #24421 [Config] Fix dumped files invalidation by OPCache (nicolas-grekas) - * bug #24418 [DI] Allow setting any public non-initialized services (nicolas-grekas) - * bug #23980 Tests and fix for issue in array model data in EntityType field with multiple=true (stoccc) - * bug #22586 [Form] Fixed PercentToLocalizedStringTransformer to accept both comma and dot as decimal separator, if possible (aaa2000) - * bug #24157 [Intl] Fixed support of Locale::getFallback (lyrixx) - * bug #24198 [HttpFoundation] Fix file upload multiple with no files (enumag) - * bug #24379 [PHPUnitBridge] don't remove when set to empty string (Simperfit) - * bug #24036 [Form] Fix precision of MoneyToLocalizedStringTransformer's divisions and multiplications (Rubinum) - * bug #24191 [DependencyInjection] include file and line number in deprecation (xabbuh) - * bug #24367 PdoSessionHandler: fix advisory lock for pgsql (Tobion) - * bug #24189 [Yaml] parse merge keys with PARSE_OBJECT_FOR_MAP flag (xabbuh) - * bug #24243 HttpCache does not consider ESI resources in HEAD requests (mpdude) - * bug #24237 [WebProfilerBundle] Added missing link to profile token (vtsykun) - * bug #24244 TwigBundle exception/deprecation tweaks (ro0NL) - * bug #24281 [TwigBundle] Remove profiler related scripting (ro0NL, javiereguiluz) - * bug #24251 [PropertyAccess] Set a NullLogger in ApcuAdapter when Apcu is disabled in CLI (iamluc) - * bug #24304 [FrameworkBundle] Fix Routing\DelegatingLoader (nicolas-grekas) - * bug #24305 [HttpKernel] Make array vs "::" controller definitions consistent (nicolas-grekas) - * bug #24255 [TwigBundle] Break long lines in exceptions (kevin-verschaeve) - * bug #24219 [Console] Preserving line breaks between sentences according to the exception message (yceruto) - * bug #24192 [PhpUnitBridge] do not require an error context (xabbuh) - * bug #23722 [Form] Fixed GroupSequence with "constraints" option (HeahDude) - * bug #22321 [Filesystem] Fixed makePathRelative (ausi) - * bug #24234 [DI] Fix decorated service merge in ResolveInstanceofConditionalsPass (dunglas) - * bug #24203 [Security] Preserve URI fragment in HttpUtils::generateUri() (chalasr) - * bug #24199 [DI] Fix non-instantiables auto-discovery (nicolas-grekas) - * bug #23473 [Filesystem] mirror - fix copying content with same name as source/target. (gitlost) - * bug #24177 [FrameworkBundle] Add support to environment variables APP_ENV/DEBUG in KernelTestCase (yceruto) - * bug #24162 [WebProfilerBundle] fixed TemplateManager when using Twig 2 without compat interfaces (fabpot) - -* 3.3.9 (2017-09-11) - - * bug #24141 [DomCrawler] Fix conversion to int on GetPhpFiles (MaraBlaga) - * bug #23853 Filtering empty uuids in ORMQueryBuilderLoader. (mlazovla) - * bug #24101 [Security] Fix exception when use_referer option is true and referer is not set or empty (linniksa) - * bug #24105 [Filesystem] check permissions if dump target dir is missing (xabbuh) - * bug #24126 [HttpKernel] "controller.service_arguments" services should be public (nicolas-grekas) - * bug #24113 [FrameworkBundle] Get KERNEL_CLASS through $_ENV too for KernelTestCase (yceruto) - * bug #24115 [FrameworkBundle] Get KERNEL_DIR through $_ENV too for KernelTestCase (yceruto) - * bug #24041 [ExpressionLanguage] throws an exception on calling uncallable method (fmata) - * bug #24096 Fix ArrayInput::toString() for VALUE_IS_ARRAY options/args (chalasr) - * bug #24082 [DI] Minor fix in dumped code (nicolas-grekas) - * bug #23969 [Cache] Use namespace versioning for backends that dont support clearing by keys (nicolas-grekas) - * bug #24021 [DI] Don't track merged configs when the extension doesn't expose it (nicolas-grekas) - * bug #24011 [Cache] Always require symfony/polyfill-apcu to provide APCuIterator everywhere (guillaumelecerf) - * bug #23730 Fixed the escaping of back slashes and << in console output (javiereguiluz) - -* 3.3.8 (2017-08-28) - - * bug #24016 [DI] Fix tracking env var placeholders nested in object graphs (nicolas-grekas) - -* 3.3.7 (2017-08-28) - - * bug #24009 [DI] Fix tracking env vars when merging configs (bis) (nicolas-grekas) - * bug #23952 [PhpUnitBridge] install PHPUnit 6 on PHP 7.2 (xabbuh) - * bug #23985 [Cache] Workaround zend.detect_unicode + zend.multibyte (nicolas-grekas) - * bug #23989 [Debug] Remove false-positive check in DebugClassLoader (nicolas-grekas) - * bug #23983 [VarDumper] Strengthen dumped JS (nicolas-grekas) - * bug #23982 [VarDumper] Strengthen dumped JS (nicolas-grekas) - * bug #23925 [Validator] Fix use of GroupSequenceProvider in child classes (linniksa) - * bug #23971 [Cache] Fix lazy Memcached connections (nicolas-grekas) - * bug #23970 [Cache] Fix >30 days expirations with Memcached (nicolas-grekas) - * bug #23949 [Dotenv] Get env using $_SERVER to work with fastcgi_param and workaround thread safety issues (nicolas-grekas) - * bug #23799 [Dotenv][WebServerBundle] Override previously loaded variables (voronkovich) - * bug #23676 [WebProfilerBundle] Re add missing link to the controller (lyrixx) - * bug #23870 [DI] Use GlobResource for non-tracked directories (vudaltsov) - * bug #23945 [Validator] Fix Greek translation (azhurb) - * bug #23940 [DI] Fix resolving env vars when compiling a ContainerBuilder (nicolas-grekas) - * bug #23903 [DI] Fix merging of env vars in configs (nicolas-grekas) - * bug #23825 Revert "feature #21038 [FrameworkBundle] deprecated cache:clear with warmup (fabpot)" (nicolas-grekas) - * bug #23899 [DI] Fix reading env vars from fastcgi params (nicolas-grekas) - * bug #23909 [Console] Initialize lazily to render exceptions properly (nicolas-grekas) - * bug #23878 [VarDumper] play nice with open_basedir when looking for composer.json (nicolas-grekas) - * bug #23897 Allow phpdocumentor/reflection-docblock 4 (derrabus) - * bug #23865 [Workflow] fixed InvalidDefinitionException message for StateMachineValidator (fmata) - * bug #23856 [DI] Fix dumping abstract with YamlDumper (nicolas-grekas) - * bug #23848 restrict reflection doc block (ElectricMaxxx) - * bug #23854 [DI] Fix YamlDumper not dumping abstract and autoconfigure (nicolas-grekas) - * bug #23752 Ignore memcached missing key error on session destroy (jderusse) - * bug #23829 Fixed the exception page design in responsive mode (javiereguiluz) - * bug #23828 [Console] Log exit codes as debug messages instead of errors (haroldiedema) - * bug #23763 [Cache] Hash cache key on save (lstrojny) - * bug #23806 [Profiler] Fix request_collector check in main layout (ogizanagi) - * bug #23658 [HttpFoundation] Generate safe fallback filename for wrongly encoded filename (xelaris) - * bug #23776 [FrameworkBundle] Warmup annotations for bundle-less controllers and entities (nicolas-grekas) - * bug #23783 Avoid infinite loops when profiler data is malformed (javiereguiluz) - * bug #23638 [FrameworkBundle][Workflow] better errors when security deps are missing (xabbuh) - * bug #23729 [Bridge\ProxyManager] Dont call __destruct() on non-instantiated services (nicolas-grekas) - * bug #23703 Bump minimal PHP version to ^5.5.9|>=7.0.8 (nicolas-grekas) - * bug #23755 [Config] Fix checking class existence freshness (nicolas-grekas) - -* 3.3.6 (2017-08-01) - - * bug #22244 [Console] Fix passing options with defaultCommand (Jakub Sacha) - * bug #23705 [Form] Add notice to upgrade to PHP v7.0.8+ (nicolas-grekas) - * bug #23683 [VarDumper] Keep and reuse array stubs in memory (nicolas-grekas) - * bug #23686 [Console][WebServerBundle] Use "exec" when possible (nicolas-grekas) - * bug #23684 [Debug] Missing escape in debug output (c960657) - * bug #23644 [VarDumper] Dont use Stub objects for arrays - lower GC pressure (nicolas-grekas) - * bug #23615 [Cache] Handle serialization failures for Memcached (nicolas-grekas) - * bug #23654 [DI] Fix using private services in expressions (nicolas-grekas) - * bug #23662 [VarDumper] Adapt to php 7.2 changes (nicolas-grekas) - * bug #23649 [Form][TwigBridge] Don't render _method in form_rest() for a child form (fmarchalemisys) - * bug #23588 [WebProfilerBundle] Display trace and context in the logger profiler (lyrixx) - * bug #23023 [DoctrineBridge][PropertyInfo] Added support for Doctrine Embeddables (vudaltsov) - * bug #23618 [Routing] allow HEAD method to be defined first (DavidBadura) - * bug #23619 [Validator] Fix IbanValidator for ukrainian IBANs (paroe) - * bug #23605 [DI][Bug] Autowiring thinks optional args on core classes are required (weaverryan) - * bug #23586 Fix case sensitive sameSite cookie (mikefrancis) - * bug #23584 Fix the design of the profiler exceptions when there is no message (javiereguiluz) - * bug #23238 [Security] ensure the 'route' index is set before attempting to use it (gsdevme) - * bug #23330 [WebProfilerBundle] Fix full sized dump hovering in toolbar (ogizanagi) - * bug #23581 [Config] Minor fix (nicolas-grekas) - * bug #23580 Fix login redirect when referer contains a query string (fabpot) - * bug #23577 [WebProfilerBundle][TwigBundle] Fix infinite js loop on exception pages (ogizanagi) - * bug #23558 [FrameworkBundle] fix ValidatorCacheWarmer: use serializing ArrayAdapter (dmaicher) - * bug #23573 [Config] Make ClassExistenceResource throw on invalid parents (nicolas-grekas) - * bug #23574 [VarDumper] Move locale sniffing to dump() time (nicolas-grekas) - * bug #23575 [VarDumper] Use "C" locale when using "comma" flags (nicolas-grekas) - -* 3.3.5 (2017-07-17) - - * bug #23549 [PropertyInfo] conflict for phpdocumentor/reflection-docblock 3.2 (xabbuh) - * bug #23513 [FrameworkBundle] Set default public directory on install assets (yceruto) - * security #23507 [Security] validate empty passwords again (xabbuh) - * bug #23526 [HttpFoundation] Set meta refresh time to 0 in RedirectResponse content (jnvsor) - * bug #23535 Make server:* commands work out of the box with the public/ root dir (fabpot) - * bug #23540 Disable inlining deprecated services (alekitto) - * bug #23498 [Process] Fixed issue between process builder and exec (lyrixx) - * bug #23490 [DependencyInjection] non-conflicting anonymous service ids across files (xabbuh) - * bug #23468 [DI] Handle root namespace in service definitions (ro0NL) - * bug #23477 [Process] Fix parsing args on Windows (nicolas-grekas) - * bug #23256 [Security] Fix authentication.failure event not dispatched on AccountStatusException (chalasr) - * bug #23461 Use rawurlencode() to transform the Cookie into a string (javiereguiluz) - * bug #23465 [HttpKernel][VarDumper] Truncate profiler data & optim perf (nicolas-grekas) - * bug #23457 [FrameworkBundle] check _controller attribute is a string before parsing it (alekitto) - * bug #23459 [TwigBundle] allow to configure custom formats in XML configs (xabbuh) - * bug #23460 Don't display the Symfony debug toolbar when printing the page (javiereguiluz) - * bug #23469 [FrameworkBundle] do not wire namespaces for the ArrayAdapter (xabbuh) - * bug #23434 [DotEnv] Fix variable substitution (brieucthomas) - * bug #23426 Fixed HttpOnly flag when using Cookie::fromString() (Toflar) - * bug #22439 [DX] [TwigBundle] Enhance the new exception page design (sustmi) - * bug #23417 [DI][Security] Prevent unwanted deprecation notices when using Expression Languages (dunglas) - * bug #23261 Fixed absolute url generation for query strings and hash urls (alexander-schranz) - * bug #23398 [Filesystem] Dont copy perms when origin is remote (nicolas-grekas) - -* 3.3.4 (2017-07-05) - - * bug #23413 [VarDumper] Reduce size of serialized Data objects (nicolas-grekas) - * bug #23385 [DoctrineBridge] Fix resetting entity managers with case sensitive id (chalasr) - * bug #23390 [Cache] Handle APCu failures gracefully (nicolas-grekas) - * bug #23371 [FrameworkBundle] 3.3: Don't get() private services from debug:router (ogizanagi) - * bug #23378 [FrameworkBundle] Do not remove files from assets dir (1ed) - -* 3.3.3 (2017-07-04) - - * bug #23366 [FrameworkBundle] Don't get() private services from debug:router (chalasr) - * bug #23239 [FrameworkBundle] call setContainer() for autowired controllers (xabbuh) - * bug #23351 [Dotenv] parse concatenated variable values (xabbuh) - * bug #23341 [DoctrineBridge][Security][Validator] do not validate empty values (xabbuh) - * bug #23274 Display a better error design when the toolbar cannot be displayed (yceruto) - * bug #23342 [Dotenv] parse escaped quotes in unquoted env var values (xabbuh) - * bug #23291 [Security] Fix Firewall ExceptionListener priority (chalasr) - * bug #23296 [WebProfilerBundle] Fix css trick used for offsetting html anchor from fixed header (ogizanagi) - * bug #23333 [PropertyAccess] Fix TypeError discard (dunglas) - * bug #23326 [Cache] fix cleanup of expired items for PdoAdapter (dmaicher) - * bug #23345 [Console] fix description of INF default values (xabbuh) - * bug #23328 [FrameworkBundle] Display a proper warning on cache:clear without the --no-warmup option (ogizanagi) - * bug #23299 [Workflow] Added more events to the announce function (Nyholm) - * bug #23279 Don't call count on non countable object (pierredup) - * bug #23283 [TwigBundle] add back exception check (xabbuh) - * bug #23268 Show exception is checked twice in ExceptionController of twig (gmponos) - * bug #23266 Display a better error message when the toolbar cannot be displayed (javiereguiluz) - * bug #23271 [FrameworkBundle] allow SSI fragments configuration in XML files (xabbuh) - * bug #23254 [Form][TwigBridge] render hidden _method field in form_rest() (xabbuh) - * bug #23250 [Translation] return fallback locales whenever possible (xabbuh) - * bug #23237 [Cache] Fix Predis client cluster with pipeline (flolivaud) - * bug #23240 [Console] Fix catching exception type in QuestionHelper (voronkovich) - * bug #23218 [DI] Dedup tags when using instanceof/autoconfigure (ogizanagi) - * bug #23231 Improved the exception page when there is no message (javiereguiluz) - * bug #23229 [WebProfilerBundle] Eliminate line wrap on count column (routing) (e-moe) - * bug #22732 [Security] fix switch user _exit without having current token (dmaicher) - * bug #23226 [Validator] replace hardcoded service id (xabbuh) - * bug #22730 [FrameworkBundle] Sessions: configurable "use_strict_mode" option for NativeSessionStorage (MacDada) - * bug #23195 [FrameworkBundle] [Command] Clean bundle directory, fixes #23177 (NicolasPion) - * bug #23213 Fixed composer resources between web/cli (iltar) - * bug #23160 [WebProfilerBundle] Fix the icon for the Cache panel (javiereguiluz) - * bug #23052 [TwigBundle] Add Content-Type header for exception response (rchoquet) - * bug #23173 [WebServerBundle] Fix router script option BC (1ed) - * bug #23199 Reset redirectCount when throwing exception (hvanoch) - * bug #23180 [FrameworkBundle] Expose the AbstractController's container to its subclasses (BPScott) - * bug #23186 [TwigBundle] Move template.xml loading to a compiler pass (ogizanagi) - * bug #23130 Keep s-maxage when expiry and validation are used in combination (mpdude) - * bug #23129 Fix two edge cases in ResponseCacheStrategy (mpdude) - * feature #22636 [Routing] Expose request in route conditions, if needed and possible (ro0NL) - * bug #22636 [Routing] Expose request in route conditions, if needed and possible (ro0NL) - * bug #22943 [SecurityBundle] Move cache of the firewall context into the request parameters (GromNaN) - * bug #23088 [FrameworkBundle] Dont set pre-defined esi/ssi services (ro0NL) - * bug #23057 [Translation][FrameworkBundle] Fix resource loading order inconsistency reported in #23034 (mpdude) - * bug #23092 [Filesystem] added workaround in Filesystem::rename for PHP bug (VolCh) - * bug #23074 [HttpFoundation] add back support for legacy constant values (xabbuh) - * bug #23128 [HttpFoundation] fix for Support for new 7.1 session options (vincentaubert) - * bug #23176 [VarDumper] fixes (nicolas-grekas) - * bug #23100 [PropertyAccess] Do not silence TypeErrors from client code. (tsufeki) - * bug #23156 [PropertyAccess] Fix Usage with anonymous classes (mablae) - * bug #23168 [Config] Fix ** GlobResource on Windows (nicolas-grekas) - * bug #23171 [Yaml] Fix linting yaml with constants as keys (chalasr) - * bug #23121 [Routing] Revert the change in [#b42018] with respect to Routing/Route.php (Dan Wilga) - * bug #23141 [DI] Fix keys resolution in ResolveParameterPlaceHoldersPass (nicolas-grekas) - * bug #23145 Fix the conditional definition of the SymfonyTestsListener (stof) - * bug #23091 [Cache] ApcuAdapter::isSupported() should return true when apc.enable_cli=Off (nicolas-grekas) - * bug #22953 #22839 - changed debug toolbar dump section to relative and use full window width (mkurzeja) - * bug #23086 [FrameworkBundle] Fix perf issue in CacheClearCommand::warmup() (nicolas-grekas) - * bug #23090 [SecurityBundle] Made 2 service aliases private (nicolas-grekas) - * bug #23108 [Yaml] Remove line number in deprecation notices (nicolas-grekas) - * bug #23098 Cache ipCheck (2.7) (gonzalovilaseca) - * bug #23082 [MonologBridge] Do not silence errors in ServerLogHandler::formatRecord (lyrixx) - * bug #23007 [HttpKernel][Debug] Fix missing trace on deprecations collected during bootstrapping & silenced errors (ogizanagi) - * bug #23069 [SecurityBundle] Show unique Inherited roles in profile panel (yceruto) - -* 3.3.2 (2017-06-06) - - * bug #23073 [TwigBridge] Fix namespaced classes (ogizanagi) - * bug #23063 [Cache] Fix extensibility of TagAwareAdapter::TAGS_PREFIX (wucdbm) - * bug #22936 [Form] Mix attr option between guessed options and user options (yceruto) - * bug #22976 [DependencyInjection] Use more clear message when unused environment variables detected (voronkovich) - -* 3.3.1 (2017-06-05) - - * bug #23067 [HttpFoundation][FrameworkBundle] Revert "trusted proxies" BC break (nicolas-grekas) - * bug #23065 [Cache] Fallback to positional when keyed results are broken (nicolas-grekas) - * bug #22981 [DependencyInjection] Fix named args support in ChildDefinition (dunglas) - * bug #23050 [Form][Profiler] Fixes form collector triggering deprecations (ogizanagi) - * bug #22971 [Profiler] Fix code excerpt wrapping (ogizanagi) - * bug #23049 [FrameworkBundle] mitigate BC break with empty trusted_proxies (xabbuh) - * bug #23045 [Cache] fix Redis scheme detection (xabbuh) - * bug #23013 Parse the _controller format in sub-requests (weaverryan) - * bug #23015 [PhpUnitBridge] Fix detection of PHPUnit 5 (enumag) - * bug #23041 [Config] Always protected ClassExistenceResource against bad parents (nicolas-grekas) - * bug #22988 [PropertyInfo][DoctrineBridge] The bigint Doctrine's type must be converted to string (dunglas) - * bug #23014 Fix optional cache warmers are always instantiated whereas they should be lazy-loaded (romainneutron) - * feature #23022 [Di] Remove closure-proxy arguments (nicolas-grekas) - * bug #23024 [EventDispatcher] Fix ContainerAwareEventDispatcher::hasListeners(null) (nicolas-grekas) - * bug #23008 [EventDispatcher] Handle laziness internally instead of relying on ClosureProxyArgument (nicolas-grekas) - * bug #23018 [FrameworkBundle] Fix CacheCollectorPass priority (chalasr) - * bug #23009 [Routing] Allow GET requests to be redirected. Fixes #23004 (frankdejonge) - * bug #22996 [Form] Fix \IntlDateFormatter timezone parameter usage to bypass PHP bug #66323 (romainneutron) - * bug #22965 [Cache] Ignore missing annotations.php (ro0NL) - * bug #22993 [DI] Autowiring exception thrown when inlined service is removed (weaverryan) - * bug #22999 Better DI type deprecation message (weaverryan) - * bug #22985 [Config] Allow empty globs (nicolas-grekas) - * bug #22961 [HttpKernel] Support unknown format in LoggerDataCollector (iltar) - * bug #22991 [DI] Don't throw Autowire exception for removed service with private __construct (weaverryan) - * bug #22968 [Profiler] Fix text selection & click on file links on exception pages (ogizanagi) - * bug #22994 Harden the debugging of Twig filters and functions (stof) - * bug #22960 [Cache] Fix decoration of TagAware adapters in dev (chalasr) - -* 3.3.0 (2017-05-29) - - * bug #22940 [Config] Fallback to regular import when glob fails (nicolas-grekas) - * bug #22847 [Console] ChoiceQuestion must have choices (ro0NL) - * bug #22900 [FrameworkBundle][Console] Fix the override of a command registered by the kernel (aaa2000) - * bug #22930 Revert "bug #22925 [PhpUnitBridge] Adjust PHPUnit class_alias check (nicolas-grekas) - * bug #22910 [Filesystem] improve error handling in lock() (xabbuh) - * bug #22924 [Cache] Dont use pipelining with RedisCluster (nicolas-grekas) - * bug #22928 [WebProfilerBundle] Fixed options stub values display in form profiler (HeahDude) - * feature #22838 Make the simple exception pages match the new style (javiereguiluz) - * bug #22925 [PhpUnitBridge] Adjust PHPUnit class_alias check to also check for namespaced class (GawainLynch) - * bug #22718 [Console] Fixed different behaviour of key and value user inputs in multiple choice question (borNfreee) - * bug #22921 [FrameworkBundle] Only override getProjectDir if it exists in the kernel (aschempp) - * feature #22905 [FrameworkBundle][Validator] Move the PSR-11 factory to the component (ogizanagi) - * bug #22728 [HttpKernel] Fix kernel.project_dir extensibility (chalasr) - * bug #22829 [Yaml] fix colon without space deprecation (xabbuh) - * bug #22901 Fix missing abstract key in XmlDumper (weaverryan) - * bug #22912 [DI] Avoid private call to Container::has() (ro0NL) - * feature #22904 [HttpFoundation] Add Request::HEADER_X_FORWARDED_AWS_ELB const (nicolas-grekas) - * bug #22878 [Yaml] parse PHP constants in mapping keys (xabbuh) - * bug #22873 [HttpKernel] don't call getTrustedHeaderName() if possible (xabbuh) - * feature #22892 [ProxyManager] Add FC layer (nicolas-grekas) - * bug #22866 [DI] Check for privates before shared services (ro0NL) - * feature #22884 [DI] Add missing deprecation on Extension::getClassesToCompile (nicolas-grekas) - * bug #22874 [WebProfilerBundle] Fix sub-requests display in time profiler panel (nicolas-grekas) - * bug #22853 [Yaml] fix multiline block handling (xabbuh) - * bug #22872 [FrameworkBundle] Handle project dir in cache:clear command (nicolas-grekas) - * feature #22808 [FrameworkBundle][Validator] Deprecate passing validator instances/aliases over using the service locator (ogizanagi) - * bug #22857 [DI] Fix autowire error for inlined services (weaverryan) - * bug #22858 [SecurityBundle] Prevent auto-registration of UserPasswordEncoderCommand (chalasr) - * bug #22859 [Profiler][VarDumper] Fix searchbar css when in toolbar (ogizanagi) - * bug #22614 [Process] Fixed escaping arguments on Windows when inheritEnvironmentVariables is set to false (maryo) - * bug #22817 [PhpUnitBridge] optional error handler arguments (xabbuh) - * bug #22781 [DI][Serializer] Fix missing de(normalizer|coder) autoconfig (ogizanagi) - * bug #22790 [DependencyInjection] Fix dumping of RewindableGenerator with empty IteratorArgument (meyerbaptiste) - * bug #22787 [MonologBridge] Fix the Monlog ServerLogHandler from Hanging on Windows (ChadSikorra) - * bug #22768 Use 0.0.0.0 as the server log command host default. (ChadSikorra) - * bug #22752 Improved how profiler errors are displayed on small screens (javiereguiluz) - -* 3.3.0-RC1 (2017-05-17) - - * bug #22715 [FrameworkBundle] remove Security deps from the require section (xabbuh) - * bug #22613 [Process] Fix incorrectly calling PHP process when path contains space (maryo) - * feature #22680 [DI] Fixing missing "exclude" functionality from PSR4 loader (weaverryan) - * bug #22699 [TwigBundle] service workflow.twig_extension should stay public (ogizanagi) - * feature #22708 Adding autowire alias for AuthenticationUtils (weaverryan) - * bug #22695 [WebServerBundle] fix dependencies (xabbuh) - * bug #22647 [VarDumper] Fix dumping of non-nested stubs (nicolas-grekas) - * bug #22409 [Yaml] respect inline level when dumping objects as maps (goetas, xabbuh) - * bug #22584 [Security] Avoid unnecessary route lookup for empty logout path (ro0NL) - * bug #22642 [DX] Making the RegisterControllerArgumentLocatorsPass throw exception on bad types (weaverryan) - * bug #22664 [Security] Fix TraceableAccessDecisionManager / DebugAccessDecisionManager BC layer (ogizanagi) - * bug #22690 [Console] Fix errors not rethrown even if not handled by console.error listeners (chalasr) - * bug #22681 Fixing a bug where abstract classes were wired with the prototype loader (weaverryan) - * feature #22665 [DI] Do not throw autowiring exceptions for a service that will be removed (weaverryan) - * bug #22669 [FrameworkBundle] AbstractConfigCommand: do not try registering bundles twice (ogizanagi) - * bug #22676 [FrameworkBundle] Adding the extension XML (flug) - * bug #22611 [FrameworkBundle] Fix "Locale class not found" in AboutCommand (rubenrua) - * bug #22677 [DI] Fixed index args bug with ResolveNamedArgumentsPass (weaverryan) - * bug #22652 [Workflow] Move twig extension registration to twig bundle (ogizanagi) - * feature #22668 [FrameworkBundle] KernelTestCase: allow to provide the kernel class with a var (ogizanagi) - * bug #22639 [WebLink][TwigBundle] Fix registration of the twig extension (ogizanagi) - * bug #22658 Make the exception pages work when the WebProfilerBundle is not installed (javiereguiluz) - * bug #22657 [DI] Fix Cannot declare class ...\DefinitionDecorator, because the name is already in use (ogizanagi) - * feature #22624 debug:container --types (classes/interfaces) (weaverryan) - * bug #22626 Fix missing parenthesis (yceruto) - * bug #22621 [Config] Fix resource tracking with new GlobResource (nicolas-grekas) - * feature #22385 [DX][FrameworkBundle] Show private aliases in debug:container (chalasr) - * bug #22615 [DI] Defaults to public=false in all service config files (nicolas-grekas) - -* 3.3.0-BETA1 (2017-05-01) - - * feature #22530 Making tags under _defaults always apply (weaverryan) - * feature #22590 [Lock] remove the component from 3.3 (fabpot) - * feature #22527 [DI] Throw useful exception on bad XML argument tags (nicolas-grekas) - * feature #22537 [Serializer] Allow to pass csv encoder options in context (ogizanagi) - * feature #22563 Not allowing autoconfigure, instanceofConditionals or defaults for ChildDefinition (weaverryan) - * feature #22441 [Console] Review console.ERROR related behavior (nicolas-grekas) - * feature #22234 [DI] Introducing autoconfigure: automatic _instanceof configuration (weaverryan) - * feature #21502 Persist app bootstrapping logs for logger datacollector (ScullWM, nicolas-grekas) - * feature #22459 [HttpKernel] Fix deprecation of Extension::addClassesToCompile() / AddClassesToCachePass (nicolas-grekas) - * feature #22416 [FrameworkBundle][Workflow] Deprecate the default type of a workflow (lyrixx) - * feature #22313 [Workflow] Move ValidateWorkflowsPass to the Workflow component (chalasr) - * feature #22420 [DI] Make tagged abstract services throw earlier (nicolas-grekas) - * feature #22384 [DI] Replace autowiring BC break by regular deprecation (nicolas-grekas) - * feature #22383 added a more specialized exception for a better error message (fabpot) - * feature #22356 [DI] Rework config hierarchy: defaults > instanceof > service config (weaverryan, nicolas-grekas) - * feature #22362 [DI] Populate class of ChildDefinition when its id matches an existing FQCN (nicolas-grekas) - * feature #22239 [Finder] Glob wildcard while using double-star without ending slash (sroze) - * feature #22273 Add a new Link component (dunglas) - * feature #22315 Add Kernel::getProjectDir() (fabpot) - * feature #22314 [HttpKernel][FrameworkBundle] Dump container logs in Kernel, to have them also on errors (nicolas-grekas) - * feature #22316 [WebServerBundle] added a way to dump current status host/port/address when getting the status (fabpot) - * feature #22323 [DI] Report cascades of autowiring error messages (nicolas-grekas) - * feature #22306 [DI] Restrict autowired registration to "same-vendor" namespaces (nicolas-grekas) - * feature #22295 [BC BREAK][DI] Always autowire "by id" instead of using reflection against all existing services (nicolas-grekas) - * feature #20951 Redesigned the exception pages (javiereguiluz) - * feature #21919 [Form] Deprecated usage of "choices" option in sub types (HeahDude) - * feature #22274 [Yaml] report deprecations when linting YAML files (xabbuh) - * feature #22286 [DI/Yaml] Remove `@experimental` flag from "instanceof" and "prototype" (nicolas-grekas) - * feature #22181 [Console] Allow to catch CommandNotFoundException (chalasr) - * feature #22296 Bump monolog to 1.19 and use the agent regex const from parent (redthor) - * feature #21437 [Security] Use IteratorArgument for voters (jvasseur) - * feature #22277 [DI] Add "factory" support to named args and autowiring (nicolas-grekas) - * feature #22276 [FrameworkBundle] Returns the kernel instance in KernelTestCase::bootKernel (lyrixx) - * feature #22256 [DI] Reduce complexity of autowiring (nicolas-grekas) - * feature #22238 [BC BREAK][HttpFoundation] Request::setTrustedProxies() takes a new required $trustedHeaderSet argument (nicolas-grekas) - * feature #22175 [DI] add ServiceLocatorTagPass::register() to share service locators (nicolas-grekas) - * feature #22180 [Workflow] Added 'workflow_marked_places' twig function (lyrixx) - * feature #22185 [DI] Enhance DX by throwing instead of triggering a deprecation notice (nicolas-grekas) - * feature #22060 [DI] Add "by-id" autowiring: a side-effect free variant of it based on the class<>id convention (nicolas-grekas) - * feature #22158 Revert "feature #20973 [DI] Add getter injection (nicolas-grekas)" (nicolas-grekas) - * feature #22157 [FrameworkBundle] Introduce AbstractController, replacing ControllerTrait (nicolas-grekas) - * feature #22046 [Asset] Adding a new version strategy that reads from a manifest JSON file (weaverryan) - * feature #22129 [WebProfilerBundle] Improve cache panel (ro0NL) - * feature #21819 [Twig Bridge] A simpler way to retrieve flash messages (javiereguiluz) - * feature #19026 [Security] Strengthen comparison of target_url vs login_path (mrzard) - * feature #19496 [DX][Form][Validator] Add ability check if cocrete constraint fails. (Koc) - * feature #18140 [Console] Add console.ERROR event and deprecate console.EXCEPTION (wouterj) - * feature #22120 [FrameworkBundle] Multiple services on one Command class (SenseException) - * feature #21771 [FrameworkBundle] Add new "controller.service_arguments" tag to inject services into actions (nicolas-grekas) - * feature #22114 [lock] Rename Quorum into Strategy (jderusse) - * feature #20516 [Security][SecurityBundle] Enhance automatic logout url generation (ogizanagi) - * feature #22081 [FrameworkBundle][Validator] Move Validator passes to the component (chalasr) - * feature #20567 [WebProfilerBundle] Improved cookie traffic (ro0NL) - * feature #19887 Sort alternatives alphabetically when a command is not found (javiereguiluz) - * feature #20851 [Cache] Add CacheItem::getPreviousTags() (nicolas-grekas) - * feature #21830 [HttpFoundation] Add $trustedHeaderSet arg to Request::setTrustedProxies() - deprecate not setting it (nicolas-grekas) - * feature #21924 [FrameworkBundle] Allow to configure Serializer mapping paths (chalasr) - * feature #19278 [FrameworkBundle] Added about command (ro0NL) - * feature #21708 [DI] Add and wire ServiceSubscriberInterface - aka explicit service locators (nicolas-grekas) - * feature #22011 [FrameworkBundle][Serializer] Add option to register a circular_reference_handler (lyrixx) - * feature #19673 [DI] Deprecate Container::isFrozen and introduce isCompiled (ro0NL) - * feature #19954 [Console] Exclude empty namespaces in text descriptor (ro0NL) - * feature #21093 [Lock] Create a lock component (jderusse) - * feature #21007 [WebProfilerBundle] Improve AJAX toolbar panel (ro0NL) - * feature #20642 [FrameworkBundle] Add project directory default for installing assets (Noah Heck) - * feature #20365 [TwigBridge] Handle form label attributes like others (ro0NL) - * feature #22010 [FrameworkBundle][Translator] Make the Translator works with any PSR-11 container (chalasr) - * feature #21038 [FrameworkBundle] deprecated cache:clear with warmup (fabpot) - * feature #22098 [*Bundle] Add autowiring aliases for common services (nicolas-grekas) - * feature #22095 [DI] Add logging and better failure recovery to AutowirePass (nicolas-grekas) - * feature #21889 Deprecate the special SYMFONY__ environment variables (javiereguiluz) - * feature #22059 [Yaml] deprecate "? " starting unquoted strings (xabbuh) - * feature #22030 [DI] Remove skipping magic for autowired methods (nicolas-grekas) - * feature #22024 [DI] Introduce "container.service_locator" tag, replaces ServiceLocatorArgument (nicolas-grekas) - * feature #21837 [FrameworkBundle] Lazy configuration of annotations' loader and `@required` (nicolas-grekas) - * feature #21970 [DependencyInjection] Support anonymous services in Yaml (GuilhemN) - * feature #21979 [FrameworkBundle][TwigBundle] Require PSR-11 container instead of Symfony container (enumag) - * feature #21935 [FrameworkBundle][Workflow] Add a way to register a guard expression in the configuration (lyrixx) - * feature #21080 [FrameworkBundle][Monolog] Added a new way to follow logs (lyrixx) - * feature #21978 [DoctrineBridge][Routing] Require PSR-11 container instead of Symfony container (enumag) - * feature #21950 [Workflow] Added fluent interface to the DefinitionBuilder (lyrixx) - * feature #21933 [FrameworkBundle][Workflow] Add a way to enable the AuditTrail Logger (lyrixx) - * feature #21925 [Workflow] Added the workflow name to all events dispatched (lyrixx) - * feature #21774 [Yaml] deprecate implicit string casting of mapping keys (xabbuh) - * feature #21780 [DX] [Form] Add helper method to register form extensions during unit testing (pierredup) - * feature #21842 [HttpKernel] Allow signing URIs with a custom query string parameter (thewilkybarkid) - * feature #21705 [Bridge/Monolog] Enhance the Console Handler (lyrixx) - * feature #21893 Added a castToArray() config helper (javiereguiluz) - * feature #21421 Use proper error message when session write fails #20807 (digilist) - * feature #21770 [DI] Allow extensions to create ServiceLocator as services (nicolas-grekas) - * feature #21767 [DI][Router][DX] Invalidate routing cache when container parameters changed (ogizanagi) - * feature #21835 [FrameworkBundle][Routing] Move RoutingResolverPass to the Routing component (chalasr) - * feature #21815 [FrameworkBundle][HttpKernel] Move ControllerArgumentValueResolverPass to the HttpKernel component (chalasr) - * feature #21824 Add deprecation note on routing class parameters (lepiaf) - * feature #21854 [Router] Follow symlinks and skip dots in the annotation directory loader (jakzal) - * feature #18193 [FrameworkBundle] Introduce autowirable ControllerTrait (dunglas) - * feature #20680 DoctrineDataCollector: taught sanitizeParam to support classes with __toString implemented. (FractalizeR) - * feature #21828 [PhpUnitBridge] include expected deprecations in assertion counter (xabbuh) - * feature #21763 [DI] Replace wildcard-based methods autowiring by `@required` annotation (nicolas-grekas) - * feature #21730 [DependencyInjection] Use a service locator in AddConstraintValidatorsPass (GuilhemN) - * feature #21118 [Yaml] parse omitted inlined mapping values as null (xabbuh) - * feature #21806 [FrameworkBundle][PropertyInfo] Move PropertyInfoPass to the PropertyInfo component (chalasr) - * feature #19822 [HttpKernel] Deprecate X-Status-Code for better alternative (jameshalsall) - * feature #21228 [Console] Explicitly passed options without value (or empty) should remain empty (chalasr) - * feature #21723 [Routing][DX] Add full route definition for invokable controller/class (yceruto) - * feature #21768 [HttpKernel] Add a ContainerControllerResolver (psr-11) (ogizanagi) - * feature #21690 [Form] allow form types + form type extensions + form type guessers to be private services (hhamon) - * feature #21755 [Routing] Optimised dumped router matcher, prevent unneeded function calls. (frankdejonge) - * feature #21375 [FrameworkBundle][Config] Move ConfigCachePass from FrameworkBundle to Config (Deamon) - * feature #21786 [PhpUnitBridge] testing for deprecations is not risky (xabbuh) - * feature #21792 [Security] deprecate multiple providers in context listener (xabbuh) - * feature #21625 Remove some container injections in favor of service locators (nicolas-grekas, chalasr) - * feature #21539 Introduce weak vendors mode (greg0ire) - * feature #21638 [VarDumper] Allow seamless use of Data clones (nicolas-grekas) - * feature #21164 [HttpKernel] Added the SessionValueResolver (iltar) - * feature #21718 [SecurityBundle] Don't normalize username of in-memory users (chalasr) - * feature #20107 Added a build method to the kernel to replace Bundle::build() (iltar) - * feature #21694 [Bridge/PhpUnit] Add PHPUnit 6 support (nicolas-grekas) - * feature #21122 [ExpressionLanguage] Create an ExpressionFunction from a PHP function name (maidmaid) - * feature #21653 [VarDumper] Added a way to print or not comma separator and/or trailing comma (lyrixx) - * feature #21471 [Yaml] Allow dumping empty array as YAML sequence (c960657) - * feature #21478 [Asset] Add support for preloading with links and HTTP/2 push (dunglas) - * feature #20632 [FrameworkBundle] Make use of stderr for non reliable output (chalasr, ogizanagi) - * feature #21664 [Console] simplify the implementation of SymfonyStyle::comment() (fabpot) - * feature #21578 [Translation] Added a lint:xliff command for XLIFF files (javiereguiluz) - * feature #21635 added support for glob loaders in Config (fabpot) - * feature #21654 [PropertyInfo] Use iterators for PropertyInfoExtractor (GuilhemN) - * feature #21655 [PropertyInfo] Make classes final (GuilhemN) - * feature #21530 [DependencyInjection] Add "instanceof" section for local interface-defined configs (nicolas-grekas, dunglas) - * feature #21643 [Yaml] deprecate parsing mappings without keys (xabbuh) - * feature #20677 [DX][SecurityBundle] UserPasswordEncoderCommand: ask user class choice question (ogizanagi) - * feature #21283 [Form][FrameworkBundle] Move FormPass to the Form component (chalasr) - * feature #21293 [FrameworkBundle][Serializer] Move SerializerPass to the Serializer (chalasr) - * feature #21450 [Security] Lazy load guard authenticators and authentication providers (chalasr) - * feature #21484 [DI] Deprecate underscore-services in YamlFileLoader (nicolas-grekas) - * feature #21270 [DependencyInjection] Use glob pattern to load config files (pierredup) - * feature #19815 [WebProfilerBundle] Make the IP address in the profiler header clickable (jameshalsall) - * feature #21383 [DependencyInjection] Add support for named arguments (dunglas, nicolas-grekas) - * feature #19371 [Serializer] Give access to the context to support* methods (dunglas) - * feature #21553 [DI] Replace container injection by explicit service locators (chalasr) - * feature #18834 [Serializer] Add the possibility to filter attributes (dunglas) - * feature #20787 [Workflow] Added an entered event (Padam87) - * feature #21289 [DI] Add prototype services for PSR4-based discovery and registration (nicolas-grekas) - * feature #21465 [Debug] Support `@final` on methods (GuilhemN) - * feature #21505 [Config][DI] Add ComposerResource to track the runtime engine + deps (nicolas-grekas) - * feature #21533 [DI] Deprecate (un)setting pre-defined services (ro0NL) - * feature #21194 [Yaml] Add tags support (GuilhemN) - * feature #21460 [DI] ContainerBuilder::compile() can optionally resolve env vars in parameter bag (nicolas-grekas) - * feature #21572 [Finder] Add double-star matching to Glob::toRegex() (nicolas-grekas) - * feature #21265 [DI] Implement PSR-11 (greg0ire) - * feature #21474 [Process] Accept command line arrays and per-run env vars, fixing signaling and escaping (nicolas-grekas) - * feature #21517 [FrameworkBundle] Add missing autowiring aliases for common interfaces (chalasr) - * feature #21516 [HttpKernel][FrameworkBundle] Lazy load argument value resolvers (chalasr) - * feature #21031 [DI] Getter autowiring (dunglas) - * feature #21419 [DI][Config] Add & use ReflectionClassResource (nicolas-grekas) - * feature #21455 [DI] Allow to count on lazy collection arguments (ogizanagi) - * feature #21408 [DI] Add ContainerBuilder::fileExists() for checking/tracking resource existence (chalasr) - * feature #21470 [Process] Deprecate not inheriting env vars + compat related settings (nicolas-grekas) - * feature #21494 [DI] Deprecate autowiring-types in favor of aliases (nicolas-grekas) - * feature #21451 [SecurityBundle] Lazy load request matchers in FirewallMap (chalasr) - * feature #20973 [DI] Add getter injection (nicolas-grekas) - * feature #21396 [DI] Enhance logging in compiler passes (nicolas-grekas) - * feature #21402 [Security] make LdapBindAuthenticationProvider capable of searching for the DN (lsmith77, nietonfir) - * feature #21404 [DI] Generalize constructor autowiring to partial method calls (nicolas-grekas) - * feature #21388 [Debug] Deprecate ContextErrorException (nicolas-grekas) - * feature #20943 [DependencyInjection] Use current class as default class for factory declarations (ogizanagi) - * feature #21003 [Console][FrameworkBundle] Log console exceptions (jameshalsall, chalasr) - * feature #21313 [DI] Add Yaml syntax for short services definition (ogizanagi) - * feature #20694 [Cache] Implement PSR-16 SimpleCache v1.0 (nicolas-grekas) - * feature #21327 [DI] Factorize compiler passes around new AbstractRecursivePass (nicolas-grekas) - * feature #19086 [FrameworkBundle] add "mapping" configuration key at validation secti… (davewwww) - * feature #21350 [Yaml] Remove internal arguments from the api (GuilhemN) - * feature #21353 [ClassLoader] Deprecated the component (nicolas-grekas) - * feature #21334 [Workflow] Introduce concept of SupportStrategyInterface (andesk, lyrixx) - * feature #20390 [Ldap] added Ldap entry rename for ExtLdap adapter (fruitwasp) - * feature #21065 Added cache data collector and profiler page (Nyholm) - * feature #21306 [DependencyInjection] Always autowire the constructor (dunglas) - * feature #20493 [Debug] Trigger deprecation on `@final` annotation in DebugClassLoader - prepare making some classes final (GuilhemN) - * feature #21244 [DI] Remove synthetic services from methodMap + generated methods (nicolas-grekas) - * feature #21238 [VarDumper] Add search keyboard shortcuts (ogizanagi) - * feature #21290 [FrameworkBundle] Fix debug:container --show-arguments missing cases (chalasr) - * feature #21263 [DI] Mark generated containers as final (nicolas-grekas) - * feature #21253 [TwigBridge][Worklow] Added a new workflow_has_place function (Padam87, lyrixx) - * feature #21234 Add a new Dotenv component (fabpot) - * feature #20861 Add a --show-arguments flag to the debug:container command (Cydonia7) - * feature #21223 [DI] Deprecate case insentivity of service identifiers (nicolas-grekas) - * feature #20887 [Form] DateIntervalType: Allow to configure labels & enhance form theme (ogizanagi) - * feature #19443 [Console] Move AddConsoleCommandPass from FrameworkBundle to Console. (bcremer) - * feature #21231 [FrameworkBundle] allow to reference files directly from kernel.root_dir (fabpot) - * feature #20611 [DI] FileLoaders: Allow to explicit type to load (ogizanagi) - * feature #20689 [Config][FrameworkBundle] Allow to dump extension config reference sub-path (ogizanagi) - * feature #21188 [HttpFoundation] Add File\Stream for size-unknown BinaryFileResponse (nicolas-grekas) - * feature #21214 [DI] Allow ~ instead of {} for services in Yaml (wouterj) - * feature #20612 [Filesystem] Add appendToFile() (chalasr) - * feature #20612 [Filesystem] Add appendToFile() (chalasr) - * feature #21114 [Yaml] parse multi-line strings (xabbuh) - * feature #21196 [FrameworkBundle] changed some default configs from canBeEnabled to canBeDisabled (fabpot) - * feature #20937 [EventDispatcher] Deprecate ContainerAwareEventDispatcher (nicolas-grekas) - * feature #21190 [WebServerBundle] Decouple server commands from the container (chalasr) - * feature #21071 [DI] Add "inherit-tags" with configurable defaults + same for "public", "tags" & "autowire" (nicolas-grekas, ogizanagi) - * feature #21133 [DI] Optional class for named services (hason, nicolas-grekas) - * feature #20953 [DI][EventDispatcher] Add & wire closure-proxy argument type (nicolas-grekas) - * feature #20586 [Console] Ease writing to stderr using SymfonyStyle (chalasr) - * feature #20547 [FrameworkBundle] Allowed symlinks when searching for translation, searialization and validation files (tifabien) - * feature #20735 Deprecate ClassCollectionLoader and Kernel::loadClassCache (dbrumann) - * feature #21140 [PhpUnitBridge] deprecate the testLegacy test name prefix (xabbuh) - * feature #21109 [Profiler][VarDumper] Add a search feature to the HtmlDumper (ogizanagi) - * feature #21039 Web server bundle (fabpot) - * feature #20907 [DependencyInjection] Implement lazy collection type using generators (tgalopin, nicolas-grekas) - * feature #21075 [Console] Show hidden commands in json & xml descriptors (ogizanagi) - * feature #21129 [FrameworkBundle] Display original definition for aliases in debug:container (chalasr) - * feature #21108 [Cache] Add DSN, createClient & better error reporting to MemcachedAdapter (nicolas-grekas, robfrawley) - * feature #21147 [PhpUnitBridger] Bump simple-phpunit to PHPUnit 5.7 by default (nicolas-grekas) - * feature #21112 [PhpUnitBridge] run PHPUnit in the same process (xabbuh) - * feature #21106 [Validator] support DateTimeInterface instances for times (xabbuh) - * feature #20809 [FrameworkBundle] Display the controller class name in 'debug:router' (lyrixx) - * feature #21082 [Cache] TraceableAdapter (Nyholm) - * feature #20938 [DI] Prepare dropping "strict" handling in loaders (nicolas-grekas) - * feature #20971 [WebProfilerBundle] Split PHP version if needed (ro0NL) - * feature #20634 [DI] Deprecate dumping an uncompiled container (ro0NL) - * feature #20923 #20921 [Config] Provide shorthand methods for ArrayNodeDefinition::pr… (skafandri) - * feature #20569 [HttpFoundation] Create cookie from string + synchronize response cookies (ro0NL) - * feature #20618 [DI] Make ContainerBuilder::resolveEnvPlaceholders() able to inline the values of referenced env vars. (nicolas-grekas) - * feature #20962 Request exceptions (thewilkybarkid, fabpot) - * feature #20928 [FrameworkBundle] don't load translator services if not required (xabbuh) - * feature #20644 [HttpFoundation] Compute cookie max-age attribute (ro0NL) - * feature #20167 [DependencyInjection] Make method (setter) autowiring configurable (dunglas) - * feature #20663 [DependencyInjection] replace DefinitionDecorator by ChildDefinition (xabbuh) - * feature #20197 [WebProfilerBundle] Improve Ajax Profiling Performance (javascript) (patrick-mcdougle) - * feature #20487 [Console] Disallow inheritance from ProgressBar (a-ast) - * feature #20651 [DependencyInjection] Added Yaml syntax shortcut for name-only tags (wouterj) - * feature #20648 [DependencyInjection] Added a shortcut method for autowired definitions (wouterj) - * feature #20697 Updated the "PHP config" panel in the profiler (javiereguiluz) - * feature #20773 [FrameworkBundle] Added GlobalVariables::getToken() (HeahDude) - * feature #20866 [Console] Improve markdown format (ro0NL) - * feature #20867 [Console] Include application name/version in JSON descriptions (ro0NL) - * feature #20869 [Console] Improve UX on not found namespace/command (Seldaek) - * feature #20858 [Cache] Simple Memcached Adapter (robfrawley) - * feature #20881 [VarDumper] Add SymfonyCaster::castRequest() (nicolas-grekas) - * feature #20810 [FrameworkBundle] Allow clearing private cache pools in cache:pool:clear (chalasr) - * feature #20417 [SecurityBundle] Rename FirewallContext#getContext() (chalasr) - * feature #20801 [Security] deprecate the RoleInterface (xabbuh) - * feature #20260 [DependencyInjection] Support autowiring for EventDispatcher/EventDispatcherInterface (chalasr) - * feature #20777 [ClassLoader] Deprecate Apc/WinCache/Xcache class loaders (nicolas-grekas) - * feature #20524 [Serializer][XmlEncoder] Allow removing empty tags in generated XML (amoiraud) - * feature #19958 [Serializer] Throw exception when extra attributes are used during an object denor… (juliendidier) - * feature #20310 [Ldap] Allow search scoping (xunto) - * feature #18952 [Security] Add a JSON authentication listener (dunglas) - * feature #20161 add toolbar & profiler SVG style classes (havvg) - * feature #20467 [DomCrawler] Add support for formaction and formmethod attributes (stof) - * feature #20509 [Serializer] Allow to specify a single value in @Groups (dunglas) - * feature #20722 Updated the "Symfony Config" panel in the profiler (javiereguiluz) - diff --git a/CHANGELOG-3.4.md b/CHANGELOG-4.0.md similarity index 83% rename from CHANGELOG-3.4.md rename to CHANGELOG-4.0.md index 39bdf7acc9a6f..b411c42ae9c81 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-4.0.md @@ -1,15 +1,14 @@ -CHANGELOG for 3.4.x +CHANGELOG for 4.0.x =================== This changelog references the relevant changes (bug and security fixes) done -in 3.4 minor versions. +in 4.0 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 +To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.0.0...v4.0.1 -* 3.4.4 (2018-01-29) +* 4.0.4 (2018-01-29) - * bug #25932 Don't stop PSR-4 service discovery if a parent class is missing (derrabus) * bug #25922 [HttpFoundation] Use the correct syntax for session gc based on Pdo driver (tanasecosminromeo) * bug #25933 Disable CSP header on exception pages only in debug (ostrolucky) * bug #25926 [Form] Fixed Button::setParent() when already submitted (HeahDude) @@ -18,9 +17,11 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25858 [DI] Fix initialization of legacy containers by delaying include_once (nicolas-grekas) * bug #25891 [DependencyInjection] allow null values for root nodes in YAML configs (xabbuh) * bug #24864 Have weak_vendors ignore deprecations from outside (greg0ire) + * bug #25873 [Console] Fix using finally where the catch can also fail (nicolas-grekas) * bug #25848 [Validator] add missing parent isset and add test (Simperfit) * bug #25869 [Process] Skip environment variables with false value in Process (francoispluchino) * bug #25864 [Yaml] don't split lines on carriage returns when dumping (xabbuh) + * bug #25863 [Yaml] trim spaces from unquoted scalar values (xabbuh) * bug #25861 do not conflict with egulias/email-validator 2.0+ (xabbuh) * bug #25851 [Validator] Conflict with egulias/email-validator 2.0 (emodric) * bug #25837 [SecurityBundle] Don't register in memory users as services (chalasr) @@ -58,7 +59,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * feature #25669 [Security] Fail gracefully if the security token cannot be unserialized from the session (thewilkybarkid) * bug #25700 Run simple-phpunit with --no-suggest option (ro0NL) -* 3.4.3 (2018-01-05) +* 4.0.3 (2018-01-05) * bug #25685 Use triggering file to determine weak vendors if when the test is run in a separate process (alexpott) * bug #25671 Remove randomness from dumped containers (nicolas-grekas) @@ -89,7 +90,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25552 [WebProfilerBundle] Let fetch() cast URL to string (ro0NL) * bug #25521 [Console] fix a bug when you are passing a default value and passing -n would output the index (Simperfit) -* 3.4.2 (2017-12-15) +* 4.0.2 (2017-12-15) * bug #25489 [FrameworkBundle] remove esi/ssi renderers if inactive (dmaicher) * bug #25502 Fixing wrong class_exists on interface (weaverryan) @@ -105,12 +106,14 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25330 [HttpFoundation] Support 0 bit netmask in IPv6 (`::/0`) (stephank) * bug #25378 [VarDumper] Fixed file links leave blank pages when ide is configured (antalaron) * bug #25410 [HttpKernel] Fix logging of post-terminate errors/exceptions (nicolas-grekas) + * bug #25409 [Bridge/Doctrine] Drop "memcache" type (nicolas-grekas) * bug #25417 [Process] Dont rely on putenv(), it fails on ZTS PHP (nicolas-grekas) * bug #25333 [DI] Impossible to set an environment variable and then an array as container parameter (Phantas0s) * bug #25447 [Process] remove false-positive BC breaking exception on Windows (nicolas-grekas) * bug #25381 [DI] Add context to service-not-found exceptions thrown by service locators (nicolas-grekas) * bug #25438 [Yaml] empty lines don't count for indent detection (xabbuh) * bug #25412 Extend Argon2i support check to account for sodium_compat (mbabker) + * bug #25392 [HttpFoundation] Fixed default user-agent (3.X -> 4.X) (lyrixx) * bug #25389 [Yaml] fix some edge cases with indented blocks (xabbuh) * bug #25396 [Form] Fix debug:form command definition (yceruto) * bug #25398 [HttpFoundation] don't prefix cookies with "Set-Cookie:" (pableu) @@ -123,7 +126,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25364 [DependencyInjection] Prevent a loop in aliases within the `findDefinition` method (sroze) * bug #25337 Remove Exclusive Lock That Breaks NFS Caching (brianfreytag) -* 3.4.1 (2017-12-04) +* 4.0.1 (2017-12-05) * bug #25304 [Bridge/PhpUnit] Prefer $_SERVER['argv'] over $argv (ricknox) * bug #25272 [SecurityBundle] fix setLogoutOnUserChange calls for context listeners (dmaicher) @@ -137,8 +140,10 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25312 [DI] Fix deep-inlining of non-shared refs (nicolas-grekas) * bug #25309 [Yaml] parse newlines in quoted multiline strings (xabbuh) * bug #25313 [DI] Fix missing unset leading to false-positive circular ref (nicolas-grekas) + * bug #25268 [DI] turn $private to protected in dumped container, to make cache:clear BC (nicolas-grekas) * bug #25285 [DI] Throw an exception if Expression Language is not installed (sroze) * bug #25241 [Yaml] do not eagerly filter comment lines (xabbuh) + * bug #25284 [DI] Cast ids to string, as done on 3.4 (nicolas-grekas, sroze) * bug #25297 [Validator] Fixed the @Valid(groups={"group"}) against null exception case (vudaltsov) * bug #25255 [Console][DI] Fail gracefully (nicolas-grekas) * bug #25264 [DI] Trigger deprecation when setting a to-be-private synthetic service (nicolas-grekas) @@ -150,12 +155,13 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25230 Use a more specific file for detecting the bridge (greg0ire) * bug #25232 [WebProfilerBundle] [TwigBundle] Fix Profiler breaking XHTML pages (tistre) -* 3.4.0 (2017-11-30) +* 4.0.0 (2017-11-30) * bug #25220 [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one (nicolas-grekas) * bug #25209 [VarDumper] Dont use empty(), it chokes on eg GMP objects (nicolas-grekas) * bug #25200 [HttpKernel] Arrays with scalar values passed to ESI fragment renderer throw deprecation notice (Simperfit) * bug #25201 [HttpKernel] Add a better error messages when passing a private or non-tagged controller (Simperfit) + * bug #25155 [DependencyInjection] Detect case mismatch in autowiring (Simperfit, sroze) * bug #25217 [Dotenv] Changed preg_match flags from null to 0 (deekthesqueak) * bug #25180 [DI] Fix circular reference when using setters (nicolas-grekas) * bug #25204 [DI] Clear service reference graph (nicolas-grekas) @@ -175,7 +181,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25151 [FrameworkBundle] Automatically enable the CSRF protection if CSRF manager exists (sroze) * bug #25043 [Yaml] added ability for substitute aliases when mapping is on single line (Michał Strzelecki, xabbuh) -* 3.4.0-RC2 (2017-11-24) +* 4.0.0-RC2 (2017-11-24) * bug #25146 [DI] Dont resolve envs in service ids (nicolas-grekas) * bug #25113 [Routing] Fix "config-file-relative" annotation loader resources (nicolas-grekas, sroze) @@ -191,15 +197,17 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25100 [SecurityBundle] providerIds is undefined error when firewall provider is not specified (karser) * bug #25097 [Bridge\PhpUnit] Turn "preserveGlobalState" to false by default, revert "Blacklist" removal (nicolas-grekas) -* 3.4.0-RC1 (2017-11-21) +* 4.0.0-RC1 (2017-11-21) * bug #25077 [Bridge/Twig] Let getFlashes starts the session (MatTheCat) * bug #25082 [HttpKernel] Disable container inlining when legacy inlining has been used (nicolas-grekas) + * bug #25022 [Filesystem] Updated Filesystem::makePathRelative (inso) * bug #25072 [Bridge/PhpUnit] Remove trailing "\n" from ClockMock::microtime(false) (joky) * bug #25069 [Debug] Fix undefined variable $lightTrace (nicolas-grekas) * bug #25053 [Serializer] Fixing PropertyNormalizer supports parent properties (Christopher Hertel) * bug #25055 [DI] Analyze setter-circular deps more precisely (nicolas-grekas) * feature #25056 [Bridge/PhpUnit] Sync the bridge version installed in vendor/ and in phpunit clone (nicolas-grekas) + * bug #25048 Allow EnumNode name to be null (MatTheCat) * bug #25045 [SecurityBundle] Don't trigger auto-picking notice if provider is set per listener (chalasr) * bug #25033 [FrameworkBundle] Dont create empty bundles directory by default (ro0NL) * bug #25037 [DI] Skip hot_path tag for deprecated services as their class might also be (nicolas-grekas) @@ -207,6 +215,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #25014 Move deprecation under use statements (greg0ire) * bug #25030 [Console] Fix ability to disable lazy commands (chalasr) * bug #25032 [Bridge\PhpUnit] Disable broken auto-require mechanism of phpunit (nicolas-grekas) + * bug #25016 [HttpKernel] add type-hint for the requestType (Simperfit) * bug #25027 [FrameworkBundle] Hide server:log command based on deps (sroze) * bug #24991 [DependencyInjection] Single typed argument can be applied on multiple parameters (nicolas-grekas, sroze) * bug #24983 [Validator] enter the context in which to validate (xabbuh) @@ -219,11 +228,12 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * security #24994 Prevent bundle readers from breaking out of paths (xabbuh) * security #24993 Ensure that submitted data are uploaded files (xabbuh) * security #24992 Namespace generated CSRF tokens depending of the current scheme (dunglas) + * bug #24975 [DomCrawler] Type fix Crawler:: discoverNamespace() (VolCh) * bug #24954 [DI] Fix dumping with custom base class (nicolas-grekas) * bug #24952 [HttpFoundation] Fix session-related BC break (nicolas-grekas, sroze) * bug #24943 [FrameworkBundle] Wire the translation.reader service instead of deprecated translation.loader in commands (ogizanagi) -* 3.4.0-BETA4 (2017-11-12) +* 4.0.0-BETA4 (2017-11-12) * bug #24874 [TwigBridge] Fixed the .form-check-input class in the bs4 templates (vudaltsov) * bug #24929 [Console] Fix traversable autocomplete values (ro0NL) @@ -244,10 +254,11 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #24837 [TwigBridge] [Bootstrap 4] Fix validation error design for expanded choiceType (ostrolucky) * bug #24878 [HttpFoundation] Prevent PHP from sending Last-Modified on session start (nicolas-grekas) * bug #24881 [WebserverBundle] fixed the bug that caused that the webserver would … (Serkan Yildiz) + * bug #24722 Replace more docblocks by type-hints (nicolas-grekas) * bug #24850 [DI] Fix cannot bind env var (ogizanagi) * bug #24851 [TwigBridge] Fix BC break due required twig environment (ro0NL) -* 3.4.0-BETA3 (2017-11-05) +* 4.0.0-BETA3 (2017-11-05) * bug #24531 [HttpFoundation] Fix forward-compat of NativeSessionStorage with PHP 7.2 (sroze) * bug #24828 [DI] Fix the "almost-circular refs" fix (nicolas-grekas) @@ -268,7 +279,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #24759 Removes \n or space when $context/$extra are empty (kirkmadera) * bug #24758 Throwing exception if redis and predis unavailable (aequasi) -* 3.4.0-BETA2 (2017-10-30) +* 4.0.0-BETA2 (2017-10-30) * bug #24728 [Bridge\Twig] fix bootstrap checkbox_row to render properly & remove spaceless (arkste) * bug #24709 [HttpKernel] Move services reset to Kernel::handle()+boot() (nicolas-grekas) @@ -284,6 +295,8 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #24713 [FrameworkBundle] fix CachePoolPrunerPass to use correct command service id (kbond) * bug #24686 Fix $_ENV/$_SERVER precedence in test framework (fabpot) * bug #24691 [HttpFoundation] Fix caching of session-enabled pages (nicolas-grekas) + * feature #24677 [HttpFoundation] Allow DateTimeImmutable in Response setters (derrabus) + * bug #24694 [Intl] Allow passing null as a locale fallback (jakzal) * bug #24606 [HttpFoundation] Fix FileBag issue with associative arrays (enumag) * bug #24673 [DI] Throw when a service name or an alias contains dynamic values (prevent an infinite loop) (dunglas) * bug #24684 [Security] remove invalid deprecation notice on AbstractGuardAuthenticator::supports() (kbond) @@ -300,6 +313,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #24642 [Routing] Fix resource miss (dunglas) * bug #24608 Adding the Form default theme files to be warmed up in Twig's cache (weaverryan) * bug #24626 streamed response should return $this (DQNEO) + * feature #24631 [Form] Fix FormEvents::* constant and value matching (yceruto, javiereguiluz) * bug #24630 [WebServerBundle] Prevent commands from being registered by convention (chalasr) * bug #24589 Username and password in basic auth are allowed to contain '.' (Richard Quadling) * bug #24566 Fixed unsetting from loosely equal keys OrderedHashMap (maryo) @@ -311,7 +325,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #24579 pdo session fix (mxp100) * bug #24536 [Security] Reject remember-me token if UserCheckerInterface::checkPostAuth() fails (kbond) -* 3.4.0-BETA1 (2017-10-18) +* 4.0.0-BETA1 (2017-10-19) * feature #24583 Adding a new debug:autowiring command (weaverryan) * feature #24523 [HttpFoundation] Make sessions secure and lazy (nicolas-grekas) @@ -320,11 +334,18 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #24503 [MonologBridge][EventDispatcher][HttpKernel] remove deprecated features (xabbuh) * 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 #22228 [HttpKernel] minor: add ability to construct with headers on http exceptions (gsdevme) + * feature #24467 [Process] drop non-existent working directory support (xabbuh) * feature #23499 [Workflow] add guard is_valid() method support (alain-flaus, lyrixx) + * feature #24364 [DependencyInjection][Filesystem][Security][SecurityBundle] remove deprecated features (xabbuh) + * feature #24441 [Bridge\Doctrine][FrameworkBundle] Remove legacy uses of ContainerAwareInterface (nicolas-grekas) * feature #24388 [Security] Look at headers for switch_user username (chalasr) + * feature #24447 [Session] remove deprecated session handlers (Tobion) + * feature #24446 [Security] Remove GuardAuthenticatorInterface (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) @@ -334,12 +355,14 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #24398 [DI] Remove AutowireExceptionPass (ogizanagi) * 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 #24380 [SecurityBundle] Remove auto picking the first provider (ogizanagi) * 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) @@ -354,16 +377,20 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #24348 [SecurityBundle] Remove remaining ACL stuff from SecurityBundle (ogizanagi) * 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 #24338 [HttpFoundation] Removed compatibility layer for PHP <5.4 sessions (afurculita) * 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 #24341 [SecurityBundle] Remove ACL related code (chalasr) * 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 #24161 [HttpKernel] Remove bundle inheritance (fabpot) * 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) @@ -386,17 +413,21 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #24242 [Form] Remove deprecated ChoiceLoaderInterface implementation in TimezoneType (ogizanagi) * 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 #24159 Remove the profiler.matcher configuration (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 #24176 [Translation] drop MessageSelector support in the Translator (xabbuh) * 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 #23728 [WebProfilerBundle] Removed the deprecated web_profiler.position option (javiereguiluz) * 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) @@ -405,14 +436,18 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #24067 [HttpKernel] Dont register env parameter resource (ro0NL) * feature #24075 Implemented PruneableInterface on TagAwareAdapter (Toflar) + * feature #23262 Add scalar typehints/return types (chalasr, xabbuh) * 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 #24052 [DI] Remove case insensitive parameter names (ro0NL) * 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 #24014 [Translator] Remove deprecated feature (maidmaid) * 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) @@ -421,6 +456,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #22382 [config] Add abbitily to deprecate a node (Nyholm, fabpot, sanpii) * feature #23947 [Translation] Adding the ability do load 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) @@ -432,6 +468,8 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #23815 [FrameworkBundle][SecurityBundle][TwigBundle][Yaml] remove deprecated code (xabbuh) + * feature #23857 [HttpKernel] Remove convention based commands registration (chalasr) * 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) @@ -445,31 +483,45 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #23790 [Yaml] remove legacy php/const and php/object tag support (xabbuh) + * feature #23754 [Lock] Remove Filesystem\LockHandler (jderusse) * 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 #23709 [VarDumper] Make dump() variadic (chalasr) * 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 #23614 [VarDumper] Remove low PHP version and hhvm compat in interval caster (maidmaid) + * feature #22317 [Console] Make SymfonyQuestionHelper::ask optional by default (ro0NL) * 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 #23561 [DI] Optimize use of private and pre-defined services (nicolas-grekas) + * feature #23569 Remove last legacy codes (nicolas-grekas) * feature #23570 [FrameworkBundle] Make RouterCacheWarmer implement ServiceSubscriberInterface (nicolas-grekas) + * feature #22783 [TwigBridge] remove deprecated features (xabbuh) * feature #23437 [TwigBridge] deprecate TwigRenderer (Tobion) + * feature #22801 [DI] Removed deprecated setting private/pre-defined services (ro0NL) * feature #23515 [VarDumper] Added setMinDepth to VarCloner (james-johnston-thumbtack) + * feature #23484 [DI] Remove remaining deprecated features (nicolas-grekas) * feature #23404 [Serializer] AbstractObjectNormalizer: Allow to disable type enforcement (ogizanagi) + * feature #23380 [Process] Remove enhanced sigchild compatibility (maidmaid) * 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 #22811 [DI] Remove deprecated case insensitive service ids (ro0NL) * 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 #22804 [Debug] Removed ContextErrorException (mbabker) + * feature #22762 [Yaml] Support tagged scalars (GuilhemN) * 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) @@ -477,7 +529,10 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #23402 [Ldap] Remove the RenameEntryInterface interface (maidmaid) * feature #23337 [Component][Serializer][Normalizer] : Deal it with Has Method for the Normalizer/Denormalizer (jordscream) + * feature #23391 [Validator] Remove support of boolean value for the checkDNS option (maidmaid) + * feature #23376 [Process] Remove enhanced Windows compatibility (maidmaid) * 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) @@ -486,7 +541,12 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #23324 [Security] remove support for defining voters that don't implement VoterInterface. (hhamon) + * feature #23294 [Yaml][Lint] Add line numbers to JSON output. (WybrenKoelmans) + * feature #22836 [Process] remove deprecated features (xabbuh) + * feature #23286 [Yaml] remove deprecated unspecific tag behavior (xabbuh) * feature #23026 [SecurityBundle] Add user impersonation info and exit action to the profiler (yceruto) + * feature #22863 [HttpFoundation] remove deprecated features (xabbuh) * 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) @@ -496,11 +556,13 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #23184 [HttpFoundation] Remove obsolete ini settings for sessions (fabpot) * 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 #23169 [FrameworkBundle] Remove KernelTestCase deprecated code (ogizanagi) * 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) @@ -510,12 +572,51 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * 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 #23056 [WebProfilerBundle] Remove WebProfilerExtension::dumpValue() (ogizanagi) * feature #23035 Deprecate passing a concrete service in optional cache warmers (romainneutron) * feature #23036 Implement ServiceSubscriberInterface in optional cache warmers (romainneutron) + * feature #23046 [Form] Missing deprecated paths removal (ogizanagi) * feature #23022 [Di] Remove closure-proxy arguments (nicolas-grekas) + * feature #22758 Remove HHVM support (fabpot) + * feature #22743 [Serializer] Remove support for deprecated signatures (dunglas) + * feature #22907 [Serializer] remove remaining deprecated features (xabbuh) + * feature #22886 [HttpKernel] remove deprecated features (xabbuh) + * feature #22906 [Console] remove remaining deprecated features (xabbuh) + * feature #22879 [Translation] remove deprecated features (xabbuh) + * feature #22880 [Routing] remove deprecated features (xabbuh) * feature #22903 [DI] Deprecate XML services without ID (ro0NL) + * feature #22770 [Yaml] remove deprecated features (xabbuh) + * feature #22785 [ProxyManagerBridge] remove deprecated features (xabbuh) + * feature #22800 [FrameworkBundle] Remove deprecated code (ogizanagi) * feature #22597 [Lock] Re-add the Lock component in 3.4 (jderusse) + * feature #22860 [Form] remove deprecated features (xabbuh) * feature #22803 [DI] Deprecate Container::initialized() for privates (ro0NL) + * feature #22773 [DependencyInjection] remove deprecated autowiring_types feature (hhamon) + * feature #22809 [DI] Remove deprecated generating a dumped container without populating the method map (ro0NL) + * feature #22764 [DI] Remove deprecated dumping an uncompiled container (ro0NL) + * feature #22820 Remove PHP < 7.1.3 code (ogizanagi) + * feature #22763 [DI] Remove deprecated isFrozen() (ro0NL) + * feature #22837 [VarDumper] remove deprecated features (xabbuh) + * feature #22777 [Console] Remove deprecated console.exception event (mbabker) + * feature #22792 [PhpUnitBridge] remove deprecated features (xabbuh) + * feature #22821 [Security] remove deprecated features (xabbuh) + * feature #22750 [DependencyInjection] remove deprecated code in YamlFileLoader class (hhamon) * feature #22828 [Finder] Deprecate FilterIterator (ogizanagi) + * feature #22791 [MonologBridge] remove deprecated features (xabbuh) + * feature #22740 [SecurityBundle][Security][Finder] Remove deprecated code paths (ogizanagi) + * feature #22823 [PropertyAccess] remove deprecated features (xabbuh) * feature #22826 [Validator] improve strict option value deprecation (xabbuh) + * feature #22799 [Console] Remove deprecated features (chalasr) + * feature #22786 [ClassLoader][HttpKernel] Remove ClassLoader component & Kernel::loadClassCache (ogizanagi, xabbuh) + * feature #22795 [Validator] remove deprecated features (xabbuh) + * feature #22784 [DoctrineBridge] remove deprecated features (xabbuh) + * feature #22749 Remove deprecated container injections and compiler passes (chalasr) + * feature #22796 [EventDispatcher] remove deprecated features (xabbuh) + * feature #22797 [Ldap] remove deprecated features (xabbuh) + * feature #22794 [ExpressionLanguage] remove deprecated features (xabbuh) + * feature #22779 [BC Break] Removed BC layers for ControllerResolver::getArguments() (iltar) + * feature #22782 [Debug][VarDumper] Remove the symfony_debug C extension (nicolas-grekas) + * feature #22771 [Workflow] Removed deprecated features (lyrixx) + * feature #22741 [Serializer] Remove deprecated DoctrineCache support (dunglas) + * feature #22733 Bump minimum version to PHP 7.1 for Symfony 4 (fabpot, dunglas, nicolas-grekas) diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md deleted file mode 100644 index dcf514f9f84fc..0000000000000 --- a/UPGRADE-3.0.md +++ /dev/null @@ -1,1915 +0,0 @@ -UPGRADE FROM 2.x to 3.0 -======================= - -# Table of Contents - -- [ClassLoader](#classloader) -- [Config](#config) -- [Console](#console) -- [DependencyInjection](#dependencyinjection) -- [DoctrineBridge](#doctrinebridge) -- [DomCrawler](#domcrawler) -- [EventDispatcher](#eventdispatcher) -- [Form](#form) -- [FrameworkBundle](#frameworkbundle) -- [HttpFoundation](#httpfoundation) -- [HttpKernel](#httpkernel) -- [Locale](#locale) -- [Monolog Bridge](#monolog-bridge) -- [Process](#process) -- [PropertyAccess](#propertyaccess) -- [Routing](#routing) -- [Security](#security) -- [SecurityBundle](#securitybundle) -- [Serializer](#serializer) -- [Swiftmailer Bridge](#swiftmailer-bridge) -- [Translator](#translator) -- [Twig Bridge](#twig-bridge) -- [TwigBundle](#twigbundle) -- [Validator](#validator) -- [WebProfiler](#webprofiler) -- [Yaml](#yaml) - -### ClassLoader - - * The `UniversalClassLoader` class has been removed in favor of - `ClassLoader`. The only difference is that some method names are different: - - | Old name | New name - | -------- | --- - | `registerNamespaces()` | `addPrefixes()` - | `registerPrefixes()` | `addPrefixes()` - | `registerNamespace()` | `addPrefix()` - | `registerPrefix()` | `addPrefix()` - | `getNamespaces()` | `getPrefixes()` - | `getNamespaceFallbacks()` | `getFallbackDirs()` - | `getPrefixFallbacks()` | `getFallbackDirs()` - - * The `DebugUniversalClassLoader` class has been removed in favor of - `DebugClassLoader`. The difference is that the constructor now takes a - loader to wrap. - -### Config - - * `\Symfony\Component\Config\Resource\ResourceInterface::isFresh()` has been removed. Also, - cache validation through this method (which was still supported in 2.8 for BC) does no longer - work because the `\Symfony\Component\Config\Resource\BCResourceInterfaceChecker` helper class - has been removed as well. - - * The `__toString()` method of the `\Symfony\Component\Config\ConfigCache` class - was removed in favor of the new `getPath()` method. - -### Console - - * The `dialog` helper has been removed in favor of the `question` helper. - - * The methods `isQuiet`, `isVerbose`, `isVeryVerbose` and `isDebug` were added - to `Symfony\Component\Console\Output\OutputInterface`. - - * `ProgressHelper` has been removed in favor of `ProgressBar`. - - Before: - - ```php - $h = new ProgressHelper(); - $h->start($output, 10); - for ($i = 1; $i < 5; $i++) { - usleep(200000); - $h->advance(); - } - $h->finish(); - ``` - - After: - - ```php - $bar = new ProgressBar($output, 10); - $bar->start(); - for ($i = 1; $i < 5; $i++) { - usleep(200000); - $bar->advance(); - } - ``` - - * `TableHelper` has been removed in favor of `Table`. - - Before: - - ```php - $table = $app->getHelperSet()->get('table'); - $table - ->setHeaders(array('ISBN', 'Title', 'Author')) - ->setRows(array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), - array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), - array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), - )) - ; - $table->render($output); - ``` - - After: - - ```php - use Symfony\Component\Console\Helper\Table; - - $table = new Table($output); - $table - ->setHeaders(array('ISBN', 'Title', 'Author')) - ->setRows(array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), - array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), - array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), - )) - ; - $table->render(); - ``` - -* Parameters of `renderException()` method of the - `Symfony\Component\Console\Application` are type hinted. - You must add the type hint to your implementations. - -### DependencyInjection - - * The method `remove` was added to `Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface`. - - * The concept of scopes was removed, the removed methods are: - - - `Symfony\Component\DependencyInjection\ContainerBuilder::getScopes()` - - `Symfony\Component\DependencyInjection\ContainerBuilder::getScopeChildren()` - - `Symfony\Component\DependencyInjection\ContainerInterface::enterScope()` - - `Symfony\Component\DependencyInjection\ContainerInterface::leaveScope()` - - `Symfony\Component\DependencyInjection\ContainerInterface::addScope()` - - `Symfony\Component\DependencyInjection\ContainerInterface::hasScope()` - - `Symfony\Component\DependencyInjection\ContainerInterface::isScopeActive()` - - `Symfony\Component\DependencyInjection\Definition::setScope()` - - `Symfony\Component\DependencyInjection\Definition::getScope()` - - `Symfony\Component\DependencyInjection\Reference::isStrict()` - - Also, the `$scope` and `$strict` parameters of `Symfony\Component\DependencyInjection\ContainerInterface::set()` - and `Symfony\Component\DependencyInjection\Reference` respectively were removed. - - * A new `shared` flag has been added to the service definition - in replacement of the `prototype` scope. - - Before: - - ```php - use Symfony\Component\DependencyInjection\ContainerBuilder; - - $container = new ContainerBuilder(); - $container - ->register('foo', 'stdClass') - ->setScope(ContainerBuilder::SCOPE_PROTOTYPE) - ; - ``` - - ```yml - services: - foo: - class: stdClass - scope: prototype - ``` - - ```xml - - - - ``` - - After: - - ```php - use Symfony\Component\DependencyInjection\ContainerBuilder; - - $container = new ContainerBuilder(); - $container - ->register('foo', 'stdClass') - ->setShared(false) - ; - ``` - - ```yml - services: - foo: - class: stdClass - shared: false - ``` - - ```xml - - - - ``` - - * `Symfony\Component\DependencyInjection\ContainerAware` was removed, use - `Symfony\Component\DependencyInjection\ContainerAwareTrait` or implement - `Symfony\Component\DependencyInjection\ContainerAwareInterface` manually - - * The methods `Definition::setFactoryClass()`, - `Definition::setFactoryMethod()`, and `Definition::setFactoryService()` have - been removed in favor of `Definition::setFactory()`. Services defined using - YAML or XML use the same syntax as configurators. - - * Synchronized services are deprecated and the following methods have been - removed: `ContainerBuilder::synchronize()`, `Definition::isSynchronized()`, - and `Definition::setSynchronized()`. - -### DomCrawler - - * The interface of the `Symfony\Component\DomCrawler\Crawler` changed. It does no longer implement `\Iterator` but `\IteratorAggregate`. If you rely on methods of the `\Iterator` interface, call the `getIterator` method of the `\IteratorAggregate` interface before. No changes are required in a `\Traversable`-aware control structure, such as `foreach`. - - Before: - - ```php - $crawler->current(); - ``` - - After: - - ```php - $crawler->getIterator()->current(); - ``` - -### DoctrineBridge - - * The `property` option of `DoctrineType` was removed in favor of the `choice_label` option. - - * The `loader` option of `DoctrineType` was removed. You now have to override the `getLoader()` - method in your custom type. - - * The `Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList` was removed in favor - of `Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader`. - - * Passing a query builder closure to `ORMQueryBuilderLoader` is not supported anymore. - You should pass resolved query builders only. - - Consequently, the arguments `$manager` and `$class` of `ORMQueryBuilderLoader` - have been removed as well. - - Note that the `query_builder` option of `DoctrineType` *does* support - closures, but the closure is now resolved in the type instead of in the - loader. - - * Using the entity provider with a Doctrine repository implementing `UserProviderInterface` is not supported anymore. - You should make the repository implement `UserLoaderInterface` instead. - -### EventDispatcher - - * The method `getListenerPriority($eventName, $listener)` has been added to the - `EventDispatcherInterface`. - * The interface `Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface` - extends `Symfony\Component\EventDispatcher\EventDispatcherInterface`. - -### Form - - * The `getBlockPrefix()` method was added to the `FormTypeInterface` in replacement of - the `getName()` method which has been removed. - - * The `configureOptions()` method was added to the `FormTypeInterface` in replacement - of the `setDefaultOptions()` method which has been removed. - - * The `getBlockPrefix()` method was added to the `ResolvedFormTypeInterface` in - replacement of the `getName()` method which has been removed. - - * The option `options` of the `CollectionType` has been removed in favor - of the `entry_options` option. - - * The `cascade_validation` option was removed. Use the `constraints` option - together with the `Valid` constraint instead. - - * Type names were removed. Instead of referencing types by name, you must - reference them by their fully-qualified class name (FQCN) instead: - - Before: - - ```php - $form = $this->createFormBuilder() - ->add('name', 'text') - ->add('age', 'integer') - ->getForm(); - ``` - - After: - - ```php - use Symfony\Component\Form\Extension\Core\Type\IntegerType; - use Symfony\Component\Form\Extension\Core\Type\TextType; - - $form = $this->createFormBuilder() - ->add('name', TextType::class) - ->add('age', IntegerType::class) - ->getForm(); - ``` - - If you want to customize the block prefix of a type in Twig, you must now - implement `FormTypeInterface::getBlockPrefix()`: - - Before: - - ```php - class UserProfileType extends AbstractType - { - public function getName() - { - return 'profile'; - } - } - ``` - - After: - - ```php - class UserProfileType extends AbstractType - { - public function getBlockPrefix() - { - return 'profile'; - } - } - ``` - - If you don't customize `getBlockPrefix()`, it defaults to the class name - without "Type" suffix in underscore notation (here: "user_profile"). - - Type extension must return the fully-qualified class name of the extended - type from `FormTypeExtensionInterface::getExtendedType()` now. - - Before: - - ```php - class MyTypeExtension extends AbstractTypeExtension - { - public function getExtendedType() - { - return 'form'; - } - } - ``` - - After: - - ```php - use Symfony\Component\Form\Extension\Core\Type\FormType; - - class MyTypeExtension extends AbstractTypeExtension - { - public function getExtendedType() - { - return FormType::class; - } - } - ``` - - * The `FormTypeInterface::getName()` method was removed. - - * Returning type instances from `FormTypeInterface::getParent()` is not - supported anymore. Return the fully-qualified class name of the parent - type class instead. - - Before: - - ```php - class MyType - { - public function getParent() - { - return new ParentType(); - } - } - ``` - - After: - - ```php - class MyType - { - public function getParent() - { - return ParentType::class; - } - } - ``` - - * The option `type` of the `CollectionType` has been removed in favor of - the `entry_type` option. The value for the `entry_type` option must be - the fully-qualified class name (FQCN). - - * Passing type instances to `Form::add()`, `FormBuilder::add()` and the - `FormFactory::create*()` methods is not supported anymore. Pass the - fully-qualified class name of the type instead. - - Before: - - ```php - $form = $this->createForm(new MyType()); - ``` - - After: - - ```php - $form = $this->createForm(MyType::class); - ``` - - * Passing custom data to forms now needs to be done - through the options resolver. - - In the controller: - - Before: - ```php - $form = $this->createForm(new MyType($variable), $entity, array( - 'action' => $this->generateUrl('action_route'), - 'method' => 'PUT', - )); - ``` - After: - ```php - $form = $this->createForm(MyType::class, $entity, array( - 'action' => $this->generateUrl('action_route'), - 'method' => 'PUT', - 'custom_value' => $variable, - )); - ``` - In the form type: - - Before: - ```php - class MyType extends AbstractType - { - private $value; - - public function __construct($variableValue) - { - $this->value = $value; - } - // ... - } - ``` - - After: - ```php - public function buildForm(FormBuilderInterface $builder, array $options) - { - $value = $options['custom_value']; - // ... - } - - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults(array( - 'custom_value' => null, - )); - } - ``` - - * The alias option of the `form.type_extension` tag was removed in favor of - the `extended_type`/`extended-type` option. - - Before: - ```xml - - - - ``` - - After: - ```xml - - - - ``` - - * The `max_length` option was removed. Use the `attr` option instead by setting it to - an `array` with a `maxlength` key. - - * The `ChoiceToBooleanArrayTransformer`, `ChoicesToBooleanArrayTransformer`, - `FixRadioInputListener`, and `FixCheckboxInputListener` classes were removed. - - * The `choice_list` option of `ChoiceType` was removed. - - * The option "precision" was renamed to "scale". - - Before: - - ```php - use Symfony\Component\Form\Extension\Core\Type\NumberType; - - $builder->add('length', NumberType::class, array( - 'precision' => 3, - )); - ``` - - After: - - ```php - use Symfony\Component\Form\Extension\Core\Type\NumberType; - - $builder->add('length', NumberType::class, array( - 'scale' => 3, - )); - ``` - - * The option "`virtual`" was renamed to "`inherit_data`". - - Before: - - ```php - use Symfony\Component\Form\Extension\Core\Type\FormType; - - $builder->add('address', FormType::class, array( - 'virtual' => true, - )); - ``` - - After: - - ```php - use Symfony\Component\Form\Extension\Core\Type\FormType; - - $builder->add('address', FormType::class, array( - 'inherit_data' => true, - )); - ``` - - * The method `AbstractType::setDefaultOptions(OptionsResolverInterface $resolver)` and - `AbstractTypeExtension::setDefaultOptions(OptionsResolverInterface $resolver)` have been - renamed. You should use `AbstractType::configureOptions(OptionsResolver $resolver)` and - `AbstractTypeExtension::configureOptions(OptionsResolver $resolver)` instead. - - * The methods `Form::bind()` and `Form::isBound()` were removed. You should - use `Form::submit()` and `Form::isSubmitted()` instead. - - Before: - - ```php - $form->bind(array(...)); - ``` - - After: - - ```php - $form->submit(array(...)); - ``` - - * Passing a `Symfony\Component\HttpFoundation\Request` instance, as was - supported by `FormInterface::bind()`, is not possible with - `FormInterface::submit()` anymore. You should use `FormInterface::handleRequest()` - instead. - - Before: - - ```php - if ('POST' === $request->getMethod()) { - $form->bind($request); - - if ($form->isValid()) { - // ... - } - } - ``` - - After: - - ```php - $form->handleRequest($request); - - if ($form->isValid()) { - // ... - } - ``` - - If you want to test whether the form was submitted separately, you can use - the method `isSubmitted()`: - - ```php - $form->handleRequest($request); - - if ($form->isSubmitted()) { - // ... - - if ($form->isValid()) { - // ... - } - } - ``` - - * The events `PRE_BIND`, `BIND` and `POST_BIND` were renamed to `PRE_SUBMIT`, `SUBMIT` - and `POST_SUBMIT`. - - Before: - - ```php - $builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) { - // ... - }); - ``` - - After: - - ```php - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - // ... - }); - ``` - - * The class `VirtualFormAwareIterator` was renamed to `InheritDataAwareIterator`. - - Before: - - ```php - use Symfony\Component\Form\Util\VirtualFormAwareIterator; - - $iterator = new VirtualFormAwareIterator($forms); - ``` - - After: - - ```php - use Symfony\Component\Form\Util\InheritDataAwareIterator; - - $iterator = new InheritDataAwareIterator($forms); - ``` - - * The `TypeTestCase` class was moved from the `Symfony\Component\Form\Tests\Extension\Core\Type` namespace to the `Symfony\Component\Form\Test` namespace. - - Before: - - ```php - use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase - - class MyTypeTest extends TypeTestCase - { - // ... - } - ``` - - After: - - ```php - use Symfony\Component\Form\Test\TypeTestCase; - - class MyTypeTest extends TypeTestCase - { - // ... - } - ``` - - * The option "options" of the CollectionType has been renamed to "entry_options". - - * The option "type" of the CollectionType has been renamed to "entry_type". - As a value for the option you must provide the fully-qualified class name (FQCN) - now as well. - - * The `FormIntegrationTestCase` and `FormPerformanceTestCase` classes were moved form the `Symfony\Component\Form\Tests` namespace to the `Symfony\Component\Form\Test` namespace. - - * The constants `ROUND_HALFEVEN`, `ROUND_HALFUP` and `ROUND_HALFDOWN` in class - `NumberToLocalizedStringTransformer` were renamed to `ROUND_HALF_EVEN`, - `ROUND_HALF_UP` and `ROUND_HALF_DOWN`. - - * The `Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface` was - removed in favor of `Symfony\Component\Form\ChoiceList\ChoiceListInterface`. - - * `Symfony\Component\Form\Extension\Core\View\ChoiceView` was removed in favor of - `Symfony\Component\Form\ChoiceList\View\ChoiceView`. - - * The interface `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface` - and all of its implementations were removed. Use the new interface - `Symfony\Component\Security\Csrf\CsrfTokenManagerInterface` instead. - - * The options "`csrf_provider`" and "`intention`" were renamed to "`csrf_token_generator`" - and "`csrf_token_id`". - - * The method `Form::getErrorsAsString()` was removed. Use `Form::getErrors()` - instead with the argument `$deep` set to true and `$flatten` set to false - and cast the returned iterator to a string (if not done implicitly by PHP). - - Before: - - ```php - echo $form->getErrorsAsString(); - ``` - - After: - - ```php - echo $form->getErrors(true, false); - ``` - - * The `Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList` class has been removed in - favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. - - * The `Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList` class has been removed in - favor of `Symfony\Component\Form\ChoiceList\LazyChoiceList`. - - * The `Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList` class has been removed in - favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. - - * The `Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList` class has been removed in - favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. - - * The `TimezoneType::getTimezones()` method was removed. You should not use - this method. - - * The `Symfony\Component\Form\ChoiceList\ArrayKeyChoiceList` class has been removed in - favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. - -### FrameworkBundle - - * The `config:debug`, `container:debug`, `router:debug`, `translation:debug` - and `yaml:lint` commands have been deprecated since Symfony 2.7 and will - be removed in Symfony 3.0. Use the `debug:config`, `debug:container`, - `debug:router`, `debug:translation` and `lint:yaml` commands instead. - - * The base `Controller`class is now abstract. - - * The visibility of all methods of the base `Controller` class has been changed from - `public` to `protected`. - - * The `getRequest` method of the base `Controller` class has been deprecated - since Symfony 2.4 and must be therefore removed in 3.0. The only reliable - way to get the `Request` object is to inject it in the action method. - - Before: - - ```php - namespace Acme\FooBundle\Controller; - - class DemoController - { - public function showAction() - { - $request = $this->getRequest(); - // ... - } - } - ``` - - After: - - ```php - namespace Acme\FooBundle\Controller; - - use Symfony\Component\HttpFoundation\Request; - - class DemoController - { - public function showAction(Request $request) - { - // ... - } - } - ``` - - * In Symfony 2.7 a small BC break was introduced with the new choices_as_values - option. In order to have the choice values populated to the html value attribute - you had to define the choice_value option. This is now not any more needed. - - Before: - - ```php - $form->add('status', 'choice', array( - 'choices' => array( - 'Enabled' => Status::ENABLED, - 'Disabled' => Status::DISABLED, - 'Ignored' => Status::IGNORED, - ), - // choices_as_values defaults to true in Symfony 3.0 - // and setting it to anything else is deprecated as of 3.0 - 'choices_as_values' => true, - // important if you rely on your option value attribute (e.g. for JavaScript) - // this will keep the same functionality as before - 'choice_value' => function ($choice) { - return $choice; - }, - )); - ``` - - After: - - ```php - $form->add('status', ChoiceType::class, array( - 'choices' => array( - 'Enabled' => Status::ENABLED, - 'Disabled' => Status::DISABLED, - 'Ignored' => Status::IGNORED, - ) - )); - ``` - - * The `request` service was removed. You must inject the `request_stack` - service instead. - - * The `enctype` method of the `form` helper was removed. You should use the - new method `start` instead. - - Before: - - ```php -
enctype($form) ?>> - ... -
- ``` - - After: - - ```php - start($form) ?> - ... - end($form) ?> - ``` - - The method and action of the form default to "POST" and the current - document. If you want to change these values, you can set them explicitly in - the controller. - - Alternative 1: - - ```php - $form = $this->createForm('my_form', $formData, array( - 'method' => 'PUT', - 'action' => $this->generateUrl('target_route'), - )); - ``` - - Alternative 2: - - ```php - $form = $this->createFormBuilder($formData) - // ... - ->setMethod('PUT') - ->setAction($this->generateUrl('target_route')) - ->getForm(); - ``` - - It is also possible to override the method and the action in the template: - - ```php - start($form, array('method' => 'GET', 'action' => 'http://example.com')) ?> - ... - end($form) ?> - ``` - - * The `RouterApacheDumperCommand` was removed. - - * The `createEsi` method of `Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache` was removed. Use `createSurrogate` instead. - - * The `templating.helper.router` service was moved to `templating_php.xml`. You - have to ensure that the PHP templating engine is enabled to be able to use it: - - ```yaml - framework: - templating: - engines: ['php'] - ``` - - * The `form.csrf_provider` service is removed as it implements an adapter for - the new token manager to the deprecated - `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface` - interface. - The `security.csrf.token_manager` should be used instead. - - * The `validator.mapping.cache.apc` service has been removed in favor of the `validator.mapping.cache.doctrine.apc` one. - - * The ability to pass `apc` as the `framework.validation.cache` configuration key value has been removed. - Use `validator.mapping.cache.doctrine.apc` instead: - - Before: - - ```yaml - framework: - validation: - cache: apc - ``` - - After: - - ```yaml - framework: - validation: - cache: validator.mapping.cache.doctrine.apc - ``` - -### HttpKernel - - * The `Symfony\Component\HttpKernel\Log\LoggerInterface` has been removed in - favor of `Psr\Log\LoggerInterface`. The only difference is that some method - names are different: - - | Old name | New name - | -------- | --- - | `emerg()` | `emergency()` - | `crit()` | `critical()` - | `err()` | `error()` - | `warn()` | `warning()` - - The previous method renames also happened to the following classes: - - * `Symfony\Bridge\Monolog\Logger` - * `Symfony\Component\HttpKernel\Log\NullLogger` - - * The `Symfony\Component\HttpKernel\Kernel::init()` method has been removed. - - * The following classes have been renamed as they have been moved to the - Debug component: - - | Old name | New name - | -------- | --- - | `Symfony\Component\HttpKernel\Debug\ErrorHandler` | `Symfony\Component\Debug\ErrorHandler` - | `Symfony\Component\HttpKernel\Debug\ExceptionHandler` | `Symfony\Component\Debug\ExceptionHandler` - | `Symfony\Component\HttpKernel\Exception\FatalErrorException` | `Symfony\Component\Debug\Exception\FatalErrorException` - | `Symfony\Component\HttpKernel\Exception\FlattenException` | `Symfony\Component\Debug\Exception\FlattenException` - - * The `Symfony\Component\HttpKernel\EventListener\ExceptionListener` now - passes the Request format as the `_format` argument instead of `format`. - - * The `Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass` has been renamed to - `Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass` and moved to the EventDispatcher component. - -### Locale - - * The Locale component was removed and replaced by the Intl component. - Instead of the methods in `Symfony\Component\Locale\Locale`, you should use - these equivalent methods in `Symfony\Component\Intl\Intl` now: - - | Old way | New way - | ------- | --- - | `Locale::getDisplayCountries()` | `Intl::getRegionBundle()->getCountryNames()` - | `Locale::getCountries()` | `array_keys(Intl::getRegionBundle()->getCountryNames())` - | `Locale::getDisplayLanguages()` | `Intl::getLanguageBundle()->getLanguageNames()` - | `Locale::getLanguages()` | `array_keys(Intl::getLanguageBundle()->getLanguageNames())` - | `Locale::getDisplayLocales()` | `Intl::getLocaleBundle()->getLocaleNames()` - | `Locale::getLocales()` | `array_keys(Intl::getLocaleBundle()->getLocaleNames())` - -### PropertyAccess - - * Renamed `PropertyAccess::getPropertyAccessor` to `createPropertyAccessor`. - - Before: - - ```php - use Symfony\Component\PropertyAccess\PropertyAccess; - - $accessor = PropertyAccess::getPropertyAccessor(); - ``` - - After: - - ```php - use Symfony\Component\PropertyAccess\PropertyAccess; - - $accessor = PropertyAccess::createPropertyAccessor(); - ``` - -### Routing - - * Some route settings have been renamed: - - * The `pattern` setting has been removed in favor of `path` - * The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings - - Before: - - ```yaml - article_edit: - pattern: /article/{id} - requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' } - ``` - - ```xml - - POST|PUT - https - \d+ - - ``` - - ```php - $route = new Route(); - $route->setPattern('/article/{id}'); - $route->setRequirement('_method', 'POST|PUT'); - $route->setRequirement('_scheme', 'https'); - ``` - - After: - - ```yaml - article_edit: - path: /article/{id} - methods: [POST, PUT] - schemes: https - requirements: { 'id': '\d+' } - ``` - - ```xml - - \d+ - - ``` - - ```php - $route = new Route(); - $route->setPath('/article/{id}'); - $route->setMethods(array('POST', 'PUT')); - $route->setSchemes('https'); - ``` - - * The `ApacheMatcherDumper` and `ApacheUrlMatcher` were removed since - the performance gains were minimal and it's hard to replicate the behaviour - of PHP implementation. - - * The `getMatcherDumperInstance()` and `getGeneratorDumperInstance()` methods in the - `Symfony\Component\Routing\Router` have been changed from `public` to `protected`. - - * Use the constants defined in the UrlGeneratorInterface for the $referenceType argument of the UrlGeneratorInterface::generate method. - - Before: - - ```php - // url generated in controller - $this->generateUrl('blog_show', array('slug' => 'my-blog-post'), true); - - // url generated in @router service - $router->generate('blog_show', array('slug' => 'my-blog-post'), true); - ``` - - After: - - ```php - use Symfony\Component\Routing\Generator\UrlGeneratorInterface; - - // url generated in controller - $this->generateUrl('blog_show', array('slug' => 'my-blog-post'), UrlGeneratorInterface::ABSOLUTE_URL); - - // url generated in @router service - $router->generate('blog_show', array('slug' => 'my-blog-post'), UrlGeneratorInterface::ABSOLUTE_URL); - ``` - -### Security - - * The `vote()` method from the `VoterInterface` was changed to now accept arbitrary - types and not only objects. You can rely on the new abstract `Voter` class introduced - in 2.8 to ease integrating your own voters. - - * The `AbstractVoter` class was removed in favor of the new `Voter` class. - - * The `Resources/` directory was moved to `Core/Resources/` - - * The `key` settings of `anonymous`, `remember_me` and `http_digest` are - renamed to `secret`. - - Before: - - ```yaml - security: - # ... - firewalls: - default: - # ... - anonymous: { key: "%secret%" } - remember_me: - key: "%secret%" - http_digest: - key: "%secret%" - ``` - - ```xml - - - - - - - - - - - - - ``` - - ```php - // ... - $container->loadFromExtension('security', array( - // ... - 'firewalls' => array( - // ... - 'anonymous' => array('key' => '%secret%'), - 'remember_me' => array('key' => '%secret%'), - 'http_digest' => array('key' => '%secret%'), - ), - )); - ``` - - After: - - ```yaml - security: - # ... - firewalls: - default: - # ... - anonymous: { secret: "%secret%" } - remember_me: - secret: "%secret%" - http_digest: - secret: "%secret%" - ``` - - ```xml - - - - - - - - - - - - - ``` - - ```php - // ... - $container->loadFromExtension('security', array( - // ... - 'firewalls' => array( - // ... - 'anonymous' => array('secret' => '%secret%'), - 'remember_me' => array('secret' => '%secret%'), - 'http_digest' => array('secret' => '%secret%'), - ), - )); - ``` - - * The `AbstractVoter` class was removed. Instead, extend the new `Voter` class, - introduced in 2.8, and move your voting logic to the to the `supports($attribute, $subject)` - and `voteOnAttribute($attribute, $object, TokenInterface $token)` methods. - - * The `vote()` method from the `VoterInterface` was changed to now accept arbitrary - types, and not only objects. - - * The `supportsClass` and `supportsAttribute` methods were - removed from the `VoterInterface` interface. - - Before: - - ```php - class MyVoter extends AbstractVoter - { - protected function getSupportedAttributes() - { - return array('CREATE', 'EDIT'); - } - - protected function getSupportedClasses() - { - return array('AppBundle\Entity\Post'); - } - - // ... - } - ``` - - After: - - ```php - use Symfony\Component\Security\Core\Authorization\Voter\Voter; - - class MyVoter extends Voter - { - protected function supports($attribute, $object) - { - return $object instanceof Post && in_array($attribute, array('CREATE', 'EDIT')); - } - - protected function voteOnAttribute($attribute, $object, TokenInterface $token) - { - // Return true or false - } - } - ``` - - * The `AbstractVoter::isGranted()` method has been replaced by `Voter::voteOnAttribute()`. - - Before: - - ```php - class MyVoter extends AbstractVoter - { - protected function isGranted($attribute, $object, $user = null) - { - return 'EDIT' === $attribute && $user === $object->getAuthor(); - } - - // ... - } - ``` - - After: - - ```php - class MyVoter extends Voter - { - protected function voteOnAttribute($attribute, $object, TokenInterface $token) - { - return 'EDIT' === $attribute && $token->getUser() === $object->getAuthor(); - } - - // ... - } - ``` - - * The `supportsAttribute()` and `supportsClass()` methods of the `AuthenticatedVoter`, `ExpressionVoter`, - and `RoleVoter` classes have been removed. - - * The `intention` option was renamed to `csrf_token_id` for all the authentication listeners. - - * The `csrf_provider` option was renamed to `csrf_token_generator` for all the authentication listeners. - -### SecurityBundle - - * The `intention` firewall listener setting was renamed to `csrf_token_id`. - - * The `csrf_provider` firewall listener setting was renamed to `csrf_token_generator`. - -### Serializer - - * The `setCamelizedAttributes()` method of the - `Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer` and - `Symfony\Component\Serializer\Normalizer\PropertyNormalizer` classes - was removed. - - * The `Symfony\Component\Serializer\Exception\Exception` interface was removed - in favor of the new `Symfony\Component\Serializer\Exception\ExceptionInterface`. - -### Translator - - * The `Translator::setFallbackLocale()` method has been removed in favor of - `Translator::setFallbackLocales()`. - - * The visibility of the `locale` property has been changed from protected to private. Rely on `getLocale` and `setLocale` - instead. - - Before: - - ```php - class CustomTranslator extends Translator - { - public function fooMethod() - { - // get locale - $locale = $this->locale; - - // update locale - $this->locale = $locale; - } - } - ``` - - After: - - ```php - class CustomTranslator extends Translator - { - public function fooMethod() - { - // get locale - $locale = $this->getLocale(); - - // update locale - $this->setLocale($locale); - } - } - ``` - - * The method `FileDumper::format()` was removed. You should use - `FileDumper::formatCatalogue()` instead. - - Before: - - ```php - class CustomDumper extends FileDumper - { - protected function format(MessageCatalogue $messages, $domain) - { - ... - } - } - ``` - - After: - - ```php - class CustomDumper extends FileDumper - { - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) - { - ... - } - } - ``` - - * The `getMessages()` method of the `Symfony\Component\Translation\Translator` - class was removed. You should use the `getCatalogue()` method of the - `Symfony\Component\Translation\TranslatorBagInterface`. - -### Twig Bridge - - * The `twig:lint` command has been deprecated since Symfony 2.7 and will be - removed in Symfony 3.0. Use the `lint:twig` command instead. - - * The `render` tag is deprecated in favor of the `render` function. - - * The `form_enctype` helper was removed. You should use the new `form_start` - function instead. - - Before: - - ```php -
- ... -
- ``` - - After: - - ```jinja - {{ form_start(form) }} - ... - {{ form_end(form) }} - ``` - - The method and action of the form default to "POST" and the current - document. If you want to change these values, you can set them explicitly in - the controller. - - Alternative 1: - - ```php - $form = $this->createForm('my_form', $formData, array( - 'method' => 'PUT', - 'action' => $this->generateUrl('target_route'), - )); - ``` - - Alternative 2: - - ```php - $form = $this->createFormBuilder($formData) - // ... - ->setMethod('PUT') - ->setAction($this->generateUrl('target_route')) - ->getForm(); - ``` - - It is also possible to override the method and the action in the template: - - ```jinja - {{ form_start(form, {'method': 'GET', 'action': 'http://example.com'}) }} - ... - {{ form_end(form) }} - ``` - -### TwigBundle - - * The `Symfony\Bundle\TwigBundle\TwigDefaultEscapingStrategy` was removed - in favor of `Twig_FileExtensionEscapingStrategy`. - - * The `twig:debug` command has been deprecated since Symfony 2.7 and will be - removed in Symfony 3.0. Use the `debug:twig` command instead. - -### Validator - - * The PHP7-incompatible constraints (`Null`, `True`, `False`) and their related - validators (`NullValidator`, `TrueValidator`, `FalseValidator`) have been - removed in favor of their `Is`-prefixed equivalent. - - * The class `Symfony\Component\Validator\Mapping\Cache\ApcCache` has been removed in favor - of `Symfony\Component\Validator\Mapping\Cache\DoctrineCache`. - - Before: - - ```php - use Symfony\Component\Validator\Mapping\Cache\ApcCache; - - $cache = new ApcCache('symfony.validator'); - ``` - - After: - - ```php - use Symfony\Component\Validator\Mapping\Cache\DoctrineCache; - use Doctrine\Common\Cache\ApcCache; - - $apcCache = new ApcCache(); - $apcCache->setNamespace('symfony.validator'); - - $cache = new DoctrineCache($apcCache); - ``` - - * The constraints `Optional` and `Required` were moved to the - `Symfony\Component\Validator\Constraints\` namespace. You should adapt - the path wherever you used them. - - Before: - - ```php - use Symfony\Component\Validator\Constraints as Assert; - - /** - * @Assert\Collection({ - * "foo" = @Assert\Collection\Required(), - * "bar" = @Assert\Collection\Optional(), - * }) - */ - private $property; - ``` - - After: - - ```php - use Symfony\Component\Validator\Constraints as Assert; - - /** - * @Assert\Collection({ - * "foo" = @Assert\Required(), - * "bar" = @Assert\Optional(), - * }) - */ - private $property; - ``` - - * The option "`methods`" of the `Callback` constraint was removed. You should - use the option "`callback`" instead. If you have multiple callbacks, add - multiple callback constraints instead. - - Before (YAML): - - ```yaml - constraints: - - Callback: [firstCallback, secondCallback] - ``` - - After (YAML): - - ```yaml - constraints: - - Callback: firstCallback - - Callback: secondCallback - ``` - - When using annotations, you can now put the `Callback` constraint directly on - the method that should be executed. - - Before (Annotations): - - ```php - use Symfony\Component\Validator\Constraints as Assert; - use Symfony\Component\Validator\ExecutionContextInterface; - - /** - * @Assert\Callback({"callback"}) - */ - class MyClass - { - public function callback(ExecutionContextInterface $context) - { - // ... - } - } - ``` - - After (Annotations): - - ```php - use Symfony\Component\Validator\Constraints as Assert; - use Symfony\Component\Validator\ExecutionContextInterface; - - class MyClass - { - /** - * @Assert\Callback - */ - public function callback(ExecutionContextInterface $context) - { - // ... - } - } - ``` - - * The interface `ValidatorInterface` was replaced by the more powerful - interface `Validator\ValidatorInterface`. The signature of the `validate()` - method is slightly different in that interface and accepts a value, zero - or more constraints and validation group. It replaces both - `validate()` and `validateValue()` in the previous interface. - - Before: - - ```php - $validator->validate($object, 'Strict'); - - $validator->validateValue($value, new NotNull()); - ``` - - After: - - ```php - $validator->validate($object, null, 'Strict'); - - $validator->validate($value, new NotNull()); - ``` - - Apart from this change, the new methods `startContext()` and `inContext()` - were added. The first of them allows to run multiple validations in the - same context and aggregate their violations: - - ```php - $violations = $validator->startContext() - ->atPath('firstName')->validate($firstName, new NotNull()) - ->atPath('age')->validate($age, new Type('integer')) - ->getViolations(); - ``` - - The second allows to run validation in an existing context. This is - especially useful when calling the validator from within constraint - validators: - - ```php - $validator->inContext($context)->validate($object); - ``` - - Instead of a `Validator`, the validator builder now returns a - `Validator\RecursiveValidator` instead. - - * The interface `ValidationVisitorInterface` and its implementation - `ValidationVisitor` were removed. The implementation of the visitor pattern - was flawed. Fixing that implementation would have drastically slowed down - the validator execution, so the visitor was removed completely instead. - - Along with the visitor, the method `accept()` was removed from - `MetadataInterface`. - - * The interface `MetadataInterface` was moved to the `Mapping` namespace. - - Before: - - ```php - use Symfony\Component\Validator\MetadataInterface; - ``` - - After: - - ```php - use Symfony\Component\Validator\Mapping\MetadataInterface; - ``` - - The methods `getCascadingStrategy()` and `getTraversalStrategy()` were - added to the interface. The first method should return a bit mask of the - constants in class `CascadingStrategy`. The second should return a bit - mask of the constants in `TraversalStrategy`. - - Example: - - ```php - use Symfony\Component\Validator\Mapping\TraversalStrategy; - - public function getTraversalStrategy() - { - return TraversalStrategy::TRAVERSE; - } - ``` - - * The interface `PropertyMetadataInterface` was moved to the `Mapping` - namespace. - - Before: - - ```php - use Symfony\Component\Validator\PropertyMetadataInterface; - ``` - - After: - - ```php - use Symfony\Component\Validator\Mapping\PropertyMetadataInterface; - ``` - - * The interface `PropertyMetadataContainerInterface` was moved to the `Mapping` - namespace and renamed to `ClassMetadataInterface`. - - Before: - - ```php - use Symfony\Component\Validator\PropertyMetadataContainerInterface; - ``` - - After: - - ```php - use Symfony\Component\Validator\Mapping\ClassMetadataInterface; - ``` - - The interface now contains four additional methods: - - * `getConstrainedProperties()` - * `hasGroupSequence()` - * `getGroupSequence()` - * `isGroupSequenceProvider()` - - See the inline documentation of these methods for more information. - - * The interface `ClassBasedInterface` was removed. You should use - `Mapping\ClassMetadataInterface` instead: - - Before: - - ```php - use Symfony\Component\Validator\ClassBasedInterface; - - class MyClassMetadata implements ClassBasedInterface - { - // ... - } - ``` - - After: - - ```php - use Symfony\Component\Validator\Mapping\ClassMetadataInterface; - - class MyClassMetadata implements ClassMetadataInterface - { - // ... - } - ``` - - * The class `ElementMetadata` was renamed to `GenericMetadata`. - - Before: - - ```php - use Symfony\Component\Validator\Mapping\ElementMetadata; - - class MyMetadata extends ElementMetadata - { - } - ``` - - After: - - ```php - use Symfony\Component\Validator\Mapping\GenericMetadata; - - class MyMetadata extends GenericMetadata - { - } - ``` - - * The interface `ExecutionContextInterface` and its implementation - `ExecutionContext` were moved to the `Context` namespace. - - Before: - - ```php - use Symfony\Component\Validator\ExecutionContextInterface; - ``` - - After: - - ```php - use Symfony\Component\Validator\Context\ExecutionContextInterface; - ``` - - The interface now contains the following additional methods: - - * `getValidator()` - * `getObject()` - * `setNode()` - * `setGroup()` - * `markGroupAsValidated()` - * `isGroupValidated()` - * `markConstraintAsValidated()` - * `isConstraintValidated()` - - See the inline documentation of these methods for more information. - - The method `addViolationAt()` was removed. You should use `buildViolation()` - instead. - - Before: - - ```php - $context->addViolationAt('property', 'The value {{ value }} is invalid.', array( - '{{ value }}' => $invalidValue, - )); - ``` - - After: - - ```php - $context->buildViolation('The value {{ value }} is invalid.') - ->atPath('property') - ->setParameter('{{ value }}', $invalidValue) - ->addViolation(); - ``` - - The methods `validate()` and `validateValue()` were removed. You should use - `getValidator()` together with `inContext()` instead. - - Before: - - ```php - $context->validate($object); - ``` - - After: - - ```php - $context->getValidator() - ->inContext($context) - ->validate($object); - ``` - - The parameters `$invalidValue`, `$plural` and `$code` were removed from - `addViolation()`. You should use `buildViolation()` instead. See above for - an example. - - The method `getMetadataFactory()` was removed. You can use `getValidator()` - instead and use the methods `getMetadataFor()` or `hasMetadataFor()` on the - validator instance. - - Before: - - ```php - $metadata = $context->getMetadataFactory()->getMetadataFor($myClass); - ``` - - After: - - ```php - $metadata = $context->getValidator()->getMetadataFor($myClass); - ``` - - * The interface `GlobalExecutionContextInterface` was removed. Most of the - information provided by that interface can be queried from - `Context\ExecutionContextInterface` instead. - - * The interface `MetadataFactoryInterface` was moved to the `Mapping\Factory` - namespace along with its implementations `BlackholeMetadataFactory` and - `ClassMetadataFactory`. These classes were furthermore renamed to - `BlackHoleMetadataFactory` and `LazyLoadingMetadataFactory`. - - Before: - - ```php - use Symfony\Component\Validator\Mapping\ClassMetadataFactory; - - $factory = new ClassMetadataFactory($loader); - ``` - - After: - - ```php - use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; - - $factory = new LazyLoadingMetadataFactory($loader); - ``` - - * The option `$deep` was removed from the constraint `Valid`. When traversing - arrays, nested arrays are always traversed (same behavior as before). When - traversing nested objects, their traversal strategy is used. - - * The method `ValidatorBuilder::setPropertyAccessor()` was removed. The - validator now functions without a property accessor. - - * The methods `getMessageParameters()` and `getMessagePluralization()` in - `ConstraintViolation` were renamed to `getParameters()` and `getPlural()`. - - Before: - - ```php - $parameters = $violation->getMessageParameters(); - $plural = $violation->getMessagePluralization(); - ``` - - After: - - ```php - $parameters = $violation->getParameters(); - $plural = $violation->getPlural(); - ``` - - * The class `Symfony\Component\Validator\DefaultTranslator` was removed. You - should use `Symfony\Component\Translation\IdentityTranslator` instead. - - Before: - - ```php - $translator = new \Symfony\Component\Validator\DefaultTranslator(); - ``` - - After: - - ```php - $translator = new \Symfony\Component\Translation\IdentityTranslator(); - $translator->setLocale('en'); - ``` - -### Yaml - - * Using a colon in an unquoted mapping value leads to a `ParseException`. - * Starting an unquoted string with `@`, `` ` ``, `|`, or `>` leads to a `ParseException`. - * When surrounding strings with double-quotes, you must now escape `\` characters. Not - escaping those characters (when surrounded by double-quotes) leads to a `ParseException`. - - Before: - - ```yml - class: "Foo\Var" - ``` - - After: - - ```yml - class: "Foo\\Var" - ``` - - - * The ability to pass file names to `Yaml::parse()` has been removed. - - Before: - - ```php - Yaml::parse($fileName); - ``` - - After: - - ```php - Yaml::parse(file_get_contents($fileName)); - ``` - -### WebProfiler - - * The `profiler:import` and `profiler:export` commands have been removed. - - * All the profiler storages different than `FileProfilerStorage` have been - removed. The removed classes are: - - - `Symfony\Component\HttpKernel\Profiler\BaseMemcacheProfilerStorage` - - `Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage` - - `Symfony\Component\HttpKernel\Profiler\MemcacheProfilerStorage` - - `Symfony\Component\HttpKernel\Profiler\MongoDbProfilerStorage` - - `Symfony\Component\HttpKernel\Profiler\MysqlProfilerStorage` - - `Symfony\Component\HttpKernel\Profiler\PdoProfilerStorage` - - `Symfony\Component\HttpKernel\Profiler\RedisProfilerStorage` - - `Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage` - -### Process - - * `Process::setStdin()` and `Process::getStdin()` have been removed. Use - `Process::setInput()` and `Process::getInput()` that works the same way. - * `Process::setInput()` and `ProcessBuilder::setInput()` do not accept non-scalar types. - -### Monolog Bridge - - * `Symfony\Bridge\Monolog\Logger::emerg()` was removed. Use `emergency()` which is PSR-3 compatible. - * `Symfony\Bridge\Monolog\Logger::crit()` was removed. Use `critical()` which is PSR-3 compatible. - * `Symfony\Bridge\Monolog\Logger::err()` was removed. Use `error()` which is PSR-3 compatible. - * `Symfony\Bridge\Monolog\Logger::warn()` was removed. Use `warning()` which is PSR-3 compatible. - -### Swiftmailer Bridge - - * `Symfony\Bridge\Swiftmailer\DataCollector\MessageDataCollector` was removed. Use the `Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector` class instead. - -### HttpFoundation - - * The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY" - - * `Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface` no longer implements the `IteratorAggregate` interface. Use the `all()` method instead of iterating over the flash bag. - - * Removed the feature that allowed finding deep items in `ParameterBag::get()`. - This may affect you when getting parameters from the `Request` class: - - Before: - - ```php - $request->query->get('foo[bar]', null, true); - ``` - - After: - - ```php - $request->query->get('foo')['bar']; - ``` -### Monolog Bridge - - * `Symfony\Bridge\Monolog\Logger::emerg()` was removed. Use `emergency()` which is PSR-3 compatible. - * `Symfony\Bridge\Monolog\Logger::crit()` was removed. Use `critical()` which is PSR-3 compatible. - * `Symfony\Bridge\Monolog\Logger::err()` was removed. Use `error()` which is PSR-3 compatible. - * `Symfony\Bridge\Monolog\Logger::warn()` was removed. Use `warning()` which is PSR-3 compatible. diff --git a/UPGRADE-3.1.md b/UPGRADE-3.1.md deleted file mode 100644 index 58ce838cfb714..0000000000000 --- a/UPGRADE-3.1.md +++ /dev/null @@ -1,215 +0,0 @@ -UPGRADE FROM 3.0 to 3.1 -======================= - -DependencyInjection -------------------- - - * Using unsupported configuration keys in YAML configuration files has been - deprecated and will raise an exception in Symfony 4.0. - - * Using unsupported options to configure service aliases has been deprecated - and will raise an exception in Symfony 4.0. - -Form ----- - - * The `choices_as_values` option of the `ChoiceType` has been deprecated and - will be removed in Symfony 4.0. - - * Support for data objects that implements both `Traversable` and `ArrayAccess` - in `ResizeFormListener::preSubmit` method has been deprecated and will be - removed in Symfony 4.0. - * `TextType` now implements `DataTransformerInterface` and will always return - an empty string when `empty_data` option is explicitly assigned to it. - - * Using callable strings as choice options in ChoiceType has been deprecated - in favor of `PropertyPath` in Symfony 4.0 use a "\Closure" instead. - - Before: - - ```php - 'choice_value' => new PropertyPath('range'), - 'choice_label' => 'strtoupper', - ``` - - After: - - ```php - 'choice_value' => 'range', - 'choice_label' => function ($choice) { - return strtoupper($choice); - }, - ``` - - * Caching of the loaded `ChoiceListInterface` in the `LazyChoiceList` has been deprecated, - it must be cached in the `ChoiceLoaderInterface` implementation instead. - -FrameworkBundle ---------------- - - * As it was never an officially supported feature, the support for absolute - template paths has been deprecated and will be removed in Symfony 4.0. - - * The abstract `Controller` class now has a `json()` helper method that creates - a `JsonResponse`. If you have existing controllers extending `Controller` - that contain a method with this name, you need to rename that method to avoid - conflicts. - - * The following form types registered as services have been deprecated and - will be removed in Symfony 4.0; use their fully-qualified class name instead: - - - `"form.type.birthday"` - - `"form.type.checkbox"` - - `"form.type.collection"` - - `"form.type.country"` - - `"form.type.currency"` - - `"form.type.date"` - - `"form.type.datetime"` - - `"form.type.email"` - - `"form.type.file"` - - `"form.type.hidden"` - - `"form.type.integer"` - - `"form.type.language"` - - `"form.type.locale"` - - `"form.type.money"` - - `"form.type.number"` - - `"form.type.password"` - - `"form.type.percent"` - - `"form.type.radio"` - - `"form.type.range"` - - `"form.type.repeated"` - - `"form.type.search"` - - `"form.type.textarea"` - - `"form.type.text"` - - `"form.type.time"` - - `"form.type.timezone"` - - `"form.type.url"` - - `"form.type.button"` - - `"form.type.submit"` - - `"form.type.reset"` - - * The `framework.serializer.cache` option and the service - `serializer.mapping.cache.apc` have been deprecated. APCu should now be - automatically used when available. - -HttpKernel ----------- - - * Passing non-scalar values as URI attributes to the ESI and SSI renderers has been - deprecated and will be removed in Symfony 4.0. The inline fragment - renderer should be used with non-scalar attributes. - - * The `ControllerResolver::getArguments()` method has been deprecated and will - be removed in 4.0. If you have your own `ControllerResolverInterface` - implementation, you should inject either an `ArgumentResolverInterface` - instance or the new `ArgumentResolver` in the `HttpKernel`. - -Serializer ----------- - - * Passing a Doctrine `Cache` instance to the `ClassMetadataFactory` has been - deprecated and will not be supported in Symfony 4.0. You should use the - `CacheClassMetadataFactory` class instead. - - * The `AbstractObjectNormalizer::isAttributeToNormalize()` method has been removed - because it was initially added by mistake, has never been used and is not tested - nor documented. - -Translation ------------ - - * Deprecated the backup feature of the file dumper classes. It will be removed - in Symfony 4.0. - -Yaml ----- - - * Usage of `%` at the beginning of an unquoted string has been deprecated and - will lead to a `ParseException` in Symfony 4.0. - - * The `Dumper::setIndentation()` method is deprecated and will be removed in - Symfony 4.0. Pass the indentation level to the constructor instead. - - * Deprecated support for passing `true`/`false` as the second argument to the - `parse()` method to trigger exceptions when an invalid type was passed. - - Before: - - ```php - Yaml::parse('{ "foo": "bar", "fiz": "cat" }', true); - ``` - - After: - - ```php - Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); - ``` - - * Deprecated support for passing `true`/`false` as the third argument to the - `parse()` method to toggle object support. - - Before: - - ```php - Yaml::parse('{ "foo": "bar", "fiz": "cat" }', false, true); - ``` - - After: - - ```php - Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_OBJECT); - ``` - - * Deprecated support for passing `true`/`false` as the fourth argument to the - `parse()` method to parse objects as maps. - - Before: - - ```php - Yaml::parse('{ "foo": "bar", "fiz": "cat" }', false, false, true); - ``` - - After: - - ```php - Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_OBJECT_FOR_MAP); - ``` - - * Deprecated support for passing `true`/`false` as the fourth argument to the - `dump()` method to trigger exceptions when an invalid type was passed. - - Before: - - ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, true); - ``` - - After: - - ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); - ``` - - * Deprecated support for passing `true`/`false` as the fifth argument to the - `dump()` method to toggle object support. - - Before: - - ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); - ``` - - After: - - ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_OBJECT); - ``` - - * The `!!php/object` tag to indicate dumped PHP objects has been deprecated - and will be removed in Symfony 4.0. Use the `!php/object` tag instead. - -Validator ---------- - - * The `DateTimeValidator::PATTERN` constant has been deprecated and will be - removed in Symfony 4.0. diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md deleted file mode 100644 index 8a14f2d4b6281..0000000000000 --- a/UPGRADE-3.2.md +++ /dev/null @@ -1,276 +0,0 @@ -UPGRADE FROM 3.1 to 3.2 -======================= - -BrowserKit ----------- - - * Client HTTP user agent has been changed to 'Symfony BrowserKit' (was 'Symfony2 BrowserKit' before). - -Console -------- - - * Setting unknown style options is deprecated and will throw an exception in - Symfony 4.0. - - * The `QuestionHelper::setInputStream()` method is deprecated and will be - removed in Symfony 4.0. Use `StreamableInputInterface::setStream()` or - `CommandTester::setInputs()` instead. - - Before: - - ```php - $input = new ArrayInput(); - - $questionHelper->setInputStream($stream); - $questionHelper->ask($input, $output, $question); - ``` - - After: - - ```php - $input = new ArrayInput(); - $input->setStream($stream); - - $questionHelper->ask($input, $output, $question); - ``` - - Before: - - ```php - $commandTester = new CommandTester($command); - - $stream = fopen('php://memory', 'r+', false); - fputs($stream, "AppBundle\nYes"); - rewind($stream); - - $command->getHelper('question')->setInputStream($stream); - - $commandTester->execute(); - ``` - - After: - - ```php - $commandTester = new CommandTester($command); - - $commandTester->setInputs(array('AppBundle', 'Yes')); - - $commandTester->execute(); - ``` - -DependencyInjection -------------------- - - * Calling `get()` on a `ContainerBuilder` instance before compiling the - container is deprecated and will throw an exception in Symfony 4.0. - - * Setting or unsetting a private service with the `Container::set()` method is - deprecated. Only public services can be set or unset in Symfony 4.0. - - * Checking the existence of a private service with the `Container::has()` - method is deprecated and will return `false` in Symfony 4.0. - - * Requesting a private service with the `Container::get()` method is deprecated - and will no longer be supported in Symfony 4.0. - -ExpressionLanguage -------------------- - - * Passing a `ParserCacheInterface` instance to the `ExpressionLanguage` has been - deprecated and will not be supported in Symfony 4.0. You should use the - `CacheItemPoolInterface` interface instead. - -Form ----- - - * Calling `isValid()` on a `Form` instance before submitting it - is deprecated and will throw an exception in Symfony 4.0. - - Before: - - ```php - if ($form->isValid()) { - // ... - } - ``` - - After: - - ```php - if ($form->isSubmitted() && $form->isValid()) { - // ... - } - ``` - -FrameworkBundle ---------------- - - * The `doctrine/annotations` dependency has been removed; require it via `composer - require doctrine/annotations` if you are using annotations in your project - * The `symfony/security-core` and `symfony/security-csrf` dependencies have - been removed; require them via `composer require symfony/security-core - symfony/security-csrf` if you depend on them and don't already depend on - `symfony/symfony` - * The `symfony/templating` dependency has been removed; require it via `composer - require symfony/templating` if you depend on it and don't already depend on - `symfony/symfony` - * The `symfony/translation` dependency has been removed; require it via `composer - require symfony/translation` if you depend on it and don't already depend on - `symfony/symfony` - * The `symfony/asset` dependency has been removed; require it via `composer - require symfony/asset` if you depend on it and don't already depend on - `symfony/symfony` - * The `Resources/public/images/*` files have been removed. - * The `Resources/public/css/*.css` files have been removed (they are now inlined - in TwigBundle). - * The service `serializer.mapping.cache.doctrine.apc` is deprecated. APCu should now - be automatically used when available. - -HttpFoundation ---------------- - - * Extending the following methods of `Response` - is deprecated (these methods will be `final` in 4.0): - - - `setDate`/`getDate` - - `setExpires`/`getExpires` - - `setLastModified`/`getLastModified` - - `setProtocolVersion`/`getProtocolVersion` - - `setStatusCode`/`getStatusCode` - - `setCharset`/`getCharset` - - `setPrivate`/`setPublic` - - `getAge` - - `getMaxAge`/`setMaxAge` - - `setSharedMaxAge` - - `getTtl`/`setTtl` - - `setClientTtl` - - `getEtag`/`setEtag` - - `hasVary`/`getVary`/`setVary` - - `isInvalid`/`isSuccessful`/`isRedirection`/`isClientError`/`isServerError` - - `isOk`/`isForbidden`/`isNotFound`/`isRedirect`/`isEmpty` - - * Checking only for cacheable HTTP methods with `Request::isMethodSafe()` is deprecated - since version 3.2 and will throw an exception in 4.0. Disable checking only for - cacheable methods by calling the method with `false` as first argument or use - `Request::isMethodCacheable()` instead. - -HttpKernel ----------- - - * `DataCollector::varToString()` is deprecated and will be removed in Symfony - 4.0. Use the `cloneVar()` method instead. - - * Surrogate name in a `Surrogate-Capability` HTTP request header has been changed to 'symfony'. - - Before: - ``` - Surrogate-Capability: symfony2="ESI/1.0" - ``` - - After: - ``` - Surrogate-Capability: symfony="ESI/1.0" - ``` - -Router ------- - - * `UrlGenerator` now generates URLs in compliance with [`RFC 3986`](https://www.ietf.org/rfc/rfc3986.txt), - which means spaces will be percent encoded (%20) inside query strings. - -Serializer ----------- - - * Method `AbstractNormalizer::instantiateObject()` will have a 6th - `$format = null` argument in Symfony 4.0. Not defining it when overriding - the method is deprecated. - -TwigBridge ----------- - - * Injecting the Form `TwigRenderer` into the `FormExtension` is deprecated and has no more effect. - Upgrade Twig to `^1.30`, inject the `Twig_Environment` into the `TwigRendererEngine` and load - the `TwigRenderer` using the `Twig_FactoryRuntimeLoader` instead. - - Before: - - ```php - use Symfony\Bridge\Twig\Extension\FormExtension; - use Symfony\Bridge\Twig\Form\TwigRenderer; - use Symfony\Bridge\Twig\Form\TwigRendererEngine; - - // ... - $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig')); - $rendererEngine->setEnvironment($twig); - $twig->addExtension(new FormExtension(new TwigRenderer($rendererEngine, $csrfTokenManager))); - ``` - - After: - - ```php - $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig'), $twig); - $twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader(array( - TwigRenderer::class => function () use ($rendererEngine, $csrfTokenManager) { - return new TwigRenderer($rendererEngine, $csrfTokenManager); - }, - ))); - $twig->addExtension(new FormExtension()); - ``` - - * Deprecated the `TwigRendererEngineInterface` interface, it will be removed in 4.0. - -Validator ---------- - - * `Tests\Constraints\AbstractConstraintValidatorTest` has been deprecated in - favor of `Test\ConstraintValidatorTestCase`. - - Before: - - ```php - // ... - use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; - - class MyCustomValidatorTest extends AbstractConstraintValidatorTest - { - // ... - } - ``` - - After: - - ```php - // ... - use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; - - class MyCustomValidatorTest extends ConstraintValidatorTestCase - { - // ... - } - ``` - - * Setting the strict option of the `Choice` Constraint to `false` has been - deprecated and the option will be changed to `true` as of 4.0. - - ```php - // ... - use Symfony\Component\Validator\Constraints as Assert; - - class MyEntity - { - /** - * @Assert\Choice(choices={"MR", "MRS"}, strict=true) - */ - private $salutation; - } - ``` - -Yaml ----- - - * Support for silently ignoring duplicate mapping keys in YAML has been - deprecated and will lead to a `ParseException` in Symfony 4.0. - - * Mappings with a colon (`:`) that is not followed by a whitespace are deprecated - and will lead to a `ParseException` in Symfony 4.0 (e.g. `foo:bar` must be - `foo: bar`). diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md deleted file mode 100644 index 9e1110ea2a5a0..0000000000000 --- a/UPGRADE-3.3.md +++ /dev/null @@ -1,392 +0,0 @@ -UPGRADE FROM 3.2 to 3.3 -======================= - -BrowserKit ----------- - - * The request method is dropped from POST to GET when the response - status code is 301. - -ClassLoader ------------ - - * The component is deprecated and will be removed in 4.0. Use Composer instead. - -Console -------- - -* `Input::getOption()` no longer returns the default value for options - with value optional explicitly passed empty. - - For: - - ```php - protected function configure() - { - $this - // ... - ->setName('command') - ->addOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default') - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - var_dump($input->getOption('foo')); - } - ``` - - Before: - - ``` - $ php console.php command - "default" - - $ php console.php command --foo - "default" - - $ php console.php command --foo "" - "default" - - $ php console.php command --foo= - "default" - ``` - - After: - - ``` - $ php console.php command - "default" - - $ php console.php command --foo - NULL - - $ php console.php command --foo "" - "" - - $ php console.php command --foo= - "" - ``` - - * The `console.exception` event and the related `ConsoleExceptionEvent` class - have been deprecated in favor of the `console.error` event and the `ConsoleErrorEvent` - class. The deprecated event and class will be removed in 4.0. - - * The `SymfonyQuestionHelper::ask` default validation has been deprecated and will be removed in 4.0. Apply validation using `Question::setValidator` instead. - -Debug ------ - - * The `ContextErrorException` class is deprecated. `\ErrorException` will be used instead in 4.0. - -DependencyInjection -------------------- - - * Autowiring services based on the types they implement is deprecated and won't be supported in version 4.0. Rename (or alias) your services to their FQCN id to make them autowirable. - - * The `NullDumper` class has been made final - - * [BC BREAK] `_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names. - - * [BC BREAK] non-numeric keys in methods and constructors arguments have never been supported and are now forbidden. Please remove them if you happen to have one. - - * Service names that start with an underscore are deprecated in Yaml files and will be reserved in 4.0. Please rename any services with such names. - - * Autowiring-types have been deprecated, use aliases instead. - - Before: - - ```xml - - Doctrine\Common\Annotations\Reader - - ``` - - After: - - ```xml - - - ``` - - * The `Reference` and `Alias` classes do not make service identifiers lowercase anymore. - - * Case insensitivity of service identifiers is deprecated and will be removed in 4.0. - - * Using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and - will not be supported anymore in 4.0. - - * Extending the containers generated by `PhpDumper` is deprecated and won't be - supported in 4.0. - - * The `DefinitionDecorator` class is deprecated and will be removed in 4.0, use - the `ChildDefinition` class instead. - - * The ``strict`` attribute in service arguments has been deprecated and will be removed in 4.0. - The attribute is ignored since 3.0, so you can simply remove it. - -EventDispatcher ---------------- - - * The `ContainerAwareEventDispatcher` class has been deprecated. - Use `EventDispatcher` with closure factories instead. - -Finder ------- - - * The `ExceptionInterface` has been deprecated and will be removed in 4.0. - -Form ----- - - * Using the "choices" option in ``CountryType``, ``CurrencyType``, ``LanguageType``, - ``LocaleType``, and ``TimezoneType`` without overriding the ``choice_loader`` - option has been deprecated and will be ignored in 4.0. - - Before: - ```php - $builder->add('custom_locales', LocaleType::class, array( - 'choices' => $availableLocales, - )); - ``` - - After: - ```php - $builder->add('custom_locales', LocaleType::class, array( - 'choices' => $availableLocales, - 'choice_loader' => null, - )); - // or - $builder->add('custom_locales', LocaleType::class, array( - 'choice_loader' => new CallbackChoiceLoader(function () { - return $this->getAvailableLocales(); - }), - )); - ``` - -FrameworkBundle ---------------- - - * [BC BREAK] 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. - - * Not defining the `type` option of the `framework.workflows.*` configuration entries is deprecated. - The default value will be `state_machine` in Symfony 4.0. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDumpPass` has been deprecated. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass` has been deprecated. - Use `Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass` instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass` class has been - deprecated and will be removed in 4.0. - Use the `Symfony\Component\Serializer\DependencyInjection\SerializerPass` class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass` class has been - deprecated and will be removed in 4.0. Use the `Symfony\Component\Form\DependencyInjection\FormPass` - class instead. - - * The `Symfony\Bundle\FrameworkBundle\EventListener\SessionListener` class has been - deprecated and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\EventListener\SessionListener` - class instead. - - * The `Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener` class has been - deprecated and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\EventListener\TestSessionListener` - class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass` class has been - deprecated and will be removed in 4.0. Use `Symfony\Component\Config\DependencyInjection\ConfigCachePass` - class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass` class has been - deprecated and will be removed in 4.0. Use the `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` - class instead. - - * Class parameters related to routing have been deprecated and will be removed in 4.0. - * router.options.generator_class - * router.options.generator_base_class - * router.options.generator_dumper_class - * router.options.matcher_class - * router.options.matcher_base_class - * router.options.matcher_dumper_class - * router.options.matcher.cache_class - * router.options.generator.cache_class - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass` class - has been deprecated and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` - class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` class instead. - - * The `server:run`, `server:start`, `server:stop` and - `server:status` console commands have been moved to a dedicated bundle. - Require `symfony/web-server-bundle` in your composer.json and register - `Symfony\Bundle\WebServerBundle\WebServerBundle` in your AppKernel to use them. - - * The `Symfony\Bundle\FrameworkBundle\Translation\Translator` constructor now takes the - default locale as 3rd argument. Not passing it will trigger an error in 4.0. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass` - class has been deprecated and will be removed in 4.0. - Use the `Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass` class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass` - class has been deprecated and will be removed in 4.0. - Use the `Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass` class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflowsPass` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` class instead. - - * The `Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory` - class has been deprecated and will be removed in 4.0. - Use `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. - -HttpFoundation --------------- - - * [BC BREAK] The `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument. - See http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info. - - * The `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods are deprecated, - use the RFC7239 `Forwarded` header, or the `X-Forwarded-*` headers instead. - -HttpKernel ------------ - - * The `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` methods have been deprecated and will be removed in 4.0. - - * The `Psr6CacheClearer::addPool()` method has been deprecated. Pass an array - of pools indexed by name to the constructor instead. - - * The `LazyLoadingFragmentHandler::addRendererService()` method has been - deprecated and will be removed in 4.0. - - * The `X-Status-Code` header method of setting a custom status code in the - response when handling exceptions has been removed. There is now a new - `GetResponseForExceptionEvent::allowCustomResponseCode()` method instead, - which will tell the Kernel to use the response code set on the event's - response object. - - * The `Kernel::getEnvParameters()` method has been deprecated and will be - removed in 4.0. - - * The `SYMFONY__` environment variables have been deprecated and they will be - no longer processed automatically by Symfony in 4.0. Use the `%env()%` syntax - to get the value of any environment variable from configuration files instead. - -Process -------- - - * The `ProcessUtils::escapeArgument()` method has been deprecated, use a command line array or give env vars to the `Process::start/run()` method instead. - - * Not inheriting environment variables is deprecated. - - * Configuring `proc_open()` options is deprecated. - - * Configuring Windows and sigchild compatibility is deprecated - they will be always enabled in 4.0. - - * Extending `Process::run()`, `Process::mustRun()` and `Process::restart()` is - deprecated and won't be supported in 4.0. - -ProxyManager ------------- - - * [BC BREAK] The `ProxyDumper` class has been made final - -Security --------- - - * The `RoleInterface` has been deprecated. Extend the `Symfony\Component\Security\Core\Role\Role` - class in your custom role implementations instead. - - * The `LogoutUrlGenerator::registerListener()` method will expect a 6th `string $context = null` argument in 4.0. - Define the argument when overriding this method. - - * The `AccessDecisionManager::setVoters()` method has been deprecated. Pass - the voters to the constructor instead. - -SecurityBundle --------------- - - * The `FirewallContext::getContext()` method has been deprecated and will be removed in 4.0. - Use the `getListeners()` and/or `getExceptionListener()` method instead. - - * The `FirewallMap::$map` and `$container` properties have been deprecated and will be removed in 4.0. - - * The `UserPasswordEncoderCommand` command expects to be registered as a service and its - constructor arguments fully provided. - Registering by convention the command or commands extending it is deprecated and will - not be allowed anymore in 4.0. - - * `UserPasswordEncoderCommand::getContainer()` is deprecated, and this class won't - extend `ContainerAwareCommand` nor implement `ContainerAwareInterface` anymore in 4.0. - - * [BC BREAK] Keys of the `users` node for `in_memory` user provider are no longer normalized. - -Serializer ----------- - - * Extending `ChainDecoder`, `ChainEncoder`, `ArrayDenormalizer` is deprecated - and won't be supported in 4.0. - -TwigBridge ----------- - - * The `TwigRendererEngine::setEnvironment()` method has been deprecated and will be removed - in 4.0. Pass the Twig Environment as second argument of the constructor instead. - -TwigBundle ----------- - -* The `ContainerAwareRuntimeLoader` class has been deprecated and will be removed in 4.0. - Use the Twig `Twig_ContainerRuntimeLoader` class instead. - -Workflow --------- - - * Deprecated class name support in `WorkflowRegistry::add()` as second parameter. - Wrap the class name in an instance of ClassInstanceSupportStrategy instead. - -Yaml ----- - - * Starting an unquoted string with a question mark followed by a space is - deprecated and will throw a `ParseException` in Symfony 4.0. - - * Deprecated support for implicitly parsing non-string mapping keys as strings. - Mapping keys that are no strings will lead to a `ParseException` in Symfony - 4.0. Use quotes to opt-in for keys to be parsed as strings. - - Before: - - ```php - $yaml = <<setDefault('choice_loader', ...); // override the option instead - } - } - ``` - -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. - - * The `doctrine/cache` dependency has been removed; require it via `composer - require doctrine/cache` if you are using Doctrine cache in your project. - - * The `validator.mapping.cache.doctrine.apc` service has been deprecated. - - * The `symfony/stopwatch` dependency has been removed, require it via `composer - require symfony/stopwatch` in your `dev` environment. - - * Using the `KERNEL_DIR` environment variable or the automatic guessing based - on the `phpunit.xml` / `phpunit.xml.dist` file location is deprecated since 3.4. - Set the `KERNEL_CLASS` environment variable to the fully-qualified class name - of your Kernel instead. Not setting the `KERNEL_CLASS` environment variable - will throw an exception on 4.0 unless you override the `KernelTestCase::createKernel()` - or `KernelTestCase::getKernelClass()` method. - - * The `KernelTestCase::getPhpUnitXmlDir()` and `KernelTestCase::getPhpUnitCliConfigArgument()` - methods are deprecated since 3.4 and will be removed in 4.0. - - * The `--no-prefix` option of the `translation:update` command is deprecated and - will be removed in 4.0. Use the `--prefix` option with an empty string as value - instead (e.g. `--prefix=""`) - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheClearerPass` - class has been deprecated and will be removed in 4.0. Use tagged iterator arguments instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass` - class has been deprecated and will be removed in 4.0. Use tagged iterator arguments instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Translation\DependencyInjection\TranslationDumperPass` class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass` class instead. - - * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Translation\DependencyInjection\TranslatorPass` class instead. - - * The `Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Translation\Reader\TranslationReader` class instead. - - * The `translation.loader` service has been deprecated and will be removed in 4.0. - Use the `translation.reader` service instead.. - - * `AssetsInstallCommand::__construct()` now takes an instance of - `Symfony\Component\Filesystem\Filesystem` as first argument. - Not passing it is deprecated and will throw a `TypeError` in 4.0. - - * `CacheClearCommand::__construct()` now takes an instance of - `Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface` as - first argument. Not passing it is deprecated and will throw - a `TypeError` in 4.0. - - * `CachePoolClearCommand::__construct()` now takes an instance of - `Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer` as - first argument. Not passing it is deprecated and will throw - a `TypeError` in 4.0. - - * `EventDispatcherDebugCommand::__construct()` now takes an instance of - `Symfony\Component\EventDispatcher\EventDispatcherInterface` as - first argument. Not passing it is deprecated and will throw - a `TypeError` in 4.0. - - * `RouterDebugCommand::__construct()` now takes an instance of - `Symfony\Component\Routing\RouterInterface` as - first argument. Not passing it is deprecated and will throw - a `TypeError` in 4.0. - - * `RouterMatchCommand::__construct()` now takes an instance of - `Symfony\Component\Routing\RouterInterface` as - first argument. Not passing it is deprecated and will throw - a `TypeError` in 4.0. - - * `TranslationDebugCommand::__construct()` now takes an instance of - `Symfony\Component\Translation\TranslatorInterface` as - first argument. Not passing it is deprecated and will throw - a `TypeError` in 4.0. - - * `TranslationUpdateCommand::__construct()` now takes an instance of - `Symfony\Component\Translation\TranslatorInterface` as - first argument. Not passing it is deprecated and will throw - a `TypeError` in 4.0. - - * `AssetsInstallCommand`, `CacheClearCommand`, `CachePoolClearCommand`, - `EventDispatcherDebugCommand`, `RouterDebugCommand`, `RouterMatchCommand`, - `TranslationDebugCommand`, `TranslationUpdateCommand`, `XliffLintCommand` - and `YamlLintCommand` classes have been marked as final - - * The `Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Translation\Extractor\PhpExtractor` class instead. - - * The `Symfony\Bundle\FrameworkBundle\Translation\PhpStringTokenParser` - class has been deprecated and will be removed in 4.0. Use the - `Symfony\Component\Translation\Extractor\PhpStringTokenParser` class instead. - -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\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. - - * 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. - - * The `Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler` class has been deprecated and - will be removed in 4.0. Use `Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler` instead. - -HttpKernel ----------- - - * Bundle inheritance has been deprecated. - - * Relying on convention-based commands discovery has been deprecated and - won't be supported in 4.0. Use PSR-4 based service discovery instead. - - Before: - - ```yml - # app/config/services.yml - services: - # ... - - # implicit registration of all commands in the `Command` folder - ``` - - After: - - ```yml - # app/config/services.yml - services: - # ... - - # explicit commands registration - AppBundle\Command\: - resource: '../../src/AppBundle/Command/*' - tags: ['console.command'] - ``` - - * The `getCacheDir()` method of your kernel should not be called while building the container. - Use the `%kernel.cache_dir%` parameter instead. Not doing so may break the `cache:clear` command. - - * The `Symfony\Component\HttpKernel\Config\EnvParametersResource` class has been deprecated and will be removed in 4.0. - - * Implementing `DataCollectorInterface` without a `reset()` method has been deprecated and will be unsupported in 4.0. - - * Implementing `DebugLoggerInterface` without a `clear()` method has been deprecated and will be unsupported in 4.0. - - * The `ChainCacheClearer::add()` method has been deprecated and will be removed in 4.0, - inject the list of clearers as a constructor argument instead. - - * The `CacheWarmerAggregate::add()` and `setWarmers()` methods have been deprecated and will be removed in 4.0, - inject the list of clearers as a constructor argument instead. - - * The `CacheWarmerAggregate` and `ChainCacheClearer` classes have been made final. - -Process -------- - - * The `Symfony\Component\Process\ProcessBuilder` class has been deprecated, - use the `Symfony\Component\Process\Process` class directly instead. - - * Calling `Process::start()` without setting a valid working directory (via `setWorkingDirectory()` or constructor) beforehand is deprecated and will throw an exception in 4.0. - -Profiler --------- - - * The `profiler.matcher` option has been deprecated. - -Security --------- - - * Deprecated the HTTP digest authentication: `NonceExpiredException`, - `DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` will be - removed in 4.0. Use another authentication system like `http_basic` instead. - - * The `GuardAuthenticatorInterface` has been deprecated and will be removed in 4.0. - Use `AuthenticatorInterface` instead. - -SecurityBundle --------------- - - * Using voters that do not implement the `VoterInterface`is now deprecated in - the `AccessDecisionManager` and this functionality will be removed in 4.0. - - * `FirewallContext::getListeners()` now returns `\Traversable|array` - - * `InitAclCommand::__construct()` now takes an instance of - `Doctrine\DBAL\Connection` as first argument. Not passing it is - deprecated and will throw a `TypeError` in 4.0. - - * The `acl:set` command has been deprecated along with the `SetAclCommand` class, - both will be removed in 4.0. Install symfony/acl-bundle instead - - * The `init:acl` command has been deprecated along with the `InitAclCommand` class, - both will be removed in 4.0. Install symfony/acl-bundle and use `acl:init` instead - - * Added `logout_on_user_change` to the firewall options. This config item will - trigger a logout when the user has changed. Should be set to true to avoid - deprecations in the configuration. - - * Deprecated the HTTP digest authentication: `HttpDigestFactory` will be removed in 4.0. - Use another authentication system like `http_basic` instead. - - * Deprecated setting the `switch_user.stateless` option to false when the firewall is `stateless`. - Setting it to false will have no effect in 4.0. - - * Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider. - Using the first configured provider is deprecated since 3.4 and will throw an exception on 4.0. - Explicitly configure the provider to use on your firewalls. - -Translation ------------ - - * `Symfony\Component\Translation\Writer\TranslationWriter::writeTranslations` has been deprecated - and will be removed in 4.0, use `Symfony\Component\Translation\Writer\TranslationWriter::write` - instead. - - * Passing a `Symfony\Component\Translation\MessageSelector` to `Translator` has been - deprecated. You should pass a message formatter instead - - Before: - - ```php - use Symfony\Component\Translation\Translator; - use Symfony\Component\Translation\MessageSelector; - - $translator = new Translator('fr_FR', new MessageSelector()); - ``` - - After: - - ```php - use Symfony\Component\Translation\Translator; - use Symfony\Component\Translation\Formatter\MessageFormatter; - - $translator = new Translator('fr_FR', new MessageFormatter()); - ``` - -TwigBridge ----------- - - * deprecated the `Symfony\Bridge\Twig\Form\TwigRenderer` class, use the `FormRenderer` - class from the Form component instead - - * deprecated `Symfony\Bridge\Twig\Command\DebugCommand::set/getTwigEnvironment` and the ability - to pass a command name as first argument - - * deprecated `Symfony\Bridge\Twig\Command\LintCommand::set/getTwigEnvironment` and the ability - to pass a command name as first argument - -TwigBundle ----------- - - * deprecated the `Symfony\Bundle\TwigBundle\Command\DebugCommand` class, use the `DebugCommand` - class from the Twig bridge instead - - * deprecated relying on the `ContainerAwareInterface` implementation for - `Symfony\Bundle\TwigBundle\Command\LintCommand` - -Validator ---------- - - * Not setting the `strict` option of the `Choice` constraint to `true` is - deprecated and will throw an exception in Symfony 4.0. - -Yaml ----- - - * the `Dumper`, `Parser`, and `Yaml` classes are marked as final - - * using the `!php/object:` tag is deprecated and won't be supported in 4.0. Use - the `!php/object` tag (without the colon) instead. - - * using the `!php/const:` tag is deprecated and won't be supported in 4.0. Use - the `!php/const` tag (without the colon) instead. - - Before: - - ```yml - !php/const:PHP_INT_MAX - ``` - - After: - - ```yml - !php/const PHP_INT_MAX - ``` - - * Support for the `!str` tag is deprecated, use the `!!str` tag instead. - - * Using the non-specific tag `!` is deprecated and will have a different - behavior in 4.0. Use a plain integer or `!!float` instead. - - * Using the `Yaml::PARSE_KEYS_AS_STRINGS` flag is deprecated as it will be - removed in 4.0. - - Before: - - ```php - $yaml = <<"strict" instead. + * Calling `EmailValidator::__construct()` method with a boolean parameter is deprecated and will be removed in 5.0, use `EmailValidator("strict")` instead. + * Deprecated the `checkDNS` and `dnsMessage` options of the `Url` constraint. They will be removed in 5.0. + +Workflow +-------- + + * Deprecated the `add` method in favor of the `addWorkflow` method in `Workflow\Registry`. + * Deprecated `SupportStrategyInterface` in favor of `WorkflowSupportStrategyInterface`. + * Deprecated the class `ClassInstanceSupportStrategy` in favor of the class `InstanceOfSupportStrategy`. diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md new file mode 100644 index 0000000000000..ddf6a965179d8 --- /dev/null +++ b/UPGRADE-5.0.md @@ -0,0 +1,61 @@ +UPGRADE FROM 4.x to 5.0 +======================= + +Config +------ + + * Added the `getChildNodeDefinitions()` method to `ParentNodeDefinitionInterface`. + +EventDispatcher +--------------- + + * The `TraceableEventDispatcherInterface` has been removed. + +FrameworkBundle +--------------- + + * Using a `RouterInterface` that does not implement the `WarmableInterface` is not supported anymore. + * The `RequestDataCollector` class has been removed. Use the `Symfony\Component\HttpKernel\DataCollector\RequestDataCollector` class instead. + +HttpFoundation +-------------- + + * The `$size` argument of the `UploadedFile` constructor has been removed. + * The `getClientSize()` method of the `UploadedFile` class has been removed. + +Security +-------- + + * The `ContextListener::setLogoutOnUserChange()` method has been removed. + * The `Symfony\Component\Security\Core\User\AdvancedUserInterface` has been removed. + +SecurityBundle +-------------- + + * The `logout_on_user_change` firewall option has been removed. + * The `SecurityUserValueResolver` class has been removed. + +Translation +----------- + + * The `FileDumper::setBackup()` method has been removed. + * The `TranslationWriter::disableBackup()` method has been removed. + +TwigBundle +---------- + + * The default value (`false`) of the `twig.strict_variables` configuration option has been changed to `%kernel.debug%`. + +Validator +-------- + + * The `Email::__construct()` 'strict' property has been removed. Use 'mode'=>"strict" instead. + * Calling `EmailValidator::__construct()` method with a boolean parameter has been removed, use `EmailValidator("strict")` instead. + * Removed the `checkDNS` and `dnsMessage` options from the `Url` constraint. + +Workflow +-------- + + * `add` method has been removed use `addWorkflow` method in `Workflow\Registry` instead. + * `SupportStrategyInterface` has been removed, use `WorkflowSupportStrategyInterface` instead. + * `ClassInstanceSupportStrategy` has been removed, use `InstanceOfSupportStrategy` instead. diff --git a/appveyor.yml b/appveyor.yml index cc1de26c5f8ef..a6575052a974b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,20 +11,15 @@ init: - SET COMPOSER_NO_INTERACTION=1 - SET SYMFONY_DEPRECATIONS_HELPER=strict - SET ANSICON=121x90 (121x90) - - SET SYMFONY_PHPUNIT_VERSION=4.8 - REG ADD "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v DelayedExpansion /t REG_DWORD /d 1 /f install: - mkdir c:\php && cd c:\php - - appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/cacert.pem - - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php-5.5.9-nts-Win32-VC11-x86.zip - - 7z x php-5.5.9-nts-Win32-VC11-x86.zip -y >nul - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php-7.1.3-Win32-VC14-x86.zip + - 7z x php-7.1.3-Win32-VC14-x86.zip -y >nul - cd ext - - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_apcu-4.0.10-5.5-nts-vc11-x86.zip - - 7z x php_apcu-4.0.10-5.5-nts-vc11-x86.zip -y >nul - - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_memcache-3.0.8-5.5-nts-vc11-x86.zip - - 7z x php_memcache-3.0.8-5.5-nts-vc11-x86.zip -y >nul + - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_apcu-5.1.8-7.1-ts-vc14-x86.zip + - 7z x php_apcu-5.1.8-7.1-ts-vc14-x86.zip -y >nul - cd .. - copy /Y php.ini-development php.ini-min - echo memory_limit=-1 >> php.ini-min @@ -38,13 +33,11 @@ install: - echo extension=php_openssl.dll >> php.ini-max - echo extension=php_apcu.dll >> php.ini-max - echo apc.enable_cli=1 >> php.ini-max - - echo extension=php_memcache.dll >> php.ini-max - echo extension=php_intl.dll >> php.ini-max - echo extension=php_mbstring.dll >> php.ini-max - echo extension=php_fileinfo.dll >> php.ini-max - echo extension=php_pdo_sqlite.dll >> php.ini-max - echo extension=php_curl.dll >> php.ini-max - - echo curl.cainfo=c:\php\cacert.pem >> php.ini-max - copy /Y php.ini-max php.ini - cd c:\projects\symfony - IF NOT EXIST composer.phar (appveyor DownloadFile https://getcomposer.org/download/1.3.0/composer.phar) @@ -57,12 +50,8 @@ install: test_script: - SET X=0 - - cd c:\php && 7z x php-7.1.3-Win32-VC14-x86.zip -y >nul && copy /Y php.ini-min php.ini - - cd c:\projects\symfony - - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! - - cd c:\php && 7z x php-5.5.9-nts-Win32-VC11-x86.zip -y >nul && copy /Y php.ini-min php.ini - - cd c:\projects\symfony - SET SYMFONY_PHPUNIT_SKIPPED_TESTS=phpunit.skipped + - copy /Y c:\php\php.ini-min c:\php\php.ini - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! - copy /Y c:\php\php.ini-max c:\php\php.ini - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! diff --git a/composer.json b/composer.json index e1c3fa6b450c5..3ed1464ae1aff 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "ext-xml": "*", "doctrine/common": "~2.4", "fig/link-util": "^1.0", @@ -26,17 +26,14 @@ "psr/link": "^1.0", "psr/log": "~1.0", "psr/simple-cache": "^1.0", - "symfony/polyfill-apcu": "~1.1", "symfony/polyfill-intl-icu": "~1.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php56": "~1.0", - "symfony/polyfill-php70": "~1.6" + "symfony/polyfill-php72": "~1.5" }, "replace": { "symfony/asset": "self.version", "symfony/browser-kit": "self.version", "symfony/cache": "self.version", - "symfony/class-loader": "self.version", "symfony/config": "self.version", "symfony/console": "self.version", "symfony/css-selector": "self.version", @@ -104,7 +101,7 @@ "conflict": { "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", "phpdocumentor/type-resolver": "<0.2.1", - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + "phpunit/phpunit": "<5.4.3" }, "provide": { "psr/cache-implementation": "1.0", @@ -134,7 +131,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index 378db70d14926..a3865db662c2b 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -1,6 +1,21 @@ CHANGELOG ========= +4.1.0 +----- + + * added support for datetime immutable types in form type guesser + +4.0.0 +----- + + * the first constructor argument of the `DoctrineChoiceLoader` class must be + an `ObjectManager` implementation + * removed the `MergeDoctrineCollectionListener::onBind()` method + * trying to reset a non-lazy manager service using the `ManagerRegistry::resetService()` + method throws an exception + * removed the `DoctrineParserCache` class + 3.4.0 ----- diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php index bca53ef4092b7..dae5d43f364a0 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -165,19 +165,15 @@ private function sanitizeQuery($connectionName, $query) * The return value is an array with the sanitized value and a boolean * indicating if the original value was kept (allowing to use the sanitized * value to explain the query). - * - * @param mixed $var - * - * @return array */ - private function sanitizeParam($var) + private function sanitizeParam($var): array { if (is_object($var)) { $className = get_class($var); return method_exists($var, '__toString') ? - array(sprintf('Object(%s): "%s"', $className, $var->__toString()), false) : - array(sprintf('Object(%s)', $className), false); + array(sprintf('/* Object(%s): */"%s"', $className, $var->__toString()), false) : + array(sprintf('/* Object(%s) */', $className), false); } if (is_array($var)) { @@ -193,7 +189,7 @@ private function sanitizeParam($var) } if (is_resource($var)) { - return array(sprintf('Resource(%s)', get_resource_type($var)), false); + return array(sprintf('/* Resource(%s) */', get_resource_type($var)), false); } return array($var, true); diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index 5c04c5c97aecc..2850ab47cd3ea 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -318,20 +318,6 @@ protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheD $container->setAlias($cacheDriverServiceId, new Alias($cacheDriver['id'], false)); return $cacheDriverServiceId; - case 'memcache': - $memcacheClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.memcache.class').'%'; - $memcacheInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.memcache_instance.class').'%'; - $memcacheHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.memcache_host').'%'; - $memcachePort = !empty($cacheDriver['port']) || (isset($cacheDriver['port']) && 0 === $cacheDriver['port']) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.memcache_port').'%'; - $cacheDef = new Definition($memcacheClass); - $memcacheInstance = new Definition($memcacheInstanceClass); - $memcacheInstance->setPrivate(true); - $memcacheInstance->addMethodCall('connect', array( - $memcacheHost, $memcachePort, - )); - $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcache_instance', $objectManagerName)), $memcacheInstance); - $cacheDef->addMethodCall('setMemcache', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcache_instance', $objectManagerName))))); - break; case 'memcached': $memcachedClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.memcached.class').'%'; $memcachedInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.memcached_instance.class').'%'; diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php index f154083b29de6..5f74ecfbcc5ae 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php @@ -23,10 +23,7 @@ class DoctrineValidationPass implements CompilerPassInterface { private $managerType; - /** - * @param string $managerType - */ - public function __construct($managerType) + public function __construct(string $managerType) { $this->managerType = $managerType; } @@ -43,12 +40,8 @@ public function process(ContainerBuilder $container) /** * Gets the validation mapping files for the format and extends them with * files matching a doctrine search pattern (Resources/config/validation.orm.xml). - * - * @param ContainerBuilder $container - * @param string $mapping - * @param string $extension */ - private function updateValidatorMappingFiles(ContainerBuilder $container, $mapping, $extension) + private function updateValidatorMappingFiles(ContainerBuilder $container, string $mapping, string $extension) { if (!$container->hasParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files')) { return; diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index e7015fd1e4119..9611e6b6f5998 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -37,7 +37,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface * manager's service ID for a connection name * @param string $tagPrefix Tag prefix for listeners and subscribers */ - public function __construct($connections, $managerTemplate, $tagPrefix) + public function __construct(string $connections, string $managerTemplate, string $tagPrefix) { $this->connections = $connections; $this->managerTemplate = $managerTemplate; diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php index 21a9fd29a80cb..4f140f81edd0e 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php @@ -117,7 +117,7 @@ abstract class RegisterMappingsPass implements CompilerPassInterface * register alias * @param string[] $aliasMap Map of alias to namespace */ - public function __construct($driver, array $namespaces, array $managerParameters, $driverPattern, $enabledParameter = false, $configurationPattern = '', $registerAliasMethodName = '', array $aliasMap = array()) + public function __construct($driver, array $namespaces, array $managerParameters, string $driverPattern, $enabledParameter = false, string $configurationPattern = '', string $registerAliasMethodName = '', array $aliasMap = array()) { $this->driver = $driver; $this->namespaces = $namespaces; diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php index 49c528ba00fdb..1f946266a0958 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php @@ -27,7 +27,7 @@ class EntityFactory implements UserProviderFactoryInterface private $key; private $providerId; - public function __construct($key, $providerId) + public function __construct(string $key, string $providerId) { $this->key = $key; $this->providerId = $providerId; diff --git a/src/Symfony/Bridge/Doctrine/ExpressionLanguage/DoctrineParserCache.php b/src/Symfony/Bridge/Doctrine/ExpressionLanguage/DoctrineParserCache.php deleted file mode 100644 index 1bc22ce7aa632..0000000000000 --- a/src/Symfony/Bridge/Doctrine/ExpressionLanguage/DoctrineParserCache.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\ExpressionLanguage; - -@trigger_error('The '.__NAMESPACE__.'\DoctrineParserCache class is deprecated since Symfony 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\DoctrineAdapter class instead.', E_USER_DEPRECATED); - -use Doctrine\Common\Cache\Cache; -use Symfony\Component\ExpressionLanguage\ParsedExpression; -use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; - -/** - * @author Adrien Brault - * - * @deprecated DoctrineParserCache class is deprecated since version 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\DoctrineAdapter class instead. - */ -class DoctrineParserCache implements ParserCacheInterface -{ - private $cache; - - public function __construct(Cache $cache) - { - $this->cache = $cache; - } - - /** - * {@inheritdoc} - */ - public function fetch($key) - { - if (false === $value = $this->cache->fetch($key)) { - return; - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function save($key, ParsedExpression $expression) - { - $this->cache->save($key, $expression); - } -} diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php index 0073fb6c1749e..a5a08b13ea426 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php @@ -14,7 +14,6 @@ use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\ChoiceListInterface; -use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; /** @@ -45,22 +44,9 @@ class DoctrineChoiceLoader implements ChoiceLoaderInterface * @param string $class The class name of the loaded objects * @param IdReader $idReader The reader for the object IDs * @param null|EntityLoaderInterface $objectLoader The objects loader - * @param ChoiceListFactoryInterface $factory The factory for creating the loaded choice list */ - public function __construct($manager, $class, $idReader = null, $objectLoader = null, $factory = null) + public function __construct(ObjectManager $manager, string $class, IdReader $idReader = null, EntityLoaderInterface $objectLoader = null) { - // BC to be removed and replace with type hints in 4.0 - if ($manager instanceof ChoiceListFactoryInterface) { - @trigger_error(sprintf('Passing a ChoiceListFactoryInterface to %s is deprecated since Symfony 3.1 and will no longer be supported in 4.0. You should either call "%s::loadChoiceList" or override it to return a ChoiceListInterface.', __CLASS__, __CLASS__), E_USER_DEPRECATED); - - // Provide a BC layer since $factory has changed - // form first to last argument as of 3.1 - $manager = $class; - $class = $idReader; - $idReader = $objectLoader; - $objectLoader = $factory; - } - $classMetadata = $manager->getClassMetadata($class); $this->manager = $manager; diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php index 75798d279919e..e1e4e4226982c 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php @@ -63,7 +63,7 @@ public function __construct(ObjectManager $om, ClassMetadata $classMetadata) * @return bool returns `true` if the class has a single-column ID and * `false` otherwise */ - public function isSingleId() + public function isSingleId(): bool { return $this->singleId; } @@ -74,7 +74,7 @@ public function isSingleId() * @return bool returns `true` if the class has a single-column integer ID * and `false` otherwise */ - public function isIntId() + public function isIntId(): bool { return $this->intId; } @@ -119,7 +119,7 @@ public function getIdValue($object) * * @return string The name of the ID field */ - public function getIdField() + public function getIdField(): string { return $this->idField; } diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index 5721528f4d5ff..86d96f08a172a 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -60,12 +60,19 @@ public function guessType($class, $property) case Type::DATETIMETZ: case 'vardatetime': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', array(), Guess::HIGH_CONFIDENCE); + case 'datetime_immutable'; + case 'datetimetz_immutable'; + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', array('input' => 'datetime_immutable'), Guess::HIGH_CONFIDENCE); case 'dateinterval': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateIntervalType', array(), Guess::HIGH_CONFIDENCE); case Type::DATE: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', array(), Guess::HIGH_CONFIDENCE); + case 'date_immutable': + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', array('input' => 'datetime_immutable'), Guess::HIGH_CONFIDENCE); case Type::TIME: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', array(), Guess::HIGH_CONFIDENCE); + case 'time_immutable': + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', array('input' => 'datetime_immutable'), Guess::HIGH_CONFIDENCE); case Type::DECIMAL: case Type::FLOAT: return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\NumberType', array(), Guess::MEDIUM_CONFIDENCE); diff --git a/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php b/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php index 277becb18e2ed..487523dd5dfe1 100644 --- a/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php +++ b/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php @@ -27,17 +27,12 @@ */ class MergeDoctrineCollectionListener implements EventSubscriberInterface { - // Keep BC. To be removed in 4.0 - private $bc = true; - private $bcLayer = false; - public static function getSubscribedEvents() { // Higher priority than core MergeCollectionListener so that this one // is called before return array( FormEvents::SUBMIT => array( - array('onBind', 10), // deprecated array('onSubmit', 5), ), ); @@ -45,16 +40,6 @@ public static function getSubscribedEvents() public function onSubmit(FormEvent $event) { - if ($this->bc) { - // onBind() has been overridden from a child class - @trigger_error('The onBind() method is deprecated since Symfony 3.1 and will be removed in 4.0. Use the onSubmit() method instead.', E_USER_DEPRECATED); - - if (!$this->bcLayer) { - // If parent::onBind() has not been called, then logic has been executed - return; - } - } - $collection = $event->getForm()->getData(); $data = $event->getData(); @@ -64,20 +49,4 @@ public function onSubmit(FormEvent $event) $collection->clear(); } } - - /** - * Alias of {@link onSubmit()}. - * - * @deprecated since version 3.1, to be removed in 4.0. - * Use {@link onSubmit()} instead. - */ - public function onBind(FormEvent $event) - { - if (__CLASS__ === get_class($this)) { - $this->bc = false; - } else { - // parent::onBind() has been called - $this->bcLayer = true; - } - } } diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php deleted file mode 100644 index a02437dab30f5..0000000000000 --- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php +++ /dev/null @@ -1,276 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\HttpFoundation; - -@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler instead.', DbalSessionHandler::class), E_USER_DEPRECATED); - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\DriverException; -use Doctrine\DBAL\Driver\ServerInfoAwareConnection; -use Doctrine\DBAL\Platforms\SQLServer2008Platform; - -/** - * DBAL based session storage. - * - * This implementation is very similar to Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler - * but uses a Doctrine connection and thus also works with non-PDO-based drivers like mysqli and OCI8. - * - * @author Fabien Potencier - * @author Johannes M. Schmitt - * @author Tobias Schultze - * - * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler instead. - */ -class DbalSessionHandler implements \SessionHandlerInterface -{ - /** - * @var Connection - */ - private $con; - - /** - * @var string - */ - private $table; - - /** - * @var string Column for session id - */ - private $idCol = 'sess_id'; - - /** - * @var string Column for session data - */ - private $dataCol = 'sess_data'; - - /** - * @var string Column for timestamp - */ - private $timeCol = 'sess_time'; - - /** - * @param Connection $con A connection - * @param string $tableName Table name - */ - public function __construct(Connection $con, $tableName = 'sessions') - { - $this->con = $con; - $this->table = $tableName; - } - - /** - * {@inheritdoc} - */ - public function open($savePath, $sessionName) - { - return true; - } - - /** - * {@inheritdoc} - */ - public function close() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function destroy($sessionId) - { - // delete the record associated with this id - $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; - - try { - $stmt = $this->con->prepare($sql); - $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $stmt->execute(); - } catch (\Exception $e) { - throw new \RuntimeException(sprintf('Exception was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function gc($maxlifetime) - { - // delete the session records that have expired - $sql = "DELETE FROM $this->table WHERE $this->timeCol < :time"; - - try { - $stmt = $this->con->prepare($sql); - $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); - $stmt->execute(); - } catch (\Exception $e) { - throw new \RuntimeException(sprintf('Exception was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function read($sessionId) - { - $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id"; - - try { - $stmt = $this->con->prepare($sql); - $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $stmt->execute(); - - // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed - $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); - - if ($sessionRows) { - return base64_decode($sessionRows[0][0]); - } - - return ''; - } catch (\Exception $e) { - throw new \RuntimeException(sprintf('Exception was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); - } - } - - /** - * {@inheritdoc} - */ - public function write($sessionId, $data) - { - $encoded = base64_encode($data); - - try { - // We use a single MERGE SQL query when supported by the database. - $mergeSql = $this->getMergeSql(); - - if (null !== $mergeSql) { - $mergeStmt = $this->con->prepare($mergeSql); - $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); - $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); - - // Oracle has a bug that will intermittently happen if you - // have only 1 bind on a CLOB field for 2 different statements - // (INSERT and UPDATE in this case) - if ('oracle' == $this->con->getDatabasePlatform()->getName()) { - $mergeStmt->bindParam(':data2', $encoded, \PDO::PARAM_STR); - } - - $mergeStmt->execute(); - - return true; - } - - $updateStmt = $this->con->prepare( - "UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id" - ); - $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); - $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); - $updateStmt->execute(); - - // When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in - // duplicate key errors when the same session is written simultaneously. We can just catch such an - // error and re-execute the update. This is similar to a serializable transaction with retry logic - // on serialization failures but without the overhead and without possible false positives due to - // longer gap locking. - if (!$updateStmt->rowCount()) { - try { - $insertStmt = $this->con->prepare( - "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)" - ); - $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); - $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); - $insertStmt->execute(); - } catch (\Exception $e) { - $driverException = $e->getPrevious(); - // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys - // DriverException only available since DBAL 2.5 - if ( - ($driverException instanceof DriverException && 0 === strpos($driverException->getSQLState(), '23')) || - ($driverException instanceof \PDOException && 0 === strpos($driverException->getCode(), '23')) - ) { - $updateStmt->execute(); - } else { - throw $e; - } - } - } - } catch (\Exception $e) { - throw new \RuntimeException(sprintf('Exception was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); - } - - return true; - } - - /** - * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database. - * - * @return string|null The SQL string or null when not supported - */ - private function getMergeSql() - { - $platform = $this->con->getDatabasePlatform()->getName(); - - switch (true) { - case 'mysql' === $platform: - return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)"; - case 'oracle' === $platform: - // DUAL is Oracle specific dummy table - return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data2, $this->timeCol = :time"; - case $this->con->getDatabasePlatform() instanceof SQLServer2008Platform: - // MERGE is only available since SQL Server 2008 and must be terminated by semicolon - // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx - return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;"; - case 'sqlite' === $platform: - return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"; - case 'postgresql' === $platform && version_compare($this->getServerVersion(), '9.5', '>='): - return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->timeCol)"; - } - } - - private function getServerVersion() - { - $params = $this->con->getParams(); - - // Explicit platform version requested (supersedes auto-detection), so we respect it. - if (isset($params['serverVersion'])) { - return $params['serverVersion']; - } - - $wrappedConnection = $this->con->getWrappedConnection(); - - if ($wrappedConnection instanceof ServerInfoAwareConnection) { - return $wrappedConnection->getServerVersion(); - } - - // Support DBAL 2.4 by accessing it directly when using PDO PgSQL - if ($wrappedConnection instanceof \PDO) { - return $wrappedConnection->getAttribute(\PDO::ATTR_SERVER_VERSION); - } - - // If we cannot guess the version, the empty string will mean we won't use the code for newer versions when doing version checks. - return ''; - } -} diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php deleted file mode 100644 index 0696abef602c5..0000000000000 --- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\HttpFoundation; - -@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::createTable instead.', DbalSessionHandlerSchema::class), E_USER_DEPRECATED); - -use Doctrine\DBAL\Schema\Schema; - -/** - * DBAL Session Storage Schema. - * - * @author Johannes M. Schmitt - * - * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::createTable instead. - */ -final class DbalSessionHandlerSchema extends Schema -{ - public function __construct($tableName = 'sessions') - { - parent::__construct(); - - $this->addSessionTable($tableName); - } - - public function addToSchema(Schema $schema) - { - foreach ($this->getTables() as $table) { - $schema->_addTable($table); - } - } - - private function addSessionTable($tableName) - { - $table = $this->createTable($tableName); - $table->addColumn('sess_id', 'string'); - $table->addColumn('sess_data', 'text')->setNotNull(true); - $table->addColumn('sess_time', 'integer')->setNotNull(true)->setUnsigned(true); - $table->setPrimaryKey(array('sess_id')); - } -} diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index ae3618d75b4d4..c019c31a9ac06 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -13,8 +13,6 @@ use ProxyManager\Proxy\LazyLoadingInterface; use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Doctrine\Common\Persistence\AbstractManagerRegistry; /** @@ -22,24 +20,13 @@ * * @author Lukas Kahwe Smith */ -abstract class ManagerRegistry extends AbstractManagerRegistry implements ContainerAwareInterface +abstract class ManagerRegistry extends AbstractManagerRegistry { /** * @var Container */ protected $container; - /** - * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. - * @final since version 3.4 - */ - public function setContainer(SymfonyContainerInterface $container = null) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Inject a PSR-11 container using the constructor instead.', __METHOD__), E_USER_DEPRECATED); - - $this->container = $container; - } - /** * {@inheritdoc} */ @@ -59,15 +46,11 @@ protected function resetService($name) $manager = $this->container->get($name); if (!$manager instanceof LazyLoadingInterface) { - @trigger_error(sprintf('Resetting a non-lazy manager service is deprecated since Symfony 3.2 and will throw an exception in version 4.0. Set the "%s" service as lazy and require "symfony/proxy-manager-bridge" in your composer.json file instead.', $name), E_USER_DEPRECATED); - - $this->container->set($name, null); - - return; + throw new \LogicException(sprintf('Resetting a non-lazy manager service is not supported. Set the "%s" service as lazy and require "symfony/proxy-manager-bridge" in your composer.json file instead.', $name)); } $manager->setProxyInitializer(\Closure::bind( function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) { - if (isset($this->normalizedIds[$normalizedId = strtolower($name)])) { + if (isset($this->normalizedIds[$normalizedId = strtolower($name)])) { // BC with DI v3.4 $name = $this->normalizedIds[$normalizedId]; } if (isset($this->aliases[$name])) { @@ -76,7 +59,7 @@ function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) { if (isset($this->fileMap[$name])) { $wrappedInstance = $this->load($this->fileMap[$name]); } else { - $method = !isset($this->methodMap[$name]) ? 'get'.strtr($name, $this->underscoreMap).'Service' : $this->methodMap[$name]; + $method = $this->methodMap[$name] ?? 'get'.strtr($name, $this->underscoreMap).'Service'; // BC with DI v3.4 $wrappedInstance = $this->{$method}(false); } diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index 079ff8263fe29..aa1d95ecc6bb7 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -157,13 +157,9 @@ public function getTypes($class, $property, array $context = array()) /** * Determines whether an association is nullable. * - * @param array $associationMapping - * - * @return bool - * * @see https://github.com/doctrine/doctrine2/blob/v2.5.4/lib/Doctrine/ORM/Tools/EntityGenerator.php#L1221-L1246 */ - private function isAssociationNullable(array $associationMapping) + private function isAssociationNullable(array $associationMapping): bool { if (isset($associationMapping['id']) && $associationMapping['id']) { return false; @@ -185,12 +181,8 @@ private function isAssociationNullable(array $associationMapping) /** * Gets the corresponding built-in PHP type. - * - * @param string $doctrineType - * - * @return string|null */ - private function getPhpType($doctrineType) + private function getPhpType(string $doctrineType): ?string { switch ($doctrineType) { case DBALType::SMALLINT: @@ -217,5 +209,7 @@ private function getPhpType($doctrineType) case DBALType::OBJECT: return Type::BUILTIN_TYPE_OBJECT; } + + return null; } } diff --git a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php index 22f6b388a1c4c..ea2793d471e33 100644 --- a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php @@ -33,7 +33,7 @@ class EntityUserProvider implements UserProviderInterface private $class; private $property; - public function __construct(ManagerRegistry $registry, $classOrAlias, $property = null, $managerName = null) + public function __construct(ManagerRegistry $registry, string $classOrAlias, string $property = null, string $managerName = null) { $this->registry = $registry; $this->managerName = $managerName; diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php index cc20869c7c5c6..d104931511f4f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php @@ -140,12 +140,12 @@ public function paramProvider() array(true, array(), true, true), array(null, array(), null, true), array(new \DateTime('2011-09-11'), array('date'), '2011-09-11', true), - array(fopen(__FILE__, 'r'), array(), 'Resource(stream)', false), - array(new \stdClass(), array(), 'Object(stdClass)', false), + array(fopen(__FILE__, 'r'), array(), '/* Resource(stream) */', false), + array(new \stdClass(), array(), '/* Object(stdClass) */', false), array( new StringRepresentableClass(), array(), - 'Object(Symfony\Bridge\Doctrine\Tests\DataCollector\StringRepresentableClass): "string representation"', + '/* Object(Symfony\Bridge\Doctrine\Tests\DataCollector\StringRepresentableClass): */"string representation"', false, ), ); diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index 82558c724af9b..5de9e50e7cba6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -181,18 +181,14 @@ public function providerBasicDrivers() array('doctrine.orm.cache.wincache.class', array('type' => 'wincache')), array('doctrine.orm.cache.zenddata.class', array('type' => 'zenddata')), array('doctrine.orm.cache.redis.class', array('type' => 'redis'), array('setRedis')), - array('doctrine.orm.cache.memcache.class', array('type' => 'memcache'), array('setMemcache')), array('doctrine.orm.cache.memcached.class', array('type' => 'memcached'), array('setMemcached')), ); } /** - * @param string $class - * @param array $config - * * @dataProvider providerBasicDrivers */ - public function testLoadBasicCacheDriver($class, array $config, array $expectedCalls = array()) + public function testLoadBasicCacheDriver(string $class, array $config, array $expectedCalls = array()) { $container = $this->createContainer(); $cacheName = 'metadata_cache'; diff --git a/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php b/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php deleted file mode 100644 index 394b1b0dfe9a2..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Tests\ExpressionLanguage; - -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\Doctrine\ExpressionLanguage\DoctrineParserCache; - -/** - * @group legacy - */ -class DoctrineParserCacheTest extends TestCase -{ - public function testFetch() - { - $doctrineCacheMock = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); - $parserCache = new DoctrineParserCache($doctrineCacheMock); - - $doctrineCacheMock->expects($this->once()) - ->method('fetch') - ->will($this->returnValue('bar')); - - $result = $parserCache->fetch('foo'); - - $this->assertEquals('bar', $result); - } - - public function testFetchUnexisting() - { - $doctrineCacheMock = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); - $parserCache = new DoctrineParserCache($doctrineCacheMock); - - $doctrineCacheMock - ->expects($this->once()) - ->method('fetch') - ->will($this->returnValue(false)); - - $this->assertNull($parserCache->fetch('')); - } - - public function testSave() - { - $doctrineCacheMock = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); - $parserCache = new DoctrineParserCache($doctrineCacheMock); - - $expression = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParsedExpression') - ->disableOriginalConstructor() - ->getMock(); - - $doctrineCacheMock->expects($this->once()) - ->method('save') - ->with('foo', $expression); - - $parserCache->save('foo', $expression); - } -} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapper.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapper.php index 4f647627bb2f0..d46798aa84bb4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapper.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapper.php @@ -15,10 +15,7 @@ class StringWrapper { private $string; - /** - * @param string $string - */ - public function __construct($string = null) + public function __construct(string $string = null) { $this->string = $string; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php index 2ab9eac244915..b24a374fedf59 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php @@ -119,37 +119,6 @@ public function testLoadChoiceList() $this->assertEquals($choiceList, $loader->loadChoiceList($value)); } - /** - * @group legacy - */ - public function testLegacyLoadChoiceList() - { - $factory = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface')->getMock(); - $loader = new DoctrineChoiceLoader( - $factory, - $this->om, - $this->class, - $this->idReader - ); - - $choices = array($this->obj1, $this->obj2, $this->obj3); - $value = function () {}; - $choiceList = new ArrayChoiceList($choices, $value); - - $this->repository->expects($this->once()) - ->method('findAll') - ->willReturn($choices); - - $factory->expects($this->never()) - ->method('createListFromChoices'); - - $this->assertEquals($choiceList, $loaded = $loader->loadChoiceList($value)); - - // no further loads on subsequent calls - - $this->assertSame($loaded, $loader->loadChoiceList($value)); - } - public function testLoadChoiceListUsesObjectLoaderIfAvailable() { $loader = new DoctrineChoiceLoader( diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php index dbcbc0f325e4b..52ea54dfefbc5 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php @@ -78,37 +78,4 @@ public function testOnSubmitNullClearCollection() $this->assertTrue($this->collection->isEmpty()); } - - /** - * @group legacy - */ - public function testLegacyChildClassOnSubmitCallParent() - { - $form = $this->getBuilder('name') - ->setData($this->collection) - ->addEventSubscriber(new TestClassExtendingMergeDoctrineCollectionListener()) - ->getForm(); - $submittedData = array(); - $event = new FormEvent($form, $submittedData); - - $this->dispatcher->dispatch(FormEvents::SUBMIT, $event); - - $this->assertTrue($this->collection->isEmpty()); - $this->assertTrue(TestClassExtendingMergeDoctrineCollectionListener::$onBindCalled); - } -} - -/** - * @group legacy - */ -class TestClassExtendingMergeDoctrineCollectionListener extends MergeDoctrineCollectionListener -{ - public static $onBindCalled = false; - - public function onBind(FormEvent $event) - { - self::$onBindCalled = true; - - parent::onBind($event); - } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php b/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php deleted file mode 100644 index 8d46bf9e63beb..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Tests\HttpFoundation; - -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler; - -/** - * Test class for DbalSessionHandler. - * - * @author Drak - * - * @group legacy - */ -class DbalSessionHandlerTest extends TestCase -{ - public function testConstruct() - { - $connection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock(); - $handler = new DbalSessionHandler($connection); - - $this->assertInstanceOf('Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler', $handler); - } -} diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 8996b87389578..e1a9d0d8ba8ce 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -16,22 +16,22 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "doctrine/common": "~2.4", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/form": "^3.3.10|~4.0", - "symfony/http-kernel": "~2.8|~3.0|~4.0", - "symfony/property-access": "~2.8|~3.0|~4.0", - "symfony/property-info": "~2.8|3.0|~4.0", - "symfony/proxy-manager-bridge": "~2.8|~3.0|~4.0", - "symfony/security": "~2.8|~3.0|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/validator": "^3.2.5|~4.0", - "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/form": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/property-access": "~3.4|~4.0", + "symfony/property-info": "~3.4|~4.0", + "symfony/proxy-manager-bridge": "~3.4|~4.0", + "symfony/security": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.4", "doctrine/orm": "^2.4.5" @@ -57,7 +57,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bridge/Monolog/CHANGELOG.md b/src/Symfony/Bridge/Monolog/CHANGELOG.md index f91d4c5d9a224..e024b186e79ee 100644 --- a/src/Symfony/Bridge/Monolog/CHANGELOG.md +++ b/src/Symfony/Bridge/Monolog/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +4.0.0 +----- + + * the `$format`, `$dateFormat`, `$allowInlineLineBreaks`, and `$ignoreEmptyContextAndExtra` + constructor arguments of the `ConsoleFormatter` class have been removed, use + `$options` instead + * the `DebugHandler` class has been removed + 3.3.0 ----- diff --git a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php index f8fdec165cbad..83abd5687902c 100644 --- a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php +++ b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php @@ -53,24 +53,8 @@ class ConsoleFormatter implements FormatterInterface * * colors: If true, the log string contains ANSI code to add color; * * multiline: If false, "context" and "extra" are dumped on one line. */ - public function __construct($options = array()) + public function __construct(array $options = array()) { - // BC Layer - if (!is_array($options)) { - @trigger_error(sprintf('The constructor arguments $format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra of "%s" are deprecated since Symfony 3.3 and will be removed in 4.0. Use $options instead.', self::class), E_USER_DEPRECATED); - $args = func_get_args(); - $options = array(); - if (isset($args[0])) { - $options['format'] = $args[0]; - } - if (isset($args[1])) { - $options['date_format'] = $args[1]; - } - if (isset($args[2])) { - $options['multiline'] = $args[2]; - } - } - $this->options = array_replace(array( 'format' => self::SIMPLE_FORMAT, 'date_format' => self::SIMPLE_DATE, diff --git a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php index 04943779dd241..a23dc327c48f6 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php @@ -58,7 +58,7 @@ class ConsoleHandler extends AbstractProcessingHandler implements EventSubscribe * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging * level (leave empty to use the default mapping) */ - public function __construct(OutputInterface $output = null, $bubble = true, array $verbosityLevelMap = array()) + public function __construct(OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = array()) { parent::__construct(Logger::DEBUG, $bubble); $this->output = $output; diff --git a/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php b/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php deleted file mode 100644 index 342fecc241224..0000000000000 --- a/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Monolog\Handler; - -@trigger_error('The '.__NAMESPACE__.'\DebugHandler class is deprecated since Symfony 3.2 and will be removed in 4.0. Use Symfony\Bridge\Monolog\Processor\DebugProcessor instead.', E_USER_DEPRECATED); - -use Monolog\Logger; -use Monolog\Handler\TestHandler; -use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; - -/** - * DebugLogger. - * - * @author Jordi Boggiano - * - * @deprecated since version 3.2, to be removed in 4.0. Use Symfony\Bridge\Monolog\Processor\DebugProcessor instead. - */ -class DebugHandler extends TestHandler implements DebugLoggerInterface -{ - /** - * {@inheritdoc} - */ - public function getLogs() - { - $records = array(); - foreach ($this->records as $record) { - $records[] = array( - 'timestamp' => $record['datetime']->getTimestamp(), - 'message' => $record['message'], - 'priority' => $record['level'], - 'priorityName' => $record['level_name'], - 'context' => $record['context'], - 'channel' => isset($record['channel']) ? $record['channel'] : '', - ); - } - - return $records; - } - - /** - * {@inheritdoc} - */ - public function countErrors() - { - $cnt = 0; - $levels = array(Logger::ERROR, Logger::CRITICAL, Logger::ALERT, Logger::EMERGENCY); - foreach ($levels as $level) { - if (isset($this->recordsByLevel[$level])) { - $cnt += count($this->recordsByLevel[$level]); - } - } - - return $cnt; - } -} diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php index d1d4968df4cda..99979c5a80ba1 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php @@ -24,7 +24,7 @@ class ServerLogHandler extends AbstractHandler private $context; private $socket; - public function __construct($host, $level = Logger::DEBUG, $bubble = true, $context = array()) + public function __construct(string $host, int $level = Logger::DEBUG, bool $bubble = true, array $context = array()) { parent::__construct($level, $bubble); diff --git a/src/Symfony/Bridge/Monolog/Logger.php b/src/Symfony/Bridge/Monolog/Logger.php index 7cd75c5ec11ca..d4771f2894d89 100644 --- a/src/Symfony/Bridge/Monolog/Logger.php +++ b/src/Symfony/Bridge/Monolog/Logger.php @@ -50,7 +50,7 @@ public function countErrors() */ public function clear() { - if (($logger = $this->getDebugLogger()) && method_exists($logger, 'clear')) { + if ($logger = $this->getDebugLogger()) { $logger->clear(); } } diff --git a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php index fdd36336cb4a5..aeb3b55fdc49f 100644 --- a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php @@ -13,24 +13,11 @@ use Monolog\Handler\TestHandler; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\Monolog\Handler\DebugHandler; use Symfony\Bridge\Monolog\Processor\DebugProcessor; use Symfony\Bridge\Monolog\Logger; class LoggerTest extends TestCase { - /** - * @group legacy - */ - public function testGetLogsWithDebugHandler() - { - $handler = new DebugHandler(); - $logger = new Logger(__METHOD__, array($handler)); - - $this->assertTrue($logger->error('error message')); - $this->assertCount(1, $logger->getLogs()); - } - public function testGetLogsWithoutDebugProcessor() { $handler = new TestHandler(); @@ -40,43 +27,6 @@ public function testGetLogsWithoutDebugProcessor() $this->assertSame(array(), $logger->getLogs()); } - /** - * @group legacy - */ - public function testCountErrorsWithDebugHandler() - { - $handler = new DebugHandler(); - $logger = new Logger(__METHOD__, array($handler)); - - $this->assertTrue($logger->debug('test message')); - $this->assertTrue($logger->info('test message')); - $this->assertTrue($logger->notice('test message')); - $this->assertTrue($logger->warning('test message')); - - $this->assertTrue($logger->error('test message')); - $this->assertTrue($logger->critical('test message')); - $this->assertTrue($logger->alert('test message')); - $this->assertTrue($logger->emergency('test message')); - - $this->assertSame(4, $logger->countErrors()); - } - - /** - * @group legacy - */ - public function testGetLogsWithDebugHandler2() - { - $logger = new Logger('test'); - $logger->pushHandler(new DebugHandler()); - - $logger->addInfo('test'); - $this->assertCount(1, $logger->getLogs()); - list($record) = $logger->getLogs(); - - $this->assertEquals('test', $record['message']); - $this->assertEquals(Logger::INFO, $record['priority']); - } - public function testCountErrorsWithoutDebugProcessor() { $handler = new TestHandler(); diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php index e73002e0292fa..5f2be509231a8 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php @@ -68,10 +68,7 @@ public function testCanBeConstructedWithExtraFields() $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); } - /** - * @return array - */ - private function createRequestEvent($additionalServerParameters = array()) + private function createRequestEvent($additionalServerParameters = array()): array { $server = array_merge( array( @@ -101,13 +98,7 @@ private function createRequestEvent($additionalServerParameters = array()) return array($event, $server); } - /** - * @param int $level - * @param string $message - * - * @return array Record - */ - private function getRecord($level = Logger::WARNING, $message = 'test') + private function getRecord(int $level = Logger::WARNING, string $message = 'test'): array { return array( 'message' => $message, diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index 1deb3ca9d3164..a4d3e984100c9 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -16,18 +16,18 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "monolog/monolog": "~1.19", - "symfony/http-kernel": "~2.8|~3.0|~4.0" + "symfony/http-kernel": "~3.4|~4.0" }, "require-dev": { - "symfony/console": "~2.8|~3.0|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/security-core": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~3.3|~4.0" + "symfony/console": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/security-core": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" }, "conflict": { - "symfony/http-foundation": "<3.3" + "symfony/http-foundation": "<3.4" }, "suggest": { "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", @@ -44,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md index 5c6ee47309d95..3c21d4cc7c8c7 100644 --- a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md +++ b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md @@ -1,6 +1,17 @@ CHANGELOG ========= +4.0.0 +----- + + * support for the `testLegacy` prefix in method names to mark a test as legacy + has been dropped, use the `@group legacy` notation instead + * support for the `Legacy` prefix in class names to mark tests as legacy has + been dropped, use the `@group legacy` notation instead + * support for passing an array of mocked namespaces not indexed by the mock + feature to the constructor of the `SymfonyTestsListenerTrait` class was + dropped + 3.4.0 ----- diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php index 63b7cb0c14d84..7ded32a782464 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php @@ -52,16 +52,10 @@ public function __construct(array $mockedNamespaces = array()) Blacklist::$blacklistedClassNames['\Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait'] = 2; } - $warn = false; foreach ($mockedNamespaces as $type => $namespaces) { if (!is_array($namespaces)) { $namespaces = array($namespaces); } - if (is_int($type)) { - // @deprecated BC with v2.8 to v3.0 - $type = 'time-sensitive'; - $warn = true; - } if ('time-sensitive' === $type) { foreach ($namespaces as $ns) { ClockMock::register($ns.'\DummyClass'); @@ -77,9 +71,6 @@ public function __construct(array $mockedNamespaces = array()) $this->state = -2; } else { self::$globallyEnabled = true; - if ($warn) { - echo "Clock-mocked namespaces for SymfonyTestsListener need to be nested in a \"time-sensitive\" key. This will be enforced in Symfony 4.0.\n"; - } } } @@ -311,22 +302,6 @@ public function endTest($test, $time) DnsMock::withMockedHosts(array()); } } - - if (($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) && 0 === strpos($test->getName(), 'testLegacy') && !isset($this->testsWithWarnings[$test->getName()]) && !in_array('legacy', $groups, true)) { - $result = $test->getTestResultObject(); - - if (method_exists($result, 'addWarning')) { - $result->addWarning($test, new $Warning('Using the "testLegacy" prefix to mark tests as legacy is deprecated since version 3.3 and will be removed in 4.0. Use the "@group legacy" notation instead to add the test to the legacy group.'), $time); - } - } - - if (($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) && strpos($className, '\Legacy') && !isset($this->testsWithWarnings[$test->getName()]) && !in_array('legacy', $classGroups, true)) { - $result = $test->getTestResultObject(); - - if (method_exists($result, 'addWarning')) { - $result->addWarning($test, new $Warning('Using the "Legacy" prefix to mark all tests of a class as legacy is deprecated since version 3.3 and will be removed in 4.0. Use the "@group legacy" notation instead to add the test to the legacy group.'), $time); - } - } } public function handleError($type, $msg, $file, $line, $context = array()) diff --git a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php index b07effe3d27dc..b408b74010bd9 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php @@ -12,10 +12,6 @@ public function test() $this->markTestSkipped('This test cannot be run on Windows.'); } - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('This test cannot be run on HHVM.'); - } - if (\PHP_VERSION_ID >= 70000) { $php = 'phpdbg -qrr'; } else { diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 24a48b600d1b5..7338fca00db74 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -40,7 +40,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" }, "thanks": { "name": "phpunit/phpunit", diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index 2b827324fa7b6..c15f6d2014c8a 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -13,7 +13,7 @@ use ProxyManager\Generator\ClassGenerator; use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; -use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; @@ -30,10 +30,7 @@ class ProxyDumper implements DumperInterface private $proxyGenerator; private $classGenerator; - /** - * @param string $salt - */ - public function __construct($salt = '') + public function __construct(string $salt = '') { $this->salt = $salt; $this->proxyGenerator = new LazyLoadingValueHolderGenerator(); @@ -56,16 +53,13 @@ public function getProxyFactoryCode(Definition $definition, $id, $factoryCode = $instantiation = 'return'; if ($definition->isShared()) { - $instantiation .= " \$this->services['$id'] ="; + $instantiation .= sprintf(' $this->%s[\'%s\'] =', $definition->isPublic() || !method_exists(ContainerBuilder::class, 'addClassResource') ? 'services' : 'privates', $id); } if (null === $factoryCode) { - @trigger_error(sprintf('The "%s()" method expects a third argument defining the code to execute to construct your service since Symfony 3.4, providing it will be required in 4.0.', __METHOD__), E_USER_DEPRECATED); - $factoryCode = '$this->get'.Container::camelize($id).'Service(false)'; - } elseif (false === strpos($factoryCode, '(')) { - @trigger_error(sprintf('The "%s()" method expects its third argument to define the code to execute to construct your service since Symfony 3.4, providing it will be required in 4.0.', __METHOD__), E_USER_DEPRECATED); - $factoryCode = "\$this->$factoryCode(false)"; + throw new \InvalidArgumentException(sprintf('Missing factory code to construct the service "%s".', $id)); } + $proxyClass = $this->getProxyClassName($definition); $hasStaticConstructor = $this->generateProxyClass($definition)->hasMethod('staticProxyConstructor'); diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 640290e2b45a2..6dee01fccec85 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -37,11 +37,8 @@ protected function setUp() /** * @dataProvider getProxyCandidates - * - * @param Definition $definition - * @param bool $expected */ - public function testIsProxyCandidate(Definition $definition, $expected) + public function testIsProxyCandidate(Definition $definition, bool $expected) { $this->assertSame($expected, $this->dumper->isProxyCandidate($definition)); } @@ -76,20 +73,14 @@ public function testGetProxyFactoryCode() } /** - * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing factory code to construct the service "foo". */ - public function testLegacyGetProxyFactoryCode() + public function testGetProxyFactoryCodeWithoutCustomMethod() { $definition = new Definition(__CLASS__); - $definition->setLazy(true); - - $code = $this->dumper->getProxyFactoryCode($definition, 'foo'); - - $this->assertStringMatchesFormat( - '%A$wrappedInstance = $this->getFooService(false);%w$proxy->setProxyInitializer(null);%A', - $code - ); + $this->dumper->getProxyFactoryCode($definition, 'foo'); } /** diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 7e34cd90b5139..90f000c828618 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -16,12 +16,12 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "symfony/dependency-injection": "~3.4|~4.0", "ocramius/proxy-manager": "~0.4|~1.0|~2.0" }, "require-dev": { - "symfony/config": "~2.8|~3.0|~4.0" + "symfony/config": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Bridge\\ProxyManager\\": "" }, @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 1e45cf4d285af..d6d8f1cdc816a 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -32,43 +32,14 @@ class DebugCommand extends Command private $twig; private $projectDir; - /** - * @param Environment $twig - * @param string|null $projectDir - */ - public function __construct($twig = null, $projectDir = null) + public function __construct(Environment $twig, string $projectDir = null) { - if (!$twig instanceof Environment) { - @trigger_error(sprintf('Passing a command name as the first argument of "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); - - parent::__construct($twig); - - return; - } - parent::__construct(); $this->twig = $twig; $this->projectDir = $projectDir; } - public function setTwigEnvironment(Environment $twig) - { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - $this->twig = $twig; - } - - /** - * @return Environment $twig - */ - protected function getTwigEnvironment() - { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - return $this->twig; - } - protected function configure() { $this @@ -100,20 +71,6 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); - - // BC to be removed in 4.0 - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, 'getTwigEnvironment'); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Usage of method "%s" is deprecated since Symfony 3.4 and will no longer be supported in 4.0. Construct the command with its required arguments instead.', get_class($this).'::getTwigEnvironment'), E_USER_DEPRECATED); - - $this->twig = $this->getTwigEnvironment(); - } - } - if (null === $this->twig) { - throw new \RuntimeException('The Twig environment needs to be set.'); - } - $types = array('functions', 'filters', 'tests', 'globals'); if ('json' === $input->getOption('format')) { diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php index afb1d1bf31cf2..c5e415bd0b9ec 100644 --- a/src/Symfony/Bridge/Twig/Command/LintCommand.php +++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php @@ -35,41 +35,13 @@ class LintCommand extends Command private $twig; - /** - * @param Environment $twig - */ - public function __construct($twig = null) + public function __construct(Environment $twig) { - if (!$twig instanceof Environment) { - @trigger_error(sprintf('Passing a command name as the first argument of "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); - - parent::__construct($twig); - - return; - } - parent::__construct(); $this->twig = $twig; } - public function setTwigEnvironment(Environment $twig) - { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - $this->twig = $twig; - } - - /** - * @return Environment $twig - */ - protected function getTwigEnvironment() - { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - return $this->twig; - } - protected function configure() { $this @@ -101,20 +73,6 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); - - // BC to be removed in 4.0 - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, 'getTwigEnvironment'); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Usage of method "%s" is deprecated since Symfony 3.4 and will no longer be supported in 4.0. Construct the command with its required arguments instead.', get_class($this).'::getTwigEnvironment'), E_USER_DEPRECATED); - - $this->twig = $this->getTwigEnvironment(); - } - } - if (null === $this->twig) { - throw new \RuntimeException('The Twig environment needs to be set.'); - } - $filenames = $input->getArgument('filename'); if (0 === count($filenames)) { diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php index ee48f39b8fa91..8d9f58e64c1df 100644 --- a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php +++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -138,11 +138,7 @@ public function getHtmlCallGraph() public function getProfile() { if (null === $this->profile) { - if (\PHP_VERSION_ID >= 70000) { - $this->profile = unserialize($this->data['profile'], array('allowed_classes' => array('Twig_Profiler_Profile', 'Twig\Profiler\Profile'))); - } else { - $this->profile = unserialize($this->data['profile']); - } + $this->profile = unserialize($this->data['profile'], array('allowed_classes' => array('Twig_Profiler_Profile', 'Twig\Profiler\Profile'))); } return $this->profile; diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index e47772481bdb7..cc9dfe36de64b 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -31,7 +31,7 @@ class CodeExtension extends AbstractExtension * @param string $rootDir The project root directory * @param string $charset The charset */ - public function __construct($fileLinkFormat, $rootDir, $charset) + public function __construct($fileLinkFormat, string $rootDir, string $charset) { $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->rootDir = str_replace('/', DIRECTORY_SEPARATOR, dirname($rootDir)).DIRECTORY_SEPARATOR; diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index e63203f39bd5b..e2ddbaf47fdaf 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -12,13 +12,9 @@ namespace Symfony\Bridge\Twig\Extension; use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; -use Symfony\Bridge\Twig\Form\TwigRendererInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\FormView; -use Twig\Environment; use Twig\Extension\AbstractExtension; -use Twig\Extension\InitRuntimeInterface; use Twig\TwigFilter; use Twig\TwigFunction; use Twig\TwigTest; @@ -29,37 +25,8 @@ * @author Fabien Potencier * @author Bernhard Schussek */ -class FormExtension extends AbstractExtension implements InitRuntimeInterface +class FormExtension extends AbstractExtension { - /** - * @deprecated since version 3.2, to be removed in 4.0 alongside with magic methods below - */ - private $renderer; - - public function __construct($renderer = null) - { - if ($renderer instanceof TwigRendererInterface) { - @trigger_error(sprintf('Passing a Twig Form Renderer to the "%s" constructor is deprecated since Symfony 3.2 and won\'t be possible in 4.0. Pass the Twig\Environment to the TwigRendererEngine constructor instead.', static::class), E_USER_DEPRECATED); - } elseif (null !== $renderer && !(is_array($renderer) && isset($renderer[0], $renderer[1]) && $renderer[0] instanceof ContainerInterface)) { - throw new \InvalidArgumentException(sprintf('Passing any arguments the constructor of %s is reserved for internal use.', __CLASS__)); - } - $this->renderer = $renderer; - } - - /** - * {@inheritdoc} - * - * To be removed in 4.0 - */ - public function initRuntime(Environment $environment) - { - if ($this->renderer instanceof TwigRendererInterface) { - $this->renderer->setEnvironment($environment); - } elseif (is_array($this->renderer)) { - $this->renderer[2] = $environment; - } - } - /** * {@inheritdoc} */ @@ -110,62 +77,6 @@ public function getTests() ); } - /** - * @internal - */ - public function __get($name) - { - if ('renderer' === $name) { - @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since Symfony 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - - if (is_array($this->renderer)) { - $renderer = $this->renderer[0]->get($this->renderer[1]); - if (isset($this->renderer[2]) && $renderer instanceof TwigRendererInterface) { - $renderer->setEnvironment($this->renderer[2]); - } - $this->renderer = $renderer; - } - } - - return $this->$name; - } - - /** - * @internal - */ - public function __set($name, $value) - { - if ('renderer' === $name) { - @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since Symfony 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - } - - $this->$name = $value; - } - - /** - * @internal - */ - public function __isset($name) - { - if ('renderer' === $name) { - @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since Symfony 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - } - - return isset($this->$name); - } - - /** - * @internal - */ - public function __unset($name) - { - if ('renderer' === $name) { - @trigger_error(sprintf('Using the "%s::$renderer" property is deprecated since Symfony 3.2 as it will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - } - - unset($this->$name); - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php index 92a520eda3fb5..4875b1fab8452 100644 --- a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php @@ -91,9 +91,9 @@ public function getUrl($name, $parameters = array(), $schemeRelative = false) * * @return array An array with the contexts the URL is safe * - * @final since version 3.4, type-hint to be changed to "\Twig\Node\Node" in 4.0 + * @final since version 3.4 */ - public function isUrlGenerationSafe(\Twig_Node $argsNode) + public function isUrlGenerationSafe(Node $argsNode) { // support named arguments $paramsNode = $argsNode->hasNode('parameters') ? $argsNode->getNode('parameters') : ( diff --git a/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php b/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php index 48d99b8c7eac3..1afbea5f363a3 100644 --- a/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php @@ -25,7 +25,7 @@ class StopwatchExtension extends AbstractExtension private $stopwatch; private $enabled; - public function __construct(Stopwatch $stopwatch = null, $enabled = true) + public function __construct(Stopwatch $stopwatch = null, bool $enabled = true) { $this->stopwatch = $stopwatch; $this->enabled = $enabled; diff --git a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php index 164b5383e7407..81f1d32446ab1 100644 --- a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php @@ -43,15 +43,7 @@ public function encode($input, $inline = 0, $dumpObjects = 0) } if (defined('Symfony\Component\Yaml\Yaml::DUMP_OBJECT')) { - if (is_bool($dumpObjects)) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - $flags = $dumpObjects ? Yaml::DUMP_OBJECT : 0; - } else { - $flags = $dumpObjects; - } - - return $dumper->dump($input, $inline, 0, $flags); + return $dumper->dump($input, $inline, 0, $dumpObjects); } return $dumper->dump($input, $inline, 0, false, $dumpObjects); diff --git a/src/Symfony/Bridge/Twig/Form/TwigRenderer.php b/src/Symfony/Bridge/Twig/Form/TwigRenderer.php deleted file mode 100644 index b4cb7faa4d105..0000000000000 --- a/src/Symfony/Bridge/Twig/Form/TwigRenderer.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Twig\Form; - -use Symfony\Component\Form\FormRenderer; -use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; -use Twig\Environment; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use %s instead.', TwigRenderer::class, FormRenderer::class), E_USER_DEPRECATED); - -/** - * @author Bernhard Schussek - * - * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\Form\FormRenderer instead. - */ -class TwigRenderer extends FormRenderer implements TwigRendererInterface -{ - public function __construct(TwigRendererEngineInterface $engine, CsrfTokenManagerInterface $csrfTokenManager = null) - { - parent::__construct($engine, $csrfTokenManager); - } - - /** - * Returns the engine used by this renderer. - * - * @return TwigRendererEngineInterface The renderer engine - */ - public function getEngine() - { - return parent::getEngine(); - } - - /** - * {@inheritdoc} - */ - public function setEnvironment(Environment $environment) - { - $this->getEngine()->setEnvironment($environment); - } -} diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php index 711291db2f752..51abce710435e 100644 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php +++ b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php @@ -19,7 +19,7 @@ /** * @author Bernhard Schussek */ -class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererEngineInterface +class TwigRendererEngine extends AbstractRendererEngine { /** * @var Environment @@ -31,30 +31,12 @@ class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererE */ private $template; - public function __construct(array $defaultThemes = array(), Environment $environment = null) + public function __construct(array $defaultThemes, Environment $environment) { - if (null === $environment) { - @trigger_error(sprintf('Not passing a Twig Environment as the second argument for "%s" constructor is deprecated since Symfony 3.2 and won\'t be possible in 4.0.', static::class), E_USER_DEPRECATED); - } - parent::__construct($defaultThemes); $this->environment = $environment; } - /** - * {@inheritdoc} - * - * @deprecated since version 3.3, to be removed in 4.0 - */ - public function setEnvironment(Environment $environment) - { - if ($this->environment) { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass the Twig Environment as second argument of the constructor instead.', __METHOD__), E_USER_DEPRECATED); - } - - $this->environment = $environment; - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php b/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php deleted file mode 100644 index a58f491f1c8a0..0000000000000 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Twig\Form; - -use Symfony\Component\Form\FormRendererEngineInterface; -use Twig\Environment; - -// BC/FC with namespaced Twig -class_exists('Twig\Environment'); - -/** - * @author Bernhard Schussek - * - * @deprecated since version 3.2, to be removed in 4.0. - */ -interface TwigRendererEngineInterface extends FormRendererEngineInterface -{ - public function setEnvironment(Environment $environment); -} diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php b/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php deleted file mode 100644 index 3bcbf5992d76f..0000000000000 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Twig\Form; - -use Symfony\Component\Form\FormRendererInterface; -use Twig\Environment; - -// BC/FC with namespaced Twig -class_exists('Twig\Environment'); - -/** - * @author Bernhard Schussek - * - * @deprecated since version 3.2, to be removed in 4.0. - */ -interface TwigRendererInterface extends FormRendererInterface -{ - public function setEnvironment(Environment $environment); -} diff --git a/src/Symfony/Bridge/Twig/Node/DumpNode.php b/src/Symfony/Bridge/Twig/Node/DumpNode.php index d820d75cc7db9..452b64ccb1011 100644 --- a/src/Symfony/Bridge/Twig/Node/DumpNode.php +++ b/src/Symfony/Bridge/Twig/Node/DumpNode.php @@ -21,7 +21,7 @@ class DumpNode extends Node { private $varPrefix; - public function __construct($varPrefix, Node $values = null, $lineno, $tag = null) + public function __construct($varPrefix, Node $values = null, int $lineno, string $tag = null) { $nodes = array(); if (null !== $values) { diff --git a/src/Symfony/Bridge/Twig/Node/FormThemeNode.php b/src/Symfony/Bridge/Twig/Node/FormThemeNode.php index 8fcd9380a620a..b898ec58bf26c 100644 --- a/src/Symfony/Bridge/Twig/Node/FormThemeNode.php +++ b/src/Symfony/Bridge/Twig/Node/FormThemeNode.php @@ -11,10 +11,8 @@ namespace Symfony\Bridge\Twig\Node; -use Symfony\Bridge\Twig\Form\TwigRenderer; use Symfony\Component\Form\FormRenderer; use Twig\Compiler; -use Twig\Error\RuntimeError; use Twig\Node\Node; /** @@ -22,24 +20,17 @@ */ class FormThemeNode extends Node { - public function __construct(Node $form, Node $resources, $lineno, $tag = null, $only = false) + public function __construct(Node $form, Node $resources, int $lineno, string $tag = null, bool $only = false) { - parent::__construct(array('form' => $form, 'resources' => $resources), array('only' => (bool) $only), $lineno, $tag); + parent::__construct(array('form' => $form, 'resources' => $resources), array('only' => $only), $lineno, $tag); } public function compile(Compiler $compiler) { - try { - $compiler->getEnvironment()->getRuntime(FormRenderer::class); - $renderer = FormRenderer::class; - } catch (RuntimeError $e) { - $renderer = TwigRenderer::class; - } - $compiler ->addDebugInfo($this) ->write('$this->env->getRuntime(') - ->string($renderer) + ->string(FormRenderer::class) ->raw(')->setTheme(') ->subcompile($this->getNode('form')) ->raw(', ') diff --git a/src/Symfony/Bridge/Twig/Node/StopwatchNode.php b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php index fac770c2499ba..d95c63028c2eb 100644 --- a/src/Symfony/Bridge/Twig/Node/StopwatchNode.php +++ b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php @@ -22,7 +22,7 @@ */ class StopwatchNode extends Node { - public function __construct(Node $name, Node $body, AssignNameExpression $var, $lineno = 0, $tag = null) + public function __construct(Node $name, Node $body, AssignNameExpression $var, int $lineno = 0, string $tag = null) { parent::__construct(array('body' => $body, 'name' => $name, 'var' => $var), array(), $lineno, $tag); } diff --git a/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php b/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php index c9c82b33e541c..63ad1f6796152 100644 --- a/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php @@ -20,7 +20,7 @@ */ class TransDefaultDomainNode extends Node { - public function __construct(AbstractExpression $expr, $lineno = 0, $tag = null) + public function __construct(AbstractExpression $expr, int $lineno = 0, string $tag = null) { parent::__construct(array('expr' => $expr), array(), $lineno, $tag); } diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php index 020810f7b7c55..7eb8d743e9b3e 100644 --- a/src/Symfony/Bridge/Twig/Node/TransNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -27,7 +27,7 @@ class_exists('Twig\Node\Expression\ArrayExpression'); */ class TransNode extends Node { - public function __construct(Node $body, Node $domain = null, AbstractExpression $count = null, AbstractExpression $vars = null, AbstractExpression $locale = null, $lineno = 0, $tag = null) + public function __construct(Node $body, Node $domain = null, AbstractExpression $count = null, AbstractExpression $vars = null, AbstractExpression $locale = null, int $lineno = 0, string $tag = null) { $nodes = array('body' => $body); if (null !== $domain) { diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php index 1fbce9c6af811..fef722be64ce5 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php @@ -103,29 +103,20 @@ public function getPriority() return 0; } - /** - * @param Node $arguments - * @param int $index - * - * @return string|null - */ - private function getReadDomainFromArguments(Node $arguments, $index) + private function getReadDomainFromArguments(Node $arguments, int $index): ?string { if ($arguments->hasNode('domain')) { $argument = $arguments->getNode('domain'); } elseif ($arguments->hasNode($index)) { $argument = $arguments->getNode($index); } else { - return; + return null; } return $this->getReadDomainFromNode($argument); } - /** - * @return string|null - */ - private function getReadDomainFromNode(Node $node) + private function getReadDomainFromNode(Node $node): ?string { if ($node instanceof ConstantExpression) { return $node->getAttribute('value'); diff --git a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php index 27eca0c44a74a..a9389dd82af09 100644 --- a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php +++ b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php @@ -50,9 +50,8 @@ public function testEnvironment() */ public function testGetSession() { - $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); - $request->method('getSession')->willReturn($session); + $request->method('getSession')->willReturn($session = new Session()); $this->setRequestStack($request); diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php index 9ef18640684f9..ce7175ab1adfe 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php @@ -68,24 +68,6 @@ public function testLintFileCompileTimeException() $this->assertRegExp('/ERROR in \S+ \(line /', trim($tester->getDisplay())); } - /** - * @group legacy - * @expectedDeprecation Passing a command name as the first argument of "Symfony\Bridge\Twig\Command\LintCommand::__construct" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead. - * @expectedException \RuntimeException - * @expectedExceptionMessage The Twig environment needs to be set. - */ - public function testLegacyLintCommand() - { - $command = new LintCommand(); - - $application = new Application(); - $application->add($command); - $command = $application->find('lint:twig'); - - $tester = new CommandTester($command); - $tester->execute(array()); - } - /** * @return CommandTester */ diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTest.php deleted file mode 100644 index a4d7350716e64..0000000000000 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTest.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Twig\Tests\Extension; - -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\Twig\Extension\FormExtension; -use Symfony\Bridge\Twig\Form\TwigRendererInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\Form\FormRendererInterface; -use Twig\Environment; - -/** - * @group legacy - */ -class FormExtensionTest extends TestCase -{ - /** - * @dataProvider rendererDataProvider - */ - public function testInitRuntimeAndAccessRenderer($rendererConstructor, $expectedAccessedRenderer) - { - $extension = new FormExtension($rendererConstructor); - $extension->initRuntime($this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock()); - $this->assertSame($expectedAccessedRenderer, $extension->renderer); - } - - /** - * @dataProvider rendererDataProvider - */ - public function testAccessRendererAndInitRuntime($rendererConstructor, $expectedAccessedRenderer) - { - $extension = new FormExtension($rendererConstructor); - $this->assertSame($expectedAccessedRenderer, $extension->renderer); - $extension->initRuntime($this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock()); - } - - public function rendererDataProvider() - { - $twigRenderer = $this->getMockBuilder(TwigRendererInterface::class)->getMock(); - $twigRenderer->expects($this->once()) - ->method('setEnvironment'); - - yield array($twigRenderer, $twigRenderer); - - $twigRenderer = $this->getMockBuilder(TwigRendererInterface::class)->getMock(); - $twigRenderer->expects($this->once()) - ->method('setEnvironment'); - - $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); - $container->expects($this->once()) - ->method('get') - ->with('service_id') - ->willReturn($twigRenderer); - - yield array(array($container, 'service_id'), $twigRenderer); - - $formRenderer = $this->getMockBuilder(FormRendererInterface::class)->getMock(); - - $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); - $container->expects($this->once()) - ->method('get') - ->with('service_id') - ->willReturn($formRenderer); - - yield array(array($container, 'service_id'), $formRenderer); - } -} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php index 2adf12d99ea88..af675d34ffb52 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Workflow\Definition; use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\SupportStrategy\ClassInstanceSupportStrategy; +use Symfony\Component\Workflow\SupportStrategy\InstanceOfSupportStrategy; use Symfony\Component\Workflow\Transition; use Symfony\Component\Workflow\Workflow; @@ -25,6 +26,10 @@ class WorkflowExtensionTest extends TestCase protected function setUp() { + if (!class_exists(Workflow::class)) { + $this->markTestSkipped('The Workflow component is needed to run tests for this extension.'); + } + $places = array('ordered', 'waiting_for_payment', 'processed'); $transitions = array( new Transition('t1', 'ordered', 'waiting_for_payment'), @@ -34,8 +39,11 @@ protected function setUp() $workflow = new Workflow($definition); $registry = new Registry(); - $registry->add($workflow, new ClassInstanceSupportStrategy(\stdClass::class)); - + $addWorkflow = method_exists($registry, 'addWorkflow') ? 'addWorkflow' : 'add'; + $supportStrategy = class_exists(InstanceOfSupportStrategy::class) + ? new InstanceOfSupportStrategy(\stdClass::class) + : new ClassInstanceSupportStrategy(\stdClass::class); + $registry->$addWorkflow($workflow, $supportStrategy); $this->extension = new WorkflowExtension($registry); } diff --git a/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php index 7f8737a3af6ef..f554164f6b16d 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php @@ -86,11 +86,7 @@ public function testOneVar() EOTXT; - if (\PHP_VERSION_ID >= 70000) { - $expected = preg_replace('/%(.*?)%/', '($context["$1"] ?? null)', $expected); - } else { - $expected = preg_replace('/%(.*?)%/', '(isset($context["$1"]) ? $context["$1"] : null)', $expected); - } + $expected = preg_replace('/%(.*?)%/', '($context["$1"] ?? null)', $expected); $this->assertSame($expected, $compiler->compile($node)->getSource()); } @@ -117,11 +113,7 @@ public function testMultiVars() EOTXT; - if (\PHP_VERSION_ID >= 70000) { - $expected = preg_replace('/%(.*?)%/', '($context["$1"] ?? null)', $expected); - } else { - $expected = preg_replace('/%(.*?)%/', '(isset($context["$1"]) ? $context["$1"] : null)', $expected); - } + $expected = preg_replace('/%(.*?)%/', '($context["$1"] ?? null)', $expected); $this->assertSame($expected, $compiler->compile($node)->getSource()); } diff --git a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php index 1f7f6322bdef0..86c5d54167db2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php @@ -102,10 +102,6 @@ public function testCompile() protected function getVariableGetter($name) { - if (\PHP_VERSION_ID >= 70000) { - return sprintf('($context["%s"] ?? null)', $name); - } - - return sprintf('(isset($context["%s"]) ? $context["%1$s"] : null)', $name); + return sprintf('($context["%s"] ?? null)', $name); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php index 4f66406a8ae32..4e4215c5ad8f2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php @@ -271,10 +271,6 @@ public function testCompileLabelWithLabelThatEvaluatesToNullAndAttributes() protected function getVariableGetter($name) { - if (\PHP_VERSION_ID >= 70000) { - return sprintf('($context["%s"] ?? null)', $name); - } - - return sprintf('(isset($context["%s"]) ? $context["%1$s"] : null)', $name); + return sprintf('($context["%s"] ?? null)', $name); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php index dd4c7136c138e..d45f60c09d1c6 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php @@ -44,11 +44,7 @@ public function testCompileStrict() protected function getVariableGetterWithoutStrictCheck($name) { - if (\PHP_VERSION_ID >= 70000) { - return sprintf('($context["%s"] ?? null)', $name); - } - - return sprintf('(isset($context["%s"]) ? $context["%1$s"] : null)', $name); + return sprintf('($context["%s"] ?? null)', $name); } protected function getVariableGetterWithStrictCheck($name) @@ -61,10 +57,6 @@ protected function getVariableGetterWithStrictCheck($name) return sprintf('(isset($context["%s"]) || array_key_exists("%1$s", $context) ? $context["%1$s"] : (function () { throw new Twig_Error_Runtime(\'Variable "%1$s" does not exist.\', 0, $this->getSourceContext()); })())', $name); } - if (\PHP_VERSION_ID >= 70000) { - return sprintf('($context["%s"] ?? $this->getContext($context, "%1$s"))', $name); - } - - return sprintf('(isset($context["%s"]) ? $context["%1$s"] : $this->getContext($context, "%1$s"))', $name); + return sprintf('($context["%s"] ?? $this->getContext($context, "%1$s"))', $name); } } diff --git a/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php index 82c58d40bbf8d..dd78175d219fa 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php @@ -25,7 +25,7 @@ class StopwatchTokenParser extends AbstractTokenParser { protected $stopwatchIsAvailable; - public function __construct($stopwatchIsAvailable) + public function __construct(bool $stopwatchIsAvailable) { $this->stopwatchIsAvailable = $stopwatchIsAvailable; } diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index aea0d10a67791..93af12915fbfb 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -16,29 +16,29 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "twig/twig": "^1.35|^2.4.4" }, "require-dev": { - "symfony/asset": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/asset": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", "symfony/form": "~3.4|~4.0", - "symfony/http-foundation": "^3.3.11|~4.0", - "symfony/http-kernel": "~3.2|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/translation": "~2.8|~3.0|~4.0", - "symfony/yaml": "~2.8|~3.0|~4.0", - "symfony/security": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0", + "symfony/security": "~3.4|~4.0", "symfony/security-acl": "~2.8|~3.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~3.4|~4.0", "symfony/console": "~3.4|~4.0", - "symfony/var-dumper": "~2.8.10|~3.1.4|~3.2|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/web-link": "~3.3|~4.0", - "symfony/workflow": "~3.3|~4.0" + "symfony/var-dumper": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/web-link": "~3.4|~4.0", + "symfony/workflow": "~3.4|~4.0" }, "conflict": { "symfony/form": "<3.4", @@ -68,7 +68,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php index d7c0ee7287468..835d823664021 100644 --- a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php +++ b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php @@ -35,9 +35,6 @@ public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.xml'); - $container->getDefinition('debug.dump_listener')->setPrivate(true); - $container->getDefinition('var_dumper.cli_dumper')->setPrivate(true); - $container->getDefinition('var_dumper.cloner') ->addMethodCall('setMaxItems', array($config['max_items'])) ->addMethodCall('setMinDepth', array($config['min_depth'])) diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index c5224da2e45b8..091a94ddbf35f 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -16,16 +16,16 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "ext-xml": "*", - "symfony/http-kernel": "~2.8|~3.0|~4.0", - "symfony/twig-bridge": "~2.8|~3.0|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/twig-bridge": "~3.4|~4.0", "symfony/var-dumper": "~3.4|~4.0" }, "require-dev": { - "symfony/config": "~3.3|~4.0", + "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/web-profiler-bundle": "~2.8|~3.0|~4.0" + "symfony/web-profiler-bundle": "~3.4|~4.0" }, "conflict": { "symfony/dependency-injection": "<3.4" @@ -43,7 +43,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 9d63e08afe8be..6d615a013f7a0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -1,6 +1,39 @@ CHANGELOG ========= +4.1.0 +----- + + * Allowed to pass an optional `LoggerInterface $logger` instance to the `Router` + * Added a new `parameter_bag` service with related autowiring aliases to access parameters as-a-service + * Allowed the `Router` to work with any PSR-11 container + * Added option in workflow dump command to label graph with a custom label + * Using a `RouterInterface` that does not implement the `WarmableInterface` is deprecated and will not be supported in Symfony 5.0. + * The `RequestDataCollector` class has been deprecated and will be removed in Symfony 5.0. Use the `Symfony\Component\HttpKernel\DataCollector\RequestDataCollector` class instead. + +4.0.0 +----- + + * The default `type` option of the `framework.workflows.*` configuration entries is `state_machine` + * removed `AddConsoleCommandPass`, `AddConstraintValidatorsPass`, + `AddValidatorInitializersPass`, `CompilerDebugDumpPass`, `ConfigCachePass`, + `ControllerArgumentValueResolverPass`, `FormPass`, `PropertyInfoPass`, + `RoutingResolverPass`, `SerializerPass`, `ValidateWorkflowsPass` + * made `Translator::__construct()` `$defaultLocale` argument required + * removed `SessionListener`, `TestSessionListener` + * Removed `cache:clear` warmup part along with the `--no-optional-warmers` option + * Removed core form types services registration when unnecessary + * Removed `framework.serializer.cache` option and `serializer.mapping.cache.apc`, `serializer.mapping.cache.doctrine.apc` services + * Removed `ConstraintValidatorFactory` + * Removed class parameters related to routing + * Removed absolute template paths support in the template name parser + * Removed support of the `KERNEL_DIR` environment variable with `KernelTestCase::getKernelClass()`. + * Removed the `KernelTestCase::getPhpUnitXmlDir()` and `KernelTestCase::getPhpUnitCliConfigArgument()` methods. + * 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 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php index 25801a7829c91..cbd864d2ede3e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php @@ -30,7 +30,7 @@ abstract class AbstractPhpFileCacheWarmer implements CacheWarmerInterface * @param string $phpArrayFile The PHP file where metadata are cached * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached */ - public function __construct($phpArrayFile, CacheItemPoolInterface $fallbackPool) + public function __construct(string $phpArrayFile, CacheItemPoolInterface $fallbackPool) { $this->phpArrayFile = $phpArrayFile; if (!$fallbackPool instanceof AdapterInterface) { diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php index ec55e9c6fc93a..e5839b76901de 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php @@ -34,7 +34,7 @@ class AnnotationsCacheWarmer extends AbstractPhpFileCacheWarmer * @param string $phpArrayFile The PHP file where annotations are cached * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached */ - public function __construct(Reader $annotationReader, $phpArrayFile, CacheItemPoolInterface $fallbackPool, $excludeRegexp = null) + public function __construct(Reader $annotationReader, string $phpArrayFile, CacheItemPoolInterface $fallbackPool, string $excludeRegexp = null) { parent::__construct($phpArrayFile, $fallbackPool); $this->annotationReader = $annotationReader; diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php deleted file mode 100644 index 058d22bcb2391..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; - -use Symfony\Component\ClassLoader\ClassCollectionLoader; -use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; - -/** - * Generates the Class Cache (classes.php) file. - * - * @author Tugdual Saunier - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class ClassCacheCacheWarmer implements CacheWarmerInterface -{ - private $declaredClasses; - - public function __construct(array $declaredClasses = null) - { - if (\PHP_VERSION_ID >= 70000) { - @trigger_error('The '.__CLASS__.' class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); - } - - $this->declaredClasses = $declaredClasses; - } - - /** - * Warms up the cache. - * - * @param string $cacheDir The cache directory - */ - public function warmUp($cacheDir) - { - $classmap = $cacheDir.'/classes.map'; - - if (!is_file($classmap)) { - return; - } - - if (file_exists($cacheDir.'/classes.php')) { - return; - } - $declared = null !== $this->declaredClasses ? $this->declaredClasses : array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); - - ClassCollectionLoader::inline(include($classmap), $cacheDir.'/classes.php', $declared); - } - - /** - * Checks whether this warmer is optional or not. - * - * @return bool always true - */ - public function isOptional() - { - return true; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php index 795fdbfc514a5..9035bf7402932 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php @@ -26,22 +26,12 @@ */ class RouterCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { - protected $router; + private $container; - /** - * @param ContainerInterface $container - */ - public function __construct($container) + public function __construct(ContainerInterface $container) { // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. - if ($container instanceof ContainerInterface) { - $this->router = $container->get('router'); // For BC, the $router property must be populated in the constructor - } elseif ($container instanceof RouterInterface) { - $this->router = $container; - @trigger_error(sprintf('Using a "%s" as first argument of %s is deprecated since Symfony 3.4 and will be unsupported in version 4.0. Use a %s instead.', RouterInterface::class, __CLASS__, ContainerInterface::class), E_USER_DEPRECATED); - } else { - throw new \InvalidArgumentException(sprintf('%s only accepts instance of Psr\Container\ContainerInterface as first argument.', __CLASS__)); - } + $this->container = $container; } /** @@ -51,9 +41,15 @@ public function __construct($container) */ public function warmUp($cacheDir) { - if ($this->router instanceof WarmableInterface) { - $this->router->warmUp($cacheDir); + $router = $this->container->get('router'); + + if ($router instanceof WarmableInterface) { + $router->warmUp($cacheDir); + + return; } + + @trigger_error(sprintf('Passing a %s without implementing %s is deprecated since Symfony 4.1.', RouterInterface::class, WarmableInterface::class), \E_USER_DEPRECATED); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php index 22d2bcfe9cf50..0b2ede48646a0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php @@ -35,7 +35,7 @@ class SerializerCacheWarmer extends AbstractPhpFileCacheWarmer * @param string $phpArrayFile The PHP file where metadata are cached * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached */ - public function __construct(array $loaders, $phpArrayFile, CacheItemPoolInterface $fallbackPool) + public function __construct(array $loaders, string $phpArrayFile, CacheItemPoolInterface $fallbackPool) { parent::__construct($phpArrayFile, $fallbackPool); $this->loaders = $loaders; diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php index 9c69980ab14f9..cd1e4806b71b6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php @@ -34,7 +34,7 @@ class TemplateFinder implements TemplateFinderInterface * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance * @param string $rootDir The directory where global templates can be stored */ - public function __construct(KernelInterface $kernel, TemplateNameParserInterface $parser, $rootDir) + public function __construct(KernelInterface $kernel, TemplateNameParserInterface $parser, string $rootDir) { $this->kernel = $kernel; $this->parser = $parser; diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php index 7d9ec9f9d45c1..eab97ce9dbca0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php @@ -27,22 +27,10 @@ class TranslationsCacheWarmer implements CacheWarmerInterface, ServiceSubscriber private $container; private $translator; - /** - * TranslationsCacheWarmer constructor. - * - * @param ContainerInterface $container - */ - public function __construct($container) + public function __construct(ContainerInterface $container) { // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. - if ($container instanceof ContainerInterface) { - $this->container = $container; - } elseif ($container instanceof TranslatorInterface) { - $this->translator = $container; - @trigger_error(sprintf('Using a "%s" as first argument of %s is deprecated since Symfony 3.4 and will be unsupported in version 4.0. Use a %s instead.', TranslatorInterface::class, __CLASS__, ContainerInterface::class), E_USER_DEPRECATED); - } else { - throw new \InvalidArgumentException(sprintf('%s only accepts instance of Psr\Container\ContainerInterface as first argument.', __CLASS__)); - } + $this->container = $container; } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php index 2fb4ace86401c..51a965f71baa2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php @@ -37,7 +37,7 @@ class ValidatorCacheWarmer extends AbstractPhpFileCacheWarmer * @param string $phpArrayFile The PHP file where metadata are cached * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached */ - public function __construct(ValidatorBuilderInterface $validatorBuilder, $phpArrayFile, CacheItemPoolInterface $fallbackPool) + public function __construct(ValidatorBuilderInterface $validatorBuilder, string $phpArrayFile, CacheItemPoolInterface $fallbackPool) { parent::__construct($phpArrayFile, $fallbackPool); $this->validatorBuilder = $validatorBuilder; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index 35e9d73cd74a6..3c352b4c051a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; @@ -26,7 +27,7 @@ * * @final since version 3.4 */ -class AboutCommand extends ContainerAwareCommand +class AboutCommand extends Command { protected static $defaultName = 'about'; @@ -102,12 +103,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->table(array(), $rows); } - private static function formatPath($path, $baseDir = null) + private static function formatPath(string $path, string $baseDir = null): string { return null !== $baseDir ? preg_replace('~^'.preg_quote($baseDir, '~').'~', '.', $path) : $path; } - private static function formatFileSize($path) + private static function formatFileSize(string $path): string { if (is_file($path)) { $size = filesize($path) ?: 0; @@ -121,14 +122,14 @@ private static function formatFileSize($path) return Helper::formatMemory($size); } - private static function isExpired($date) + private static function isExpired(string $date): bool { $date = \DateTime::createFromFormat('m/Y', $date); return false !== $date && new \DateTime() > $date->modify('last day of this month 23:59:59'); } - private static function getDotenvVars() + private static function getDotenvVars(): array { $vars = array(); foreach (explode(',', getenv('SYMFONY_DOTENV_VARS')) as $name) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index 4aaf847ff453a..ecd47a2092f40 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -29,7 +30,7 @@ * * @final since version 3.4 */ -class AssetsInstallCommand extends ContainerAwareCommand +class AssetsInstallCommand extends Command { const METHOD_COPY = 'copy'; const METHOD_ABSOLUTE_SYMLINK = 'absolute symlink'; @@ -39,19 +40,8 @@ class AssetsInstallCommand extends ContainerAwareCommand private $filesystem; - /** - * @param Filesystem $filesystem - */ - public function __construct($filesystem = null) + public function __construct(Filesystem $filesystem) { - if (!$filesystem instanceof Filesystem) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, Filesystem::class), E_USER_DEPRECATED); - - parent::__construct($filesystem); - - return; - } - parent::__construct(); $this->filesystem = $filesystem; @@ -68,6 +58,7 @@ protected function configure() )) ->addOption('symlink', null, InputOption::VALUE_NONE, 'Symlinks the assets instead of copying it') ->addOption('relative', null, InputOption::VALUE_NONE, 'Make relative symlinks') + ->addOption('no-cleanup', null, InputOption::VALUE_NONE, 'Do not remove the assets of the bundles that no longer exist') ->setDescription('Installs bundles web assets under a public directory') ->setHelp(<<<'EOT' The %command.name% command installs bundle assets into a given @@ -97,26 +88,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->filesystem) { - $this->filesystem = $this->getContainer()->get('filesystem'); - $baseDir = $this->getContainer()->getParameter('kernel.project_dir'); - } - $kernel = $this->getApplication()->getKernel(); $targetArg = rtrim($input->getArgument('target'), '/'); if (!is_dir($targetArg)) { - $targetArg = (isset($baseDir) ? $baseDir : $kernel->getContainer()->getParameter('kernel.project_dir')).'/'.$targetArg; + $targetArg = $kernel->getContainer()->getParameter('kernel.project_dir').'/'.$targetArg; if (!is_dir($targetArg)) { - // deprecated, logic to be removed in 4.0 - // this allows the commands to work out of the box with web/ and public/ - if (is_dir(dirname($targetArg).'/web')) { - $targetArg = dirname($targetArg).'/web'; - } else { - throw new \InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); - } + throw new \InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); } } @@ -184,7 +163,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } } // remove the assets of the bundles that no longer exist - if (is_dir($bundlesDir)) { + if (!$input->getOption('no-cleanup') && is_dir($bundlesDir)) { $dirsToRemove = Finder::create()->depth(0)->directories()->exclude($validAssetDirs)->in($bundlesDir); $this->filesystem->remove($dirsToRemove); } @@ -209,13 +188,8 @@ protected function execute(InputInterface $input, OutputInterface $output) * Try to create relative symlink. * * Falling back to absolute symlink and finally hard copy. - * - * @param string $originDir - * @param string $targetDir - * - * @return string */ - private function relativeSymlinkWithFallback($originDir, $targetDir) + private function relativeSymlinkWithFallback(string $originDir, string $targetDir): string { try { $this->symlink($originDir, $targetDir, true); @@ -231,13 +205,8 @@ private function relativeSymlinkWithFallback($originDir, $targetDir) * Try to create absolute symlink. * * Falling back to hard copy. - * - * @param string $originDir - * @param string $targetDir - * - * @return string */ - private function absoluteSymlinkWithFallback($originDir, $targetDir) + private function absoluteSymlinkWithFallback(string $originDir, string $targetDir): string { try { $this->symlink($originDir, $targetDir); @@ -253,13 +222,9 @@ private function absoluteSymlinkWithFallback($originDir, $targetDir) /** * Creates symbolic link. * - * @param string $originDir - * @param string $targetDir - * @param bool $relative - * * @throws IOException if link can not be created */ - private function symlink($originDir, $targetDir, $relative = false) + private function symlink(string $originDir, string $targetDir, bool $relative = false) { if ($relative) { $this->filesystem->mkdir(dirname($targetDir)); @@ -273,13 +238,8 @@ private function symlink($originDir, $targetDir, $relative = false) /** * Copies origin to target. - * - * @param string $originDir - * @param string $targetDir - * - * @return string */ - private function hardCopy($originDir, $targetDir) + private function hardCopy(string $originDir, string $targetDir): string { $this->filesystem->mkdir($targetDir, 0777); // We use a custom iterator to ignore VCS files diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index 5437b0bcc23b5..a33b3f3d908dd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -19,7 +20,6 @@ use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\RebootableInterface; use Symfony\Component\Finder\Finder; @@ -31,28 +31,15 @@ * * @final since version 3.4 */ -class CacheClearCommand extends ContainerAwareCommand +class CacheClearCommand extends Command { protected static $defaultName = 'cache:clear'; private $cacheClearer; private $filesystem; - private $warning; - /** - * @param CacheClearerInterface $cacheClearer - * @param Filesystem|null $filesystem - */ - public function __construct($cacheClearer = null, Filesystem $filesystem = null) + public function __construct(CacheClearerInterface $cacheClearer, Filesystem $filesystem = null) { - if (!$cacheClearer instanceof CacheClearerInterface) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, CacheClearerInterface::class), E_USER_DEPRECATED); - - parent::__construct($cacheClearer); - - return; - } - parent::__construct(); $this->cacheClearer = $cacheClearer; @@ -86,18 +73,11 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->cacheClearer) { - $this->cacheClearer = $this->getContainer()->get('cache_clearer'); - $this->filesystem = $this->getContainer()->get('filesystem'); - $realCacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); - } - $fs = $this->filesystem; $io = new SymfonyStyle($input, $output); $kernel = $this->getApplication()->getKernel(); - $realCacheDir = isset($realCacheDir) ? $realCacheDir : $kernel->getContainer()->getParameter('kernel.cache_dir'); + $realCacheDir = $kernel->getContainer()->getParameter('kernel.cache_dir'); // the old cache dir name must not be longer than the real one to avoid exceeding // the maximum length of a directory or file path within it (esp. Windows MAX_PATH) $oldCacheDir = substr($realCacheDir, 0, -1).('~' === substr($realCacheDir, -1) ? '+' : '~'); @@ -131,12 +111,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->comment('Warming up cache...'); } $this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers')); - - if ($this->warning) { - @trigger_error($this->warning, E_USER_DEPRECATED); - $io->warning($this->warning); - $this->warning = null; - } } $containerDir = $fs->exists($warmupDir.'/'.$containerDir) ? false : $containerDir; @@ -166,35 +140,17 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); } - /** - * @param string $warmupDir - * @param string $realCacheDir - * @param bool $enableOptionalWarmers - */ - protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = true) + private function warmup(string $warmupDir, string $realCacheDir, bool $enableOptionalWarmers = true) { // create a temporary kernel - $realKernel = $this->getApplication()->getKernel(); - if ($realKernel instanceof RebootableInterface) { - $realKernel->reboot($warmupDir); - $tempKernel = $realKernel; - } else { - $this->warning = 'Calling "cache:clear" with a kernel that does not implement "Symfony\Component\HttpKernel\RebootableInterface" is deprecated since Symfony 3.4 and will be unsupported in 4.0.'; - $realKernelClass = get_class($realKernel); - $namespace = ''; - if (false !== $pos = strrpos($realKernelClass, '\\')) { - $namespace = substr($realKernelClass, 0, $pos); - $realKernelClass = substr($realKernelClass, $pos + 1); - } - $tempKernel = $this->getTempKernel($realKernel, $namespace, $realKernelClass, $warmupDir); - $tempKernel->boot(); - - $tempKernelReflection = new \ReflectionObject($tempKernel); - $tempKernelFile = $tempKernelReflection->getFileName(); + $kernel = $this->getApplication()->getKernel(); + if (!$kernel instanceof RebootableInterface) { + throw new \LogicException('Calling "cache:clear" with a kernel that does not implement "Symfony\Component\HttpKernel\RebootableInterface" is not supported.'); } + $kernel->reboot($warmupDir); // warmup temporary dir - $warmer = $tempKernel->getContainer()->get('cache_warmer'); + $warmer = $kernel->getContainer()->get('cache_warmer'); if ($enableOptionalWarmers) { $warmer->enableOptionalWarmers(); } @@ -209,133 +165,5 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr file_put_contents($file, $content); } } - - if ($realKernel instanceof RebootableInterface) { - return; - } - - // fix references to the Kernel in .meta files - $safeTempKernel = str_replace('\\', '\\\\', get_class($tempKernel)); - $realKernelFQN = get_class($realKernel); - - foreach (Finder::create()->files()->depth('<3')->name('*.meta')->in($warmupDir) as $file) { - file_put_contents($file, preg_replace( - '/(C\:\d+\:)"'.$safeTempKernel.'"/', - sprintf('$1"%s"', $realKernelFQN), - file_get_contents($file) - )); - } - - // fix references to container's class - $tempContainerClass = $tempKernel->getContainerClass(); - $realContainerClass = $tempKernel->getRealContainerClass(); - foreach (Finder::create()->files()->depth('<2')->name($tempContainerClass.'*')->in($warmupDir) as $file) { - $content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file)); - file_put_contents($file, $content); - rename($file, str_replace(DIRECTORY_SEPARATOR.$tempContainerClass, DIRECTORY_SEPARATOR.$realContainerClass, $file)); - } - if (is_dir($tempContainerDir = $warmupDir.'/'.get_class($tempKernel->getContainer()))) { - foreach (Finder::create()->files()->in($tempContainerDir) as $file) { - $content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file)); - file_put_contents($file, $content); - } - } - - // remove temp kernel file after cache warmed up - @unlink($tempKernelFile); - } - - /** - * @param KernelInterface $parent - * @param string $namespace - * @param string $parentClass - * @param string $warmupDir - * - * @return KernelInterface - */ - protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir) - { - $projectDir = ''; - $cacheDir = var_export($warmupDir, true); - $rootDir = var_export(realpath($parent->getRootDir()), true); - $logDir = var_export(realpath($parent->getLogDir()), true); - // the temp kernel class name must have the same length than the real one - // to avoid the many problems in serialized resources files - $class = substr($parentClass, 0, -1).'_'; - // the temp container class must be changed too - $container = $parent->getContainer(); - $realContainerClass = var_export($container->hasParameter('kernel.container_class') ? $container->getParameter('kernel.container_class') : get_class($parent->getContainer()), true); - $containerClass = substr_replace($realContainerClass, '_', -2, 1); - - if (method_exists($parent, 'getProjectDir')) { - $projectDir = var_export(realpath($parent->getProjectDir()), true); - $projectDir = <<getResources(); - \$filteredResources = array(); - foreach (\$resources as \$resource) { - if ((string) \$resource !== __FILE__) { - \$filteredResources[] = \$resource; - } - } - - \$container->setResources(\$filteredResources); - - return \$container; - } - } -} -EOF; - $this->filesystem->mkdir($warmupDir); - file_put_contents($file = $warmupDir.'/kernel.tmp', $code); - require_once $file; - $class = "$namespace\\$class"; - - return new $class($parent->getEnvironment(), $parent->isDebug()); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php index 24ab64cc9c696..aa17ad3a39908 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; @@ -23,25 +24,14 @@ * * @author Nicolas Grekas */ -final class CachePoolClearCommand extends ContainerAwareCommand +final class CachePoolClearCommand extends Command { protected static $defaultName = 'cache:pool:clear'; private $poolClearer; - /** - * @param Psr6CacheClearer $poolClearer - */ - public function __construct($poolClearer = null) + public function __construct(Psr6CacheClearer $poolClearer) { - if (!$poolClearer instanceof Psr6CacheClearer) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, Psr6CacheClearer::class), E_USER_DEPRECATED); - - parent::__construct($poolClearer); - - return; - } - parent::__construct(); $this->poolClearer = $poolClearer; @@ -71,12 +61,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->poolClearer) { - $this->poolClearer = $this->getContainer()->get('cache.global_clearer'); - $cacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); - } - $io = new SymfonyStyle($input, $output); $kernel = $this->getApplication()->getKernel(); $pools = array(); @@ -100,7 +84,7 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($clearers as $id => $clearer) { $io->comment(sprintf('Calling cache clearer: %s', $id)); - $clearer->clear(isset($cacheDir) ? $cacheDir : $kernel->getContainer()->getParameter('kernel.cache_dir')); + $clearer->clear($kernel->getContainer()->getParameter('kernel.cache_dir')); } foreach ($pools as $id => $pool) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php index 1c9cef9b93eda..145ef4fee6fdf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php @@ -31,7 +31,7 @@ final class CachePoolPruneCommand extends Command /** * @param iterable|PruneableInterface[] $pools */ - public function __construct($pools) + public function __construct(iterable $pools) { parent::__construct(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php index a5874df234674..5219e1dbbad39 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -24,25 +25,14 @@ * * @final since version 3.4 */ -class CacheWarmupCommand extends ContainerAwareCommand +class CacheWarmupCommand extends Command { protected static $defaultName = 'cache:warmup'; private $cacheWarmer; - /** - * @param CacheWarmerAggregate $cacheWarmer - */ - public function __construct($cacheWarmer = null) + public function __construct(CacheWarmerAggregate $cacheWarmer) { - if (!$cacheWarmer instanceof CacheWarmerAggregate) { - @trigger_error(sprintf('Passing a command name as the first argument of "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); - - parent::__construct($cacheWarmer); - - return; - } - parent::__construct(); $this->cacheWarmer = $cacheWarmer; @@ -78,12 +68,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->cacheWarmer) { - $this->cacheWarmer = $this->getContainer()->get('cache_warmer'); - $cacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); - } - $io = new SymfonyStyle($input, $output); $kernel = $this->getApplication()->getKernel(); @@ -93,7 +77,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->cacheWarmer->enableOptionalWarmers(); } - $this->cacheWarmer->warmUp(isset($cacheDir) ? $cacheDir : $kernel->getContainer()->getParameter('kernel.cache_dir')); + $this->cacheWarmer->warmUp($kernel->getContainer()->getParameter('kernel.cache_dir')); $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully warmed.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php index 5a55332b334f3..d2fb64e763d62 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Yaml\Yaml; /** @@ -112,7 +113,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->writeln(Yaml::dump($config, 10)); } - private function compileContainer() + private function compileContainer(): ContainerBuilder { $kernel = clone $this->getApplication()->getKernel(); $kernel->boot(); @@ -128,13 +129,11 @@ private function compileContainer() /** * Iterate over configuration until the last step of the given path. * - * @param array $config A bundle configuration - * * @throws LogicException If the configuration does not exist * * @return mixed */ - private function getConfigForPath(array $config, $path, $alias) + private function getConfigForPath(array $config, string $path, string $alias) { $steps = explode('.', $path); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index ccc6b38d92e65..cd3e2da829aae 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -13,6 +13,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; @@ -30,7 +31,7 @@ * * @internal since version 3.4 */ -class ContainerDebugCommand extends ContainerAwareCommand +class ContainerDebugCommand extends Command { protected static $defaultName = 'debug:container'; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php index 23d688495db74..41c9cc9fabc19 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php @@ -85,13 +85,17 @@ protected function execute(InputInterface $input, OutputInterface $output) } $io->newLine(); $tableRows = array(); + $hasAlias = array(); foreach ($serviceIds as $serviceId) { - $tableRows[] = array(sprintf('%s', $serviceId)); if ($builder->hasAlias($serviceId)) { + $tableRows[] = array(sprintf('%s', $serviceId)); $tableRows[] = array(sprintf(' alias to %s', $builder->getAlias($serviceId))); + $hasAlias[(string) $builder->getAlias($serviceId)] = true; + } else { + $tableRows[$serviceId] = array(sprintf('%s', $serviceId)); } } - $io->table(array(), $tableRows); + $io->table(array(), array_diff_key($tableRows, $hasAlias)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php index 2eee99377d1c6..75d98807984d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; @@ -26,24 +27,13 @@ * * @final since version 3.4 */ -class EventDispatcherDebugCommand extends ContainerAwareCommand +class EventDispatcherDebugCommand extends Command { protected static $defaultName = 'debug:event-dispatcher'; private $dispatcher; - /** - * @param EventDispatcherInterface $dispatcher - */ - public function __construct($dispatcher = null) + public function __construct(EventDispatcherInterface $dispatcher) { - if (!$dispatcher instanceof EventDispatcherInterface) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, EventDispatcherInterface::class), E_USER_DEPRECATED); - - parent::__construct($dispatcher); - - return; - } - parent::__construct(); $this->dispatcher = $dispatcher; @@ -81,11 +71,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->dispatcher) { - $this->dispatcher = $this->getEventDispatcher(); - } - $io = new SymfonyStyle($input, $output); $options = array(); @@ -105,16 +90,4 @@ protected function execute(InputInterface $input, OutputInterface $output) $options['output'] = $io; $helper->describe($io, $this->dispatcher, $options); } - - /** - * Loads the Event Dispatcher from the container. - * - * BC to removed in 4.0 - * - * @return EventDispatcherInterface - */ - protected function getEventDispatcher() - { - return $this->getContainer()->get('event_dispatcher'); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php index 5533fae74b76f..425f607f84a07 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php @@ -13,6 +13,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -30,50 +31,18 @@ * * @final since version 3.4 */ -class RouterDebugCommand extends ContainerAwareCommand +class RouterDebugCommand extends Command { protected static $defaultName = 'debug:router'; private $router; - /** - * @param RouterInterface $router - */ - public function __construct($router = null) + public function __construct(RouterInterface $router) { - if (!$router instanceof RouterInterface) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, RouterInterface::class), E_USER_DEPRECATED); - - parent::__construct($router); - - return; - } - parent::__construct(); $this->router = $router; } - /** - * {@inheritdoc} - * - * BC to be removed in 4.0 - */ - public function isEnabled() - { - if (null !== $this->router) { - return parent::isEnabled(); - } - if (!$this->getContainer()->has('router')) { - return false; - } - $router = $this->getContainer()->get('router'); - if (!$router instanceof RouterInterface) { - return false; - } - - return parent::isEnabled(); - } - /** * {@inheritdoc} */ @@ -104,11 +73,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->router) { - $this->router = $this->getContainer()->get('router'); - } - $io = new SymfonyStyle($input, $output); $name = $input->getArgument('name'); $helper = new DescriptorHelper(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php index 06cae3e7cf912..74ef9fdc96a07 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -27,51 +28,19 @@ * * @final since version 3.4 */ -class RouterMatchCommand extends ContainerAwareCommand +class RouterMatchCommand extends Command { protected static $defaultName = 'router:match'; private $router; - /** - * @param RouterInterface $router - */ - public function __construct($router = null) + public function __construct(RouterInterface $router) { - if (!$router instanceof RouterInterface) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, RouterInterface::class), E_USER_DEPRECATED); - - parent::__construct($router); - - return; - } - parent::__construct(); $this->router = $router; } - /** - * {@inheritdoc} - * - * BC to be removed in 4.0 - */ - public function isEnabled() - { - if (null !== $this->router) { - return parent::isEnabled(); - } - if (!$this->getContainer()->has('router')) { - return false; - } - $router = $this->getContainer()->get('router'); - if (!$router instanceof RouterInterface) { - return false; - } - - return parent::isEnabled(); - } - /** * {@inheritdoc} */ @@ -104,11 +73,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->router) { - $this->router = $this->getContainer()->get('router'); - } - $io = new SymfonyStyle($input, $output); $context = $this->router->getContext(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index 22d154872cf39..a444df3a5affb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; @@ -34,7 +35,7 @@ * * @final since version 3.4 */ -class TranslationDebugCommand extends ContainerAwareCommand +class TranslationDebugCommand extends Command { const MESSAGE_MISSING = 0; const MESSAGE_UNUSED = 1; @@ -48,16 +49,8 @@ class TranslationDebugCommand extends ContainerAwareCommand private $defaultTransPath; private $defaultViewsPath; - public function __construct($translator = null, TranslationReaderInterface $reader = null, ExtractorInterface $extractor = null, $defaultTransPath = null, $defaultViewsPath = null) + public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null) { - if (!$translator instanceof TranslatorInterface) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, TranslatorInterface::class), E_USER_DEPRECATED); - - parent::__construct($translator); - - return; - } - parent::__construct(); $this->translator = $translator; @@ -116,35 +109,11 @@ protected function configure() ; } - /** - * {@inheritdoc} - * - * BC to be removed in 4.0 - */ - public function isEnabled() - { - if (null !== $this->translator) { - return parent::isEnabled(); - } - if (!class_exists('Symfony\Component\Translation\Translator')) { - return false; - } - - return parent::isEnabled(); - } - /** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->translator) { - $this->translator = $this->getContainer()->get('translator'); - $this->reader = $this->getContainer()->get('translation.reader'); - $this->extractor = $this->getContainer()->get('translation.extractor'); - } - $io = new SymfonyStyle($input, $output); $locale = $input->getArgument('locale'); @@ -274,7 +243,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->table($headers, $rows); } - private function formatState($state) + private function formatState($state): string { if (self::MESSAGE_MISSING === $state) { return ' missing '; @@ -291,7 +260,7 @@ private function formatState($state) return $state; } - private function formatStates(array $states) + private function formatStates(array $states): string { $result = array(); foreach ($states as $state) { @@ -301,12 +270,12 @@ private function formatStates(array $states) return implode(' ', $result); } - private function formatId($id) + private function formatId(string $id): string { return sprintf('%s', $id); } - private function sanitizeString($string, $length = 40) + private function sanitizeString(string $string, int $length = 40): string { $string = trim(preg_replace('/\s+/', ' ', $string)); @@ -321,13 +290,7 @@ private function sanitizeString($string, $length = 40) return $string; } - /** - * @param string $locale - * @param array $transPaths - * - * @return MessageCatalogue - */ - private function extractMessages($locale, $transPaths) + private function extractMessages(string $locale, array $transPaths): MessageCatalogue { $extractedCatalogue = new MessageCatalogue($locale); foreach ($transPaths as $path) { @@ -339,13 +302,7 @@ private function extractMessages($locale, $transPaths) return $extractedCatalogue; } - /** - * @param string $locale - * @param array $transPaths - * - * @return MessageCatalogue - */ - private function loadCurrentMessages($locale, $transPaths) + private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue { $currentCatalogue = new MessageCatalogue($locale); foreach ($transPaths as $path) { @@ -358,12 +315,9 @@ private function loadCurrentMessages($locale, $transPaths) } /** - * @param string $locale - * @param array $transPaths - * * @return MessageCatalogue[] */ - private function loadFallbackCatalogues($locale, $transPaths) + private function loadFallbackCatalogues(string $locale, array $transPaths): array { $fallbackCatalogues = array(); if ($this->translator instanceof Translator || $this->translator instanceof DataCollectorTranslator || $this->translator instanceof LoggingTranslator) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php index d2c88d9c9fd0d..2404dbdba7c5f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Translation\Catalogue\TargetOperation; @@ -32,7 +33,7 @@ * * @final since version 3.4 */ -class TranslationUpdateCommand extends ContainerAwareCommand +class TranslationUpdateCommand extends Command { protected static $defaultName = 'translation:update'; @@ -43,24 +44,8 @@ class TranslationUpdateCommand extends ContainerAwareCommand private $defaultTransPath; private $defaultViewsPath; - /** - * @param TranslationWriterInterface $writer - * @param TranslationReaderInterface $reader - * @param ExtractorInterface $extractor - * @param string $defaultLocale - * @param string $defaultTransPath - * @param string $defaultViewsPath - */ - public function __construct($writer = null, TranslationReaderInterface $reader = null, ExtractorInterface $extractor = null, $defaultLocale = null, $defaultTransPath = null, $defaultViewsPath = null) + public function __construct(TranslationWriterInterface $writer, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultLocale, string $defaultTransPath = null, string $defaultViewsPath = null) { - if (!$writer instanceof TranslationWriterInterface) { - @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, TranslationWriterInterface::class), E_USER_DEPRECATED); - - parent::__construct($writer); - - return; - } - parent::__construct(); $this->writer = $writer; @@ -81,7 +66,6 @@ protected function configure() new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages, defaults to app/Resources folder'), new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'), - new InputOption('no-prefix', null, InputOption::VALUE_NONE, '[DEPRECATED] If set, no prefix is added to the translations'), new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'yml'), new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'), new InputOption('force', null, InputOption::VALUE_NONE, 'Should the update be done'), @@ -109,36 +93,11 @@ protected function configure() ; } - /** - * {@inheritdoc} - * - * BC to be removed in 4.0 - */ - public function isEnabled() - { - if (null !== $this->writer) { - return parent::isEnabled(); - } - if (!class_exists('Symfony\Component\Translation\Translator')) { - return false; - } - - return parent::isEnabled(); - } - /** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { - // BC to be removed in 4.0 - if (null === $this->writer) { - $this->writer = $this->getContainer()->get('translation.writer'); - $this->reader = $this->getContainer()->get('translation.reader'); - $this->extractor = $this->getContainer()->get('translation.extractor'); - $this->defaultLocale = $this->getContainer()->getParameter('kernel.default_locale'); - } - $io = new SymfonyStyle($input, $output); $errorIo = $io->getErrorStyle(); @@ -203,13 +162,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // load any messages from templates $extractedCatalogue = new MessageCatalogue($input->getArgument('locale')); $errorIo->comment('Parsing templates...'); - $prefix = $input->getOption('prefix'); - // @deprecated since version 3.4, to be removed in 4.0 along with the --no-prefix option - if ($input->getOption('no-prefix')) { - @trigger_error('The "--no-prefix" option is deprecated since Symfony 3.4 and will be removed in 4.0. Use the "--prefix" option with an empty string as value instead.', E_USER_DEPRECATED); - $prefix = ''; - } - $this->extractor->setPrefix($prefix); + $this->extractor->setPrefix($input->getOption('prefix')); foreach ($viewsPaths as $path) { if (is_dir($path)) { $this->extractor->extract($path, $extractedCatalogue); @@ -306,7 +259,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $errorIo->success($resultMessage.'.'); } - private function filterCatalogue(MessageCatalogue $catalogue, $domain) + private function filterCatalogue(MessageCatalogue $catalogue, string $domain): MessageCatalogue { $filteredCatalogue = new MessageCatalogue($catalogue->getLocale()); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php index 3d72226c677c3..a5a34899f5a82 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @@ -11,10 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Workflow\Dumper\GraphvizDumper; +use Symfony\Component\Workflow\Dumper\PlantUmlDumper; use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper; use Symfony\Component\Workflow\Marking; @@ -23,7 +26,7 @@ * * @final since version 3.4 */ -class WorkflowDumpCommand extends ContainerAwareCommand +class WorkflowDumpCommand extends Command { protected static $defaultName = 'workflow:dump'; @@ -36,13 +39,16 @@ protected function configure() ->setDefinition(array( new InputArgument('name', InputArgument::REQUIRED, 'A workflow name'), new InputArgument('marking', InputArgument::IS_ARRAY, 'A marking (a list of places)'), + new InputOption('label', 'l', InputOption::VALUE_REQUIRED, 'Labels a graph'), + new InputOption('dump-format', null, InputOption::VALUE_REQUIRED, 'The dump format [dot|puml]', 'dot'), )) ->setDescription('Dump a workflow') ->setHelp(<<<'EOF' The %command.name% command dumps the graphical representation of a -workflow in DOT format +workflow in different formats - %command.full_name% | dot -Tpng > workflow.png +DOT: %command.full_name% | dot -Tpng > workflow.png +PUML: %command.full_name% --dump-format=puml | java -jar plantuml.jar -p > workflow.png EOF ) @@ -56,22 +62,39 @@ protected function execute(InputInterface $input, OutputInterface $output) { $container = $this->getApplication()->getKernel()->getContainer(); $serviceId = $input->getArgument('name'); + if ($container->has('workflow.'.$serviceId)) { $workflow = $container->get('workflow.'.$serviceId); - $dumper = new GraphvizDumper(); + $type = 'workflow'; } elseif ($container->has('state_machine.'.$serviceId)) { $workflow = $container->get('state_machine.'.$serviceId); - $dumper = new StateMachineGraphvizDumper(); + $type = 'state_machine'; } else { throw new \InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $serviceId)); } + if ('puml' === $input->getOption('dump-format')) { + $transitionType = 'workflow' === $type ? PlantUmlDumper::WORKFLOW_TRANSITION : PlantUmlDumper::STATEMACHINE_TRANSITION; + $dumper = new PlantUmlDumper($transitionType); + } elseif ('workflow' === $type) { + $dumper = new GraphvizDumper(); + } else { + $dumper = new StateMachineGraphvizDumper(); + } + $marking = new Marking(); foreach ($input->getArgument('marking') as $place) { $marking->mark($place); } - $output->writeln($dumper->dump($workflow->getDefinition(), $marking)); + $options = array( + 'name' => $serviceId, + 'nofooter' => true, + 'graph' => array( + 'label' => $input->getOption('label'), + ), + ); + $output->writeln($dumper->dump($workflow->getDefinition(), $marking, $options)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php index 534ab36c34a6f..42ee30e145077 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php @@ -26,29 +26,21 @@ class XliffLintCommand extends BaseLintCommand { protected static $defaultName = 'lint:xliff'; - public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) + public function __construct() { - if (func_num_args()) { - @trigger_error(sprintf('Passing a constructor argument in "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); - } + $directoryIteratorProvider = function ($directory, $default) { + if (!is_dir($directory)) { + $directory = $this->getApplication()->getKernel()->locateResource($directory); + } - if (null === $directoryIteratorProvider) { - $directoryIteratorProvider = function ($directory, $default) { - if (!is_dir($directory)) { - $directory = $this->getApplication()->getKernel()->locateResource($directory); - } + return $default($directory); + }; - return $default($directory); - }; - } + $isReadableProvider = function ($fileOrDirectory, $default) { + return 0 === strpos($fileOrDirectory, '@') || $default($fileOrDirectory); + }; - if (null === $isReadableProvider) { - $isReadableProvider = function ($fileOrDirectory, $default) { - return 0 === strpos($fileOrDirectory, '@') || $default($fileOrDirectory); - }; - } - - parent::__construct($name, $directoryIteratorProvider, $isReadableProvider); + parent::__construct(null, $directoryIteratorProvider, $isReadableProvider); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php index 2ca60ca0673c8..4edd92ff6974e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php @@ -25,29 +25,21 @@ class YamlLintCommand extends BaseLintCommand { protected static $defaultName = 'lint:yaml'; - public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) + public function __construct() { - if (func_num_args()) { - @trigger_error(sprintf('Passing a constructor argument in "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); - } + $directoryIteratorProvider = function ($directory, $default) { + if (!is_dir($directory)) { + $directory = $this->getApplication()->getKernel()->locateResource($directory); + } - if (null === $directoryIteratorProvider) { - $directoryIteratorProvider = function ($directory, $default) { - if (!is_dir($directory)) { - $directory = $this->getApplication()->getKernel()->locateResource($directory); - } + return $default($directory); + }; - return $default($directory); - }; - } + $isReadableProvider = function ($fileOrDirectory, $default) { + return 0 === strpos($fileOrDirectory, '@') || $default($fileOrDirectory); + }; - if (null === $isReadableProvider) { - $isReadableProvider = function ($fileOrDirectory, $default) { - return 0 === strpos($fileOrDirectory, '@') || $default($fileOrDirectory); - }; - } - - parent::__construct($name, $directoryIteratorProvider, $isReadableProvider); + parent::__construct(null, $directoryIteratorProvider, $isReadableProvider); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index fde8e9280f5f2..7b87fcf8965cd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -207,13 +207,7 @@ protected function getRouteData(Route $route) ); } - /** - * @param Definition $definition - * @param bool $omitTags - * - * @return array - */ - private function getContainerDefinitionData(Definition $definition, $omitTags = false, $showArguments = false) + private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false): array { $data = array( 'class' => (string) $definition->getClass(), @@ -226,13 +220,6 @@ private function getContainerDefinitionData(Definition $definition, $omitTags = 'autoconfigure' => $definition->isAutoconfigured(), ); - // forward compatibility with DependencyInjection component in version 4.0 - if (method_exists($definition, 'getAutowiringTypes')) { - foreach ($definition->getAutowiringTypes(false) as $autowiringType) { - $data['autowiring_types'][] = $autowiringType; - } - } - if ($showArguments) { $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $showArguments); } @@ -274,10 +261,7 @@ private function getContainerDefinitionData(Definition $definition, $omitTags = return $data; } - /** - * @return array - */ - private function getContainerAliasData(Alias $alias) + private function getContainerAliasData(Alias $alias): array { return array( 'service' => (string) $alias, @@ -285,13 +269,7 @@ private function getContainerAliasData(Alias $alias) ); } - /** - * @param EventDispatcherInterface $eventDispatcher - * @param string|null $event - * - * @return array - */ - private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, $event = null) + private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, string $event = null): array { $data = array(); @@ -317,13 +295,7 @@ private function getEventDispatcherListenersData(EventDispatcherInterface $event return $data; } - /** - * @param callable $callable - * @param array $options - * - * @return array - */ - private function getCallableData($callable, array $options = array()) + private function getCallableData($callable, array $options = array()): array { $data = array(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 9644a440df4f2..8836cc234bfb0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -191,13 +191,6 @@ protected function describeContainerDefinition(Definition $definition, array $op ."\n".'- Autoconfigured: '.($definition->isAutoconfigured() ? 'yes' : 'no') ; - // forward compatibility with DependencyInjection component in version 4.0 - if (method_exists($definition, 'getAutowiringTypes')) { - foreach ($definition->getAutowiringTypes(false) as $autowiringType) { - $output .= "\n".'- Autowiring Type: `'.$autowiringType.'`'; - } - } - if (isset($options['show_arguments']) && $options['show_arguments']) { $output .= "\n".'- Arguments: '.($definition->getArguments() ? 'yes' : 'no'); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index ab04ff47f794e..e4d4d1ea209a3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -310,11 +310,6 @@ protected function describeContainerDefinition(Definition $definition, array $op $tableRows[] = array('Autowired', $definition->isAutowired() ? 'yes' : 'no'); $tableRows[] = array('Autoconfigured', $definition->isAutoconfigured() ? 'yes' : 'no'); - // forward compatibility with DependencyInjection component in version 4.0 - if (method_exists($definition, 'getAutowiringTypes') && $autowiringTypes = $definition->getAutowiringTypes(false)) { - $tableRows[] = array('Autowiring Types', implode(', ', $autowiringTypes)); - } - if ($definition->getFile()) { $tableRows[] = array('Required File', $definition->getFile() ? $definition->getFile() : '-'); } @@ -433,12 +428,7 @@ private function renderEventListenerTable(EventDispatcherInterface $eventDispatc $io->table($tableHeaders, $tableRows); } - /** - * @param array $config - * - * @return string - */ - private function formatRouterConfig(array $config) + private function formatRouterConfig(array $config): string { if (empty($config)) { return 'NONE'; @@ -454,12 +444,7 @@ private function formatRouterConfig(array $config) return trim($configAsString); } - /** - * @param callable $callable - * - * @return string - */ - private function formatCallable($callable) + private function formatCallable($callable): string { if (is_array($callable)) { if (is_object($callable[0])) { @@ -484,11 +469,7 @@ private function formatCallable($callable) throw new \InvalidArgumentException('Callable is not describable.'); } - /** - * @param string $content - * @param array $options - */ - private function writeText($content, array $options = array()) + private function writeText(string $content, array $options = array()) { $this->write( isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index cadd2ff6059ee..1e3148db9a697 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -141,10 +141,7 @@ private function writeDocument(\DOMDocument $dom) $this->write($dom->saveXML()); } - /** - * @return \DOMDocument - */ - private function getRouteCollectionDocument(RouteCollection $routes) + private function getRouteCollectionDocument(RouteCollection $routes): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($routesXML = $dom->createElement('routes')); @@ -157,13 +154,7 @@ private function getRouteCollectionDocument(RouteCollection $routes) return $dom; } - /** - * @param Route $route - * @param string|null $name - * - * @return \DOMDocument - */ - private function getRouteDocument(Route $route, $name = null) + private function getRouteDocument(Route $route, string $name = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($routeXML = $dom->createElement('route')); @@ -226,10 +217,7 @@ private function getRouteDocument(Route $route, $name = null) return $dom; } - /** - * @return \DOMDocument - */ - private function getContainerParametersDocument(ParameterBag $parameters) + private function getContainerParametersDocument(ParameterBag $parameters): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($parametersXML = $dom->createElement('parameters')); @@ -243,13 +231,7 @@ private function getContainerParametersDocument(ParameterBag $parameters) return $dom; } - /** - * @param ContainerBuilder $builder - * @param bool $showPrivate - * - * @return \DOMDocument - */ - private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivate = false) + private function getContainerTagsDocument(ContainerBuilder $builder, bool $showPrivate = false): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); @@ -267,15 +249,7 @@ private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivat return $dom; } - /** - * @param mixed $service - * @param string $id - * @param ContainerBuilder|null $builder - * @param bool $showArguments - * - * @return \DOMDocument - */ - private function getContainerServiceDocument($service, $id, ContainerBuilder $builder = null, $showArguments = false) + private function getContainerServiceDocument($service, string $id, ContainerBuilder $builder = null, bool $showArguments = false): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); @@ -295,16 +269,7 @@ private function getContainerServiceDocument($service, $id, ContainerBuilder $bu return $dom; } - /** - * @param ContainerBuilder $builder - * @param string|null $tag - * @param bool $showPrivate - * @param bool $showArguments - * @param callable $filter - * - * @return \DOMDocument - */ - private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false, $showArguments = false, $filter = null) + private function getContainerServicesDocument(ContainerBuilder $builder, string $tag = null, bool $showPrivate = false, bool $showArguments = false, callable $filter = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); @@ -329,14 +294,7 @@ private function getContainerServicesDocument(ContainerBuilder $builder, $tag = return $dom; } - /** - * @param Definition $definition - * @param string|null $id - * @param bool $omitTags - * - * @return \DOMDocument - */ - private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false, $showArguments = false) + private function getContainerDefinitionDocument(Definition $definition, string $id = null, bool $omitTags = false, bool $showArguments = false): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($serviceXML = $dom->createElement('definition')); @@ -453,13 +411,7 @@ private function getArgumentNodes(array $arguments, \DOMDocument $dom) return $nodes; } - /** - * @param Alias $alias - * @param string|null $id - * - * @return \DOMDocument - */ - private function getContainerAliasDocument(Alias $alias, $id = null) + private function getContainerAliasDocument(Alias $alias, string $id = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($aliasXML = $dom->createElement('alias')); @@ -474,10 +426,7 @@ private function getContainerAliasDocument(Alias $alias, $id = null) return $dom; } - /** - * @return \DOMDocument - */ - private function getContainerParameterDocument($parameter, $options = array()) + private function getContainerParameterDocument($parameter, $options = array()): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($parameterXML = $dom->createElement('parameter')); @@ -491,13 +440,7 @@ private function getContainerParameterDocument($parameter, $options = array()) return $dom; } - /** - * @param EventDispatcherInterface $eventDispatcher - * @param string|null $event - * - * @return \DOMDocument - */ - private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, $event = null) + private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, string $event = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher')); @@ -529,12 +472,7 @@ private function appendEventListenerDocument(EventDispatcherInterface $eventDisp } } - /** - * @param callable $callable - * - * @return \DOMDocument - */ - private function getCallableDocument($callable) + private function getCallableDocument($callable): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($callableXML = $dom->createElement('callable')); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php index 3ba3b8471edc4..2f1b2a9352410 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php @@ -52,6 +52,22 @@ public function setContainer(ContainerInterface $container) return $previous; } + /** + * Gets a container parameter by its name. + * + * @return mixed + * + * @final + */ + protected function getParameter(string $name) + { + if (!$this->container->has('parameter_bag')) { + throw new \LogicException('The "parameter_bag" service is not available. Try running "composer require dependency-injection:^4.1"'); + } + + return $this->container->get('parameter_bag')->get($name); + } + public static function getSubscribedServices() { return array( @@ -67,6 +83,7 @@ public static function getSubscribedServices() 'form.factory' => '?'.FormFactoryInterface::class, 'security.token_storage' => '?'.TokenStorageInterface::class, 'security.csrf.token_manager' => '?'.CsrfTokenManagerInterface::class, + 'parameter_bag' => '?'.ContainerInterface::class, ); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index b9f15d7c2507b..c104ba10c2465 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -29,13 +29,11 @@ abstract class Controller implements ContainerAwareInterface /** * Gets a container configuration parameter by its name. * - * @param string $name The parameter name - * * @return mixed * * @final since version 3.4 */ - protected function getParameter($name) + protected function getParameter(string $name) { return $this->container->getParameter($name); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php index be2a92a29114b..21b13f91e8cc7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php @@ -47,47 +47,32 @@ public function parse($controller) } $originalController = $controller; - list($bundle, $controller, $action) = $parts; + list($bundleName, $controller, $action) = $parts; $controller = str_replace('/', '\\', $controller); - $bundles = array(); try { // this throws an exception if there is no such bundle - $allBundles = $this->kernel->getBundle($bundle, false, true); + $bundle = $this->kernel->getBundle($bundleName); } catch (\InvalidArgumentException $e) { $message = sprintf( 'The "%s" (from the _controller value "%s") does not exist or is not enabled in your kernel!', - $bundle, + $bundleName, $originalController ); - if ($alternative = $this->findAlternative($bundle)) { + if ($alternative = $this->findAlternative($bundleName)) { $message .= sprintf(' Did you mean "%s:%s:%s"?', $alternative, $controller, $action); } throw new \InvalidArgumentException($message, 0, $e); } - if (!is_array($allBundles)) { - // happens when HttpKernel is version 4+ - $allBundles = array($allBundles); + $try = $bundle->getNamespace().'\\Controller\\'.$controller.'Controller'; + if (class_exists($try)) { + return $try.'::'.$action.'Action'; } - foreach ($allBundles as $b) { - $try = $b->getNamespace().'\\Controller\\'.$controller.'Controller'; - if (class_exists($try)) { - return $try.'::'.$action.'Action'; - } - - $bundles[] = $b->getName(); - $msg = sprintf('The _controller value "%s:%s:%s" maps to a "%s" class, but this class was not found. Create this class or check the spelling of the class and its namespace.', $bundle, $controller, $action, $try); - } - - if (count($bundles) > 1) { - $msg = sprintf('Unable to find controller "%s:%s" in bundles %s.', $bundle, $controller, implode(', ', $bundles)); - } - - throw new \InvalidArgumentException($msg); + throw new \InvalidArgumentException(sprintf('The _controller value "%s:%s:%s" maps to a "%s" class, but this class was not found. Create this class or check the spelling of the class and its namespace.', $bundleName, $controller, $action, $try)); } /** @@ -121,12 +106,8 @@ public function build($controller) /** * Attempts to find a bundle that is *similar* to the given bundle name. - * - * @param string $nonExistentBundleName - * - * @return string */ - private function findAlternative($nonExistentBundleName) + private function findAlternative(string $nonExistentBundleName): ?string { $bundleNames = array_map(function ($b) { return $b->getName(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index e2a7d3267d3db..f0ac2a248faee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -60,13 +60,6 @@ protected function instantiateController($class) private function configureController($controller) { if ($controller instanceof ContainerAwareInterface) { - // @deprecated switch, to be removed in 4.0 where these classes - // won't implement ContainerAwareInterface anymore - switch (\get_class($controller)) { - case RedirectController::class: - case TemplateController::class: - return $controller; - } $controller->setContainer($this->container); } if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php index b5843319c750c..2c6cb565d0116 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php @@ -42,13 +42,9 @@ trait ControllerTrait /** * Returns true if the service id is defined. * - * @param string $id The service id - * - * @return bool true if the service id is defined, false otherwise - * * @final since version 3.4 */ - protected function has($id) + protected function has(string $id): bool { return $this->container->has($id); } @@ -56,13 +52,11 @@ protected function has($id) /** * Gets a container service by its id. * - * @param string $id The service id - * * @return object The service * * @final since version 3.4 */ - protected function get($id) + protected function get(string $id) { return $this->container->get($id); } @@ -70,17 +64,11 @@ protected function get($id) /** * Generates a URL from the given parameters. * - * @param string $route The name of the route - * @param array $parameters An array of parameters - * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) - * - * @return string The generated URL - * * @see UrlGeneratorInterface * * @final since version 3.4 */ - protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + protected function generateUrl(string $route, array $parameters = array(), int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string { return $this->container->get('router')->generate($route, $parameters, $referenceType); } @@ -89,17 +77,12 @@ protected function generateUrl($route, $parameters = array(), $referenceType = U * Forwards the request to another controller. * * @param string $controller The controller name (a string like BlogBundle:Post:index) - * @param array $path An array of path parameters - * @param array $query An array of query parameters - * - * @return Response A Response instance * * @final since version 3.4 */ - protected function forward($controller, array $path = array(), array $query = array()) + protected function forward(string $controller, array $path = array(), array $query = array()): Response { $request = $this->container->get('request_stack')->getCurrentRequest(); - $path['_forwarded'] = $request->attributes; $path['_controller'] = $controller; $subRequest = $request->duplicate($query, null, $path); @@ -109,14 +92,9 @@ protected function forward($controller, array $path = array(), array $query = ar /** * Returns a RedirectResponse to the given URL. * - * @param string $url The URL to redirect to - * @param int $status The status code to use for the Response - * - * @return RedirectResponse - * * @final since version 3.4 */ - protected function redirect($url, $status = 302) + protected function redirect(string $url, int $status = 302): RedirectResponse { return new RedirectResponse($url, $status); } @@ -124,15 +102,9 @@ protected function redirect($url, $status = 302) /** * Returns a RedirectResponse to the given route with the given parameters. * - * @param string $route The name of the route - * @param array $parameters An array of parameters - * @param int $status The status code to use for the Response - * - * @return RedirectResponse - * * @final since version 3.4 */ - protected function redirectToRoute($route, array $parameters = array(), $status = 302) + protected function redirectToRoute(string $route, array $parameters = array(), int $status = 302): RedirectResponse { return $this->redirect($this->generateUrl($route, $parameters), $status); } @@ -140,16 +112,9 @@ protected function redirectToRoute($route, array $parameters = array(), $status /** * Returns a JsonResponse that uses the serializer component if enabled, or json_encode. * - * @param mixed $data The response data - * @param int $status The status code to use for the Response - * @param array $headers Array of extra headers to add - * @param array $context Context to pass to serializer when using serializer component - * - * @return JsonResponse - * * @final since version 3.4 */ - protected function json($data, $status = 200, $headers = array(), $context = array()) + protected function json($data, int $status = 200, array $headers = array(), array $context = array()): JsonResponse { if ($this->container->has('serializer')) { $json = $this->container->get('serializer')->serialize($data, 'json', array_merge(array( @@ -165,15 +130,11 @@ protected function json($data, $status = 200, $headers = array(), $context = arr /** * Returns a BinaryFileResponse object with original or customized file name and disposition header. * - * @param \SplFileInfo|string $file File object or path to file to be sent as response - * @param string|null $fileName File name to be sent to response or null (will use original file name) - * @param string $disposition Disposition of response ("attachment" is default, other type is "inline") - * - * @return BinaryFileResponse + * @param \SplFileInfo|string $file File object or path to file to be sent as response * * @final since version 3.4 */ - protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) + protected function file($file, string $fileName = null, string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT): BinaryFileResponse { $response = new BinaryFileResponse($file); $response->setContentDisposition($disposition, null === $fileName ? $response->getFile()->getFilename() : $fileName); @@ -184,14 +145,11 @@ protected function file($file, $fileName = null, $disposition = ResponseHeaderBa /** * Adds a flash message to the current session for type. * - * @param string $type The type - * @param string $message The message - * * @throws \LogicException * * @final since version 3.4 */ - protected function addFlash($type, $message) + protected function addFlash(string $type, string $message) { if (!$this->container->has('session')) { throw new \LogicException('You can not use the addFlash method if sessions are disabled. Enable them in "config/packages/framework.yaml".'); @@ -203,16 +161,11 @@ protected function addFlash($type, $message) /** * Checks if the attributes are granted against the current authentication token and optionally supplied subject. * - * @param mixed $attributes The attributes - * @param mixed $subject The subject - * - * @return bool - * * @throws \LogicException * * @final since version 3.4 */ - protected function isGranted($attributes, $subject = null) + protected function isGranted($attributes, $subject = null): bool { if (!$this->container->has('security.authorization_checker')) { throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".'); @@ -225,15 +178,11 @@ protected function isGranted($attributes, $subject = null) * Throws an exception unless the attributes are granted against the current authentication token and optionally * supplied subject. * - * @param mixed $attributes The attributes - * @param mixed $subject The subject - * @param string $message The message passed to the exception - * * @throws AccessDeniedException * * @final since version 3.4 */ - protected function denyAccessUnlessGranted($attributes, $subject = null, $message = 'Access Denied.') + protected function denyAccessUnlessGranted($attributes, $subject = null, string $message = 'Access Denied.') { if (!$this->isGranted($attributes, $subject)) { $exception = $this->createAccessDeniedException($message); @@ -247,14 +196,9 @@ protected function denyAccessUnlessGranted($attributes, $subject = null, $messag /** * Returns a rendered view. * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * - * @return string The rendered view - * * @final since version 3.4 */ - protected function renderView($view, array $parameters = array()) + protected function renderView(string $view, array $parameters = array()): string { if ($this->container->has('templating')) { return $this->container->get('templating')->render($view, $parameters); @@ -270,15 +214,9 @@ protected function renderView($view, array $parameters = array()) /** * Renders a view. * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * @param Response $response A response instance - * - * @return Response A Response instance - * * @final since version 3.4 */ - protected function render($view, array $parameters = array(), Response $response = null) + protected function render(string $view, array $parameters = array(), Response $response = null): Response { if ($this->container->has('templating')) { $content = $this->container->get('templating')->render($view, $parameters); @@ -300,15 +238,9 @@ protected function render($view, array $parameters = array(), Response $response /** * Streams a view. * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * @param StreamedResponse $response A response instance - * - * @return StreamedResponse A StreamedResponse instance - * * @final since version 3.4 */ - protected function stream($view, array $parameters = array(), StreamedResponse $response = null) + protected function stream(string $view, array $parameters = array(), StreamedResponse $response = null): StreamedResponse { if ($this->container->has('templating')) { $templating = $this->container->get('templating'); @@ -342,14 +274,9 @@ protected function stream($view, array $parameters = array(), StreamedResponse $ * * throw $this->createNotFoundException('Page not found!'); * - * @param string $message A message - * @param \Exception|null $previous The previous exception - * - * @return NotFoundHttpException - * * @final since version 3.4 */ - protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) + protected function createNotFoundException(string $message = 'Not Found', \Exception $previous = null): NotFoundHttpException { return new NotFoundHttpException($message, $previous); } @@ -361,16 +288,11 @@ protected function createNotFoundException($message = 'Not Found', \Exception $p * * throw $this->createAccessDeniedException('Unable to access this page!'); * - * @param string $message A message - * @param \Exception|null $previous The previous exception - * - * @return AccessDeniedException - * * @throws \LogicException If the Security component is not available * * @final since version 3.4 */ - protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) + protected function createAccessDeniedException(string $message = 'Access Denied.', \Exception $previous = null): AccessDeniedException { if (!class_exists(AccessDeniedException::class)) { throw new \LogicException('You can not use the "createAccessDeniedException" method if the Security component is not available. Try running "composer require symfony/security-bundle".'); @@ -382,15 +304,9 @@ protected function createAccessDeniedException($message = 'Access Denied.', \Exc /** * Creates and returns a Form instance from the type of the form. * - * @param string $type The fully qualified class name of the form type - * @param mixed $data The initial data for the form - * @param array $options Options for the form - * - * @return FormInterface - * * @final since version 3.4 */ - protected function createForm($type, $data = null, array $options = array()) + protected function createForm(string $type, $data = null, array $options = array()): FormInterface { return $this->container->get('form.factory')->create($type, $data, $options); } @@ -398,14 +314,9 @@ protected function createForm($type, $data = null, array $options = array()) /** * Creates and returns a form builder instance. * - * @param mixed $data The initial data for the form - * @param array $options Options for the form - * - * @return FormBuilderInterface - * * @final since version 3.4 */ - protected function createFormBuilder($data = null, array $options = array()) + protected function createFormBuilder($data = null, array $options = array()): FormBuilderInterface { return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); } @@ -413,13 +324,11 @@ protected function createFormBuilder($data = null, array $options = array()) /** * Shortcut to return the Doctrine Registry service. * - * @return ManagerRegistry - * * @throws \LogicException If DoctrineBundle is not available * * @final since version 3.4 */ - protected function getDoctrine() + protected function getDoctrine(): ManagerRegistry { if (!$this->container->has('doctrine')) { throw new \LogicException('The DoctrineBundle is not registered in your application. Try running "composer require symfony/orm-pack".'); @@ -460,14 +369,12 @@ protected function getUser() /** * Checks the validity of a CSRF token. * - * @param string $id The id used when generating the token - * @param string $token The actual token sent with the request that should be validated - * - * @return bool + * @param string $id The id used when generating the token + * @param string|null $token The actual token sent with the request that should be validated * * @final since version 3.4 */ - protected function isCsrfTokenValid($id, $token) + protected function isCsrfTokenValid(string $id, ?string $token): bool { if (!$this->container->has('security.csrf.token_manager')) { throw new \LogicException('CSRF protection is not enabled in your application. Enable it with the "csrf_protection" key in "config/packages/framework.yaml".'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php index 003555b12d032..8e6ce84408d09 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -11,8 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -26,35 +24,19 @@ * * @final since version 3.4 */ -class RedirectController implements ContainerAwareInterface +class RedirectController { - /** - * @deprecated since version 3.4, to be removed in 4.0 - */ - protected $container; - private $router; private $httpPort; private $httpsPort; - public function __construct(UrlGeneratorInterface $router = null, $httpPort = null, $httpsPort = null) + public function __construct(UrlGeneratorInterface $router = null, int $httpPort = null, int $httpsPort = null) { $this->router = $router; $this->httpPort = $httpPort; $this->httpsPort = $httpsPort; } - /** - * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. - */ - public function setContainer(ContainerInterface $container = null) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Inject an UrlGeneratorInterface using the constructor instead.', __METHOD__), E_USER_DEPRECATED); - - $this->container = $container; - $this->router = $container->get('router'); - } - /** * Redirects to another route with the given name. * @@ -69,11 +51,9 @@ public function setContainer(ContainerInterface $container = null) * @param bool $permanent Whether the redirection is permanent * @param bool|array $ignoreAttributes Whether to ignore attributes or an array of attributes to ignore * - * @return Response A Response instance - * * @throws HttpException In case the route name is empty */ - public function redirectAction(Request $request, $route, $permanent = false, $ignoreAttributes = false) + public function redirectAction(Request $request, string $route, bool $permanent = false, $ignoreAttributes = false): Response { if ('' == $route) { throw new HttpException($permanent ? 410 : 404); @@ -104,14 +84,12 @@ public function redirectAction(Request $request, $route, $permanent = false, $ig * @param string $path The absolute path or URL to redirect to * @param bool $permanent Whether the redirect is permanent or not * @param string|null $scheme The URL scheme (null to keep the current one) - * @param int|null $httpPort The HTTP port (null to keep the current one for the same scheme or the configured port in the container) - * @param int|null $httpsPort The HTTPS port (null to keep the current one for the same scheme or the configured port in the container) - * - * @return Response A Response instance + * @param int|null $httpPort The HTTP port (null to keep the current one for the same scheme or the default configured port) + * @param int|null $httpsPort The HTTPS port (null to keep the current one for the same scheme or the default configured port) * * @throws HttpException In case the path is empty */ - public function urlRedirectAction(Request $request, $path, $permanent = false, $scheme = null, $httpPort = null, $httpsPort = null) + public function urlRedirectAction(Request $request, string $path, bool $permanent = false, string $scheme = null, int $httpPort = null, int $httpsPort = null): Response { if ('' == $path) { throw new HttpException($permanent ? 410 : 404); @@ -142,9 +120,6 @@ public function urlRedirectAction(Request $request, $path, $permanent = false, $ if (null === $httpPort) { if ('http' === $request->getScheme()) { $httpPort = $request->getPort(); - } elseif ($this->container && $this->container->hasParameter('request_listener.http_port')) { - @trigger_error(sprintf('Passing the http port as a container parameter is deprecated since Symfony 3.4 and won\'t be possible in 4.0. Pass it to the constructor of the "%s" class instead.', __CLASS__), E_USER_DEPRECATED); - $httpPort = $this->container->getParameter('request_listener.http_port'); } else { $httpPort = $this->httpPort; } @@ -157,9 +132,6 @@ public function urlRedirectAction(Request $request, $path, $permanent = false, $ if (null === $httpsPort) { if ('https' === $request->getScheme()) { $httpsPort = $request->getPort(); - } elseif ($this->container && $this->container->hasParameter('request_listener.https_port')) { - @trigger_error(sprintf('Passing the https port as a container parameter is deprecated since Symfony 3.4 and won\'t be possible in 4.0. Pass it to the constructor of the "%s" class instead.', __CLASS__), E_USER_DEPRECATED); - $httpsPort = $this->container->getParameter('request_listener.https_port'); } else { $httpsPort = $this->httpsPort; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php index f72d556f60b51..77622099fedfb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php @@ -11,8 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Templating\EngineInterface; use Twig\Environment; @@ -24,13 +22,8 @@ * * @final since version 3.4 */ -class TemplateController implements ContainerAwareInterface +class TemplateController { - /** - * @deprecated since version 3.4, to be removed in 4.0 - */ - protected $container; - private $twig; private $templating; @@ -40,21 +33,6 @@ public function __construct(Environment $twig = null, EngineInterface $templatin $this->templating = $templating; } - /** - * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. - */ - public function setContainer(ContainerInterface $container = null) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Inject a Twig Environment or an EngineInterface using the constructor instead.', __METHOD__), E_USER_DEPRECATED); - - if ($container->has('templating')) { - $this->templating = $container->get('templating'); - } elseif ($container->has('twig')) { - $this->twig = $container->get('twig'); - } - $this->container = $container; - } - /** * Renders a template. * @@ -62,10 +40,8 @@ public function setContainer(ContainerInterface $container = null) * @param int|null $maxAge Max age for client caching * @param int|null $sharedAge Max age for shared (proxy) caching * @param bool|null $private Whether or not caching should apply for client caches only - * - * @return Response A Response instance */ - public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null) + public function templateAction(string $template, int $maxAge = null, int $sharedAge = null, bool $private = null): Response { if ($this->templating) { $response = new Response($this->templating->render($template)); @@ -91,4 +67,9 @@ public function templateAction($template, $maxAge = null, $sharedAge = null, $pr return $response; } + + public function __invoke(string $template, int $maxAge = null, int $sharedAge = null, bool $private = null): Response + { + return $this->templateAction($template, $maxAge, $sharedAge, $private); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DataCollector/RequestDataCollector.php b/src/Symfony/Bundle/FrameworkBundle/DataCollector/RequestDataCollector.php index 55355bdee705e..53c9bef485f70 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Bundle/FrameworkBundle/DataCollector/RequestDataCollector.php @@ -11,67 +11,17 @@ namespace Symfony\Bundle\FrameworkBundle\DataCollector; -use Symfony\Component\HttpFoundation\ParameterBag; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector as BaseRequestCollector; -use Symfony\Component\HttpKernel\Event\FilterControllerEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector as BaseRequestDataCollector; + +@trigger_error(sprintf('The "%s" class is deprecated since version 4.1 and will be removed in Symfony 5.0. Use %s instead.', RequestDataCollector::class, BaseRequestDataCollector::class), E_USER_DEPRECATED); /** * RequestDataCollector. * * @author Jules Pietri + * + * @deprecated since version 4.1, to be removed in Symfony 5.0 */ -class RequestDataCollector extends BaseRequestCollector implements EventSubscriberInterface +class RequestDataCollector extends BaseRequestDataCollector { - /** - * {@inheritdoc} - */ - public function collect(Request $request, Response $response, \Exception $exception = null) - { - parent::collect($request, $response, $exception); - - if ($parentRequestAttributes = $request->attributes->get('_forwarded')) { - if ($parentRequestAttributes instanceof ParameterBag) { - $parentRequestAttributes->set('_forward_token', $response->headers->get('x-debug-token')); - } - } - if ($request->attributes->has('_forward_controller')) { - $this->data['forward'] = array( - 'token' => $request->attributes->get('_forward_token'), - 'controller' => $this->parseController($request->attributes->get('_forward_controller')), - ); - } - } - - /** - * Gets the parsed forward controller. - * - * @return array|bool An array with keys 'token' the forward profile token, and - * 'controller' the parsed forward controller, false otherwise - */ - public function getForward() - { - return isset($this->data['forward']) ? $this->data['forward'] : false; - } - - public function onKernelController(FilterControllerEvent $event) - { - $this->controllers[$event->getRequest()] = $event->getController(); - - if ($parentRequestAttributes = $event->getRequest()->attributes->get('_forwarded')) { - if ($parentRequestAttributes instanceof ParameterBag) { - $parentRequestAttributes->set('_forward_controller', $event->getController()); - } - } - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'request'; - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php deleted file mode 100644 index 25a16060ff7ee..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', AddCacheClearerPass::class), E_USER_DEPRECATED); - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Reference; - -/** - * Registers the cache clearers. - * - * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments. - * - * @author Dustin Dobervich - */ -class AddCacheClearerPass implements CompilerPassInterface -{ - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('cache_clearer')) { - return; - } - - $clearers = array(); - foreach ($container->findTaggedServiceIds('kernel.cache_clearer', true) as $id => $attributes) { - $clearers[] = new Reference($id); - } - - $container->getDefinition('cache_clearer')->replaceArgument(0, $clearers); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php deleted file mode 100644 index c60eecdb49533..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', AddCacheWarmerPass::class), E_USER_DEPRECATED); - -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; - -/** - * Registers the cache warmers. - * - * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead. - * - * @author Fabien Potencier - */ -class AddCacheWarmerPass implements CompilerPassInterface -{ - use PriorityTaggedServiceTrait; - - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('cache_warmer')) { - return; - } - - $warmers = $this->findAndSortTaggedServices('kernel.cache_warmer', $container); - - if (empty($warmers)) { - return; - } - - $container->getDefinition('cache_warmer')->replaceArgument(0, $warmers); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php deleted file mode 100644 index 7e261bb1d372b..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('%s is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass instead.', AddConsoleCommandPass::class), E_USER_DEPRECATED); - -use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass as BaseAddConsoleCommandPass; - -/** - * Registers console commands. - * - * @author Grégoire Pineau - * - * @deprecated since version 3.3, to be removed in 4.0. Use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass instead. - */ -class AddConsoleCommandPass extends BaseAddConsoleCommandPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php deleted file mode 100644 index a118afb8241b5..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass as BaseAddConstraintValidatorsPass; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', AddConstraintValidatorsPass::class, BaseAddConstraintValidatorsPass::class), E_USER_DEPRECATED); - -/** - * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseAddConstraintValidatorsPass} instead - */ -class AddConstraintValidatorsPass extends BaseAddConstraintValidatorsPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php deleted file mode 100644 index 5824de478bdc2..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass as BaseAddValidatorsInitializerPass; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', AddValidatorInitializersPass::class, BaseAddValidatorsInitializerPass::class), E_USER_DEPRECATED); - -/** - * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseAddValidatorInitializersPass} instead - */ -class AddValidatorInitializersPass extends BaseAddValidatorsInitializerPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php index 60cdadb49b250..549cf2797c615 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CacheCollectorPass.php @@ -50,11 +50,6 @@ public function process(ContainerBuilder $container) $definition->setTags(array()); $definition->setPublic(false); - if (method_exists($definition, 'getAutowiringTypes') && $types = $definition->getAutowiringTypes(false)) { - $recorder->setAutowiringTypes($types); - $definition->setAutowiringTypes(array()); - } - $container->setDefinition($innerId, $definition); $container->setDefinition($id, $recorder); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPrunerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPrunerPass.php index d02e2c372c3d4..bd692e4261d5d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPrunerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPrunerPass.php @@ -27,7 +27,7 @@ class CachePoolPrunerPass implements CompilerPassInterface private $cacheCommandServiceId; private $cachePoolTag; - public function __construct($cacheCommandServiceId = CachePoolPruneCommand::class, $cachePoolTag = 'cache.pool') + public function __construct(string $cacheCommandServiceId = CachePoolPruneCommand::class, string $cachePoolTag = 'cache.pool') { $this->cacheCommandServiceId = $cacheCommandServiceId; $this->cachePoolTag = $cachePoolTag; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php deleted file mode 100644 index 3425f0750f170..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', CompilerDebugDumpPass::class), E_USER_DEPRECATED); - -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; - -/** - * @deprecated since version 3.3, to be removed in 4.0. - */ -class CompilerDebugDumpPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container) - { - $filename = self::getCompilerLogFilename($container); - - $filesystem = new Filesystem(); - $filesystem->dumpFile($filename, implode("\n", $container->getCompiler()->getLog()), null); - try { - $filesystem->chmod($filename, 0666, umask()); - } catch (IOException $e) { - // discard chmod failure (some filesystem may not support it) - } - } - - public static function getCompilerLogFilename(ContainerInterface $container) - { - $class = $container->getParameter('kernel.container_class'); - - return $container->getParameter('kernel.cache_dir').'/'.$class.'Compiler.log'; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php deleted file mode 100644 index 567ec19eaae43..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\Config\DependencyInjection\ConfigCachePass as BaseConfigCachePass; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED); - -/** - * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. - * - * @deprecated since version 3.3, to be removed in 4.0. Use tagged iterator arguments instead. - * - * @author Matthias Pigulla - * @author Benjamin Klotz - */ -class ConfigCachePass extends BaseConfigCachePass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php deleted file mode 100644 index 11ba453dd3c29..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass as BaseControllerArgumentValueResolverPass; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', ControllerArgumentValueResolverPass::class, BaseControllerArgumentValueResolverPass::class), E_USER_DEPRECATED); - -/** - * Gathers and configures the argument value resolvers. - * - * @author Iltar van der Berg - * - * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseControllerArgumentValueResolverPass} - */ -class ControllerArgumentValueResolverPass extends BaseControllerArgumentValueResolverPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php deleted file mode 100644 index a30f469709c86..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\Form\DependencyInjection\FormPass instead.', FormPass::class), E_USER_DEPRECATED); - -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; - -/** - * Adds all services with the tags "form.type" and "form.type_guesser" as - * arguments of the "form.extension" service. - * - * @author Bernhard Schussek - * - * @deprecated since version 3.3, to be removed in 4.0. Use FormPass in the Form component instead. - */ -class FormPass implements CompilerPassInterface -{ - use PriorityTaggedServiceTrait; - - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('form.extension')) { - return; - } - - $definition = $container->getDefinition('form.extension'); - - // Builds an array with fully-qualified type class names as keys and service IDs as values - $types = array(); - - foreach ($container->findTaggedServiceIds('form.type') as $serviceId => $tag) { - $serviceDefinition = $container->getDefinition($serviceId); - if (!$serviceDefinition->isPublic()) { - $serviceDefinition->setPublic(true); - } - - // Support type access by FQCN - $types[$serviceDefinition->getClass()] = $serviceId; - } - - $definition->replaceArgument(1, $types); - - $typeExtensions = array(); - - foreach ($this->findAndSortTaggedServices('form.type_extension', $container) as $reference) { - $serviceId = (string) $reference; - $serviceDefinition = $container->getDefinition($serviceId); - if (!$serviceDefinition->isPublic()) { - $serviceDefinition->setPublic(true); - } - - $tag = $serviceDefinition->getTag('form.type_extension'); - if (isset($tag[0]['extended_type'])) { - $extendedType = $tag[0]['extended_type']; - } else { - throw new InvalidArgumentException(sprintf('Tagged form type extension must have the extended type configured using the extended_type/extended-type attribute, none was configured for the "%s" service.', $serviceId)); - } - - $typeExtensions[$extendedType][] = $serviceId; - } - - $definition->replaceArgument(2, $typeExtensions); - - // Find all services annotated with "form.type_guesser" - $guessers = array_keys($container->findTaggedServiceIds('form.type_guesser')); - foreach ($guessers as $serviceId) { - $serviceDefinition = $container->getDefinition($serviceId); - if (!$serviceDefinition->isPublic()) { - $serviceDefinition->setPublic(true); - } - } - - $definition->replaceArgument(3, $guessers); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php deleted file mode 100644 index aa1cd6e576a3a..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass instead.', PropertyInfoPass::class), E_USER_DEPRECATED); - -use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass as BasePropertyInfoPass; - -/** - * Adds extractors to the property_info service. - * - * @author Kévin Dunglas - * - * @deprecated since version 3.3, to be removed in 4.0. Use {@link BasePropertyInfoPass instead}. - */ -class PropertyInfoPass extends BasePropertyInfoPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php deleted file mode 100644 index 87af59bd0fc03..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass as BaseRoutingResolverPass; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', RoutingResolverPass::class, BaseRoutingResolverPass::class), E_USER_DEPRECATED); - -/** - * Adds tagged routing.loader services to routing.resolver service. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseRoutingResolverPass} - */ -class RoutingResolverPass extends BaseRoutingResolverPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php deleted file mode 100644 index 44b546674e8e2..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\Serializer\DependencyInjection\SerializerPass instead.', SerializerPass::class), E_USER_DEPRECATED); - -use Symfony\Component\Serializer\DependencyInjection\SerializerPass as BaseSerializerPass; - -/** - * Adds all services with the tags "serializer.encoder" and "serializer.normalizer" as - * encoders and normalizers to the Serializer service. - * - * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseSerializerPass} instead. - * - * @author Javier Lopez - */ -class SerializerPass extends BaseSerializerPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php deleted file mode 100644 index 9ddf312bc41af..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass instead.', TranslationDumperPass::class), E_USER_DEPRECATED); - -use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass as BaseTranslationDumperPass; - -/** - * Adds tagged translation.formatter services to translation writer. - * - * @deprecated since version 3.4, to be removed in 4.0. Use {@link BaseTranslationDumperPass instead}. - */ -class TranslationDumperPass extends BaseTranslationDumperPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php deleted file mode 100644 index 8de309dc05fe2..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass instead.', TranslationExtractorPass::class), E_USER_DEPRECATED); - -use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass as BaseTranslationExtractorPass; - -/** - * Adds tagged translation.formatter services to translation writer. - * - * @deprecated since version 3.4, to be removed in 4.0. Use {@link BaseTranslationDumperPass instead}. - */ -class TranslationExtractorPass extends BaseTranslationExtractorPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php deleted file mode 100644 index 9f6a0ab9d2dcf..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\DependencyInjection\TranslatorPass instead.', TranslatorPass::class), E_USER_DEPRECATED); - -use Symfony\Component\Translation\DependencyInjection\TranslatorPass as BaseTranslatorPass; - -/** - * Adds tagged translation.formatter services to translation writer. - * - * @deprecated since version 3.4, to be removed in 4.0. Use {@link BaseTranslatorPass instead}. - */ -class TranslatorPass extends BaseTranslatorPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php deleted file mode 100644 index 4a85e4ed465e4..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass as BaseValidateWorkflowsPass; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', ValidateWorkflowsPass::class, BaseValidateWorkflowsPass::class), E_USER_DEPRECATED); - -/** - * @author Tobias Nyholm - * - * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseValidateWorkflowsPass} instead - */ -class ValidateWorkflowsPass extends BaseValidateWorkflowsPass -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 9430c8775dd82..53abbb23fa316 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -39,9 +39,9 @@ class Configuration implements ConfigurationInterface /** * @param bool $debug Whether debugging is enabled or not */ - public function __construct($debug) + public function __construct(bool $debug) { - $this->debug = (bool) $debug; + $this->debug = $debug; } /** @@ -69,39 +69,6 @@ public function getConfigTreeBuilder() ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead") ->defaultTrue() ->end() - ->arrayNode('trusted_proxies') - ->setDeprecated('The "%path%.%node%" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead.') - ->beforeNormalization() - ->ifTrue(function ($v) { - return !is_array($v) && null !== $v; - }) - ->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); }) - ->end() - ->prototype('scalar') - ->validate() - ->ifTrue(function ($v) { - if (empty($v)) { - return false; - } - - if (false !== strpos($v, '/')) { - if ('0.0.0.0/0' === $v) { - return false; - } - - list($v, $mask) = explode('/', $v, 2); - - if (strcmp($mask, (int) $mask) || $mask < 1 || $mask > (false !== strpos($v, ':') ? 128 : 32)) { - return true; - } - } - - return !filter_var($v, FILTER_VALIDATE_IP); - }) - ->thenInvalid('Invalid proxy IP "%s"') - ->end() - ->end() - ->end() ->scalarNode('ide')->defaultNull()->end() ->booleanNode('test')->end() ->scalarNode('default_locale')->defaultValue('en')->end() @@ -230,23 +197,6 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode) ->booleanNode('only_exceptions')->defaultFalse()->end() ->booleanNode('only_master_requests')->defaultFalse()->end() ->scalarNode('dsn')->defaultValue('file:%kernel.cache_dir%/profiler')->end() - ->arrayNode('matcher') - ->setDeprecated('The "profiler.matcher" configuration key has been deprecated in Symfony 3.4 and it will be removed in 4.0.') - ->canBeEnabled() - ->performNoDeepMerging() - ->fixXmlConfig('ip') - ->children() - ->scalarNode('path') - ->info('use the urldecoded format') - ->example('^/path to resource/') - ->end() - ->scalarNode('service')->end() - ->arrayNode('ips') - ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() - ->prototype('scalar')->end() - ->end() - ->end() - ->end() ->end() ->end() ->end() @@ -292,6 +242,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->end() ->enumNode('type') ->values(array('workflow', 'state_machine')) + ->defaultValue('state_machine') ->end() ->arrayNode('marking_store') ->fixXmlConfig('argument') @@ -471,10 +422,6 @@ private function addSessionSection(ArrayNodeDefinition $rootNode) ->scalarNode('gc_divisor')->end() ->scalarNode('gc_probability')->defaultValue(1)->end() ->scalarNode('gc_maxlifetime')->end() - ->booleanNode('use_strict_mode') - ->defaultTrue() - ->setDeprecated('The "%path%.%node%" option is enabled by default and deprecated since Symfony 3.4. It will be always enabled in 4.0.') - ->end() ->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end() ->integerNode('metadata_update_threshold') ->defaultValue('0') @@ -683,7 +630,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->prototype('scalar')->end() ->defaultValue(array('en')) ->end() - ->booleanNode('logging')->defaultValue($this->debug)->end() + ->booleanNode('logging')->defaultValue(false)->end() ->scalarNode('formatter')->defaultValue('translator.formatter.default')->end() ->scalarNode('default_path') ->info('The default path used to load translations') @@ -705,20 +652,29 @@ private function addValidationSection(ArrayNodeDefinition $rootNode) ->arrayNode('validation') ->info('validation configuration') ->{!class_exists(FullStack::class) && class_exists(Validation::class) ? 'canBeDisabled' : 'canBeEnabled'}() - ->children() - ->scalarNode('cache') - // Can be removed in 4.0, when validator.mapping.cache.doctrine.apc is removed - ->setDeprecated('The "%path%.%node%" option is deprecated since Symfony 3.2 and will be removed in 4.0. Configure the "cache.validator" service under "framework.cache.pools" instead.') - ->beforeNormalization() - ->ifString()->then(function ($v) { - if ('validator.mapping.cache.doctrine.apc' === $v && !class_exists('Doctrine\Common\Cache\ApcCache')) { - throw new LogicException('Doctrine APC cache for the validator cannot be enabled as the Doctrine Cache package is not installed.'); - } + ->validate() + ->ifTrue(function ($v) { return isset($v['strict_email']) && isset($v['email_validation_mode']); }) + ->thenInvalid('"strict_email" and "email_validation_mode" cannot be used together.') + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['strict_email']); }) + ->then(function ($v) { + @trigger_error('The "framework.validation.strict_email" configuration key has been deprecated in Symfony 4.1. Use the "framework.validation.email_validation_mode" configuration key instead.', E_USER_DEPRECATED); - return $v; - }) - ->end() - ->end() + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['strict_email']) && !isset($v['email_validation_mode']); }) + ->then(function ($v) { + $v['email_validation_mode'] = $v['strict_email'] ? 'strict' : 'loose'; + unset($v['strict_email']); + + return $v; + }) + ->end() + ->children() + ->scalarNode('cache')->end() ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end() ->arrayNode('static_method') ->defaultValue(array('loadValidatorMetadata')) @@ -730,7 +686,8 @@ private function addValidationSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->scalarNode('translation_domain')->defaultValue('validators')->end() - ->booleanNode('strict_email')->defaultFalse()->end() + ->booleanNode('strict_email')->end() + ->enumNode('email_validation_mode')->values(array('html5', 'loose', 'strict'))->end() ->arrayNode('mapping') ->addDefaultsIfNotSet() ->fixXmlConfig('path') @@ -772,9 +729,6 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode) ->{!class_exists(FullStack::class) && class_exists(Serializer::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end() - ->scalarNode('cache') - ->setDeprecated('The "%path%.%node%" option is deprecated since Symfony 3.1 and will be removed in 4.0. Configure the "cache.serializer" service under "framework.cache.pools" instead.') - ->end() ->scalarNode('name_converter')->end() ->scalarNode('circular_reference_handler')->end() ->arrayNode('mapping') diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 3c783f9443d0e..413d61719384f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -39,10 +39,10 @@ use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; -use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Finder\Finder; @@ -69,6 +69,7 @@ use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -115,37 +116,13 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('web.xml'); $loader->load('services.xml'); - - $container->getDefinition('kernel.class_cache.cache_warmer')->setPrivate(true); - $container->getDefinition('uri_signer')->setPrivate(true); - $container->getDefinition('config_cache_factory')->setPrivate(true); - $container->getDefinition('response_listener')->setPrivate(true); - $container->getDefinition('file_locator')->setPrivate(true); - $container->getDefinition('streamed_response_listener')->setPrivate(true); - $container->getDefinition('locale_listener')->setPrivate(true); - $container->getDefinition('validate_request_listener')->setPrivate(true); - - // forward compatibility with Symfony 4.0 where the ContainerAwareEventDispatcher class is removed - if (!class_exists(ContainerAwareEventDispatcher::class)) { - $definition = $container->getDefinition('event_dispatcher'); - $definition->setClass(EventDispatcher::class); - $definition->setArguments(array()); - } - - if (\PHP_VERSION_ID < 70000) { - $definition = $container->getDefinition('kernel.class_cache.cache_warmer'); - $definition->addTag('kernel.cache_warmer'); - // Ignore deprecation for PHP versions below 7.0 - $definition->setDeprecated(false); - } - $loader->load('fragment_renderer.xml'); - $container->getDefinition('fragment.handler')->setPrivate(true); - $container->getDefinition('fragment.renderer.inline')->setPrivate(true); - $container->getDefinition('fragment.renderer.hinclude')->setPrivate(true); - $container->getDefinition('fragment.renderer.esi')->setPrivate(true); - $container->getDefinition('fragment.renderer.ssi')->setPrivate(true); + if (!interface_exists(ContainerBagInterface::class)) { + $container->removeDefinition('parameter_bag'); + $container->removeAlias(ContainerBagInterface::class); + $container->removeAlias(ParameterBagInterface::class); + } if (class_exists(Application::class)) { $loader->load('console.xml'); @@ -161,15 +138,6 @@ public function load(array $configs, ContainerBuilder $container) // Load Cache configuration first as it is used by other components $loader->load('cache.xml'); - $container->getDefinition('cache.adapter.system')->setPrivate(true); - $container->getDefinition('cache.adapter.apcu')->setPrivate(true); - $container->getDefinition('cache.adapter.doctrine')->setPrivate(true); - $container->getDefinition('cache.adapter.filesystem')->setPrivate(true); - $container->getDefinition('cache.adapter.psr6')->setPrivate(true); - $container->getDefinition('cache.adapter.redis')->setPrivate(true); - $container->getDefinition('cache.adapter.memcached')->setPrivate(true); - $container->getDefinition('cache.default_clearer')->setPrivate(true); - $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); @@ -195,9 +163,6 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('kernel.http_method_override', $config['http_method_override']); $container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']); - if ($config['trusted_proxies']) { - $container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']); - } $container->setParameter('kernel.default_locale', $config['default_locale']); if (!$container->hasParameter('debug.file_link_format')) { @@ -208,6 +173,7 @@ public function load(array $configs, ContainerBuilder $container) 'emacs' => 'emacs://open?url=file://%%f&line=%%l', 'sublime' => 'subl://open?url=file://%%f&line=%%l', 'phpstorm' => 'phpstorm://open?file=%%f&line=%%l', + 'atom' => 'atom://core/open/file?filename=%%f&line=%%l', ); $ide = $config['ide']; @@ -218,10 +184,6 @@ public function load(array $configs, ContainerBuilder $container) if (!empty($config['test'])) { $loader->load('test.xml'); - - $container->getDefinition('test.client.history')->setPrivate(true); - $container->getDefinition('test.client.cookiejar')->setPrivate(true); - $container->getDefinition('test.session.listener')->setPrivate(true); } if ($this->isConfigEnabled($container, $config['session'])) { @@ -373,47 +335,6 @@ public function load(array $configs, ContainerBuilder $container) // remove tagged iterator argument for resource checkers $container->getDefinition('config_cache_factory')->setArguments(array()); } - - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\\Component\\Config\\ConfigCache', - 'Symfony\\Component\\Config\\FileLocator', - - 'Symfony\\Component\\Debug\\ErrorHandler', - - 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface', - 'Symfony\\Component\\DependencyInjection\\Container', - - 'Symfony\\Component\\EventDispatcher\\Event', - 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', - - 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener', - 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener', - 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle', - 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', - 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver', - 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata', - 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory', - 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', - 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', - 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', - 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent', - 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent', - 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent', - 'Symfony\\Component\\HttpKernel\\HttpKernel', - 'Symfony\\Component\\HttpKernel\\KernelEvents', - 'Symfony\\Component\\HttpKernel\\Config\\FileLocator', - - 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser', - 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', - - // Cannot be included because annotations will parse the big compiled class file - // 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', - - // cannot be included as commands are discovered based on the path to this class via Reflection - // 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle', - )); - } } /** @@ -428,18 +349,6 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont { $loader->load('form.xml'); - $container->getDefinition('form.resolved_type_factory')->setPrivate(true); - $container->getDefinition('form.registry')->setPrivate(true); - $container->getDefinition('form.type_guesser.validator')->setPrivate(true); - $container->getDefinition('form.type.form')->setPrivate(true); - $container->getDefinition('form.type.choice')->setPrivate(true); - $container->getDefinition('form.type_extension.form.http_foundation')->setPrivate(true); - $container->getDefinition('form.type_extension.form.validator')->setPrivate(true); - $container->getDefinition('form.type_extension.repeated.validator')->setPrivate(true); - $container->getDefinition('form.type_extension.submit.validator')->setPrivate(true); - $container->getDefinition('form.type_extension.upload.validator')->setPrivate(true); - $container->getDefinition('deprecated.form.registry')->setPrivate(true); - if (null === $config['form']['csrf_protection']['enabled']) { $config['form']['csrf_protection']['enabled'] = $config['csrf_protection']['enabled']; } @@ -447,9 +356,6 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) { $loader->load('form_csrf.xml'); - $container->getDefinition('form.type_extension.csrf')->setPrivate(true); - $container->getDefinition('deprecated.form.registry.csrf')->setPrivate(true); - $container->setParameter('form.type_extension.csrf.enabled', true); $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']); } else { @@ -470,9 +376,6 @@ private function registerEsiConfiguration(array $config, ContainerBuilder $conta } $loader->load('esi.xml'); - - $container->getDefinition('esi')->setPrivate(true); - $container->getDefinition('esi_listener')->setPrivate(true); } private function registerSsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) @@ -484,9 +387,6 @@ private function registerSsiConfiguration(array $config, ContainerBuilder $conta } $loader->load('ssi.xml'); - - $container->getDefinition('ssi')->setPrivate(true); - $container->getDefinition('ssi_listener')->setPrivate(true); } private function registerFragmentsConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) @@ -514,16 +414,8 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ $loader->load('collectors.xml'); $loader->load('cache_debug.xml'); - $container->getDefinition('data_collector.request')->setPrivate(true); - $container->getDefinition('data_collector.router')->setPrivate(true); - $container->getDefinition('profiler_listener')->setPrivate(true); - if ($this->formConfigEnabled) { $loader->load('form_debug.xml'); - - $container->getDefinition('form.resolved_type_factory')->setPrivate(true); - $container->getDefinition('data_collector.form.extractor')->setPrivate(true); - $container->getDefinition('data_collector.form')->setPrivate(true); } if ($this->validatorConfigEnabled) { @@ -533,8 +425,6 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ if ($this->translationConfigEnabled) { $loader->load('translation_debug.xml'); - $container->getDefinition('data_collector.translation')->setPrivate(true); - $container->getDefinition('translator.data_collector')->setDecoratedService('translator'); } @@ -549,27 +439,6 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ $container->setParameter('profiler.storage.dsn', $config['dsn']); - if ($this->isConfigEnabled($container, $config['matcher'])) { - if (isset($config['matcher']['service'])) { - $container->setAlias('profiler.request_matcher', $config['matcher']['service'])->setPrivate(true); - } elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path']) || isset($config['matcher']['ips'])) { - $definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher'); - $definition->setPublic(false); - - if (isset($config['matcher']['ip'])) { - $definition->addMethodCall('matchIp', array($config['matcher']['ip'])); - } - - if (isset($config['matcher']['ips'])) { - $definition->addMethodCall('matchIps', array($config['matcher']['ips'])); - } - - if (isset($config['matcher']['path'])) { - $definition->addMethodCall('matchPath', array($config['matcher']['path'])); - } - } - } - $container->getDefinition('profiler') ->addArgument($config['collect']) ->addTag('kernel.reset', array('method' => 'reset')); @@ -589,17 +458,9 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $loader->load('workflow.xml'); - $container->getDefinition('workflow.marking_store.multiple_state')->setPrivate(true); - $container->getDefinition('workflow.marking_store.single_state')->setPrivate(true); - $container->getDefinition('workflow.registry')->setPrivate(true); - $registryDefinition = $container->getDefinition('workflow.registry'); foreach ($config['workflows'] as $name => $workflow) { - if (!array_key_exists('type', $workflow)) { - $workflow['type'] = 'workflow'; - @trigger_error(sprintf('The "type" option of the "framework.workflows.%s" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0.', $name), E_USER_DEPRECATED); - } $type = $workflow['type']; $transitions = array(); @@ -655,7 +516,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ // Add workflow to Registry if ($workflow['supports']) { foreach ($workflow['supports'] as $supportedClassName) { - $strategyDefinition = new Definition(Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName)); + $strategyDefinition = new Definition(Workflow\SupportStrategy\InstanceOfSupportStrategy::class, array($supportedClassName)); $strategyDefinition->setPublic(false); $registryDefinition->addMethodCall('add', array(new Reference($workflowId), $strategyDefinition)); } @@ -717,8 +578,6 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con { $loader->load('debug_prod.xml'); - $container->getDefinition('debug.debug_handlers_listener')->setPrivate(true); - if (class_exists(Stopwatch::class)) { $container->register('debug.stopwatch', Stopwatch::class) ->addArgument(true) @@ -735,9 +594,6 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con if ($debug && class_exists(Stopwatch::class)) { $loader->load('debug.xml'); - $container->getDefinition('debug.event_dispatcher')->setPrivate(true); - $container->getDefinition('debug.controller_resolver')->setPrivate(true); - $container->getDefinition('debug.argument_resolver')->setPrivate(true); } $definition = $container->findDefinition('debug.debug_handlers_listener'); @@ -771,7 +627,12 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co $loader->load('routing.xml'); - $container->getDefinition('router_listener')->setPrivate(true); + if (!interface_exists(ContainerBagInterface::class)) { + $container->getDefinition('router.default') + ->replaceArgument(0, new Reference('service_container')) + ->clearTag('container.service_subscriber') + ; + } $container->setParameter('router.resource', $config['resource']); $container->setParameter('router.cache_class_prefix', $container->getParameter('kernel.container_class')); @@ -786,16 +647,6 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co $container->setParameter('request_listener.http_port', $config['http_port']); $container->setParameter('request_listener.https_port', $config['https_port']); - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', - 'Symfony\\Component\\Routing\\RequestContext', - 'Symfony\\Component\\Routing\\Router', - 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', - $container->findDefinition('router.default')->getClass(), - )); - } - if ($this->annotationsConfigEnabled) { $container->register('routing.loader.annotation', AnnotatedRouteControllerLoader::class) ->setPublic(false) @@ -824,16 +675,10 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c { $loader->load('session.xml'); - $container->getDefinition('session.storage.native')->setPrivate(true); - $container->getDefinition('session.storage.php_bridge')->setPrivate(true); - $container->getDefinition('session_listener')->setPrivate(true); - $container->getDefinition('session.save_listener')->setPrivate(true); - $container->getAlias('session.storage.filesystem')->setPrivate(true); - // session storage $container->setAlias('session.storage', $config['storage_id'])->setPrivate(true); $options = array('cache_limiter' => '0'); - 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]; } @@ -852,24 +697,6 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c $container->setParameter('session.save_path', $config['save_path']); - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\\Component\\HttpKernel\\EventListener\\SessionListener', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy', - 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy', - $container->getDefinition('session')->getClass(), - )); - - if ($container->hasDefinition($config['storage_id'])) { - $this->addClassesToCompile(array( - $container->findDefinition('session.storage')->getClass(), - )); - } - } - $container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']); } @@ -878,8 +705,6 @@ private function registerRequestConfiguration(array $config, ContainerBuilder $c if ($config['formats']) { $loader->load('request.xml'); - $container->getDefinition('request.add_request_formats_listener')->setPrivate(true); - $container ->getDefinition('request.add_request_formats_listener') ->replaceArgument(0, $config['formats']) @@ -891,9 +716,6 @@ private function registerTemplatingConfiguration(array $config, ContainerBuilder { $loader->load('templating.xml'); - $container->getDefinition('templating.name_parser')->setPrivate(true); - $container->getDefinition('templating.filename_parser')->setPrivate(true); - $container->setParameter('fragment.renderer.hinclude.global_template', $config['hinclude_default_template']); if ($container->getParameter('kernel.debug')) { @@ -929,15 +751,6 @@ private function registerTemplatingConfiguration(array $config, ContainerBuilder $container->setDefinition('templating.loader', $loaderCache); } - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', - $container->findDefinition('templating.locator')->getClass(), - )); - } - $container->setParameter('templating.engines', $config['engines']); $engines = array_map(function ($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']); @@ -961,18 +774,6 @@ private function registerTemplatingConfiguration(array $config, ContainerBuilder if (in_array('php', $config['engines'], true)) { $loader->load('templating_php.xml'); - $container->getDefinition('templating.helper.slots')->setPrivate(true); - $container->getDefinition('templating.helper.request')->setPrivate(true); - $container->getDefinition('templating.helper.session')->setPrivate(true); - $container->getDefinition('templating.helper.router')->setPrivate(true); - $container->getDefinition('templating.helper.assets')->setPrivate(true); - $container->getDefinition('templating.helper.actions')->setPrivate(true); - $container->getDefinition('templating.helper.code')->setPrivate(true); - $container->getDefinition('templating.helper.translator')->setPrivate(true); - $container->getDefinition('templating.helper.form')->setPrivate(true); - $container->getDefinition('templating.helper.stopwatch')->setPrivate(true); - $container->getDefinition('templating.globals')->setPrivate(true); - $container->setParameter('templating.helper.form.resources', $config['form']['resources']); if ($container->getParameter('kernel.debug') && class_exists(Stopwatch::class)) { @@ -982,14 +783,6 @@ private function registerTemplatingConfiguration(array $config, ContainerBuilder $container->setAlias('debug.templating.engine.php', 'templating.engine.php')->setPrivate(true); } - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\\Component\\Templating\\Storage\\FileStorage', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', - )); - } - if ($container->has('assets.packages')) { $container->getDefinition('templating.helper.assets')->replaceArgument(0, new Reference('assets.packages')); } else { @@ -1006,12 +799,6 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co { $loader->load('assets.xml'); - $container->getDefinition('assets.packages')->setPrivate(true); - $container->getDefinition('assets.context')->setPrivate(true); - $container->getDefinition('assets.path_package')->setPrivate(true); - $container->getDefinition('assets.url_package')->setPrivate(true); - $container->getDefinition('assets.static_version_strategy')->setPrivate(true); - $defaultVersion = null; if ($config['version_strategy']) { @@ -1102,35 +889,6 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder $loader->load('translation.xml'); - $container->getDefinition('translator.default')->setPrivate(true); - $container->getDefinition('translation.loader.php')->setPrivate(true); - $container->getDefinition('translation.loader.yml')->setPrivate(true); - $container->getDefinition('translation.loader.xliff')->setPrivate(true); - $container->getDefinition('translation.loader.po')->setPrivate(true); - $container->getDefinition('translation.loader.mo')->setPrivate(true); - $container->getDefinition('translation.loader.qt')->setPrivate(true); - $container->getDefinition('translation.loader.csv')->setPrivate(true); - $container->getDefinition('translation.loader.res')->setPrivate(true); - $container->getDefinition('translation.loader.dat')->setPrivate(true); - $container->getDefinition('translation.loader.ini')->setPrivate(true); - $container->getDefinition('translation.loader.json')->setPrivate(true); - $container->getDefinition('translation.dumper.php')->setPrivate(true); - $container->getDefinition('translation.dumper.xliff')->setPrivate(true); - $container->getDefinition('translation.dumper.po')->setPrivate(true); - $container->getDefinition('translation.dumper.mo')->setPrivate(true); - $container->getDefinition('translation.dumper.yml')->setPrivate(true); - $container->getDefinition('translation.dumper.qt')->setPrivate(true); - $container->getDefinition('translation.dumper.csv')->setPrivate(true); - $container->getDefinition('translation.dumper.ini')->setPrivate(true); - $container->getDefinition('translation.dumper.json')->setPrivate(true); - $container->getDefinition('translation.dumper.res')->setPrivate(true); - $container->getDefinition('translation.extractor.php')->setPrivate(true); - $container->getDefinition('translator_listener')->setPrivate(true); - $container->getDefinition('translation.loader')->setPrivate(true); - $container->getDefinition('translation.reader')->setPrivate(true); - $container->getDefinition('translation.extractor')->setPrivate(true); - $container->getDefinition('translation.writer')->setPrivate(true); - // Use the "real" translator instead of the identity default $container->setAlias('translator', 'translator.default')->setPublic(true); $container->setAlias('translator.formatter', new Alias($config['formatter'], false)); @@ -1224,11 +982,11 @@ private function registerValidationConfiguration(array $config, ContainerBuilder throw new LogicException('Validation support cannot be enabled as the Validator component is not installed.'); } - $loader->load('validator.xml'); + if (!isset($config['email_validation_mode'])) { + $config['email_validation_mode'] = 'loose'; + } - $container->getDefinition('validator.builder')->setPrivate(true); - $container->getDefinition('validator.expression')->setPrivate(true); - $container->getDefinition('validator.email')->setPrivate(true); + $loader->load('validator.xml'); $validatorBuilder = $container->getDefinition('validator.builder'); @@ -1246,7 +1004,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder } $definition = $container->findDefinition('validator.email'); - $definition->replaceArgument(0, $config['strict_email']); + $definition->replaceArgument(0, $config['email_validation_mode']); if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) { if (!$this->annotationsConfigEnabled) { @@ -1262,14 +1020,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder } } - if (isset($config['cache']) && $config['cache']) { - $container->setParameter( - 'validator.mapping.cache.prefix', - 'validator_'.$this->getKernelRootHash($container) - ); - - $validatorBuilder->addMethodCall('setMetadataCache', array(new Reference($config['cache']))); - } elseif (!$container->getParameter('kernel.debug')) { + if (!$container->getParameter('kernel.debug')) { $validatorBuilder->addMethodCall('setMetadataCache', array(new Reference('validator.mapping.cache.symfony'))); } } @@ -1348,8 +1099,6 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde $loader->load('annotations.xml'); - $container->getAlias('annotation_reader')->setPrivate(true); - if (!method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) { $container->getDefinition('annotations.dummy_registry') ->setMethodCalls(array(array('registerLoader', array('class_exists')))); @@ -1368,13 +1117,6 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde // Enable warmer only if PHP array is used for cache $definition = $container->findDefinition('annotations.cache_warmer'); $definition->addTag('kernel.cache_warmer'); - - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\Component\Cache\Adapter\PhpArrayAdapter', - 'Symfony\Component\Cache\DoctrineProvider', - )); - } } elseif ('file' === $config['cache']) { $cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']); @@ -1411,8 +1153,6 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui $loader->load('property_access.xml'); - $container->getDefinition('property_accessor')->setPrivate(true); - $container ->getDefinition('property_accessor') ->replaceArgument(0, $config['magic_call']) @@ -1446,7 +1186,10 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.normalizer.dateinterval'); } - $container->getDefinition('serializer.mapping.cache.symfony')->setPrivate(true); + if (!class_exists(ClassDiscriminatorFromClassMetadata::class)) { + $container->removeAlias('Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface'); + $container->removeDefinition('serializer.mapping.class_discriminator_resolver'); + } $chainLoader = $container->getDefinition('serializer.mapping.chain_loader'); @@ -1509,16 +1252,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $chainLoader->replaceArgument(0, $serializerLoaders); $container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders); - if (isset($config['cache']) && $config['cache']) { - $container->setParameter( - 'serializer.mapping.cache.prefix', - 'serializer_'.$this->getKernelRootHash($container) - ); - - $container->getDefinition('serializer.mapping.class_metadata_factory')->replaceArgument( - 1, new Reference($config['cache']) - ); - } elseif (!$container->getParameter('kernel.debug')) { + if (!$container->getParameter('kernel.debug')) { $cacheMetadataFactory = new Definition( CacheClassMetadataFactory::class, array( @@ -1545,8 +1279,6 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild { $loader->load('property_info.xml'); - $container->getDefinition('property_info')->setPrivate(true); - if (interface_exists('phpDocumentor\Reflection\DocBlockFactoryInterface')) { $definition = $container->register('property_info.php_doc_extractor', 'Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor'); $definition->setPrivate(true); @@ -1681,14 +1413,6 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con $propertyAccessDefinition->setArguments(array(0, false)); } } - - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\Component\Cache\Adapter\ApcuAdapter', - 'Symfony\Component\Cache\Adapter\FilesystemAdapter', - 'Symfony\Component\Cache\CacheItem', - )); - } } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php deleted file mode 100644 index aa5b4ba6804b5..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\EventListener; - -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\SessionListener instead.', SessionListener::class), E_USER_DEPRECATED); - -/** - * Sets the session in the request. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, to be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\SessionListener instead - */ -class SessionListener extends AbstractSessionListener -{ - private $container; - - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - protected function getSession() - { - if (!$this->container->has('session')) { - return; - } - - return $this->container->get('session'); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php deleted file mode 100644 index c6739b60ef481..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\EventListener; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\TestSessionListener instead.', TestSessionListener::class), E_USER_DEPRECATED); - -use Symfony\Component\HttpKernel\EventListener\AbstractTestSessionListener; -use Symfony\Component\DependencyInjection\ContainerInterface; - -/** - * TestSessionListener. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class TestSessionListener extends AbstractTestSessionListener -{ - protected $container; - - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - protected function getSession() - { - if (!$this->container->has('session')) { - return; - } - - return $this->container->get('session'); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 2a62d391a7b22..f25bb71240b9b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -63,14 +63,6 @@ public function boot() { ErrorHandler::register(null, false)->throwAt($this->container->getParameter('debug.error_handler.throw_at'), true); - if ($this->container->hasParameter('kernel.trusted_proxies')) { - @trigger_error('The "kernel.trusted_proxies" parameter is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::setTrustedProxies() method in your front controller instead.', E_USER_DEPRECATED); - - if ($trustedProxies = $this->container->getParameter('kernel.trusted_proxies')) { - Request::setTrustedProxies($trustedProxies, Request::getTrustedHeaderSet()); - } - } - if ($this->container->getParameter('kernel.http_method_override')) { Request::enableHttpMethodParameterOverride(); } @@ -105,10 +97,7 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new AddAnnotationsCachedReaderPass(), PassConfig::TYPE_AFTER_REMOVING, -255); $this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class); $this->addCompilerPassIfExists($container, AddConsoleCommandPass::class); - if (class_exists(TranslatorPass::class)) { - // Arguments to be removed in 4.0, relying on the default values - $container->addCompilerPass(new TranslatorPass('translator.default', 'translation.loader')); - } + $this->addCompilerPassIfExists($container, TranslatorPass::class); $container->addCompilerPass(new LoggingTranslatorPass()); $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); $this->addCompilerPassIfExists($container, TranslationExtractorPass::class); diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php index 68dc1bc05f4de..5877caaf592ac 100644 --- a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php +++ b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php @@ -32,7 +32,7 @@ abstract class HttpCache extends BaseHttpCache * @param HttpKernelInterface $kernel An HttpKernelInterface instance * @param string $cacheDir The cache directory (default used if null) */ - public function __construct(HttpKernelInterface $kernel, $cacheDir = null) + public function __construct(HttpKernelInterface $kernel, string $cacheDir = null) { $this->kernel = $kernel; $this->cacheDir = $cacheDir; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml index f388501ba4d32..f7162adb1c701 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml @@ -11,6 +11,10 @@ + + + + @@ -100,6 +104,15 @@ + + + + 0 + + + + + @@ -109,6 +122,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml index 95d13761ecf5c..6d3870c31fb0a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml index 9aa1367f585e1..5d64074422a06 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml @@ -17,7 +17,6 @@ - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 211bccb688b49..504fe619bf4d4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -38,7 +38,6 @@ - null @@ -67,97 +66,10 @@ - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - @@ -190,10 +102,5 @@ %validator.translation_domain% - - - - The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml index d0162ea09816c..5acfce366d771 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -16,10 +16,5 @@ %validator.translation_domain% - - - - The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml index 635dc8d2b3ed0..8816284fa78ed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml @@ -21,7 +21,7 @@ - + null %profiler_listener.only_exceptions% %profiler_listener.only_master_requests% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 1ad1a752a6a39..a2e24b00c8e3c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -5,14 +5,6 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Symfony\Component\Routing\Generator\UrlGenerator - Symfony\Component\Routing\Generator\UrlGenerator - Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper - Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher - Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher - Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper - %router.cache_class_prefix%UrlMatcher - %router.cache_class_prefix%UrlGenerator localhost http @@ -59,21 +51,25 @@ - + + + %router.resource% %kernel.cache_dir% %kernel.debug% - %router.options.generator_class% - %router.options.generator_base_class% - %router.options.generator_dumper_class% - %router.options.generator.cache_class% - %router.options.matcher_class% - %router.options.matcher_base_class% - %router.options.matcher_dumper_class% - %router.options.matcher.cache_class% + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper + %router.cache_class_prefix%UrlGenerator + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper + %router.cache_class_prefix%UrlMatcher + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index cca86f8c61dde..653cf52f7c3c9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -202,6 +202,7 @@ + @@ -210,6 +211,14 @@ + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index 42dc667530a53..90c4d1c5b5050 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -24,6 +24,12 @@ + + + + @@ -50,6 +56,7 @@ null + @@ -88,17 +95,6 @@ - - - %serializer.mapping.cache.prefix% - - The "%service_id%" service is deprecated since Symfony 3.2 and will be removed in 4.0. APCu should now be automatically used when available. - - - - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. APCu should now be automatically used when available. - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index f5c44432a3272..2c0072d85d9a1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -7,8 +7,13 @@ - + + + + + + @@ -29,18 +34,6 @@ - - - Symfony\Component\HttpFoundation\ParameterBag - Symfony\Component\HttpFoundation\HeaderBag - Symfony\Component\HttpFoundation\FileBag - Symfony\Component\HttpFoundation\ServerBag - Symfony\Component\HttpFoundation\Request - Symfony\Component\HttpKernel\Kernel - - The "%service_id%" option is deprecated since version 3.3, to be removed in 4.0. - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml index ce7a3dd7eee7d..111c75b4fd9c4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml @@ -56,10 +56,6 @@ - - The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use the `session.lazy_write` ini setting instead. - - @@ -67,13 +63,14 @@ + - + The "%service_id%" service is deprecated since Symfony 4.1 and will be removed in 5.0. Use the "session_listener" service instead. diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml index fddd2abf759eb..d756b9ae0a94f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -125,14 +125,14 @@ - - The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use "translation.reader" instead. - + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml index 66e7c7c23517e..9b90ab7c2f140 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml @@ -49,17 +49,6 @@ - - - - - %validator.mapping.cache.prefix% - - - - The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use a Psr6 cache like "validator.mapping.cache.symfony" instead. - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index 0148dbf73d86a..055609c48857f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -11,12 +11,14 @@ namespace Symfony\Bundle\FrameworkBundle\Routing; +use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; +use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\Routing\Router as BaseRouter; use Symfony\Component\Routing\RequestContext; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; @@ -31,20 +33,31 @@ class Router extends BaseRouter implements WarmableInterface, ServiceSubscriberI { private $container; private $collectedParameters = array(); + private $paramFetcher; /** - * @param ContainerInterface $container A ContainerInterface instance - * @param mixed $resource The main resource to load - * @param array $options An array of options - * @param RequestContext $context The context + * @param ContainerInterface $container A ContainerInterface instance + * @param mixed $resource The main resource to load + * @param array $options An array of options + * @param RequestContext $context The context + * @param ContainerInterface|null $parameters A ContainerInterface instance allowing to fetch parameters + * @param LoggerInterface|null $logger */ - public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null) + public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null, ContainerInterface $parameters = null, LoggerInterface $logger = null) { $this->container = $container; - $this->resource = $resource; $this->context = $context ?: new RequestContext(); + $this->logger = $logger; $this->setOptions($options); + + if ($parameters) { + $this->paramFetcher = array($parameters, 'get'); + } elseif ($container instanceof SymfonyContainerInterface) { + $this->paramFetcher = array($container, 'getParameter'); + } else { + throw new \LogicException(sprintf('You should either pass a "%s" instance or provide the $parameters argument of the "%s" method.', SymfonyContainerInterface::class, __METHOD__)); + } } /** @@ -139,9 +152,7 @@ private function resolve($value) return $value; } - $container = $this->container; - - $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($container, $value) { + $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($value) { // skip %% if (!isset($match[1])) { return '%%'; @@ -151,7 +162,7 @@ private function resolve($value) throw new RuntimeException(sprintf('Using "%%%s%%" is not allowed in routing configuration.', $match[1])); } - $resolved = $container->getParameter($match[1]); + $resolved = ($this->paramFetcher)($match[1]); if (is_string($resolved) || is_numeric($resolved)) { $this->collectedParameters[$match[1]] = $resolved; diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php index f11624be9f2a7..fa610e5c06ea2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php @@ -28,7 +28,7 @@ class CodeHelper extends Helper * @param string $rootDir The project root directory * @param string $charset The charset */ - public function __construct($fileLinkFormat, $rootDir, $charset) + public function __construct($fileLinkFormat, string $rootDir, string $charset) { $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->rootDir = str_replace('\\', '/', $rootDir).'/'; diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php index dc57cc0964c56..8f9b256d6a163 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php @@ -30,7 +30,7 @@ class TemplateLocator implements FileLocatorInterface * @param FileLocatorInterface $locator A FileLocatorInterface instance * @param string $cacheDir The cache path */ - public function __construct(FileLocatorInterface $locator, $cacheDir = null) + public function __construct(FileLocatorInterface $locator, string $cacheDir = null) { if (null !== $cacheDir && file_exists($cache = $cacheDir.'/templates.php')) { $this->cache = require $cache; diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php index 350a58a965b81..9e614ca39acf2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php @@ -50,7 +50,7 @@ public function parse($name) throw new \RuntimeException(sprintf('Template name "%s" contains invalid characters.', $name)); } - if ($this->isAbsolutePath($name) || !preg_match('/^(?:([^:]*):([^:]*):)?(.+)\.([^\.]+)\.([^\.]+)$/', $name, $matches) || 0 === strpos($name, '@')) { + if (!preg_match('/^(?:([^:]*):([^:]*):)?(.+)\.([^\.]+)\.([^\.]+)$/', $name, $matches) || 0 === strpos($name, '@')) { return parent::parse($name); } @@ -66,15 +66,4 @@ public function parse($name) return $this->cache[$name] = $template; } - - private function isAbsolutePath($file) - { - $isAbsolute = (bool) preg_match('#^(?:/|[a-zA-Z]:)#', $file); - - if ($isAbsolute) { - @trigger_error('Absolute template path support is deprecated since Symfony 3.1 and will be removed in 4.0.', E_USER_DEPRECATED); - } - - return $isAbsolute; - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php index d9bc982d74524..d03e4f59b8697 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php @@ -20,7 +20,7 @@ */ class TemplateReference extends BaseTemplateReference { - public function __construct($bundle = null, $controller = null, $name = null, $format = null, $engine = null) + public function __construct(string $bundle = null, string $controller = null, string $name = null, string $format = null, string $engine = null) { $this->parameters = array( 'bundle' => $bundle, diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 212e5e42308ee..be396e055a739 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ResettableContainerInterface; -use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\KernelInterface; /** @@ -31,125 +30,21 @@ abstract class KernelTestCase extends TestCase protected static $kernel; /** - * Finds the directory where the phpunit.xml(.dist) is stored. - * - * If you run tests with the PHPUnit CLI tool, everything will work as expected. - * If not, override this method in your test classes. - * - * @return string The directory where phpunit.xml(.dist) is stored - * - * @throws \RuntimeException - * - * @deprecated since 3.4 and will be removed in 4.0. - */ - protected static function getPhpUnitXmlDir() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - if (!isset($_SERVER['argv']) || false === strpos($_SERVER['argv'][0], 'phpunit')) { - throw new \RuntimeException('You must override the KernelTestCase::createKernel() method.'); - } - - $dir = static::getPhpUnitCliConfigArgument(); - if (null === $dir && - (is_file(getcwd().DIRECTORY_SEPARATOR.'phpunit.xml') || - is_file(getcwd().DIRECTORY_SEPARATOR.'phpunit.xml.dist'))) { - $dir = getcwd(); - } - - // Can't continue - if (null === $dir) { - throw new \RuntimeException('Unable to guess the Kernel directory.'); - } - - if (!is_dir($dir)) { - $dir = dirname($dir); - } - - return $dir; - } - - /** - * Finds the value of the CLI configuration option. - * - * PHPUnit will use the last configuration argument on the command line, so this only returns - * the last configuration argument. - * - * @return string The value of the PHPUnit CLI configuration option - * - * @deprecated since 3.4 and will be removed in 4.0. - */ - private static function getPhpUnitCliConfigArgument() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - $dir = null; - $reversedArgs = array_reverse($_SERVER['argv']); - foreach ($reversedArgs as $argIndex => $testArg) { - if (preg_match('/^-[^ \-]*c$/', $testArg) || '--configuration' === $testArg) { - $dir = realpath($reversedArgs[$argIndex - 1]); - break; - } elseif (0 === strpos($testArg, '--configuration=')) { - $argPath = substr($testArg, strlen('--configuration=')); - $dir = realpath($argPath); - break; - } elseif (0 === strpos($testArg, '-c')) { - $argPath = substr($testArg, strlen('-c')); - $dir = realpath($argPath); - break; - } - } - - return $dir; - } - - /** - * Attempts to guess the kernel location. - * - * When the Kernel is located, the file is required. - * * @return string The Kernel class name * * @throws \RuntimeException + * @throws \LogicException */ protected static function getKernelClass() { - if (isset($_SERVER['KERNEL_CLASS']) || isset($_ENV['KERNEL_CLASS'])) { - $class = isset($_ENV['KERNEL_CLASS']) ? $_ENV['KERNEL_CLASS'] : $_SERVER['KERNEL_CLASS']; - if (!class_exists($class)) { - throw new \RuntimeException(sprintf('Class "%s" doesn\'t exist or cannot be autoloaded. Check that the KERNEL_CLASS value in phpunit.xml matches the fully-qualified class name of your Kernel or override the %s::createKernel() method.', $class, static::class)); - } - - return $class; - } else { - @trigger_error(sprintf('Using the KERNEL_DIR environment variable or the automatic guessing based on the phpunit.xml / phpunit.xml.dist file location is deprecated since Symfony 3.4. Set the KERNEL_CLASS environment variable to the fully-qualified class name of your Kernel instead. Not setting the KERNEL_CLASS environment variable will throw an exception on 4.0 unless you override the %1$::createKernel() or %1$::getKernelClass() method.', static::class), E_USER_DEPRECATED); - } - - if (isset($_SERVER['KERNEL_DIR']) || isset($_ENV['KERNEL_DIR'])) { - $dir = isset($_ENV['KERNEL_DIR']) ? $_ENV['KERNEL_DIR'] : $_SERVER['KERNEL_DIR']; - - if (!is_dir($dir)) { - $phpUnitDir = static::getPhpUnitXmlDir(); - if (is_dir("$phpUnitDir/$dir")) { - $dir = "$phpUnitDir/$dir"; - } - } - } else { - $dir = static::getPhpUnitXmlDir(); + if (!isset($_SERVER['KERNEL_CLASS']) && !isset($_ENV['KERNEL_CLASS'])) { + throw new \LogicException(sprintf('You must set the KERNEL_CLASS environment variable to the fully-qualified class name of your Kernel in phpunit.xml / phpunit.xml.dist or override the %1$s::createKernel() or %1$s::getKernelClass() method.', static::class)); } - $finder = new Finder(); - $finder->name('*Kernel.php')->depth(0)->in($dir); - $results = iterator_to_array($finder); - if (!count($results)) { - throw new \RuntimeException('Either set KERNEL_DIR in your phpunit.xml according to https://symfony.com/doc/current/book/testing.html#your-first-functional-test or override the WebTestCase::createKernel() method.'); + if (!class_exists($class = $_ENV['KERNEL_CLASS'] ?? $_SERVER['KERNEL_CLASS'])) { + throw new \RuntimeException(sprintf('Class "%s" doesn\'t exist or cannot be autoloaded. Check that the KERNEL_CLASS value in phpunit.xml matches the fully-qualified class name of your Kernel or override the %s::createKernel() method.', $class, static::class)); } - $file = current($results); - $class = $file->getBasename('.php'); - - require_once $file; - return $class; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php deleted file mode 100644 index 5e442d662f12a..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\CacheWarmer; - -use Symfony\Bundle\FrameworkBundle\CacheWarmer\ClassCacheCacheWarmer; -use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\DeclaredClass; -use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\WarmedClass; -use Symfony\Bundle\FrameworkBundle\Tests\TestCase; - -/** - * @group legacy - */ -class ClassCacheCacheWarmerTest extends TestCase -{ - public function testWithDeclaredClasses() - { - $this->assertTrue(class_exists(WarmedClass::class, true)); - - $dir = sys_get_temp_dir(); - @unlink($dir.'/classes.php'); - file_put_contents($dir.'/classes.map', sprintf('warmUp($dir); - - $this->assertSame(<<<'EOTXT' - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\CacheWarmer; + +use Psr\Container\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Routing\RouterInterface; + +class RouterCacheWarmerTest extends TestCase +{ + public function testWarmUpWithWarmebleInterface() + { + $containerMock = $this->getMockBuilder(ContainerInterface::class)->setMethods(array('get', 'has'))->getMock(); + + $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmebleInterface::class)->setMethods(array('match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'))->getMock(); + $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); + $routerCacheWarmer = new RouterCacheWarmer($containerMock); + + $routerCacheWarmer->warmUp('/tmp'); + $routerMock->expects($this->any())->method('warmUp')->with('/tmp')->willReturn(''); + $this->addToAssertionCount(1); + } + + /** + * @expectedDeprecation Passing a Symfony\Component\Routing\RouterInterface without implementing Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface is deprecated since Symfony 4.1. + * @group legacy + */ + public function testWarmUpWithoutWarmebleInterface() + { + $containerMock = $this->getMockBuilder(ContainerInterface::class)->setMethods(array('get', 'has'))->getMock(); + + $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmebleInterface::class)->setMethods(array('match', 'generate', 'getContext', 'setContext', 'getRouteCollection'))->getMock(); + $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); + $routerCacheWarmer = new RouterCacheWarmer($containerMock); + $routerCacheWarmer->warmUp('/tmp'); + } +} + +interface testRouterInterfaceWithWarmebleInterface extends RouterInterface, WarmableInterface +{ +} + +interface testRouterInterfaceWithoutWarmebleInterface extends RouterInterface +{ +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index e4f3f084d2c62..6ebdb1a1b4ad8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -82,9 +82,6 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup() } $this->assertTrue($found, 'Kernel file should present as resource'); - if (defined('HHVM_VERSION')) { - return; - } $containerRef = new \ReflectionClass(require $containerFile); $containerFile = str_replace('tes_'.DIRECTORY_SEPARATOR, 'test'.DIRECTORY_SEPARATOR, $containerRef->getFileName()); $this->assertRegExp(sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', $containerClass), file_get_contents($containerFile), 'kernel.container_class is properly set on the dumped container'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePruneCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePruneCommandTest.php index 5356f4a0adb51..d13e1204420e2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePruneCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePruneCommandTest.php @@ -33,10 +33,7 @@ public function testCommandWithNoPools() $tester->execute(array()); } - /** - * @return RewindableGenerator - */ - private function getRewindableGenerator() + private function getRewindableGenerator(): RewindableGenerator { return new RewindableGenerator(function () { yield 'foo_pool' => $this->getPruneableInterfaceMock(); @@ -44,10 +41,7 @@ private function getRewindableGenerator() }, 2); } - /** - * @return RewindableGenerator - */ - private function getEmptyRewindableGenerator() + private function getEmptyRewindableGenerator(): RewindableGenerator { return new RewindableGenerator(function () { return new \ArrayIterator(array()); @@ -96,10 +90,7 @@ private function getPruneableInterfaceMock() return $pruneable; } - /** - * @return CommandTester - */ - private function getCommandTester(KernelInterface $kernel, RewindableGenerator $generator) + private function getCommandTester(KernelInterface $kernel, RewindableGenerator $generator): CommandTester { $application = new Application($kernel); $application->add(new CachePoolPruneCommand($generator)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php index 01a0af75a4075..54fb8db8c6bee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php @@ -47,22 +47,6 @@ public function testDebugInvalidRoute() $this->createCommandTester()->execute(array('name' => 'test')); } - /** - * @group legacy - * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand::__construct() expects an instance of "Symfony\Component\Routing\RouterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. - */ - public function testLegacyDebugCommand() - { - $application = new Application($this->getKernel()); - $application->add(new RouterDebugCommand()); - - $tester = new CommandTester($application->find('debug:router')); - - $tester->execute(array()); - - $this->assertRegExp('/foo\s+ANY\s+ANY\s+ANY\s+\\/foo/', $tester->getDisplay()); - } - /** * @return CommandTester */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php index d415dcd010379..7baa874355df2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php @@ -41,24 +41,6 @@ public function testWithNotMatchPath() $this->assertContains('None of the routes match the path "/test"', $tester->getDisplay()); } - /** - * @group legacy - * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand::__construct() expects an instance of "Symfony\Component\Routing\RouterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. - * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand::__construct() expects an instance of "Symfony\Component\Routing\RouterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. - */ - public function testLegacyMatchCommand() - { - $application = new Application($this->getKernel()); - $application->add(new RouterMatchCommand()); - $application->add(new RouterDebugCommand()); - - $tester = new CommandTester($application->find('router:match')); - - $tester->execute(array('path_info' => '/')); - - $this->assertContains('None of the routes match the path "/"', $tester->getDisplay()); - } - /** * @return CommandTester */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php index 67b24296e4f4c..01e6df156a8ea 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php @@ -199,51 +199,6 @@ private function createCommandTester($extractedMessages = array(), $loadedMessag return new CommandTester($application->find('debug:translation')); } - /** - * @group legacy - * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand::__construct() expects an instance of "Symfony\Component\Translation\TranslatorInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. - */ - public function testLegacyDebugCommand() - { - $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') - ->disableOriginalConstructor() - ->getMock(); - $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); - $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader')->getMock(); - $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); - $kernel - ->expects($this->any()) - ->method('getBundles') - ->will($this->returnValue(array())); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container - ->expects($this->any()) - ->method('get') - ->will($this->returnValueMap(array( - array('translation.extractor', 1, $extractor), - array('translation.reader', 1, $loader), - array('translator', 1, $translator), - array('kernel', 1, $kernel), - ))); - - $kernel - ->expects($this->any()) - ->method('getContainer') - ->will($this->returnValue($container)); - - $command = new TranslationDebugCommand(); - $command->setContainer($container); - - $application = new Application($kernel); - $application->add($command); - - $tester = new CommandTester($application->find('debug:translation')); - $tester->execute(array('locale' => 'en')); - - $this->assertContains('No defined or extracted', $tester->getDisplay()); - } - private function getBundle($path) { $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php index 7f90a1d6563ef..9232b7b027e0f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php @@ -187,53 +187,6 @@ private function createCommandTester($extractedMessages = array(), $loadedMessag return new CommandTester($application->find('translation:update')); } - /** - * @group legacy - * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand::__construct() expects an instance of "Symfony\Component\Translation\Writer\TranslationWriterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. - */ - public function testLegacyUpdateCommand() - { - $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') - ->disableOriginalConstructor() - ->getMock(); - $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); - $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader')->getMock(); - $writer = $this->getMockBuilder('Symfony\Component\Translation\Writer\TranslationWriter')->getMock(); - $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); - $kernel - ->expects($this->any()) - ->method('getBundles') - ->will($this->returnValue(array())); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container - ->expects($this->any()) - ->method('get') - ->will($this->returnValueMap(array( - array('translation.extractor', 1, $extractor), - array('translation.reader', 1, $loader), - array('translation.writer', 1, $writer), - array('translator', 1, $translator), - array('kernel', 1, $kernel), - ))); - - $kernel - ->expects($this->any()) - ->method('getContainer') - ->will($this->returnValue($container)); - - $command = new TranslationUpdateCommand(); - $command->setContainer($container); - - $application = new Application($kernel); - $application->add($command); - - $tester = new CommandTester($application->find('translation:update')); - $tester->execute(array('locale' => 'en')); - - $this->assertContains('You must choose one of --force or --dump-messages', $tester->getDisplay()); - } - private function getBundle($path) { $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php index 6783ec25c5ab5..77e92a09c0798 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php @@ -13,6 +13,9 @@ use Psr\Container\ContainerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; class AbstractControllerTest extends ControllerTraitTest { @@ -20,6 +23,23 @@ protected function createController() { return new TestAbstractController(); } + + public function testGetParameter() + { + $container = new Container(new FrozenParameterBag(array('foo' => 'bar'))); + + $controller = $this->createController(); + $controller->setContainer($container); + + if (!class_exists(ContainerBag::class)) { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The "parameter_bag" service is not available. Try running "composer require dependency-injection:^4.1"'); + } else { + $container->set('parameter_bag', new ContainerBag($container)); + } + + $this->assertSame('bar', $controller->getParameter('foo')); + } } class TestAbstractController extends AbstractController @@ -57,6 +77,11 @@ public function setContainer(ContainerInterface $container) return parent::setContainer($container); } + public function getParameter(string $name) + { + return parent::getParameter($name); + } + public function fooAction() { } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php index efe72b5b8029f..0dfed269ec20e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php @@ -40,7 +40,6 @@ public function testParse() $this->assertEquals('TestBundle\FooBundle\Controller\DefaultController::indexAction', $parser->parse('FooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); $this->assertEquals('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction', $parser->parse('FooBundle:Sub\Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); - $this->assertEquals('TestBundle\Fabpot\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); $this->assertEquals('TestBundle\Sensio\Cms\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioCmsFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); $this->assertEquals('TestBundle\FooBundle\Controller\Test\DefaultController::indexAction', $parser->parse('FooBundle:Test\\Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); $this->assertEquals('TestBundle\FooBundle\Controller\Test\DefaultController::indexAction', $parser->parse('FooBundle:Test/Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); @@ -138,7 +137,6 @@ public function getInvalidBundleNameTests() { return array( 'Alternative will be found using levenshtein' => array('FoodBundle:Default:index', 'FooBundle:Default:index'), - 'Alternative will be found using partial match' => array('FabpotFooBund:Default:index', 'FabpotFooBundle:Default:index'), 'Bundle does not exist at all' => array('CrazyBundle:Default:index', false), ); } @@ -146,10 +144,8 @@ public function getInvalidBundleNameTests() private function createParser() { $bundles = array( - 'SensioFooBundle' => array($this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), $this->getBundle('TestBundle\Sensio\FooBundle', 'SensioFooBundle')), - 'SensioCmsFooBundle' => array($this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle')), - 'FooBundle' => array($this->getBundle('TestBundle\FooBundle', 'FooBundle')), - 'FabpotFooBundle' => array($this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), $this->getBundle('TestBundle\Sensio\FooBundle', 'SensioFooBundle')), + 'SensioCmsFooBundle' => $this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle'), + 'FooBundle' => $this->getBundle('TestBundle\FooBundle', 'FooBundle'), ); $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); @@ -166,11 +162,9 @@ private function createParser() ; $bundles = array( - 'SensioFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), 'SensioCmsFooBundle' => $this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle'), 'FoooooBundle' => $this->getBundle('TestBundle\FooBundle', 'FoooooBundle'), 'FooBundle' => $this->getBundle('TestBundle\FooBundle', 'FooBundle'), - 'FabpotFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), ); $kernel ->expects($this->any()) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php index 6d9a256e06ce2..a208775cf23d7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php @@ -378,7 +378,7 @@ public function testRedirectToRoute() public function testAddFlash() { $flashBag = new FlashBag(); - $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->disableOriginalConstructor()->getMock(); + $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->getMock(); $session->expects($this->once())->method('getFlashBag')->willReturn($flashBag); $container = new Container(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index 18c27385e58fd..4a5beeb5b8f71 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -143,30 +143,6 @@ public function testUrlRedirectDefaultPorts() $this->assertRedirectUrl($returnValue, $expectedUrl); } - /** - * @group legacy - */ - public function testUrlRedirectDefaultPortParameters() - { - $host = 'www.example.com'; - $baseUrl = '/base'; - $path = '/redirect-path'; - $httpPort = 1080; - $httpsPort = 1443; - - $expectedUrl = "https://$host:$httpsPort$baseUrl$path"; - $request = $this->createRequestObject('http', $host, $httpPort, $baseUrl); - $controller = $this->createLegacyRedirectController(null, $httpsPort); - $returnValue = $controller->urlRedirectAction($request, $path, false, 'https'); - $this->assertRedirectUrl($returnValue, $expectedUrl); - - $expectedUrl = "http://$host:$httpPort$baseUrl$path"; - $request = $this->createRequestObject('https', $host, $httpPort, $baseUrl); - $controller = $this->createLegacyRedirectController($httpPort); - $returnValue = $controller->urlRedirectAction($request, $path, false, 'http'); - $this->assertRedirectUrl($returnValue, $expectedUrl); - } - public function urlRedirectProvider() { return array( @@ -276,44 +252,6 @@ private function createRedirectController($httpPort = null, $httpsPort = null) return new RedirectController(null, $httpPort, $httpsPort); } - /** - * @deprecated - */ - private function createLegacyRedirectController($httpPort = null, $httpsPort = null) - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - - if (null !== $httpPort) { - $container - ->expects($this->once()) - ->method('hasParameter') - ->with($this->equalTo('request_listener.http_port')) - ->will($this->returnValue(true)); - $container - ->expects($this->once()) - ->method('getParameter') - ->with($this->equalTo('request_listener.http_port')) - ->will($this->returnValue($httpPort)); - } - if (null !== $httpsPort) { - $container - ->expects($this->once()) - ->method('hasParameter') - ->with($this->equalTo('request_listener.https_port')) - ->will($this->returnValue(true)); - $container - ->expects($this->once()) - ->method('getParameter') - ->with($this->equalTo('request_listener.https_port')) - ->will($this->returnValue($httpsPort)); - } - - $controller = new RedirectController(); - $controller->setContainer($container); - - return $controller; - } - private function assertRedirectUrl(Response $returnResponse, $expectedUrl) { $this->assertTrue($returnResponse->isRedirect($expectedUrl), "Expected: $expectedUrl\nGot: ".$returnResponse->headers->get('Location')); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php index 497c112eedb5f..a3abae0298e36 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php @@ -23,58 +23,23 @@ class TemplateControllerTest extends TestCase public function testTwig() { $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->willReturn('bar'); + $twig->expects($this->exactly(2))->method('render')->willReturn('bar'); $controller = new TemplateController($twig); $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + $this->assertEquals('bar', $controller('mytemplate')->getContent()); } public function testTemplating() { $templating = $this->getMockBuilder(EngineInterface::class)->getMock(); - $templating->expects($this->once())->method('render')->willReturn('bar'); + $templating->expects($this->exactly(2))->method('render')->willReturn('bar'); $controller = new TemplateController(null, $templating); $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); - } - - /** - * @group legacy - */ - public function testLegacyTwig() - { - $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); - $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); - - $controller = new TemplateController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); - } - - /** - * @group legacy - */ - public function testLegacyTemplating() - { - $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); - $templating->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->willReturn(true); - $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); - - $controller = new TemplateController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + $this->assertEquals('bar', $controller('mytemplate')->getContent()); } /** @@ -86,5 +51,6 @@ public function testNoTwigNorTemplating() $controller = new TemplateController(); $controller->templateAction('mytemplate')->getContent(); + $controller('mytemplate')->getContent(); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php deleted file mode 100644 index d06fcc5fd3227..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass; - -/** - * @group legacy - */ -class AddCacheWarmerPassTest extends TestCase -{ - public function testThatCacheWarmersAreProcessedInPriorityOrder() - { - $container = new ContainerBuilder(); - $cacheWarmerDefinition = $container->register('cache_warmer')->addArgument(array()); - $container->register('my_cache_warmer_service1')->addTag('kernel.cache_warmer', array('priority' => 100)); - $container->register('my_cache_warmer_service2')->addTag('kernel.cache_warmer', array('priority' => 200)); - $container->register('my_cache_warmer_service3')->addTag('kernel.cache_warmer'); - - $addCacheWarmerPass = new AddCacheWarmerPass(); - $addCacheWarmerPass->process($container); - - $this->assertEquals( - array( - new Reference('my_cache_warmer_service2'), - new Reference('my_cache_warmer_service1'), - new Reference('my_cache_warmer_service3'), - ), - $cacheWarmerDefinition->getArgument(0) - ); - } - - public function testThatCompilerPassIsIgnoredIfThereIsNoCacheWarmerDefinition() - { - $container = new ContainerBuilder(); - - $addCacheWarmerPass = new AddCacheWarmerPass(); - $addCacheWarmerPass->process($container); - - // we just check that the pass does not break if no cache warmer is registered - $this->addToAssertionCount(1); - } - - public function testThatCacheWarmersMightBeNotDefined() - { - $container = new ContainerBuilder(); - $cacheWarmerDefinition = $container->register('cache_warmer')->addArgument(array()); - - $addCacheWarmerPass = new AddCacheWarmerPass(); - $addCacheWarmerPass->process($container); - - $this->assertSame(array(), $cacheWarmerDefinition->getArgument(0)); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php deleted file mode 100644 index 58a0da41c3d91..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php +++ /dev/null @@ -1,129 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\HttpKernel\Bundle\Bundle; - -/** - * @group legacy - */ -class AddConsoleCommandPassTest extends TestCase -{ - /** - * @dataProvider visibilityProvider - */ - public function testProcess($public) - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new AddConsoleCommandPass()); - $container->setParameter('my-command.class', 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); - - $definition = new Definition('%my-command.class%'); - $definition->setPublic($public); - $definition->addTag('console.command'); - $container->setDefinition('my-command', $definition); - - $container->compile(); - - $alias = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; - - if ($public) { - $this->assertFalse($container->hasAlias($alias)); - $id = 'my-command'; - } else { - $id = $alias; - // The alias is replaced by a Definition by the ReplaceAliasByActualDefinitionPass - // in case the original service is private - $this->assertFalse($container->hasDefinition('my-command')); - $this->assertTrue($container->hasDefinition($alias)); - } - - $this->assertTrue($container->hasParameter('console.command.ids')); - $this->assertSame(array($alias => $id), $container->getParameter('console.command.ids')); - } - - public function visibilityProvider() - { - return array( - array(true), - array(false), - ); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "my-command" tagged "console.command" must not be abstract. - */ - public function testProcessThrowAnExceptionIfTheServiceIsAbstract() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new AddConsoleCommandPass()); - - $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); - $definition->addTag('console.command'); - $definition->setAbstract(true); - $container->setDefinition('my-command', $definition); - - $container->compile(); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "my-command" tagged "console.command" must be a subclass of "Symfony\Component\Console\Command\Command". - */ - public function testProcessThrowAnExceptionIfTheServiceIsNotASubclassOfCommand() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new AddConsoleCommandPass()); - - $definition = new Definition('SplObjectStorage'); - $definition->addTag('console.command'); - $container->setDefinition('my-command', $definition); - - $container->compile(); - } - - public function testProcessPrivateServicesWithSameCommand() - { - $container = new ContainerBuilder(); - $className = 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'; - - $definition1 = new Definition($className); - $definition1->addTag('console.command')->setPublic(false); - - $definition2 = new Definition($className); - $definition2->addTag('console.command')->setPublic(false); - - $container->setDefinition('my-command1', $definition1); - $container->setDefinition('my-command2', $definition2); - - (new AddConsoleCommandPass())->process($container); - - $alias1 = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; - $alias2 = $alias1.'_my-command2'; - $this->assertTrue($container->hasAlias($alias1)); - $this->assertTrue($container->hasAlias($alias2)); - } -} - -class MyCommand extends Command -{ -} - -class ExtensionPresentBundle extends Bundle -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php deleted file mode 100644 index a5291114dc6d5..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass; -use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ServiceLocator; - -/** - * @group legacy - */ -class AddConstraintValidatorsPassTest extends TestCase -{ - public function testThatConstraintValidatorServicesAreProcessed() - { - $container = new ContainerBuilder(); - $validatorFactory = $container->register('validator.validator_factory') - ->addArgument(array()); - - $container->register('my_constraint_validator_service1', Validator1::class) - ->addTag('validator.constraint_validator', array('alias' => 'my_constraint_validator_alias1')); - $container->register('my_constraint_validator_service2', Validator2::class) - ->addTag('validator.constraint_validator'); - - $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); - $addConstraintValidatorsPass->process($container); - - $expected = (new Definition(ServiceLocator::class, array(array( - Validator1::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), - 'my_constraint_validator_alias1' => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), - Validator2::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service2')), - ))))->addTag('container.service_locator')->setPublic(false); - $this->assertEquals($expected, $container->getDefinition((string) $validatorFactory->getArgument(0))); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "my_abstract_constraint_validator" tagged "validator.constraint_validator" must not be abstract. - */ - public function testAbstractConstraintValidator() - { - $container = new ContainerBuilder(); - $validatorFactory = $container->register('validator.validator_factory') - ->addArgument(array()); - - $container->register('my_abstract_constraint_validator') - ->setAbstract(true) - ->addTag('validator.constraint_validator'); - - $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); - $addConstraintValidatorsPass->process($container); - } - - public function testThatCompilerPassIsIgnoredIfThereIsNoConstraintValidatorFactoryDefinition() - { - $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); - $addConstraintValidatorsPass->process(new ContainerBuilder()); - - // we just check that the pass does not fail if no constraint validator factory is registered - $this->addToAssertionCount(1); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php deleted file mode 100644 index e2348972d09c7..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Argument\IteratorArgument; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass; - -/** - * @group legacy - */ -class ConfigCachePassTest extends TestCase -{ - public function testThatCheckersAreProcessedInPriorityOrder() - { - $container = new ContainerBuilder(); - - $definition = $container->register('config_cache_factory')->addArgument(null); - $container->register('checker_2')->addTag('config_cache.resource_checker', array('priority' => 100)); - $container->register('checker_1')->addTag('config_cache.resource_checker', array('priority' => 200)); - $container->register('checker_3')->addTag('config_cache.resource_checker'); - - $pass = new ConfigCachePass(); - $pass->process($container); - - $expected = new IteratorArgument(array( - new Reference('checker_1'), - new Reference('checker_2'), - new Reference('checker_3'), - )); - $this->assertEquals($expected, $definition->getArgument(0)); - } - - public function testThatCheckersCanBeMissing() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('findTaggedServiceIds'))->getMock(); - - $container->expects($this->atLeastOnce()) - ->method('findTaggedServiceIds') - ->will($this->returnValue(array())); - - $pass = new ConfigCachePass(); - $pass->process($container); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php deleted file mode 100644 index 1adfdf2734f0c..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\HttpKernel\Controller\ArgumentResolver; - -/** - * @group legacy - */ -class ControllerArgumentValueResolverPassTest extends TestCase -{ - public function testServicesAreOrderedAccordingToPriority() - { - $services = array( - 'n3' => array(array()), - 'n1' => array(array('priority' => 200)), - 'n2' => array(array('priority' => 100)), - ); - - $expected = array( - new Reference('n1'), - new Reference('n2'), - new Reference('n3'), - ); - - $definition = new Definition(ArgumentResolver::class, array(null, array())); - $container = new ContainerBuilder(); - $container->setDefinition('argument_resolver', $definition); - - foreach ($services as $id => list($tag)) { - $container->register($id)->addTag('controller.argument_value_resolver', $tag); - } - - (new ControllerArgumentValueResolverPass())->process($container); - $this->assertEquals($expected, $definition->getArgument(1)->getValues()); - } - - public function testReturningEmptyArrayWhenNoService() - { - $definition = new Definition(ArgumentResolver::class, array(null, array())); - $container = new ContainerBuilder(); - $container->setDefinition('argument_resolver', $definition); - - (new ControllerArgumentValueResolverPass())->process($container); - $this->assertEquals(array(), $definition->getArgument(1)->getValues()); - } - - public function testNoArgumentResolver() - { - $container = new ContainerBuilder(); - - (new ControllerArgumentValueResolverPass())->process($container); - - $this->assertFalse($container->hasDefinition('argument_resolver')); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php deleted file mode 100644 index 58dbd2efd9c02..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php +++ /dev/null @@ -1,224 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\Form\AbstractType; - -/** - * @group legacy - * - * @author Bernhard Schussek - */ -class FormPassTest extends TestCase -{ - public function testDoNothingIfFormExtensionNotLoaded() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $container->compile(); - - $this->assertFalse($container->hasDefinition('form.extension')); - } - - public function testAddTaggedTypes() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setPublic(true); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); - $container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type')->setPublic(true); - $container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type')->setPublic(true); - - $container->compile(); - - $extDefinition = $container->getDefinition('form.extension'); - - $this->assertEquals(array( - __CLASS__.'_Type1' => 'my.type1', - __CLASS__.'_Type2' => 'my.type2', - ), $extDefinition->getArgument(1)); - } - - /** - * @dataProvider addTaggedTypeExtensionsDataProvider - */ - public function testAddTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions) - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( - new Reference('service_container'), - array(), - array(), - array(), - )); - $extDefinition->setPublic(true); - - $container->setDefinition('form.extension', $extDefinition); - - foreach ($extensions as $serviceId => $tag) { - $container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag); - } - - $container->compile(); - - $extDefinition = $container->getDefinition('form.extension'); - $this->assertSame($expectedRegisteredExtensions, $extDefinition->getArgument(2)); - } - - /** - * @return array - */ - public function addTaggedTypeExtensionsDataProvider() - { - return array( - array( - array( - 'my.type_extension1' => array('extended_type' => 'type1'), - 'my.type_extension2' => array('extended_type' => 'type1'), - 'my.type_extension3' => array('extended_type' => 'type2'), - ), - array( - 'type1' => array('my.type_extension1', 'my.type_extension2'), - 'type2' => array('my.type_extension3'), - ), - ), - array( - array( - 'my.type_extension1' => array('extended_type' => 'type1', 'priority' => 1), - 'my.type_extension2' => array('extended_type' => 'type1', 'priority' => 2), - 'my.type_extension3' => array('extended_type' => 'type1', 'priority' => -1), - 'my.type_extension4' => array('extended_type' => 'type2', 'priority' => 2), - 'my.type_extension5' => array('extended_type' => 'type2', 'priority' => 1), - 'my.type_extension6' => array('extended_type' => 'type2', 'priority' => 1), - ), - array( - 'type1' => array('my.type_extension2', 'my.type_extension1', 'my.type_extension3'), - 'type2' => array('my.type_extension4', 'my.type_extension5', 'my.type_extension6'), - ), - ), - ); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage extended-type attribute, none was configured for the "my.type_extension" service - */ - public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( - new Reference('service_container'), - array(), - array(), - array(), - )); - $extDefinition->setPublic(true); - - $container->setDefinition('form.extension', $extDefinition); - $container->register('my.type_extension', 'stdClass') - ->addTag('form.type_extension'); - - $container->compile(); - } - - public function testAddTaggedGuessers() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setPublic(true); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $definition1 = new Definition('stdClass'); - $definition1->addTag('form.type_guesser'); - $definition2 = new Definition('stdClass'); - $definition2->addTag('form.type_guesser'); - - $container->setDefinition('form.extension', $extDefinition); - $container->setDefinition('my.guesser1', $definition1); - $container->setDefinition('my.guesser2', $definition2); - - $container->compile(); - - $extDefinition = $container->getDefinition('form.extension'); - - $this->assertSame(array( - 'my.guesser1', - 'my.guesser2', - ), $extDefinition->getArgument(3)); - } - - /** - * @dataProvider privateTaggedServicesProvider - */ - public function testPrivateTaggedServices($id, $tagName) - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); - $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, array('extended_type' => 'Foo')); - - $container->compile(); - $this->assertTrue($container->getDefinition($id)->isPublic()); - } - - public function privateTaggedServicesProvider() - { - return array( - array('my.type', 'form.type'), - array('my.type_extension', 'form.type_extension'), - array('my.guesser', 'form.type_guesser'), - ); - } -} - -class FormPassTest_Type1 extends AbstractType -{ -} - -class FormPassTest_Type2 extends AbstractType -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php deleted file mode 100644 index 19b25bccb9729..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; -use Symfony\Component\DependencyInjection\Argument\IteratorArgument; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * @group legacy - */ -class PropertyInfoPassTest extends TestCase -{ - /** - * @dataProvider provideTags - */ - public function testServicesAreOrderedAccordingToPriority($index, $tag) - { - $container = new ContainerBuilder(); - - $definition = $container->register('property_info')->setArguments(array(null, null, null, null)); - $container->register('n2')->addTag($tag, array('priority' => 100)); - $container->register('n1')->addTag($tag, array('priority' => 200)); - $container->register('n3')->addTag($tag); - - $propertyInfoPass = new PropertyInfoPass(); - $propertyInfoPass->process($container); - - $expected = new IteratorArgument(array( - new Reference('n1'), - new Reference('n2'), - new Reference('n3'), - )); - $this->assertEquals($expected, $definition->getArgument($index)); - } - - public function provideTags() - { - return array( - array(0, 'property_info.list_extractor'), - array(1, 'property_info.type_extractor'), - array(2, 'property_info.description_extractor'), - array(3, 'property_info.access_extractor'), - ); - } - - public function testReturningEmptyArrayWhenNoService() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('findTaggedServiceIds'))->getMock(); - - $container - ->expects($this->any()) - ->method('findTaggedServiceIds') - ->will($this->returnValue(array())) - ; - - $propertyInfoPass = new PropertyInfoPass(); - - $method = new \ReflectionMethod( - 'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass', - 'findAndSortTaggedServices' - ); - $method->setAccessible(true); - - $actual = $method->invoke($propertyInfoPass, 'tag', $container); - - $this->assertEquals(array(), $actual); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php deleted file mode 100644 index 8ad759e834592..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; - -/** - * Tests for the SerializerPass class. - * - * @group legacy - * - * @author Javier Lopez - */ -class SerializerPassTest extends TestCase -{ - public function testThrowExceptionWhenNoNormalizers() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds'))->getMock(); - - $container->expects($this->once()) - ->method('hasDefinition') - ->with('serializer') - ->will($this->returnValue(true)); - - $container->expects($this->once()) - ->method('findTaggedServiceIds') - ->with('serializer.normalizer') - ->will($this->returnValue(array())); - - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('RuntimeException'); - - $serializerPass = new SerializerPass(); - $serializerPass->process($container); - } - - public function testThrowExceptionWhenNoEncoders() - { - $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition'))->getMock(); - - $container->expects($this->once()) - ->method('hasDefinition') - ->with('serializer') - ->will($this->returnValue(true)); - - $container->expects($this->any()) - ->method('findTaggedServiceIds') - ->will($this->onConsecutiveCalls( - array('n' => array('serializer.normalizer')), - array() - )); - - $container->expects($this->any()) - ->method('getDefinition') - ->will($this->returnValue($definition)); - - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('RuntimeException'); - - $serializerPass = new SerializerPass(); - $serializerPass->process($container); - } - - public function testServicesAreOrderedAccordingToPriority() - { - $container = new ContainerBuilder(); - - $definition = $container->register('serializer')->setArguments(array(null, null)); - $container->register('n2')->addTag('serializer.normalizer', array('priority' => 100))->addTag('serializer.encoder', array('priority' => 100)); - $container->register('n1')->addTag('serializer.normalizer', array('priority' => 200))->addTag('serializer.encoder', array('priority' => 200)); - $container->register('n3')->addTag('serializer.normalizer')->addTag('serializer.encoder'); - - $serializerPass = new SerializerPass(); - $serializerPass->process($container); - - $expected = array( - new Reference('n1'), - new Reference('n2'), - new Reference('n3'), - ); - $this->assertEquals($expected, $definition->getArgument(0)); - $this->assertEquals($expected, $definition->getArgument(1)); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php deleted file mode 100644 index d31fb18a12864..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass; -use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -/** - * @group legacy - */ -class TranslatorPassTest extends TestCase -{ - public function testValidCollector() - { - $loader = (new Definition()) - ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')); - - $translator = (new Definition()) - ->setArguments(array(null, null, null, null)); - - $container = new ContainerBuilder(); - $container->setDefinition('translator.default', $translator); - $container->setDefinition('translation.loader', $loader); - - $pass = new TranslatorPass(); - $pass->process($container); - - $expected = (new Definition()) - ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')) - ->addMethodCall('addLoader', array('xliff', new Reference('translation.loader'))) - ->addMethodCall('addLoader', array('xlf', new Reference('translation.loader'))) - ; - $this->assertEquals($expected, $loader); - - $this->assertSame(array('translation.loader' => array('xliff', 'xlf')), $translator->getArgument(3)); - - $expected = array('translation.loader' => new ServiceClosureArgument(new Reference('translation.loader'))); - $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0)); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 085911b72abf6..84921d9737d60 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -44,103 +44,6 @@ public function testDoNoDuplicateDefaultFormResources() $this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']); } - /** - * @group legacy - * @expectedDeprecation The "framework.trusted_proxies" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead. - */ - public function testTrustedProxiesSetToNullIsDeprecated() - { - $processor = new Processor(); - $configuration = new Configuration(true); - $processor->processConfiguration($configuration, array(array('trusted_proxies' => null))); - } - - /** - * @group legacy - * @expectedDeprecation The "framework.trusted_proxies" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead. - */ - public function testTrustedProxiesSetToEmptyArrayIsDeprecated() - { - $processor = new Processor(); - $configuration = new Configuration(true); - $processor->processConfiguration($configuration, array(array('trusted_proxies' => array()))); - } - - /** - * @group legacy - * @expectedDeprecation The "framework.trusted_proxies" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead. - */ - public function testTrustedProxiesSetToNonEmptyArrayIsInvalid() - { - $processor = new Processor(); - $configuration = new Configuration(true); - $processor->processConfiguration($configuration, array(array('trusted_proxies' => array('127.0.0.1')))); - } - - /** - * @group legacy - * @dataProvider getTestValidTrustedProxiesData - */ - public function testValidTrustedProxies($trustedProxies, $processedProxies) - { - $processor = new Processor(); - $configuration = new Configuration(true); - $config = $processor->processConfiguration($configuration, array(array( - 'secret' => 's3cr3t', - 'trusted_proxies' => $trustedProxies, - ))); - - $this->assertEquals($processedProxies, $config['trusted_proxies']); - } - - public function getTestValidTrustedProxiesData() - { - return array( - array(array('127.0.0.1'), array('127.0.0.1')), - array(array('::1'), array('::1')), - array(array('127.0.0.1', '::1'), array('127.0.0.1', '::1')), - array(null, array()), - array(false, array()), - array(array(), array()), - array(array('10.0.0.0/8'), array('10.0.0.0/8')), - array(array('::ffff:0:0/96'), array('::ffff:0:0/96')), - array(array('0.0.0.0/0'), array('0.0.0.0/0')), - ); - } - - /** - * @group legacy - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testInvalidTypeTrustedProxies() - { - $processor = new Processor(); - $configuration = new Configuration(true); - $processor->processConfiguration($configuration, array( - array( - 'secret' => 's3cr3t', - 'trusted_proxies' => 'Not an IP address', - ), - )); - } - - /** - * @group legacy - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testInvalidValueTrustedProxies() - { - $processor = new Processor(); - $configuration = new Configuration(true); - - $processor->processConfiguration($configuration, array( - array( - 'secret' => 's3cr3t', - 'trusted_proxies' => array('Not an IP address'), - ), - )); - } - public function testAssetsCanBeEnabled() { $processor = new Processor(); @@ -221,7 +124,6 @@ protected static function getBundleDefaultConfig() { return array( 'http_method_override' => true, - 'trusted_proxies' => array(), 'ide' => null, 'default_locale' => 'en', 'csrf_protection' => array( @@ -246,15 +148,11 @@ protected static function getBundleDefaultConfig() 'only_master_requests' => false, 'dsn' => 'file:%kernel.cache_dir%/profiler', 'collect' => true, - 'matcher' => array( - 'enabled' => false, - 'ips' => array(), - ), ), 'translator' => array( 'enabled' => !class_exists(FullStack::class), 'fallbacks' => array('en'), - 'logging' => true, + 'logging' => false, 'formatter' => 'translator.formatter.default', 'paths' => array(), 'default_path' => '%kernel.project_dir%/translations', @@ -264,7 +162,6 @@ protected static function getBundleDefaultConfig() 'enable_annotations' => !class_exists(FullStack::class), 'static_method' => array('loadValidatorMetadata'), 'translation_domain' => 'validators', - 'strict_email' => false, 'mapping' => array( 'paths' => array(), ), @@ -301,7 +198,6 @@ protected static function getBundleDefaultConfig() 'gc_probability' => 1, 'save_path' => '%kernel.cache_dir%/sessions', 'metadata_update_threshold' => '0', - 'use_strict_mode' => true, ), 'request' => array( 'enabled' => false, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_email_validation_mode.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_email_validation_mode.php new file mode 100644 index 0000000000000..373aa678de198 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_email_validation_mode.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'validation' => array( + 'email_validation_mode' => 'html5', + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_and_validation_mode.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_and_validation_mode.php new file mode 100644 index 0000000000000..9e57bc5ce2bca --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_and_validation_mode.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'validation' => array( + 'strict_email' => true, + 'email_validation_mode' => 'strict', + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_disabled.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_disabled.php new file mode 100644 index 0000000000000..d26b56eec389d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_disabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'validation' => array( + 'strict_email' => false, + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_enabled.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email_enabled.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php index c527606561ee9..3d3f4266b7eb5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php @@ -41,7 +41,6 @@ ), ), 'pull_request' => array( - 'type' => 'state_machine', 'marking_store' => array( 'type' => 'single_state', ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php deleted file mode 100644 index 63e83170fca52..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php +++ /dev/null @@ -1,26 +0,0 @@ -loadFromExtension('framework', array( - 'workflows' => array( - 'missing_type' => array( - 'marking_store' => array( - 'service' => 'workflow_service', - ), - 'supports' => array( - \stdClass::class, - ), - 'places' => array( - 'first', - 'last', - ), - 'transitions' => array( - 'go' => array( - 'from' => 'first', - 'to' => 'last', - ), - ), - ), - ), -)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_email_validation_mode.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_email_validation_mode.xml new file mode 100644 index 0000000000000..5b0e3a083e018 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_email_validation_mode.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_and_validation_mode.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_and_validation_mode.xml new file mode 100644 index 0000000000000..86b9760786957 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_and_validation_mode.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_disabled.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_disabled.xml new file mode 100644 index 0000000000000..349439a7a6774 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_disabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_enabled.xml similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email.xml rename to src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email_enabled.xml diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml index be065c4558858..02f964bc3a434 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml @@ -39,7 +39,7 @@ - + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest start diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml deleted file mode 100644 index 2e6ebad552b74..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - stdClass - first - last - - first - last - - - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_email_validation_mode.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_email_validation_mode.yml new file mode 100644 index 0000000000000..a695e1a62a7d7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_email_validation_mode.yml @@ -0,0 +1,3 @@ +framework: + validation: + email_validation_mode: html5 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_and_validation_mode.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_and_validation_mode.yml new file mode 100644 index 0000000000000..64e658ba69436 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_and_validation_mode.yml @@ -0,0 +1,4 @@ +framework: + validation: + strict_email: true + email_validation_mode: html5 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_disabled.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_disabled.yml new file mode 100644 index 0000000000000..b5be5f598b14c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_disabled.yml @@ -0,0 +1,3 @@ +framework: + validation: + strict_email: false diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_enabled.yml similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email.yml rename to src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email_enabled.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml index 36b84f71e4582..8efb803c12ad9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml @@ -28,7 +28,6 @@ framework: from: [approved_by_journalist, approved_by_spellchecker] to: [published] pull_request: - type: state_machine marking_store: type: single_state supports: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml deleted file mode 100644 index 41b81683ba445..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml +++ /dev/null @@ -1,7 +0,0 @@ -framework: - workflows: - missing_type: - supports: stdClass - places: [ first, second ] - transitions: - go: {from: first, to: last } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index b40e735c98b73..4269cf1311154 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -189,8 +189,9 @@ public function testWorkflows() { $container = $this->createContainerFromFile('workflows'); - $this->assertTrue($container->hasDefinition('workflow.article', 'Workflow is registered as a service')); - $this->assertTrue($container->hasDefinition('workflow.article.definition', 'Workflow definition is registered as a service')); + $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is registered as a service'); + $this->assertSame('workflow.abstract', $container->getDefinition('workflow.article')->getParent()); + $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); $workflowDefinition = $container->getDefinition('workflow.article.definition'); @@ -208,8 +209,9 @@ public function testWorkflows() ); $this->assertSame(array('workflow.definition' => array(array('name' => 'article', 'type' => 'workflow', 'marking_store' => 'multiple_state'))), $workflowDefinition->getTags()); - $this->assertTrue($container->hasDefinition('state_machine.pull_request', 'State machine is registered as a service')); - $this->assertTrue($container->hasDefinition('state_machine.pull_request.definition', 'State machine definition is registered as a service')); + $this->assertTrue($container->hasDefinition('state_machine.pull_request'), 'State machine is registered as a service'); + $this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent()); + $this->assertTrue($container->hasDefinition('state_machine.pull_request.definition'), 'State machine definition is registered as a service'); $this->assertCount(4, $workflowDefinition->getArgument(1)); $this->assertSame('draft', $workflowDefinition->getArgument(2)); @@ -242,15 +244,6 @@ public function testWorkflows() $this->assertGreaterThan(0, count($registryDefinition->getMethodCalls())); } - /** - * @group legacy - * @expectedDeprecation The "type" option of the "framework.workflows.missing_type" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0. - */ - public function testDeprecatedWorkflowMissingType() - { - $container = $this->createContainerFromFile('workflows_without_type'); - } - /** * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException * @expectedExceptionMessage "type" and "service" cannot be used together. @@ -628,7 +621,7 @@ public function testValidationPaths() $container = $this->createContainerFromFile('validation_annotations', array( 'kernel.bundles' => array('TestBundle' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\TestBundle'), - 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'parent' => null, 'path' => __DIR__.'/Fixtures/TestBundle')), + 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/TestBundle')), )); $calls = $container->getDefinition('validator.builder')->getMethodCalls(); @@ -664,7 +657,7 @@ public function testValidationPathsUsingCustomBundlePath() $container = $this->createContainerFromFile('validation_annotations', array( 'kernel.bundles' => array('CustomPathBundle' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\CustomPathBundle'), - 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'parent' => null, 'path' => __DIR__.'/Fixtures/CustomPathBundle')), + 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/CustomPathBundle')), )); $calls = $container->getDefinition('validator.builder')->getMethodCalls(); @@ -704,18 +697,51 @@ public function testValidationNoStaticMethod() // no cache, no annotations, no static methods } - public function testValidationTranslationDomain() + /** + * @group legacy + * @expectedDeprecation The "framework.validation.strict_email" configuration key has been deprecated in Symfony 4.1. Use the "framework.validation.email_validation_mode" configuration key instead. + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage "strict_email" and "email_validation_mode" cannot be used together. + */ + public function testCannotConfigureStrictEmailAndEmailValidationModeAtTheSameTime() { - $container = $this->createContainerFromFile('validation_translation_domain'); + $this->createContainerFromFile('validation_strict_email_and_validation_mode'); + } - $this->assertSame('messages', $container->getParameter('validator.translation_domain')); + /** + * @group legacy + * @expectedDeprecation The "framework.validation.strict_email" configuration key has been deprecated in Symfony 4.1. Use the "framework.validation.email_validation_mode" configuration key instead. + */ + public function testEnabledStrictEmailOptionIsMappedToStrictEmailValidationMode() + { + $container = $this->createContainerFromFile('validation_strict_email_enabled'); + + $this->assertSame('strict', $container->getDefinition('validator.email')->getArgument(0)); + } + + /** + * @group legacy + * @expectedDeprecation The "framework.validation.strict_email" configuration key has been deprecated in Symfony 4.1. Use the "framework.validation.email_validation_mode" configuration key instead. + */ + public function testDisabledStrictEmailOptionIsMappedToLooseEmailValidationMode() + { + $container = $this->createContainerFromFile('validation_strict_email_disabled'); + + $this->assertSame('loose', $container->getDefinition('validator.email')->getArgument(0)); } - public function testValidationStrictEmail() + public function testEmailValidationModeIsPassedToEmailValidator() + { + $container = $this->createContainerFromFile('validation_email_validation_mode'); + + $this->assertSame('html5', $container->getDefinition('validator.email')->getArgument(0)); + } + + public function testValidationTranslationDomain() { - $container = $this->createContainerFromFile('validation_strict_email'); + $container = $this->createContainerFromFile('validation_translation_domain'); - $this->assertTrue($container->getDefinition('validator.email')->getArgument(0)); + $this->assertSame('messages', $container->getParameter('validator.translation_domain')); } public function testValidationMapping() @@ -867,24 +893,9 @@ public function testSerializerCacheDisabled() $this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); } - /** - * @group legacy - * @expectedDeprecation The "framework.serializer.cache" option is deprecated %s. - */ - public function testDeprecatedSerializerCacheOption() - { - $container = $this->createContainerFromFile('serializer_legacy_cache', array('kernel.debug' => true, 'kernel.container_class' => __CLASS__)); - - $this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); - $this->assertTrue($container->hasDefinition('serializer.mapping.class_metadata_factory')); - - $cache = $container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1); - $this->assertEquals(new Reference('foo'), $cache); - } - public function testSerializerMapping() { - $container = $this->createContainerFromFile('serializer_mapping', array('kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/TestBundle', 'parent' => null)))); + $container = $this->createContainerFromFile('serializer_mapping', array('kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/TestBundle')))); $projectDir = $container->getParameter('kernel.project_dir'); $configDir = __DIR__.'/Fixtures/TestBundle/Resources/config'; $expectedLoaders = array( @@ -1037,7 +1048,7 @@ protected function createContainer(array $data = array()) { return new ContainerBuilder(new ParameterBag(array_merge(array( 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), - 'kernel.bundles_metadata' => array('FrameworkBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle', 'path' => __DIR__.'/../..', 'parent' => null)), + 'kernel.bundles_metadata' => array('FrameworkBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle', 'path' => __DIR__.'/../..')), 'kernel.cache_dir' => __DIR__, 'kernel.project_dir' => __DIR__, 'kernel.debug' => false, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php index 057a522fb3f95..0a7d2391d55e9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php @@ -17,7 +17,6 @@ use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Templating\EngineInterface as ComponentEngineInterface; use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; -use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface as FrameworkBundleEngineInterface; class AutowiringTypesTest extends WebTestCase @@ -56,12 +55,7 @@ public function testEventDispatcherAutowiring() $container = static::$kernel->getContainer(); $autowiredServices = $container->get('test.autowiring_types.autowired_services'); - - if (class_exists(ContainerAwareEventDispatcher::class)) { - $this->assertInstanceOf(ContainerAwareEventDispatcher::class, $autowiredServices->getDispatcher(), 'The event_dispatcher service should be injected if the debug is not enabled'); - } else { - $this->assertInstanceOf(EventDispatcher::class, $autowiredServices->getDispatcher(), 'The event_dispatcher service should be injected if the debug is not enabled'); - } + $this->assertInstanceOf(EventDispatcher::class, $autowiredServices->getDispatcher(), 'The event_dispatcher service should be injected if the debug is not enabled'); static::bootKernel(array('debug' => true)); $container = static::$kernel->getContainer(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.php index ae17f605a40f1..967eac3140e5f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.php @@ -24,7 +24,6 @@ class SubRequestServiceResolutionController implements ContainerAwareInterface public function indexAction() { $request = $this->container->get('request_stack')->getCurrentRequest(); - $path['_forwarded'] = $request->attributes; $path['_controller'] = 'TestBundle:SubRequestServiceResolution:fragment'; $subRequest = $request->duplicate(array(), null, $path); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php index 384beebff1f6f..386a43424eae0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php @@ -75,22 +75,6 @@ public function testClearUnexistingPool() ->execute(array('pools' => array('unknown_pool')), array('decorated' => false)); } - /** - * @group legacy - * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\CachePoolClearCommand::__construct() expects an instance of "Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. - */ - public function testLegacyClearCommand() - { - $application = new Application(static::$kernel); - $application->add(new CachePoolClearCommand()); - - $tester = new CommandTester($application->find('cache:pool:clear')); - - $tester->execute(array('pools' => array())); - - $this->assertContains('Cache was successfully cleared', $tester->getDisplay()); - } - private function createCommandTester() { $container = static::$kernel->getContainer(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php index e0548078fca2c..f686ec59d66d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php @@ -12,13 +12,24 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Routing; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use Symfony\Bundle\FrameworkBundle\Routing\Router; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class RouterTest extends TestCase { + /** + * @expectedException \LogicException + * @expectedExceptionMessage You should either pass a "Symfony\Component\DependencyInjection\ContainerInterface" instance or provide the $parameters argument of the "Symfony\Bundle\FrameworkBundle\Routing\Router::__construct" method + */ + public function testConstructThrowsOnNonSymfonyNorPsr11Container() + { + new Router($this->getMockBuilder(ContainerInterface::class)->getMock(), 'foo'); + } + public function testGenerateWithServiceParam() { $routes = new RouteCollection(); @@ -33,6 +44,33 @@ public function testGenerateWithServiceParam() ), array(), '', array(), array(), '"%foo%" == "bar"' )); + $sc = $this->getPsr11ServiceContainer($routes); + $parameters = $this->getParameterBag(array( + 'locale' => 'es', + 'foo' => 'bar', + )); + + $router = new Router($sc, 'foo', array(), null, $parameters); + + $this->assertSame('/en', $router->generate('foo', array('_locale' => 'en'))); + $this->assertSame('/', $router->generate('foo', array('_locale' => 'es'))); + $this->assertSame('"bar" == "bar"', $router->getRouteCollection()->get('foo')->getCondition()); + } + + public function testGenerateWithServiceParamWithSfContainer() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + ' /{_locale}', + array( + '_locale' => '%locale%', + ), + array( + '_locale' => 'en|es', + ), array(), '', array(), array(), '"%foo%" == "bar"' + )); + $sc = $this->getServiceContainer($routes); $sc->setParameter('locale', 'es'); $sc->setParameter('foo', 'bar'); @@ -61,6 +99,47 @@ public function testDefaultsPlaceholders() ) )); + $sc = $this->getPsr11ServiceContainer($routes); + + $parameters = $this->getParameterBag(array( + 'parameter.foo' => 'foo', + 'parameter.bar' => 'bar', + 'parameter' => 'boo', + 'bee_parameter' => 'foo_bee', + )); + + $router = new Router($sc, 'foo', array(), null, $parameters); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%escaped%', + 'boo' => array('boo', '%escaped_parameter%', array('foo_bee', 'bee')), + 'bee' => array('bee', 'bee'), + ), + $route->getDefaults() + ); + } + + public function testDefaultsPlaceholdersWithSfContainer() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + '/foo', + array( + 'foo' => 'before_%parameter.foo%', + 'bar' => '%parameter.bar%_after', + 'baz' => '%%escaped%%', + 'boo' => array('%parameter%', '%%escaped_parameter%%', array('%bee_parameter%', 'bee')), + 'bee' => array('bee', 'bee'), + ), + array( + ) + )); + $sc = $this->getServiceContainer($routes); $sc->setParameter('parameter.foo', 'foo'); @@ -98,6 +177,41 @@ public function testRequirementsPlaceholders() ) )); + $sc = $this->getPsr11ServiceContainer($routes); + $parameters = $this->getParameterBag(array( + 'parameter.foo' => 'foo', + 'parameter.bar' => 'bar', + )); + + $router = new Router($sc, 'foo', array(), null, $parameters); + + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%escaped%', + ), + $route->getRequirements() + ); + } + + public function testRequirementsPlaceholdersWithSfContainer() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + '/foo', + array( + ), + array( + 'foo' => 'before_%parameter.foo%', + 'bar' => '%parameter.bar%_after', + 'baz' => '%%escaped%%', + ) + )); + $sc = $this->getServiceContainer($routes); $sc->setParameter('parameter.foo', 'foo'); $sc->setParameter('parameter.bar', 'bar'); @@ -121,6 +235,24 @@ public function testPatternPlaceholders() $routes->add('foo', new Route('/before/%parameter.foo%/after/%%escaped%%')); + $sc = $this->getPsr11ServiceContainer($routes); + $parameters = $this->getParameterBag(array('parameter.foo' => 'foo')); + + $router = new Router($sc, 'foo', array(), null, $parameters); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%escaped%', + $route->getPath() + ); + } + + public function testPatternPlaceholdersWithSfContainer() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/before/%parameter.foo%/after/%%escaped%%')); + $sc = $this->getServiceContainer($routes); $sc->setParameter('parameter.foo', 'foo'); @@ -143,6 +275,20 @@ public function testEnvPlaceholders() $routes->add('foo', new Route('/%env(FOO)%')); + $router = new Router($this->getPsr11ServiceContainer($routes), 'foo', array(), null, $this->getParameterBag()); + $router->getRouteCollection(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Using "%env(FOO)%" is not allowed in routing configuration. + */ + public function testEnvPlaceholdersWithSfContainer() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%env(FOO)%')); + $router = new Router($this->getServiceContainer($routes), 'foo'); $router->getRouteCollection(); } @@ -156,6 +302,27 @@ public function testHostPlaceholders() $routes->add('foo', $route); + $sc = $this->getPsr11ServiceContainer($routes); + $parameters = $this->getParameterBag(array('parameter.foo' => 'foo')); + + $router = new Router($sc, 'foo', array(), null, $parameters); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%escaped%', + $route->getHost() + ); + } + + public function testHostPlaceholdersWithSfContainer() + { + $routes = new RouteCollection(); + + $route = new Route('foo'); + $route->setHost('/before/%parameter.foo%/after/%%escaped%%'); + + $routes->add('foo', $route); + $sc = $this->getServiceContainer($routes); $sc->setParameter('parameter.foo', 'foo'); @@ -172,7 +339,7 @@ public function testHostPlaceholders() * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException * @expectedExceptionMessage You have requested a non-existent parameter "nope". */ - public function testExceptionOnNonExistentParameter() + public function testExceptionOnNonExistentParameterWithSfContainer() { $routes = new RouteCollection(); @@ -194,6 +361,23 @@ public function testExceptionOnNonStringParameter() $routes->add('foo', new Route('/%object%')); + $sc = $this->getPsr11ServiceContainer($routes); + $parameters = $this->getParameterBag(array('object' => new \stdClass())); + + $router = new Router($sc, 'foo', array(), null, $parameters); + $router->getRouteCollection()->get('foo'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The container parameter "object", used in the route configuration value "/%object%", must be a string or numeric, but it is of type object. + */ + public function testExceptionOnNonStringParameterWithSfContainer() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%object%')); + $sc = $this->getServiceContainer($routes); $sc->setParameter('object', new \stdClass()); @@ -209,6 +393,23 @@ public function testDefaultValuesAsNonStrings($value) $routes = new RouteCollection(); $routes->add('foo', new Route('foo', array('foo' => $value), array('foo' => '\d+'))); + $sc = $this->getPsr11ServiceContainer($routes); + + $router = new Router($sc, 'foo', array(), null, $this->getParameterBag()); + + $route = $router->getRouteCollection()->get('foo'); + + $this->assertSame($value, $route->getDefault('foo')); + } + + /** + * @dataProvider getNonStringValues + */ + public function testDefaultValuesAsNonStringsWithSfContainer($value) + { + $routes = new RouteCollection(); + $routes->add('foo', new Route('foo', array('foo' => $value), array('foo' => '\d+'))); + $sc = $this->getServiceContainer($routes); $router = new Router($sc, 'foo'); @@ -224,6 +425,20 @@ public function testGetRouteCollectionAddsContainerParametersResource() $routeCollection->method('getIterator')->willReturn(new \ArrayIterator(array(new Route('/%locale%')))); $routeCollection->expects($this->once())->method('addResource')->with(new ContainerParametersResource(array('locale' => 'en'))); + $sc = $this->getPsr11ServiceContainer($routeCollection); + $parameters = $this->getParameterBag(array('locale' => 'en')); + + $router = new Router($sc, 'foo', array(), null, $parameters); + + $router->getRouteCollection(); + } + + public function testGetRouteCollectionAddsContainerParametersResourceWithSfContainer() + { + $routeCollection = $this->getMockBuilder(RouteCollection::class)->getMock(); + $routeCollection->method('getIterator')->willReturn(new \ArrayIterator(array(new Route('/%locale%')))); + $routeCollection->expects($this->once())->method('addResource')->with(new ContainerParametersResource(array('locale' => 'en'))); + $sc = $this->getServiceContainer($routeCollection); $sc->setParameter('locale', 'en'); @@ -260,4 +475,39 @@ private function getServiceContainer(RouteCollection $routes) return $sc; } + + private function getPsr11ServiceContainer(RouteCollection $routes): ContainerInterface + { + $loader = $this->getMockBuilder(LoaderInterface::class)->getMock(); + + $loader + ->expects($this->any()) + ->method('load') + ->will($this->returnValue($routes)) + ; + + $sc = $this->getMockBuilder(ContainerInterface::class)->getMock(); + + $sc + ->expects($this->once()) + ->method('get') + ->will($this->returnValue($loader)) + ; + + return $sc; + } + + private function getParameterBag(array $params = array()): ContainerInterface + { + $bag = $this->getMockBuilder(ContainerInterface::class)->getMock(); + $bag + ->expects($this->any()) + ->method('get') + ->will($this->returnCallback(function ($key) use ($params) { + return isset($params[$key]) ? $params[$key] : null; + })) + ; + + return $bag; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php index 3e162d167d23e..7f7829b9c0f91 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php @@ -81,29 +81,4 @@ public function testParseValidNameWithNotFoundBundle() { $this->parser->parse('BarBundle:Post:index.html.php'); } - - /** - * @group legacy - * @dataProvider provideAbsolutePaths - * @expectedDeprecation Absolute template path support is deprecated since Symfony 3.1 and will be removed in 4.0. - */ - public function testAbsolutePathsAreDeprecated($name, $logicalName, $path, $ref) - { - $template = $this->parser->parse($name); - - $this->assertSame($ref->getLogicalName(), $template->getLogicalName()); - $this->assertSame($logicalName, $template->getLogicalName()); - $this->assertSame($path, $template->getPath()); - } - - public function provideAbsolutePaths() - { - return array( - array('/path/to/section/index.html.php', '/path/to/section/index.html.php', '/path/to/section/index.html.php', new BaseTemplateReference('/path/to/section/index.html.php', 'php')), - array('C:\\path\\to\\section\\name.html.php', 'C:path/to/section/name.html.php', 'C:path/to/section/name.html.php', new BaseTemplateReference('C:path/to/section/name.html.php', 'php')), - array('C:\\path\\to\\section\\name:foo.html.php', 'C:path/to/section/name:foo.html.php', 'C:path/to/section/name:foo.html.php', new BaseTemplateReference('C:path/to/section/name:foo.html.php', 'php')), - array('\\path\\to\\section\\name.html.php', '/path/to/section/name.html.php', '/path/to/section/name.html.php', new BaseTemplateReference('/path/to/section/name.html.php', 'php')), - array('/path/to/section/name.php', '/path/to/section/name.php', '/path/to/section/name.php', new BaseTemplateReference('/path/to/section/name.php', 'php')), - ); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php deleted file mode 100644 index ca45f43329a2f..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Translation; - -use Symfony\Bundle\FrameworkBundle\Tests\TestCase; -use Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor; -use Symfony\Component\Translation\MessageCatalogue; - -/** - * @group legacy - */ -class PhpExtractorTest extends TestCase -{ - /** - * @dataProvider resourcesProvider - * - * @param array|string $resource - */ - public function testExtraction($resource) - { - // Arrange - $extractor = new PhpExtractor(); - $extractor->setPrefix('prefix'); - $catalogue = new MessageCatalogue('en'); - - // Act - $extractor->extract($resource, $catalogue); - - $expectedHeredoc = << array( - 'single-quoted key' => 'prefixsingle-quoted key', - 'double-quoted key' => 'prefixdouble-quoted key', - 'heredoc key' => 'prefixheredoc key', - 'nowdoc key' => 'prefixnowdoc key', - "double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences", - 'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences', - 'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"', - $expectedHeredoc => 'prefix'.$expectedHeredoc, - $expectedNowdoc => 'prefix'.$expectedNowdoc, - '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', - ), - 'not_messages' => array( - 'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array', - 'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array', - 'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array', - 'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array', - 'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%', - 'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%', - 'typecast' => 'prefixtypecast', - 'msg1' => 'prefixmsg1', - 'msg2' => 'prefixmsg2', - ), - ); - $actualCatalogue = $catalogue->all(); - - $this->assertEquals($expectedCatalogue, $actualCatalogue); - } - - public function resourcesProvider() - { - $directory = __DIR__.'/../Fixtures/Resources/views/'; - $splFiles = array(); - foreach (new \DirectoryIterator($directory) as $fileInfo) { - if ($fileInfo->isDot()) { - continue; - } - if ('translation.html.php' === $fileInfo->getBasename()) { - $phpFile = $fileInfo->getPathname(); - } - $splFiles[] = $fileInfo->getFileInfo(); - } - - return array( - array($directory), - array($phpFile), - array(glob($directory.'*')), - array($splFiles), - array(new \ArrayObject(glob($directory.'*'))), - array(new \ArrayObject($splFiles)), - ); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index c2c2364d7097c..ab65635d69e61 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -43,157 +43,6 @@ protected function deleteTmpDir() $fs->remove($dir); } - /** - * @group legacy - * @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. - */ - public function testTransWithoutCachingOmittingLocale() - { - $translator = $this->getTranslator($this->getLoader(), array(), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); - $translator->setLocale('fr'); - $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); - - $this->assertEquals('foo (FR)', $translator->trans('foo')); - $this->assertEquals('bar (EN)', $translator->trans('bar')); - $this->assertEquals('foobar (ES)', $translator->trans('foobar')); - $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); - $this->assertEquals('no translation', $translator->trans('no translation')); - $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); - $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); - $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); - $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); - } - - /** - * @group legacy - * @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. - */ - public function testTransWithCachingOmittingLocale() - { - // prime the cache - $translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); - $translator->setLocale('fr'); - $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); - - $this->assertEquals('foo (FR)', $translator->trans('foo')); - $this->assertEquals('bar (EN)', $translator->trans('bar')); - $this->assertEquals('foobar (ES)', $translator->trans('foobar')); - $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); - $this->assertEquals('no translation', $translator->trans('no translation')); - $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); - $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); - $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); - $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); - - // do it another time as the cache is primed now - $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); - $loader->expects($this->never())->method('load'); - - $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); - $translator->setLocale('fr'); - $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); - - $this->assertEquals('foo (FR)', $translator->trans('foo')); - $this->assertEquals('bar (EN)', $translator->trans('bar')); - $this->assertEquals('foobar (ES)', $translator->trans('foobar')); - $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); - $this->assertEquals('no translation', $translator->trans('no translation')); - $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); - $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); - $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); - $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); - } - - /** - * @group legacy - * @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. - * @expectedException \InvalidArgumentException - */ - public function testTransWithCachingWithInvalidLocaleOmittingLocale() - { - $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); - $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Tests\Translation\TranslatorWithInvalidLocale', null); - - $translator->trans('foo'); - } - - /** - * @group legacy - * @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. - */ - public function testLoadResourcesWithoutCachingOmittingLocale() - { - $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); - $resourceFiles = array( - 'fr' => array( - __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', - ), - ); - - $translator = $this->getTranslator($loader, array('resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); - $translator->setLocale('fr'); - - $this->assertEquals('répertoire', $translator->trans('folder')); - } - - /** - * @group legacy - * @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. - */ - public function testGetDefaultLocaleOmittingLocale() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container - ->expects($this->once()) - ->method('getParameter') - ->with('kernel.default_locale') - ->will($this->returnValue('en')) - ; - $translator = new Translator($container, new MessageFormatter()); - - $this->assertSame('en', $translator->getLocale()); - } - - /** - * @group legacy - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Missing third $defaultLocale argument. - */ - public function testGetDefaultLocaleOmittingLocaleWithPsrContainer() - { - $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); - $translator = new Translator($container, new MessageFormatter()); - } - - /** - * @group legacy - * @expectedDeprecation Method Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct() takes the default locale as 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. - */ - public function testWarmupOmittingLocale() - { - $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); - $resourceFiles = array( - 'fr' => array( - __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', - ), - ); - - // prime the cache - $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); - $translator->setFallbackLocales(array('fr')); - $translator->warmup($this->tmpDir); - - $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); - $loader - ->expects($this->never()) - ->method('load'); - - $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); - $translator->setLocale('fr'); - $translator->setFallbackLocales(array('fr')); - $this->assertEquals('répertoire', $translator->trans('folder')); - } - public function testTransWithoutCaching() { $translator = $this->getTranslator($this->getLoader()); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php deleted file mode 100644 index f619584023a77..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Validator; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory; -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; -use Symfony\Component\Validator\ConstraintValidator; - -/** - * @group legacy - */ -class ConstraintValidatorFactoryTest extends TestCase -{ - public function testGetInstanceCreatesValidator() - { - $class = get_class($this->getMockForAbstractClass('Symfony\\Component\\Validator\\ConstraintValidator')); - - $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); - $constraint - ->expects($this->exactly(2)) - ->method('validatedBy') - ->will($this->returnValue($class)); - - $factory = new ConstraintValidatorFactory(new Container()); - $this->assertInstanceOf($class, $factory->getInstance($constraint)); - } - - public function testGetInstanceReturnsExistingValidator() - { - $factory = new ConstraintValidatorFactory(new Container()); - $v1 = $factory->getInstance(new BlankConstraint()); - $v2 = $factory->getInstance(new BlankConstraint()); - $this->assertSame($v1, $v2); - } - - public function testGetInstanceReturnsService() - { - $service = 'validator_constraint_service'; - $validator = $this->getMockForAbstractClass(ConstraintValidator::class); - - // mock ContainerBuilder b/c it implements TaggedContainerInterface - $container = $this->getMockBuilder(ContainerBuilder::class)->setMethods(array('get', 'has'))->getMock(); - $container - ->expects($this->once()) - ->method('get') - ->with($service) - ->willReturn($validator); - $container - ->expects($this->once()) - ->method('has') - ->with($service) - ->willReturn(true); - - $constraint = $this->getMockBuilder(Constraint::class)->getMock(); - $constraint - ->expects($this->exactly(2)) - ->method('validatedBy') - ->will($this->returnValue($service)); - - $factory = new ConstraintValidatorFactory($container); - $this->assertSame($validator, $factory->getInstance($constraint)); - } - - public function testGetInstanceReturnsServiceWithAlias() - { - $service = 'validator_constraint_service'; - $alias = 'validator_constraint_alias'; - $validator = $this->getMockForAbstractClass('Symfony\\Component\\Validator\\ConstraintValidator'); - - // mock ContainerBuilder b/c it implements TaggedContainerInterface - $container = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\ContainerBuilder')->setMethods(array('get'))->getMock(); - $container - ->expects($this->once()) - ->method('get') - ->with($service) - ->will($this->returnValue($validator)); - - $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); - $constraint - ->expects($this->once()) - ->method('validatedBy') - ->will($this->returnValue($alias)); - - $factory = new ConstraintValidatorFactory($container, array('validator_constraint_alias' => 'validator_constraint_service')); - $this->assertSame($validator, $factory->getInstance($constraint)); - } - - /** - * @expectedException \Symfony\Component\Validator\Exception\ValidatorException - */ - public function testGetInstanceInvalidValidatorClass() - { - $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); - $constraint - ->expects($this->exactly(2)) - ->method('validatedBy') - ->will($this->returnValue('Fully\\Qualified\\ConstraintValidator\\Class\\Name')); - - $factory = new ConstraintValidatorFactory(new Container()); - $factory->getInstance($constraint); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php deleted file mode 100644 index 35bda9ded082b..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Translation; - -use Symfony\Component\Translation\Extractor\PhpExtractor as NewPhpExtractor; - -@trigger_error(sprintf('The class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use "%s" instead. ', PhpExtractor::class, NewPhpExtractor::class), E_USER_DEPRECATED); - -/** - * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Extractor\PhpExtractor instead - */ -class PhpExtractor extends NewPhpExtractor -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php deleted file mode 100644 index 02fd4b56aeee9..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Translation; - -@trigger_error(sprintf('The class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use "%s" instead. ', PhpStringTokenParser::class, \Symfony\Component\Translation\Extractor\PhpStringTokenParser::class), E_USER_DEPRECATED); - -/** - * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Extractor\PhpStringTokenParser instead - */ -class PhpStringTokenParser extends \Symfony\Component\Translation\Extractor\PhpStringTokenParser -{ -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php b/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php deleted file mode 100644 index 2431e79ff238e..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Translation; - -use Symfony\Component\Translation\Reader\TranslationReader; -use Symfony\Component\Translation\MessageCatalogue; - -@trigger_error(sprintf('The class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use "%s" instead. ', TranslationLoader::class, TranslationReader::class), E_USER_DEPRECATED); - -/** - * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Reader\TranslationReader instead - */ -class TranslationLoader extends TranslationReader -{ - /** - * Loads translation messages from a directory to the catalogue. - * - * @param string $directory The directory to look into - * @param MessageCatalogue $catalogue The catalogue - */ - public function loadMessages($directory, MessageCatalogue $catalogue) - { - $this->read($directory, $catalogue); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index 631d14e91b600..14cb2adecfe55 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -12,7 +12,6 @@ namespace Symfony\Bundle\FrameworkBundle\Translation; use Psr\Container\ContainerInterface; -use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Translation\Translator as BaseTranslator; use Symfony\Component\Translation\Exception\InvalidArgumentException; @@ -64,20 +63,8 @@ class Translator extends BaseTranslator implements WarmableInterface * * @throws InvalidArgumentException */ - public function __construct(ContainerInterface $container, $formatter, $defaultLocale = null, array $loaderIds = array(), array $options = array()) + public function __construct(ContainerInterface $container, MessageFormatterInterface $formatter, string $defaultLocale, array $loaderIds = array(), array $options = array()) { - // BC 3.x, to be removed in 4.0 along with the $defaultLocale default value - if (is_array($defaultLocale) || 3 > func_num_args()) { - if (!$container instanceof SymfonyContainerInterface) { - throw new \InvalidArgumentException('Missing third $defaultLocale argument.'); - } - - $options = $loaderIds; - $loaderIds = $defaultLocale; - $defaultLocale = $container->getParameter('kernel.default_locale'); - @trigger_error(sprintf('Method %s() takes the default locale as 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0.', __METHOD__), E_USER_DEPRECATED); - } - $this->container = $container; $this->loaderIds = $loaderIds; diff --git a/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php b/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php deleted file mode 100644 index 26abedbe3da45..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Validator; - -use Psr\Container\ContainerInterface; -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\ConstraintValidatorInterface; -use Symfony\Component\Validator\ContainerConstraintValidatorFactory; -use Symfony\Component\Validator\Exception\UnexpectedTypeException; -use Symfony\Component\Validator\Exception\ValidatorException; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', ConstraintValidatorFactory::class, ContainerConstraintValidatorFactory::class), E_USER_DEPRECATED); - -/** - * Uses a service container to create constraint validators. - * - * A constraint validator should be tagged as "validator.constraint_validator" - * in the service container and include an "alias" attribute: - * - * - * - * - * - * - * A constraint may then return this alias in its validatedBy() method: - * - * public function validatedBy() - * { - * return 'some_alias'; - * } - * - * @author Kris Wallsmith - * - * @deprecated since version 3.3 - */ -class ConstraintValidatorFactory extends ContainerConstraintValidatorFactory -{ - protected $container; - protected $validators; - - public function __construct(ContainerInterface $container, array $validators = array()) - { - parent::__construct($container); - - $this->validators = $validators; - $this->container = $container; - } - - /** - * Returns the validator for the supplied constraint. - * - * @return ConstraintValidatorInterface A validator for the supplied constraint - * - * @throws ValidatorException When the validator class does not exist - * @throws UnexpectedTypeException When the validator is not an instance of ConstraintValidatorInterface - */ - public function getInstance(Constraint $constraint) - { - $name = $constraint->validatedBy(); - - if (!isset($this->validators[$name])) { - return parent::getInstance($constraint); - } - - if (is_string($this->validators[$name])) { - $this->validators[$name] = $this->container->get($this->validators[$name]); - } - - if (!$this->validators[$name] instanceof ConstraintValidatorInterface) { - throw new UnexpectedTypeException($this->validators[$name], 'Symfony\Component\Validator\ConstraintValidatorInterface'); - } - - return $this->validators[$name]; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 9bb59cf8e8db7..a2114707c29fb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -16,46 +16,45 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "ext-xml": "*", "symfony/cache": "~3.4|~4.0", - "symfony/class-loader": "~3.2", "symfony/dependency-injection": "^3.4.3|^4.0.3", "symfony/config": "~3.4|~4.0", "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/filesystem": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/filesystem": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", "symfony/routing": "^3.4.5|^4.0.5" }, "require-dev": { "doctrine/cache": "~1.0", "fig/link-util": "^1.0", - "symfony/asset": "~3.3|~4.0", - "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/asset": "~3.4|~4.0", + "symfony/browser-kit": "~3.4|~4.0", "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~2.8|~3.0|~4.0", - "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~3.4|~4.0", + "symfony/dom-crawler": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/security": "~2.8|~3.0|~4.0", + "symfony/security": "~3.4|~4.0", "symfony/form": "~3.4|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/process": "~2.8|~3.0|~4.0", - "symfony/security-core": "~3.2|~4.0", - "symfony/security-csrf": "~2.8|~3.0|~4.0", - "symfony/serializer": "~3.3|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0", + "symfony/security-core": "~3.4|~4.0", + "symfony/security-csrf": "~3.4|~4.0", + "symfony/serializer": "~3.4|~4.0", "symfony/stopwatch": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/validator": "~3.4|~4.0", - "symfony/var-dumper": "~3.3|~4.0", - "symfony/workflow": "~3.3|~4.0", - "symfony/yaml": "~3.2|~4.0", - "symfony/property-info": "~3.3|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/validator": "~4.1", + "symfony/var-dumper": "~3.4|~4.0", + "symfony/workflow": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0", + "symfony/property-info": "~3.4|~4.0", "symfony/lock": "~3.4|~4.0", - "symfony/web-link": "~3.3|~4.0", + "symfony/web-link": "~3.4|~4.0", "doctrine/annotations": "~1.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0", "twig/twig": "~1.34|~2.4" @@ -64,15 +63,15 @@ "phpdocumentor/reflection-docblock": "<3.0", "phpdocumentor/type-resolver": "<0.2.1", "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/asset": "<3.3", + "symfony/asset": "<3.4", "symfony/console": "<3.4", "symfony/form": "<3.4", - "symfony/property-info": "<3.3", - "symfony/serializer": "<3.3", + "symfony/property-info": "<3.4", + "symfony/serializer": "<3.4", "symfony/stopwatch": "<3.4", "symfony/translation": "<3.4", - "symfony/validator": "<3.4", - "symfony/workflow": "<3.3" + "symfony/validator": "<4.1", + "symfony/workflow": "<3.4" }, "suggest": { "ext-apcu": "For best performance of the system caches", @@ -93,7 +92,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 357abd35ceb70..e576a8b9ba807 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -1,6 +1,30 @@ CHANGELOG ========= +4.1.0 +----- + + * The `logout_on_user_change` firewall option is deprecated and will be removed in 5.0. + * deprecated `SecurityUserValueResolver`, use + `Symfony\Component\Security\Http\Controller\UserValueResolver` instead. + +4.0.0 +----- + + * removed `FirewallContext::getContext()` + * made `FirewallMap::$container` and `::$map` private + * made the first `UserPasswordEncoderCommand::_construct()` argument mandatory + * `UserPasswordEncoderCommand` does not extend `ContainerAwareCommand` anymore + * removed support for voters that don't implement the `VoterInterface` + * removed HTTP digest authentication + * removed command `acl:set` along with `SetAclCommand` class + * removed command `init:acl` along with `InitAclCommand` class + * removed `acl` configuration key and related services, use symfony/acl-bundle instead + * removed auto picking the first registered provider when no configured provider on a firewall and ambiguous + * the firewall option `logout_on_user_change` is now always true, which will trigger a logout if the user changes + between requests + * the `switch_user.stateless` firewall option is `true` for stateless firewalls + 3.4.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php deleted file mode 100644 index 7cb1396d00d27..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\SecurityBundle\Command; - -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Security\Acl\Dbal\Schema; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Schema\SchemaException; - -@trigger_error(sprintf('Class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Bundle\AclBundle\Command\InitAclCommand instead.', InitAclCommand::class), E_USER_DEPRECATED); - -/** - * Installs the tables required by the ACL system. - * - * @author Johannes M. Schmitt - * - * @deprecated since version 3.4, to be removed in 4.0. See Symfony\Bundle\AclBundle\Command\SetAclCommand instead. - */ -class InitAclCommand extends ContainerAwareCommand -{ - protected static $defaultName = 'init:acl'; - - private $connection; - private $schema; - - public function __construct($connection = null, Schema $schema = null) - { - if (!$connection instanceof Connection) { - parent::__construct($connection); - - return; - } - - parent::__construct(); - - $this->connection = $connection; - $this->schema = $schema; - } - - /** - * {@inheritdoc} - */ - public function isEnabled() - { - if (!$this->connection && !$this->getContainer()->has('security.acl.dbal.connection')) { - return false; - } - - return parent::isEnabled(); - } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this - ->setDescription('Mounts ACL tables in the database') - ->setHelp(<<<'EOF' -The %command.name% command mounts ACL tables in the database. - - php %command.full_name% - -The name of the DBAL connection must be configured in your app/config/security.yml configuration file in the security.acl.connection variable. - - security: - acl: - connection: default -EOF - ) - ; - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - (new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output))->warning('Command "init:acl" is deprecated since Symfony 3.4 and will be removed from SecurityBundle in 4.0. Install symfony/acl-bundle and use "acl:init" instead.'); - - if (null === $this->connection) { - $this->connection = $this->getContainer()->get('security.acl.dbal.connection'); - $this->schema = $this->getContainer()->get('security.acl.dbal.schema'); - } - - try { - $this->schema->addToSchema($this->connection->getSchemaManager()->createSchema()); - } catch (SchemaException $e) { - $output->writeln('Aborting: '.$e->getMessage()); - - return 1; - } - - foreach ($this->schema->toSql($this->connection->getDatabasePlatform()) as $sql) { - $this->connection->exec($sql); - } - - $output->writeln('ACL tables have been initialized successfully.'); - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php deleted file mode 100644 index 46aacfc266142..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php +++ /dev/null @@ -1,202 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\SecurityBundle\Command; - -@trigger_error(sprintf('Class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Bundle\AclBundle\Command\SetAclCommand instead.', SetAclCommand::class), E_USER_DEPRECATED); - -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException; -use Symfony\Component\Security\Acl\Permission\MaskBuilder; -use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface; - -/** - * Sets ACL for objects. - * - * @author Kévin Dunglas - * - * @deprecated since version 3.4, to be removed in 4.0. See Symfony\Bundle\AclBundle\Command\SetAclCommand instead. - */ -class SetAclCommand extends ContainerAwareCommand -{ - protected static $defaultName = 'acl:set'; - - private $provider; - - /** - * @param MutableAclProviderInterface $provider - */ - public function __construct($provider = null) - { - if (!$provider instanceof MutableAclProviderInterface) { - parent::__construct($provider); - - return; - } - - parent::__construct(); - - $this->provider = $provider; - } - - /** - * {@inheritdoc} - */ - public function isEnabled() - { - if (null !== $this->provider) { - return parent::isEnabled(); - } - if (!$this->getContainer()->has('security.acl.provider')) { - return false; - } - - $provider = $this->getContainer()->get('security.acl.provider'); - if (!$provider instanceof MutableAclProviderInterface) { - return false; - } - - return parent::isEnabled(); - } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this - ->setDescription('Sets ACL for objects') - ->setHelp(<<%command.name% command sets ACL. -The ACL system must have been initialized with the init:acl command. - -To set VIEW and EDIT permissions for the user kevin on the instance of -Acme\MyClass having the identifier 42: - - php %command.full_name% --user=Symfony/Component/Security/Core/User/User:kevin VIEW EDIT Acme/MyClass:42 - -Note that you can use / instead of \\ for the namespace delimiter to avoid any -problem. - -To set permissions for a role, use the --role option: - - php %command.full_name% --role=ROLE_USER VIEW Acme/MyClass:1936 - -To set permissions at the class scope, use the --class-scope option: - - php %command.full_name% --class-scope --user=Symfony/Component/Security/Core/User/User:anne OWNER Acme/MyClass:42 - -EOF - ) - ->addArgument('arguments', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of permissions and object identities (class name and ID separated by a column)') - ->addOption('user', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A list of security identities') - ->addOption('role', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A list of roles') - ->addOption('class-scope', null, InputOption::VALUE_NONE, 'Use class-scope entries') - ; - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - (new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output))->warning('Command "acl:set" is deprecated since Symfony 3.4 and will be removed from SecurityBundle in 4.0. Install symfony/acl-bundle to use this command.'); - - if (null === $this->provider) { - $this->provider = $this->getContainer()->get('security.acl.provider'); - } - - // Parse arguments - $objectIdentities = array(); - $maskBuilder = $this->getMaskBuilder(); - foreach ($input->getArgument('arguments') as $argument) { - $data = explode(':', $argument, 2); - - if (count($data) > 1) { - $objectIdentities[] = new ObjectIdentity($data[1], strtr($data[0], '/', '\\')); - } else { - $maskBuilder->add($data[0]); - } - } - - // Build permissions mask - $mask = $maskBuilder->get(); - - $userOption = $input->getOption('user'); - $roleOption = $input->getOption('role'); - $classScopeOption = $input->getOption('class-scope'); - - if (empty($userOption) && empty($roleOption)) { - throw new \InvalidArgumentException('A Role or a User must be specified.'); - } - - // Create security identities - $securityIdentities = array(); - - if ($userOption) { - foreach ($userOption as $user) { - $data = explode(':', $user, 2); - - if (1 === count($data)) { - throw new \InvalidArgumentException('The user must follow the format "Acme/MyUser:username".'); - } - - $securityIdentities[] = new UserSecurityIdentity($data[1], strtr($data[0], '/', '\\')); - } - } - - if ($roleOption) { - foreach ($roleOption as $role) { - $securityIdentities[] = new RoleSecurityIdentity($role); - } - } - - // Sets ACL - foreach ($objectIdentities as $objectIdentity) { - // Creates a new ACL if it does not already exist - try { - $this->provider->createAcl($objectIdentity); - } catch (AclAlreadyExistsException $e) { - } - - $acl = $this->provider->findAcl($objectIdentity, $securityIdentities); - - foreach ($securityIdentities as $securityIdentity) { - if ($classScopeOption) { - $acl->insertClassAce($securityIdentity, $mask); - } else { - $acl->insertObjectAce($securityIdentity, $mask); - } - } - - $this->provider->updateAcl($acl); - } - } - - /** - * Gets the mask builder. - * - * @return MaskBuilder - */ - protected function getMaskBuilder() - { - return new MaskBuilder(); - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php index 0f6eb839a18e9..a09d5c3651f96 100644 --- a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php +++ b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php @@ -11,7 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\Command; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -21,7 +21,6 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\SelfSaltingEncoderInterface; -use Symfony\Component\Security\Core\User\User; /** * Encode a user's password. @@ -30,19 +29,15 @@ * * @final since version 3.4 */ -class UserPasswordEncoderCommand extends ContainerAwareCommand +class UserPasswordEncoderCommand extends Command { protected static $defaultName = 'security:encode-password'; private $encoderFactory; private $userClasses; - public function __construct(EncoderFactoryInterface $encoderFactory = null, array $userClasses = array()) + public function __construct(EncoderFactoryInterface $encoderFactory, array $userClasses = array()) { - if (null === $encoderFactory) { - @trigger_error(sprintf('Passing null as the first argument of "%s" is deprecated since Symfony 3.3 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); - } - $this->encoderFactory = $encoderFactory; $this->userClasses = $userClasses; @@ -115,8 +110,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $userClass = $this->getUserClass($input, $io); $emptySalt = $input->getOption('empty-salt'); - $encoderFactory = $this->encoderFactory ?: $this->getContainer()->get('security.encoder_factory'); - $encoder = $encoderFactory->getEncoder($userClass); + $encoder = $this->encoderFactory->getEncoder($userClass); $saltlessWithoutEmptySalt = !$emptySalt && $encoder instanceof SelfSaltingEncoderInterface; if ($saltlessWithoutEmptySalt) { @@ -170,10 +164,8 @@ protected function execute(InputInterface $input, OutputInterface $output) /** * Create the password question to ask the user for the password to be encoded. - * - * @return Question */ - private function createPasswordQuestion() + private function createPasswordQuestion(): Question { $passwordQuestion = new Question('Type in your password to be encoded'); @@ -198,11 +190,6 @@ private function getUserClass(InputInterface $input, SymfonyStyle $io) } if (empty($this->userClasses)) { - if (null === $this->encoderFactory) { - // BC to be removed and simply keep the exception whenever there is no configured user classes in 4.0 - return User::class; - } - throw new \RuntimeException('There are no configured encoders for the "security" extension.'); } diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php index a63b8ce97fd4a..2e3a085a92cfb 100644 --- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php +++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\DataCollector; +use Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; @@ -18,8 +19,6 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; -use Symfony\Component\Security\Core\Role\RoleInterface; -use Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener; use Symfony\Component\Security\Core\Role\SwitchUserRole; use Symfony\Component\Security\Http\Firewall\SwitchUserListener; use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; @@ -119,14 +118,6 @@ public function collect(Request $request, Response $response, \Exception $except // fail silently when the logout URL cannot be generated } - $extractRoles = function ($role) { - if (!$role instanceof RoleInterface && !$role instanceof Role) { - throw new \InvalidArgumentException(sprintf('Roles must be instances of %s or %s (%s given).', RoleInterface::class, Role::class, is_object($role) ? get_class($role) : gettype($role))); - } - - return $role->getRole(); - }; - $this->data = array( 'enabled' => true, 'authenticated' => $token->isAuthenticated(), @@ -137,8 +128,8 @@ public function collect(Request $request, Response $response, \Exception $except 'token_class' => $this->hasVarDumper ? new ClassStub(get_class($token)) : get_class($token), 'logout_url' => $logoutUrl, 'user' => $token->getUsername(), - 'roles' => array_map($extractRoles, $assignedRoles), - 'inherited_roles' => array_unique(array_map($extractRoles, $inheritedRoles)), + 'roles' => array_map(function (Role $role) { return $role->getRole(); }, $assignedRoles), + 'inherited_roles' => array_unique(array_map(function (Role $role) { return $role->getRole(); }, $inheritedRoles)), 'supports_role_hierarchy' => null !== $this->roleHierarchy, ); } diff --git a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php index 435ecc5feb573..85878e2193882 100644 --- a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php +++ b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php @@ -56,12 +56,12 @@ public function __call($method, $arguments) return call_user_func_array(array($this->listener, $method), $arguments); } - public function getWrappedListener() + public function getWrappedListener(): ListenerInterface { return $this->listener; } - public function getInfo() + public function getInfo(): array { if (null === $this->stub) { $this->stub = self::$hasVarDumper ? new ClassStub(get_class($this->listener)) : get_class($this->listener); diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php index 7a329ef667abc..b605879b1a438 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php @@ -38,7 +38,7 @@ public function process(ContainerBuilder $container) $voters = $this->findAndSortTaggedServices('security.voter', $container); if (!$voters) { - throw new LogicException('No security voters found. You need to tag at least one with "security.voter"'); + throw new LogicException('No security voters found. You need to tag at least one with "security.voter".'); } foreach ($voters as $voter) { @@ -46,12 +46,7 @@ public function process(ContainerBuilder $container) $class = $container->getParameterBag()->resolveValue($definition->getClass()); if (!is_a($class, VoterInterface::class, true)) { - @trigger_error(sprintf('Using a "security.voter" tag on a class without implementing the "%s" is deprecated as of 3.4 and will throw an exception in 4.0. Implement the interface instead.', VoterInterface::class), E_USER_DEPRECATED); - } - - if (!method_exists($class, 'vote')) { - // in case the vote method is completely missing, to prevent exceptions when voting - throw new LogicException(sprintf('%s should implement the %s interface when used as voter.', $class, VoterInterface::class)); + throw new LogicException(sprintf('%s must implement the %s when used as a voter.', $class, VoterInterface::class)); } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index a514ab04a8327..a8c1cd52a58f0 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -19,15 +19,7 @@ use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; /** - * This class contains the configuration information. - * - * This information is for the following tags: - * - * * security.config - * * security.acl - * - * This information is solely responsible for how the different configuration - * sections are normalized, and merged. + * SecurityExtension configuration structure. * * @author Johannes M. Schmitt */ @@ -100,7 +92,6 @@ public function getConfigTreeBuilder() ->end() ; - $this->addAclSection($rootNode); $this->addEncodersSection($rootNode); $this->addProvidersSection($rootNode); $this->addFirewallsSection($rootNode, $this->factories); @@ -110,47 +101,6 @@ public function getConfigTreeBuilder() return $tb; } - private function addAclSection(ArrayNodeDefinition $rootNode) - { - $rootNode - ->children() - ->arrayNode('acl') - ->setDeprecated('The "security.acl" configuration key is deprecated since Symfony 3.4 and will be removed in 4.0. Install symfony/acl-bundle and use the "acl" key instead.') - ->children() - ->scalarNode('connection') - ->defaultNull() - ->info('any name configured in doctrine.dbal section') - ->end() - ->arrayNode('cache') - ->addDefaultsIfNotSet() - ->children() - ->scalarNode('id')->end() - ->scalarNode('prefix')->defaultValue('sf2_acl_')->end() - ->end() - ->end() - ->scalarNode('provider')->end() - ->arrayNode('tables') - ->addDefaultsIfNotSet() - ->children() - ->scalarNode('class')->defaultValue('acl_classes')->end() - ->scalarNode('entry')->defaultValue('acl_entries')->end() - ->scalarNode('object_identity')->defaultValue('acl_object_identities')->end() - ->scalarNode('object_identity_ancestors')->defaultValue('acl_object_identity_ancestors')->end() - ->scalarNode('security_identity')->defaultValue('acl_security_identities')->end() - ->end() - ->end() - ->arrayNode('voter') - ->addDefaultsIfNotSet() - ->children() - ->booleanNode('allow_if_object_identity_unavailable')->defaultTrue()->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ; - } - private function addRoleHierarchySection(ArrayNodeDefinition $rootNode) { $rootNode @@ -248,8 +198,9 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto ->booleanNode('stateless')->defaultFalse()->end() ->scalarNode('context')->cannotBeEmpty()->end() ->booleanNode('logout_on_user_change') - ->defaultFalse() - ->info('When true, it will trigger a logout for the user if something has changed. This will be the default behavior as of Syfmony 4.0.') + ->defaultTrue() + ->info('When true, it will trigger a logout for the user if something has changed. Note: No-Op option since 4.0. Will always be true.') + ->setDeprecated('The "%path%.%node%" configuration key has been deprecated in Symfony 4.1 and will be removed in 5.0.') ->end() ->arrayNode('logout') ->treatTrueLike(array()) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php deleted file mode 100644 index 43c88024d4b88..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; - -use Symfony\Component\Config\Definition\Builder\NodeDefinition; -use Symfony\Component\DependencyInjection\ChildDefinition; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * HttpDigestFactory creates services for HTTP digest authentication. - * - * @author Fabien Potencier - * - * @deprecated since 3.4, to be removed in 4.0 - */ -class HttpDigestFactory implements SecurityFactoryInterface -{ - public function __construct($triggerDeprecation = true) - { - if ($triggerDeprecation) { - @trigger_error(sprintf('The "%s" class and the whole HTTP digest authentication system is deprecated since Symfony 3.4 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - } - } - - public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) - { - $provider = 'security.authentication.provider.dao.'.$id; - $container - ->setDefinition($provider, new ChildDefinition('security.authentication.provider.dao')) - ->replaceArgument(0, new Reference($userProvider)) - ->replaceArgument(1, new Reference('security.user_checker.'.$id)) - ->replaceArgument(2, $id) - ; - - // entry point - $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint); - - // listener - $listenerId = 'security.authentication.listener.digest.'.$id; - $listener = $container->setDefinition($listenerId, new ChildDefinition('security.authentication.listener.digest')); - $listener->replaceArgument(1, new Reference($userProvider)); - $listener->replaceArgument(2, $id); - $listener->replaceArgument(3, new Reference($entryPointId)); - - return array($provider, $listenerId, $entryPointId); - } - - public function getPosition() - { - return 'http'; - } - - public function getKey() - { - return 'http-digest'; - } - - public function addConfiguration(NodeDefinition $node) - { - $node - ->setDeprecated('The HTTP digest authentication is deprecated since 3.4 and will be removed in 4.0.') - ->children() - ->scalarNode('provider')->end() - ->scalarNode('realm')->defaultValue('Secured Area')->end() - ->scalarNode('secret')->isRequired()->cannotBeEmpty()->end() - ->end() - ; - } - - protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) - { - if (null !== $defaultEntryPoint) { - return $defaultEntryPoint; - } - - $entryPointId = 'security.authentication.digest_entry_point.'.$id; - $container - ->setDefinition($entryPointId, new ChildDefinition('security.authentication.digest_entry_point')) - ->addArgument($config['realm']) - ->addArgument($config['secret']) - ; - - return $entryPointId; - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 6f43a4ce8cfef..e3ab18570ea27 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -13,6 +13,7 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; +use Symfony\Bundle\SecurityBundle\SecurityUserValueResolver; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Console\Application; use Symfony\Component\DependencyInjection\Alias; @@ -28,6 +29,7 @@ use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder; +use Symfony\Component\Security\Http\Controller\UserValueResolver; /** * SecurityExtension. @@ -44,7 +46,6 @@ class SecurityExtension extends Extension private $factories = array(); private $userProviderFactories = array(); private $expressionLanguage; - private $logoutOnUserChangeByContextKey = array(); public function __construct() { @@ -73,19 +74,8 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('collectors.xml'); $loader->load('guard.xml'); - $container->getDefinition('security.authentication.guard_handler')->setPrivate(true); - $container->getDefinition('security.firewall')->setPrivate(true); - $container->getDefinition('security.firewall.context')->setPrivate(true); - $container->getDefinition('security.validator.user_password')->setPrivate(true); - $container->getDefinition('security.rememberme.response_listener')->setPrivate(true); - $container->getDefinition('templating.helper.logout_url')->setPrivate(true); - $container->getDefinition('templating.helper.security')->setPrivate(true); - $container->getAlias('security.encoder_factory')->setPrivate(true); - if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) { $loader->load('security_debug.xml'); - - $container->getAlias('security.firewall')->setPrivate(true); } if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { @@ -124,86 +114,12 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('security.command.user_password_encoder')->replaceArgument(1, array_keys($config['encoders'])); } - // load ACL - if (isset($config['acl'])) { - $this->aclLoad($config['acl'], $container); - } else { - $container->removeDefinition('security.command.init_acl'); - $container->removeDefinition('security.command.set_acl'); + if (!class_exists(UserValueResolver::class)) { + $container->getDefinition('security.user_value_resolver')->setClass(SecurityUserValueResolver::class); } $container->registerForAutoconfiguration(VoterInterface::class) ->addTag('security.voter'); - - if (\PHP_VERSION_ID < 70000) { - // add some required classes for compilation - $this->addClassesToCompile(array( - 'Symfony\Component\Security\Http\Firewall', - 'Symfony\Component\Security\Core\User\UserProviderInterface', - 'Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager', - 'Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage', - 'Symfony\Component\Security\Core\Authorization\AccessDecisionManager', - 'Symfony\Component\Security\Core\Authorization\AuthorizationChecker', - 'Symfony\Component\Security\Core\Authorization\Voter\VoterInterface', - 'Symfony\Bundle\SecurityBundle\Security\FirewallConfig', - 'Symfony\Bundle\SecurityBundle\Security\FirewallContext', - 'Symfony\Component\HttpFoundation\RequestMatcher', - )); - } - } - - private function aclLoad($config, ContainerBuilder $container) - { - if (!interface_exists('Symfony\Component\Security\Acl\Model\AclInterface')) { - throw new \LogicException('You must install symfony/security-acl in order to use the ACL functionality.'); - } - - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('security_acl.xml'); - - if (isset($config['cache']['id'])) { - $container->setAlias('security.acl.cache', $config['cache']['id'])->setPrivate(true); - } - $container->getDefinition('security.acl.voter.basic_permissions')->addArgument($config['voter']['allow_if_object_identity_unavailable']); - - // custom ACL provider - if (isset($config['provider'])) { - $container->setAlias('security.acl.provider', $config['provider'])->setPrivate(true); - - return; - } - - $this->configureDbalAclProvider($config, $container, $loader); - } - - private function configureDbalAclProvider(array $config, ContainerBuilder $container, $loader) - { - $loader->load('security_acl_dbal.xml'); - - $container->getDefinition('security.acl.dbal.schema')->setPrivate(true); - $container->getAlias('security.acl.dbal.connection')->setPrivate(true); - $container->getAlias('security.acl.provider')->setPrivate(true); - - if (null !== $config['connection']) { - $container->setAlias('security.acl.dbal.connection', sprintf('doctrine.dbal.%s_connection', $config['connection']))->setPrivate(true); - } - - $container - ->getDefinition('security.acl.dbal.schema_listener') - ->addTag('doctrine.event_listener', array( - 'connection' => $config['connection'], - 'event' => 'postGenerateSchema', - 'lazy' => true, - )) - ; - - $container->getDefinition('security.acl.cache.doctrine')->addArgument($config['cache']['prefix']); - - $container->setParameter('security.acl.dbal.class_table_name', $config['tables']['class']); - $container->setParameter('security.acl.dbal.entry_table_name', $config['tables']['entry']); - $container->setParameter('security.acl.dbal.oid_table_name', $config['tables']['object_identity']); - $container->setParameter('security.acl.dbal.oid_ancestors_table_name', $config['tables']['object_identity_ancestors']); - $container->setParameter('security.acl.dbal.sid_table_name', $config['tables']['security_identity']); } private function createRoleHierarchy(array $config, ContainerBuilder $container) @@ -224,12 +140,6 @@ private function createAuthorization($config, ContainerBuilder $container) return; } - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Symfony\\Component\\Security\\Http\\AccessMap', - )); - } - foreach ($config['access_control'] as $access) { $matcher = $this->createRequestMatcher( $container, @@ -338,7 +248,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $config->replaceArgument(4, $firewall['stateless']); - // Provider id (take the first registered provider if none defined) + // Provider id (must be configured explicitly per firewall/authenticator if more than one provider is set) $defaultProvider = null; if (isset($firewall['provider'])) { if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall['provider'])])) { @@ -366,16 +276,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $contextKey = $firewall['context']; } - if (!$logoutOnUserChange = $firewall['logout_on_user_change']) { - @trigger_error(sprintf('Not setting "logout_on_user_change" to true on firewall "%s" is deprecated as of 3.4, it will always be true in 4.0.', $id), E_USER_DEPRECATED); - } - - if (isset($this->logoutOnUserChangeByContextKey[$contextKey]) && $this->logoutOnUserChangeByContextKey[$contextKey][1] !== $logoutOnUserChange) { - throw new InvalidConfigurationException(sprintf('Firewalls "%s" and "%s" need to have the same value for option "logout_on_user_change" as they are sharing the context "%s"', $this->logoutOnUserChangeByContextKey[$contextKey][0], $id, $contextKey)); - } - - $this->logoutOnUserChangeByContextKey[$contextKey] = array($id, $logoutOnUserChange); - $listeners[] = new Reference($this->createContextListener($container, $contextKey, $logoutOnUserChange)); + $listeners[] = new Reference($this->createContextListener($container, $contextKey)); } $config->replaceArgument(6, $contextKey); @@ -486,7 +387,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a return array($matcher, $listeners, $exceptionListener); } - private function createContextListener($container, $contextKey, $logoutUserOnChange) + private function createContextListener($container, $contextKey) { if (isset($this->contextListeners[$contextKey])) { return $this->contextListeners[$contextKey]; @@ -495,7 +396,6 @@ private function createContextListener($container, $contextKey, $logoutUserOnCha $listenerId = 'security.context_listener.'.count($this->contextListeners); $listener = $container->setDefinition($listenerId, new ChildDefinition('security.context_listener')); $listener->replaceArgument(2, $contextKey); - $listener->addMethodCall('setLogoutOnUserChange', array($logoutUserOnChange)); return $this->contextListeners[$contextKey] = $listenerId; } @@ -518,8 +418,10 @@ private function createAuthenticationListeners($container, $id, $firewall, &$aut } elseif ('remember_me' === $key) { // RememberMeFactory will use the firewall secret when created $userProvider = null; + } elseif ($defaultProvider) { + $userProvider = $defaultProvider; } else { - $userProvider = $defaultProvider ?: $this->getFirstProvider($id, $key, $providerIds); + throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "%s" listener on "%s" firewall is ambiguous as there is more than one registered provider.', $key, $id)); } list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); @@ -706,11 +608,10 @@ private function createExceptionListener($container, $config, $id, $defaultEntry private function createSwitchUserListener($container, $id, $config, $defaultProvider, $stateless, $providerIds) { - $userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : ($defaultProvider ?: $this->getFirstProvider($id, 'switch_user', $providerIds)); + $userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : $defaultProvider; - // in 4.0, ignore the `switch_user.stateless` key if $stateless is `true` - if ($stateless && false === $config['stateless']) { - @trigger_error(sprintf('Firewall "%s" is configured as "stateless" but the "switch_user.stateless" key is set to false. Both should have the same value, the firewall\'s "stateless" value will be used as default value for the "switch_user.stateless" key in 4.0.', $id), E_USER_DEPRECATED); + if (!$userProvider) { + throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "switch_user" listener on "%s" firewall is ambiguous as there is more than one registered provider.', $id)); } $switchUserListenerId = 'security.authentication.switchuser_listener.'.$id; @@ -720,7 +621,7 @@ private function createSwitchUserListener($container, $id, $config, $defaultProv $listener->replaceArgument(3, $id); $listener->replaceArgument(6, $config['parameter']); $listener->replaceArgument(7, $config['role']); - $listener->replaceArgument(9, $config['stateless']); + $listener->replaceArgument(9, $stateless ?: $config['stateless']); return $switchUserListenerId; } @@ -810,14 +711,4 @@ private function getExpressionLanguage() return $this->expressionLanguage; } - - /** - * @deprecated since version 3.4, to be removed in 4.0 - */ - private function getFirstProvider($firewallName, $listenerName, array $providerIds) - { - @trigger_error(sprintf('Listener "%s" on firewall "%s" has no "provider" set but multiple providers exist. Using the first configured provider (%s) is deprecated since Symfony 3.4 and will throw an exception in 4.0, set the "provider" key on the firewall instead.', $listenerName, $firewallName, $first = array_keys($providerIds)[0]), E_USER_DEPRECATED); - - return $providerIds[$first]; - } } diff --git a/src/Symfony/Bundle/SecurityBundle/EventListener/AclSchemaListener.php b/src/Symfony/Bundle/SecurityBundle/EventListener/AclSchemaListener.php deleted file mode 100644 index 9119d9e94da1e..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/EventListener/AclSchemaListener.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\SecurityBundle\EventListener; - -@trigger_error(sprintf('Class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Bundle\AclBundle\EventListener\AclSchemaListener instead.', AclSchemaListener::class), E_USER_DEPRECATED); - -use Symfony\Component\Security\Acl\Dbal\Schema; -use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; - -/** - * Merges ACL schema into the given schema. - * - * @author Johannes M. Schmitt - * - * @deprecated since 3.4, to be removed in 4.0 - */ -class AclSchemaListener -{ - private $schema; - - public function __construct(Schema $schema) - { - $this->schema = $schema; - } - - public function postGenerateSchema(GenerateSchemaEventArgs $args) - { - $schema = $args->getSchema(); - $this->schema->addToSchema($schema); - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/console.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/console.xml index a8f9b510a9e00..32676bd9229a1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/console.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/console.xml @@ -7,17 +7,6 @@ - - - - - - - - - - - diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 6a4c109af4508..c5f0408d9985f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -39,7 +39,7 @@ - + @@ -164,7 +164,9 @@ - + + The "%service_id%" service is deprecated since Symfony 4.1 and will be removed in 5.0. + @@ -183,6 +185,7 @@ + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml deleted file mode 100644 index 68418dadff1c9..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml deleted file mode 100644 index c1ed332654fe0..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - %security.acl.dbal.class_table_name% - %security.acl.dbal.entry_table_name% - %security.acl.dbal.oid_table_name% - %security.acl.dbal.oid_ancestors_table_name% - %security.acl.dbal.sid_table_name% - - - - - - - %security.acl.dbal.class_table_name% - %security.acl.dbal.entry_table_name% - %security.acl.dbal.oid_table_name% - %security.acl.dbal.oid_ancestors_table_name% - %security.acl.dbal.sid_table_name% - - - - - - - - - - - - - - - - - - diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml index 73c3eed722083..5d57c69e8e9ce 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml @@ -26,8 +26,6 @@ - - @@ -180,15 +178,6 @@ - - - - - - - - - diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php index 62317f625c271..308442810bba8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php @@ -29,21 +29,7 @@ final class FirewallConfig private $listeners; private $switchUser; - /** - * @param string $name - * @param string $userChecker - * @param string|null $requestMatcher - * @param bool $securityEnabled - * @param bool $stateless - * @param string|null $provider - * @param string|null $context - * @param string|null $entryPoint - * @param string|null $accessDeniedHandler - * @param string|null $accessDeniedUrl - * @param string[] $listeners - * @param array|null $switchUser - */ - public function __construct($name, $userChecker, $requestMatcher = null, $securityEnabled = true, $stateless = false, $provider = null, $context = null, $entryPoint = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $listeners = array(), $switchUser = null) + public function __construct(string $name, string $userChecker, string $requestMatcher = null, bool $securityEnabled = true, bool $stateless = false, string $provider = null, string $context = null, string $entryPoint = null, string $accessDeniedHandler = null, string $accessDeniedUrl = null, array $listeners = array(), $switchUser = null) { $this->name = $name; $this->userChecker = $userChecker; @@ -59,7 +45,7 @@ public function __construct($name, $userChecker, $requestMatcher = null, $securi $this->switchUser = $switchUser; } - public function getName() + public function getName(): string { return $this->name; } @@ -68,30 +54,27 @@ public function getName() * @return string|null The request matcher service id or null if neither the request matcher, pattern or host * options were provided */ - public function getRequestMatcher() + public function getRequestMatcher(): ?string { return $this->requestMatcher; } - public function isSecurityEnabled() + public function isSecurityEnabled(): bool { return $this->securityEnabled; } - public function allowsAnonymous() + public function allowsAnonymous(): bool { return in_array('anonymous', $this->listeners, true); } - public function isStateless() + public function isStateless(): bool { return $this->stateless; } - /** - * @return string|null The provider service id - */ - public function getProvider() + public function getProvider(): ?string { return $this->provider; } @@ -99,55 +82,37 @@ public function getProvider() /** * @return string|null The context key (will be null if the firewall is stateless) */ - public function getContext() + public function getContext(): ?string { return $this->context; } - /** - * @return string|null The entry_point service id if configured, null otherwise - */ - public function getEntryPoint() + public function getEntryPoint(): ?string { return $this->entryPoint; } - /** - * @return string The user_checker service id - */ - public function getUserChecker() + public function getUserChecker(): string { return $this->userChecker; } - /** - * @return string|null The access_denied_handler service id if configured, null otherwise - */ - public function getAccessDeniedHandler() + public function getAccessDeniedHandler(): ?string { return $this->accessDeniedHandler; } - /** - * @return string|null The access_denied_handler URL if configured, null otherwise - */ - public function getAccessDeniedUrl() + public function getAccessDeniedUrl(): ?string { return $this->accessDeniedUrl; } - /** - * @return string[] An array of listener keys - */ - public function getListeners() + public function getListeners(): array { return $this->listeners; } - /** - * @return array|null The switch_user parameters if configured, null otherwise - */ - public function getSwitchUser() + public function getSwitchUser(): ?array { return $this->switchUser; } diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php index c07e32bc4029a..2375e6b0e3ab6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php @@ -25,12 +25,7 @@ class FirewallContext private $exceptionListener; private $config; - /** - * @param \Traversable|array $listeners - * @param ExceptionListener|null $exceptionListener - * @param FirewallConfig|null $firewallConfig - */ - public function __construct($listeners, ExceptionListener $exceptionListener = null, FirewallConfig $config = null) + public function __construct(iterable $listeners, ExceptionListener $exceptionListener = null, FirewallConfig $config = null) { $this->listeners = $listeners; $this->exceptionListener = $exceptionListener; @@ -42,20 +37,7 @@ public function getConfig() return $this->config; } - /** - * @deprecated since version 3.3, will be removed in 4.0. Use {@link getListeners()} and/or {@link getExceptionListener()} instead. - */ - public function getContext() - { - @trigger_error(sprintf('Method %s() is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s::getListeners/getExceptionListener() instead.', __METHOD__, __CLASS__), E_USER_DEPRECATED); - - return array($this->getListeners(), $this->getExceptionListener()); - } - - /** - * @return \Traversable|array - */ - public function getListeners() + public function getListeners(): iterable { return $this->listeners; } diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php index 09d49b17bc50d..3b98b0e4cadd3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -22,94 +22,13 @@ * * @author Johannes M. Schmitt */ -class FirewallMap extends _FirewallMap implements FirewallMapInterface -{ - /** - * @deprecated since version 3.3, to be removed in 4.0 alongside with magic methods below - */ - private $container; - - /** - * @deprecated since version 3.3, to be removed in 4.0 alongside with magic methods below - */ - private $map; - - public function __construct(ContainerInterface $container, $map) - { - parent::__construct($container, $map); - $this->container = $container; - $this->map = $map; - } - - /** - * @internal - */ - public function __get($name) - { - if ('map' === $name || 'container' === $name) { - @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since Symfony 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); - - if ('map' === $name && $this->map instanceof \Traversable) { - $this->map = iterator_to_array($this->map); - } - } - - return $this->$name; - } - - /** - * @internal - */ - public function __set($name, $value) - { - if ('map' === $name || 'container' === $name) { - @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since Symfony 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); - - $set = \Closure::bind(function ($name, $value) { $this->$name = $value; }, $this, parent::class); - $set($name, $value); - } - - $this->$name = $value; - } - - /** - * @internal - */ - public function __isset($name) - { - if ('map' === $name || 'container' === $name) { - @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since Symfony 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); - } - - return isset($this->$name); - } - - /** - * @internal - */ - public function __unset($name) - { - if ('map' === $name || 'container' === $name) { - @trigger_error(sprintf('Using the "%s::$%s" property is deprecated since Symfony 3.3 as it will be removed/private in 4.0.', __CLASS__, $name), E_USER_DEPRECATED); - - $unset = \Closure::bind(function ($name) { unset($this->$name); }, $this, parent::class); - $unset($name); - } - - unset($this->$name); - } -} - -/** - * @internal to be removed in 4.0 - */ -class _FirewallMap +class FirewallMap implements FirewallMapInterface { private $container; private $map; private $contexts; - public function __construct(ContainerInterface $container, $map) + public function __construct(ContainerInterface $container, iterable $map) { $this->container = $container; $this->map = $map; diff --git a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php index 0304c165a4bca..da5d4c3559b68 100644 --- a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php +++ b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php @@ -22,7 +22,6 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicLdapFactory; -use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RemoteUserFactory; @@ -49,7 +48,6 @@ public function build(ContainerBuilder $container) $extension->addSecurityListenerFactory(new JsonLoginFactory()); $extension->addSecurityListenerFactory(new HttpBasicFactory()); $extension->addSecurityListenerFactory(new HttpBasicLdapFactory()); - $extension->addSecurityListenerFactory(new HttpDigestFactory(false)); $extension->addSecurityListenerFactory(new RememberMeFactory()); $extension->addSecurityListenerFactory(new X509Factory()); $extension->addSecurityListenerFactory(new RemoteUserFactory()); diff --git a/src/Symfony/Bundle/SecurityBundle/SecurityUserValueResolver.php b/src/Symfony/Bundle/SecurityBundle/SecurityUserValueResolver.php index 01a4f2bda6d37..d9deca5a3e5b8 100644 --- a/src/Symfony/Bundle/SecurityBundle/SecurityUserValueResolver.php +++ b/src/Symfony/Bundle/SecurityBundle/SecurityUserValueResolver.php @@ -17,11 +17,16 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Http\Controller\UserValueResolver; + +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.1 and will be removed in 5.0, use "%s" instead.', SecurityUserValueResolver::class, UserValueResolver::class), E_USER_DEPRECATED); /** * Supports the argument type of {@see UserInterface}. * * @author Iltar van der Berg + * + * @deprecated since Symfony 4.1, to be removed in 5.0. Use {@link UserValueResolver} instead */ final class SecurityUserValueResolver implements ArgumentValueResolverInterface { diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php index 5cce32ea9377a..82205e5912969 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php @@ -14,16 +14,15 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\Voter; -use Symfony\Component\Security\Core\Tests\Authorization\Stub\VoterWithoutInterface; class AddSecurityVotersPassTest extends TestCase { /** * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage No security voters found. You need to tag at least one with "security.voter". */ public function testNoVoters() { @@ -71,8 +70,8 @@ public function testThatSecurityVotersAreProcessedInPriorityOrder() } /** - * @group legacy - * @expectedDeprecation Using a "security.voter" tag on a class without implementing the "Symfony\Component\Security\Core\Authorization\Voter\VoterInterface" is deprecated as of 3.4 and will throw an exception in 4.0. Implement the interface instead. + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage stdClass must implement the Symfony\Component\Security\Core\Authorization\Voter\VoterInterface when used as a voter. */ public function testVoterMissingInterface() { @@ -82,40 +81,7 @@ public function testVoterMissingInterface() ->addArgument(array()) ; $container - ->register('without_interface', VoterWithoutInterface::class) - ->addTag('security.voter') - ; - $compilerPass = new AddSecurityVotersPass(); - $compilerPass->process($container); - - $argument = $container->getDefinition('security.access.decision_manager')->getArgument(0); - $refs = $argument->getValues(); - $this->assertEquals(new Reference('without_interface'), $refs[0]); - $this->assertCount(1, $refs); - } - - /** - * @group legacy - */ - public function testVoterMissingInterfaceAndMethod() - { - $exception = LogicException::class; - $message = 'stdClass should implement the Symfony\Component\Security\Core\Authorization\Voter\VoterInterface interface when used as voter.'; - - if (method_exists($this, 'expectException')) { - $this->expectException($exception); - $this->expectExceptionMessage($message); - } else { - $this->setExpectedException($exception, $message); - } - - $container = new ContainerBuilder(); - $container - ->register('security.access.decision_manager', AccessDecisionManager::class) - ->addArgument(array()) - ; - $container - ->register('without_method', 'stdClass') + ->register('without_interface', 'stdClass') ->addTag('security.voter') ; $compilerPass = new AddSecurityVotersPass(); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index 2515fc4b564fb..cedc5dfa06a2e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -36,20 +36,6 @@ public function testRolesHierarchy() ), $container->getParameter('security.role_hierarchy.roles')); } - /** - * @group legacy - * @expectedDeprecation The "security.acl" configuration key is deprecated since Symfony 3.4 and will be removed in 4.0. Install symfony/acl-bundle and use the "acl" key instead. - */ - public function testRolesHierarchyWithAcl() - { - $container = $this->getContainer('container1_with_acl'); - $this->assertEquals(array( - 'ROLE_ADMIN' => array('ROLE_USER'), - 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'), - 'ROLE_REMOTE' => array('ROLE_USER', 'ROLE_ADMIN'), - ), $container->getParameter('security.role_hierarchy.roles')); - } - public function testUserProviders() { $container = $this->getContainer('container1'); @@ -197,134 +183,6 @@ public function testFirewalls() $this->assertFalse($container->hasAlias('Symfony\Component\Security\Core\User\UserCheckerInterface', 'No user checker alias is registered when custom user checker services are registered')); } - /** - * @group legacy - */ - public function testFirewallsWithDigest() - { - $container = $this->getContainer('container1_with_digest'); - $arguments = $container->getDefinition('security.firewall.map')->getArguments(); - $listeners = array(); - $configs = array(); - foreach (array_keys($arguments[1]->getValues()) as $contextId) { - $contextDef = $container->getDefinition($contextId); - $arguments = $contextDef->getArguments(); - $listeners[] = array_map('strval', $arguments['index_0']->getValues()); - - $configDef = $container->getDefinition((string) $arguments['index_2']); - $configs[] = array_values($configDef->getArguments()); - } - - // the IDs of the services are case sensitive or insensitive depending on - // the Symfony version. Transform them to lowercase to simplify tests. - $configs[0][2] = strtolower($configs[0][2]); - $configs[2][2] = strtolower($configs[2][2]); - - $this->assertEquals(array( - array( - 'simple', - 'security.user_checker', - 'security.request_matcher.6tndozi', - false, - ), - array( - 'secure', - 'security.user_checker', - null, - true, - true, - 'security.user.provider.concrete.default', - null, - 'security.authentication.form_entry_point.secure', - null, - null, - array( - 'logout', - 'switch_user', - 'x509', - 'remote_user', - 'form_login', - 'http_basic', - 'http_digest', - 'remember_me', - 'anonymous', - ), - array( - 'parameter' => '_switch_user', - 'role' => 'ROLE_ALLOWED_TO_SWITCH', - 'stateless' => true, - ), - ), - array( - 'host', - 'security.user_checker', - 'security.request_matcher.and0kk1', - true, - false, - 'security.user.provider.concrete.default', - 'host', - 'security.authentication.basic_entry_point.host', - null, - null, - array( - 'http_basic', - 'anonymous', - ), - null, - ), - array( - 'with_user_checker', - 'app.user_checker', - null, - true, - false, - 'security.user.provider.concrete.default', - 'with_user_checker', - 'security.authentication.basic_entry_point.with_user_checker', - null, - null, - array( - 'http_basic', - 'anonymous', - ), - null, - ), - ), $configs); - - $this->assertEquals(array( - array(), - array( - 'security.channel_listener', - 'security.logout_listener.secure', - 'security.authentication.listener.x509.secure', - 'security.authentication.listener.remote_user.secure', - 'security.authentication.listener.form.secure', - 'security.authentication.listener.basic.secure', - 'security.authentication.listener.digest.secure', - 'security.authentication.listener.rememberme.secure', - 'security.authentication.listener.anonymous.secure', - 'security.authentication.switchuser_listener.secure', - 'security.access_listener', - ), - array( - 'security.channel_listener', - 'security.context_listener.0', - 'security.authentication.listener.basic.host', - 'security.authentication.listener.anonymous.host', - 'security.access_listener', - ), - array( - 'security.channel_listener', - 'security.context_listener.1', - 'security.authentication.listener.basic.with_user_checker', - 'security.authentication.listener.anonymous.with_user_checker', - 'security.access_listener', - ), - ), $listeners); - - $this->assertFalse($container->hasAlias('Symfony\Component\Security\Core\User\UserCheckerInterface', 'No user checker alias is registered when custom user checker services are registered')); - } - public function testFirewallRequestMatchers() { $container = $this->getContainer('container1'); @@ -461,30 +319,6 @@ public function testArgon2iEncoder() ))), $this->getContainer('argon2i_encoder')->getDefinition('security.encoder_factory.generic')->getArguments()); } - /** - * @group legacy - * @expectedDeprecation The "security.acl" configuration key is deprecated since Symfony 3.4 and will be removed in 4.0. Install symfony/acl-bundle and use the "acl" key instead. - */ - public function testAcl() - { - $container = $this->getContainer('container1_with_acl'); - - $this->assertTrue($container->hasDefinition('security.acl.dbal.provider')); - $this->assertEquals('security.acl.dbal.provider', (string) $container->getAlias('security.acl.provider')); - } - - /** - * @group legacy - * @expectedDeprecation The "security.acl" configuration key is deprecated since Symfony 3.4 and will be removed in 4.0. Install symfony/acl-bundle and use the "acl" key instead. - */ - public function testCustomAclProvider() - { - $container = $this->getContainer('custom_acl_provider'); - - $this->assertFalse($container->hasDefinition('security.acl.dbal.provider')); - $this->assertEquals('foo', (string) $container->getAlias('security.acl.provider')); - } - public function testRememberMeThrowExceptionsDefault() { $container = $this->getContainer('container1'); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_encoder.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_encoder.php index 23ff1799c8300..d315f88170368 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_encoder.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_encoder.php @@ -13,7 +13,6 @@ 'main' => array( 'form_login' => false, 'http_basic' => null, - 'logout_on_user_change' => true, ), ), )); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php index 7290676a2bfc7..d60bca39e4a51 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php @@ -72,7 +72,6 @@ 'logout' => true, 'remember_me' => array('secret' => 'TheSecret'), 'user_checker' => null, - 'logout_on_user_change' => true, ), 'host' => array( 'provider' => 'default', @@ -81,14 +80,12 @@ 'methods' => array('GET', 'POST'), 'anonymous' => true, 'http_basic' => true, - 'logout_on_user_change' => true, ), 'with_user_checker' => array( 'provider' => 'default', 'user_checker' => 'app.user_checker', 'anonymous' => true, 'http_basic' => true, - 'logout_on_user_change' => true, ), ), diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1_with_acl.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1_with_acl.php deleted file mode 100644 index 28e276b238551..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1_with_acl.php +++ /dev/null @@ -1,105 +0,0 @@ -loadFromExtension('security', array( - 'acl' => array(), - 'encoders' => array( - 'JMS\FooBundle\Entity\User1' => 'plaintext', - 'JMS\FooBundle\Entity\User2' => array( - 'algorithm' => 'sha1', - 'encode_as_base64' => false, - 'iterations' => 5, - ), - 'JMS\FooBundle\Entity\User3' => array( - 'algorithm' => 'md5', - ), - 'JMS\FooBundle\Entity\User4' => array( - 'id' => 'security.encoder.foo', - ), - 'JMS\FooBundle\Entity\User5' => array( - 'algorithm' => 'pbkdf2', - 'hash_algorithm' => 'sha1', - 'encode_as_base64' => false, - 'iterations' => 5, - 'key_length' => 30, - ), - 'JMS\FooBundle\Entity\User6' => array( - 'algorithm' => 'bcrypt', - 'cost' => 15, - ), - ), - 'providers' => array( - 'default' => array( - 'memory' => array( - 'users' => array( - 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'), - ), - ), - ), - 'digest' => array( - 'memory' => array( - 'users' => array( - 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER, ROLE_ADMIN'), - ), - ), - ), - 'basic' => array( - 'memory' => array( - 'users' => array( - 'foo' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => 'ROLE_SUPER_ADMIN'), - 'bar' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => array('ROLE_USER', 'ROLE_ADMIN')), - ), - ), - ), - 'service' => array( - 'id' => 'user.manager', - ), - 'chain' => array( - 'chain' => array( - 'providers' => array('service', 'basic'), - ), - ), - ), - - 'firewalls' => array( - 'simple' => array('provider' => 'default', 'pattern' => '/login', 'security' => false), - 'secure' => array('stateless' => true, - 'provider' => 'default', - 'http_basic' => true, - 'http_digest' => array('secret' => 'TheSecret'), - 'form_login' => true, - 'anonymous' => true, - 'switch_user' => array('stateless' => true), - 'x509' => true, - 'remote_user' => true, - 'logout' => true, - 'remember_me' => array('secret' => 'TheSecret'), - 'user_checker' => null, - ), - 'host' => array( - 'provider' => 'default', - 'pattern' => '/test', - 'host' => 'foo\\.example\\.org', - 'methods' => array('GET', 'POST'), - 'anonymous' => true, - 'http_basic' => true, - ), - 'with_user_checker' => array( - 'provider' => 'default', - 'user_checker' => 'app.user_checker', - 'anonymous' => true, - 'http_basic' => true, - ), - ), - - 'access_control' => array( - array('path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => array('get', 'POST')), - array('path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), - array('path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUsername() matches '/^admin/'"), - ), - - 'role_hierarchy' => array( - 'ROLE_ADMIN' => 'ROLE_USER', - 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'), - 'ROLE_REMOTE' => 'ROLE_USER,ROLE_ADMIN', - ), -)); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1_with_digest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1_with_digest.php deleted file mode 100644 index 46f3efa4e5d68..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1_with_digest.php +++ /dev/null @@ -1,108 +0,0 @@ -loadFromExtension('security', array( - 'acl' => array(), - 'encoders' => array( - 'JMS\FooBundle\Entity\User1' => 'plaintext', - 'JMS\FooBundle\Entity\User2' => array( - 'algorithm' => 'sha1', - 'encode_as_base64' => false, - 'iterations' => 5, - ), - 'JMS\FooBundle\Entity\User3' => array( - 'algorithm' => 'md5', - ), - 'JMS\FooBundle\Entity\User4' => array( - 'id' => 'security.encoder.foo', - ), - 'JMS\FooBundle\Entity\User5' => array( - 'algorithm' => 'pbkdf2', - 'hash_algorithm' => 'sha1', - 'encode_as_base64' => false, - 'iterations' => 5, - 'key_length' => 30, - ), - 'JMS\FooBundle\Entity\User6' => array( - 'algorithm' => 'bcrypt', - 'cost' => 15, - ), - ), - 'providers' => array( - 'default' => array( - 'memory' => array( - 'users' => array( - 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'), - ), - ), - ), - 'digest' => array( - 'memory' => array( - 'users' => array( - 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER, ROLE_ADMIN'), - ), - ), - ), - 'basic' => array( - 'memory' => array( - 'users' => array( - 'foo' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => 'ROLE_SUPER_ADMIN'), - 'bar' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => array('ROLE_USER', 'ROLE_ADMIN')), - ), - ), - ), - 'service' => array( - 'id' => 'user.manager', - ), - 'chain' => array( - 'chain' => array( - 'providers' => array('service', 'basic'), - ), - ), - ), - - 'firewalls' => array( - 'simple' => array('provider' => 'default', 'pattern' => '/login', 'security' => false), - 'secure' => array('stateless' => true, - 'provider' => 'default', - 'http_basic' => true, - 'http_digest' => array('secret' => 'TheSecret'), - 'form_login' => true, - 'anonymous' => true, - 'switch_user' => array('stateless' => true), - 'x509' => true, - 'remote_user' => true, - 'logout' => true, - 'remember_me' => array('secret' => 'TheSecret'), - 'user_checker' => null, - 'logout_on_user_change' => true, - ), - 'host' => array( - 'provider' => 'default', - 'pattern' => '/test', - 'host' => 'foo\\.example\\.org', - 'methods' => array('GET', 'POST'), - 'anonymous' => true, - 'http_basic' => true, - 'logout_on_user_change' => true, - ), - 'with_user_checker' => array( - 'provider' => 'default', - 'user_checker' => 'app.user_checker', - 'anonymous' => true, - 'http_basic' => true, - 'logout_on_user_change' => true, - ), - ), - - 'access_control' => array( - array('path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => array('get', 'POST')), - array('path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), - array('path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUsername() matches '/^admin/'"), - ), - - 'role_hierarchy' => array( - 'ROLE_ADMIN' => 'ROLE_USER', - 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'), - 'ROLE_REMOTE' => 'ROLE_USER,ROLE_ADMIN', - ), -)); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/custom_acl_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/custom_acl_provider.php deleted file mode 100644 index 351dc6c09e1a6..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/custom_acl_provider.php +++ /dev/null @@ -1,9 +0,0 @@ -load('container1.php', $container); - -$container->loadFromExtension('security', array( - 'acl' => array( - 'provider' => 'foo', - ), -)); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php index da218fc61c9cf..ff9d9f6b89df6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php @@ -15,12 +15,10 @@ 'main' => array( 'provider' => 'default', 'form_login' => true, - 'logout_on_user_change' => true, ), 'other' => array( 'provider' => 'with-dash', 'form_login' => true, - 'logout_on_user_change' => true, ), ), )); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php index 46a51f91fdd22..78d461efe38d1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php @@ -12,7 +12,6 @@ 'main' => array( 'provider' => 'undefined', 'form_login' => true, - 'logout_on_user_change' => true, ), ), )); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php index 072b70b078a58..d7f1cd6973f36 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php @@ -11,7 +11,6 @@ 'firewalls' => array( 'main' => array( 'form_login' => array('provider' => 'default'), - 'logout_on_user_change' => true, ), ), )); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php index 567f8a0d4b2d7..da54f025d1a70 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php @@ -11,7 +11,6 @@ 'firewalls' => array( 'main' => array( 'form_login' => array('provider' => 'undefined'), - 'logout_on_user_change' => true, ), ), )); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php index eb34a6a6f64b6..50ef504ea4d43 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php @@ -11,7 +11,6 @@ 'main' => array( 'form_login' => false, 'http_basic' => null, - 'logout_on_user_change' => true, ), ), diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php index 6ed2d18a36709..912b9127ef369 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php @@ -6,7 +6,6 @@ 'form_login' => array( 'login_path' => '/login', ), - 'logout_on_user_change' => true, ), ), 'role_hierarchy' => array( diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php index a61fde3dc7309..e0ca4f6dedf3e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php @@ -13,7 +13,6 @@ 'catch_exceptions' => false, 'token_provider' => 'token_provider_id', ), - 'logout_on_user_change' => true, ), ), )); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_encoder.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_encoder.xml index dda4d8ec888c8..d9ff22ab82de5 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_encoder.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_encoder.xml @@ -10,7 +10,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml index 1f317ac2d2697..ef76eef02d2b5 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -57,12 +57,12 @@ - + - + app.user_checker diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1_with_acl.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1_with_acl.xml deleted file mode 100644 index 2a0eb6f6d7689..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1_with_acl.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - app.user_checker - - - ROLE_USER - ROLE_USER,ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH - ROLE_USER,ROLE_ADMIN - - - - - - diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1_with_digest.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1_with_digest.xml deleted file mode 100644 index 7affa38b8596d..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1_with_digest.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - app.user_checker - - - ROLE_USER - ROLE_USER,ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH - ROLE_USER,ROLE_ADMIN - - - - - - diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_acl_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_acl_provider.xml deleted file mode 100644 index 6addc81668253..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_acl_provider.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml index 9d37164e8d409..bd87fee4abae9 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml @@ -11,7 +11,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml index 6a05d48e539b9..f596ac5a6240b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml @@ -11,7 +11,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml index f53b91b00ef78..b1bcd8eae8155 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml @@ -11,7 +11,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml index 75271ad075f37..725e85a1d0f27 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml @@ -11,7 +11,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml index 42dc91c9975ef..8a17f6db23c55 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml index 051e2a40b3a16..81b8cffd68d9e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml @@ -6,7 +6,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml index 583720ea1de9b..b6ade91a07970 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml @@ -9,7 +9,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml index 6a1f925160fbf..70381f8d850b7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml @@ -10,4 +10,3 @@ security: main: form_login: false http_basic: ~ - logout_on_user_change: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml index ad90e433be796..9336c13343470 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -64,14 +64,12 @@ security: methods: [GET,POST] anonymous: true http_basic: true - logout_on_user_change: true with_user_checker: provider: default anonymous: ~ http_basic: ~ user_checker: app.user_checker - logout_on_user_change: true role_hierarchy: ROLE_ADMIN: ROLE_USER diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1_with_acl.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1_with_acl.yml deleted file mode 100644 index b2be009e5cf40..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1_with_acl.yml +++ /dev/null @@ -1,87 +0,0 @@ -security: - acl: ~ - encoders: - JMS\FooBundle\Entity\User1: plaintext - JMS\FooBundle\Entity\User2: - algorithm: sha1 - encode_as_base64: false - iterations: 5 - JMS\FooBundle\Entity\User3: - algorithm: md5 - JMS\FooBundle\Entity\User4: - id: security.encoder.foo - JMS\FooBundle\Entity\User5: - algorithm: pbkdf2 - hash_algorithm: sha1 - encode_as_base64: false - iterations: 5 - key_length: 30 - JMS\FooBundle\Entity\User6: - algorithm: bcrypt - cost: 15 - - providers: - default: - memory: - users: - foo: { password: foo, roles: ROLE_USER } - digest: - memory: - users: - foo: { password: foo, roles: 'ROLE_USER, ROLE_ADMIN' } - basic: - memory: - users: - foo: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: ROLE_SUPER_ADMIN } - bar: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: [ROLE_USER, ROLE_ADMIN] } - service: - id: user.manager - chain: - chain: - providers: [service, basic] - - - firewalls: - simple: { pattern: /login, security: false } - secure: - provider: default - stateless: true - http_basic: true - http_digest: - secret: TheSecret - form_login: true - anonymous: true - switch_user: - stateless: true - x509: true - remote_user: true - logout: true - remember_me: - secret: TheSecret - user_checker: ~ - - host: - provider: default - pattern: /test - host: foo\.example\.org - methods: [GET,POST] - anonymous: true - http_basic: true - - with_user_checker: - provider: default - anonymous: ~ - http_basic: ~ - user_checker: app.user_checker - - role_hierarchy: - ROLE_ADMIN: ROLE_USER - ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] - ROLE_REMOTE: ROLE_USER,ROLE_ADMIN - - access_control: - - { path: /blog/524, role: ROLE_USER, requires_channel: https, methods: [get, POST]} - - - path: /blog/.* - role: IS_AUTHENTICATED_ANONYMOUSLY - - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUsername() matches '/^admin/'" } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1_with_digest.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1_with_digest.yml deleted file mode 100644 index 70d30253532a6..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1_with_digest.yml +++ /dev/null @@ -1,89 +0,0 @@ -security: - acl: ~ - encoders: - JMS\FooBundle\Entity\User1: plaintext - JMS\FooBundle\Entity\User2: - algorithm: sha1 - encode_as_base64: false - iterations: 5 - JMS\FooBundle\Entity\User3: - algorithm: md5 - JMS\FooBundle\Entity\User4: - id: security.encoder.foo - JMS\FooBundle\Entity\User5: - algorithm: pbkdf2 - hash_algorithm: sha1 - encode_as_base64: false - iterations: 5 - key_length: 30 - JMS\FooBundle\Entity\User6: - algorithm: bcrypt - cost: 15 - - providers: - default: - memory: - users: - foo: { password: foo, roles: ROLE_USER } - digest: - memory: - users: - foo: { password: foo, roles: 'ROLE_USER, ROLE_ADMIN' } - basic: - memory: - users: - foo: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: ROLE_SUPER_ADMIN } - bar: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: [ROLE_USER, ROLE_ADMIN] } - service: - id: user.manager - chain: - chain: - providers: [service, basic] - - - firewalls: - simple: { pattern: /login, security: false } - secure: - provider: default - stateless: true - http_basic: true - http_digest: - secret: TheSecret - form_login: true - anonymous: true - switch_user: - stateless: true - x509: true - remote_user: true - logout: true - remember_me: - secret: TheSecret - user_checker: ~ - - host: - provider: default - pattern: /test - host: foo\.example\.org - methods: [GET,POST] - anonymous: true - http_basic: true - logout_on_user_change: true - - with_user_checker: - provider: default - anonymous: ~ - http_basic: ~ - user_checker: app.user_checker - logout_on_user_change: true - - role_hierarchy: - ROLE_ADMIN: ROLE_USER - ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] - ROLE_REMOTE: ROLE_USER,ROLE_ADMIN - - access_control: - - { path: /blog/524, role: ROLE_USER, requires_channel: https, methods: [get, POST]} - - - path: /blog/.* - role: IS_AUTHENTICATED_ANONYMOUSLY - - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUsername() matches '/^admin/'" } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/custom_acl_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/custom_acl_provider.yml deleted file mode 100644 index 633eed00e3418..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/custom_acl_provider.yml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - { resource: container1.yml } - -security: - acl: - provider: foo diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml index b8da52b6e45d3..11c329aa8e2fe 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml @@ -11,8 +11,6 @@ security: main: provider: default form_login: true - logout_on_user_change: true other: provider: with-dash form_login: true - logout_on_user_change: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml index 3385fc3485a0e..ec2664054009c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml @@ -8,4 +8,3 @@ security: main: provider: undefined form_login: true - logout_on_user_change: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml index 53e2784c4b3a9..652f23b5f0425 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml @@ -8,4 +8,3 @@ security: main: form_login: provider: default - logout_on_user_change: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml index ba5f69ede665d..1916df4c2e7ca 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml @@ -8,4 +8,3 @@ security: main: form_login: provider: undefined - logout_on_user_change: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml index d8f443c62f34e..60c0bbea558e7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml @@ -9,7 +9,6 @@ security: main: form_login: false http_basic: ~ - logout_on_user_change: true role_hierarchy: FOO: [MOO] diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml index a081003a49578..4f8db0a09f7b4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml @@ -3,7 +3,6 @@ security: main: form_login: login_path: /login - logout_on_user_change: true role_hierarchy: FOO: BAR diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml index 716cd4cf99d14..a521c8c6a803d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml @@ -10,4 +10,3 @@ security: secret: TheSecret catch_exceptions: false token_provider: token_provider_id - logout_on_user_change: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php index ef465e82bd7cd..24447ee89abee 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php @@ -29,7 +29,6 @@ class MainConfigurationTest extends TestCase ), 'firewalls' => array( 'stub' => array(), - 'logout_on_user_change' => true, ), ); @@ -77,7 +76,6 @@ public function testCsrfAliases() 'csrf_token_generator' => 'a_token_generator', 'csrf_token_id' => 'a_token_id', ), - 'logout_on_user_change' => true, ), ), ); @@ -107,7 +105,6 @@ public function testUserCheckers() 'firewalls' => array( 'stub' => array( 'user_checker' => 'app.henk_checker', - 'logout_on_user_change' => true, ), ), ); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 52a18d08ac494..6d587816802b6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -38,7 +38,6 @@ public function testInvalidCheckPath() 'form_login' => array( 'check_path' => '/some_area/login_check', ), - 'logout_on_user_change' => true, ), ), )); @@ -62,7 +61,6 @@ public function testFirewallWithoutAuthenticationListener() 'firewalls' => array( 'some_firewall' => array( 'pattern' => '/.*', - 'logout_on_user_change' => true, ), ), )); @@ -90,7 +88,6 @@ public function testFirewallWithInvalidUserProvider() 'some_firewall' => array( 'pattern' => '/.*', 'http_basic' => array(), - 'logout_on_user_change' => true, ), ), )); @@ -113,7 +110,6 @@ public function testDisableRoleHierarchyVoter() 'some_firewall' => array( 'pattern' => '/.*', 'http_basic' => null, - 'logout_on_user_change' => true, ), ), )); @@ -123,74 +119,6 @@ public function testDisableRoleHierarchyVoter() $this->assertFalse($container->hasDefinition('security.access.role_hierarchy_voter')); } - /** - * @group legacy - * @expectedDeprecation Not setting "logout_on_user_change" to true on firewall "some_firewall" is deprecated as of 3.4, it will always be true in 4.0. - */ - public function testConfiguresLogoutOnUserChangeForContextListenersCorrectly() - { - $container = $this->getRawContainer(); - - $container->loadFromExtension('security', array( - 'providers' => array( - 'default' => array('id' => 'foo'), - ), - 'firewalls' => array( - 'some_firewall' => array( - 'pattern' => '/.*', - 'http_basic' => null, - 'logout_on_user_change' => false, - ), - 'some_other_firewall' => array( - 'pattern' => '/.*', - 'http_basic' => null, - 'logout_on_user_change' => true, - ), - ), - )); - - $container->compile(); - - $this->assertEquals(array(array('setLogoutOnUserChange', array(false))), $container->getDefinition('security.context_listener.0')->getMethodCalls()); - $this->assertEquals(array(array('setLogoutOnUserChange', array(true))), $container->getDefinition('security.context_listener.1')->getMethodCalls()); - } - - /** - * @group legacy - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage Firewalls "some_firewall" and "some_other_firewall" need to have the same value for option "logout_on_user_change" as they are sharing the context "my_context" - */ - public function testThrowsIfLogoutOnUserChangeDifferentForSharedContext() - { - $container = $this->getRawContainer(); - - $container->loadFromExtension('security', array( - 'providers' => array( - 'default' => array('id' => 'foo'), - ), - 'firewalls' => array( - 'some_firewall' => array( - 'pattern' => '/.*', - 'http_basic' => null, - 'context' => 'my_context', - 'logout_on_user_change' => false, - ), - 'some_other_firewall' => array( - 'pattern' => '/.*', - 'http_basic' => null, - 'context' => 'my_context', - 'logout_on_user_change' => true, - ), - ), - )); - - $container->compile(); - } - - /** - * @group legacy - * @expectedDeprecation Firewall "some_firewall" is configured as "stateless" but the "switch_user.stateless" key is set to false. Both should have the same value, the firewall's "stateless" value will be used as default value for the "switch_user.stateless" key in 4.0. - */ public function testSwitchUserNotStatelessOnStatelessFirewall() { $container = $this->getRawContainer(); @@ -205,22 +133,18 @@ public function testSwitchUserNotStatelessOnStatelessFirewall() 'stateless' => true, 'http_basic' => null, 'switch_user' => array('stateless' => false), - 'logout_on_user_change' => true, ), ), )); $container->compile(); + + $this->assertTrue($container->getDefinition('security.authentication.switchuser_listener.some_firewall')->getArgument(9)); } - /** - * @group legacy - * @expectedDeprecation Listener "http_basic" on firewall "default" has no "provider" set but multiple providers exist. Using the first configured provider (first) is deprecated since Symfony 3.4 and will throw an exception in 4.0, set the "provider" key on the firewall instead. - */ - public function testDeprecationForAmbiguousProvider() + public function testPerListenerProvider() { $container = $this->getRawContainer(); - $container->loadFromExtension('security', array( 'providers' => array( 'first' => array('id' => 'foo'), @@ -229,16 +153,20 @@ public function testDeprecationForAmbiguousProvider() 'firewalls' => array( 'default' => array( - 'http_basic' => null, - 'logout_on_user_change' => true, + 'http_basic' => array('provider' => 'second'), ), ), )); $container->compile(); + $this->addToAssertionCount(1); } - public function testPerListenerProvider() + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Not configuring explicitly the provider for the "http_basic" listener on "ambiguous" firewall is ambiguous as there is more than one registered provider. + */ + public function testMissingProviderForListener() { $container = $this->getRawContainer(); $container->loadFromExtension('security', array( @@ -248,15 +176,14 @@ public function testPerListenerProvider() ), 'firewalls' => array( - 'default' => array( - 'http_basic' => array('provider' => 'second'), - 'logout_on_user_change' => true, + 'ambiguous' => array( + 'http_basic' => true, + 'form_login' => array('provider' => 'second'), ), ), )); $container->compile(); - $this->addToAssertionCount(1); } public function testPerListenerProviderWithRememberMe() @@ -271,7 +198,6 @@ public function testPerListenerProviderWithRememberMe() 'firewalls' => array( 'default' => array( 'form_login' => array('provider' => 'second'), - 'logout_on_user_change' => true, 'remember_me' => array('secret' => 'baz'), ), ), diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php deleted file mode 100644 index 85f6cf86a403d..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php +++ /dev/null @@ -1,177 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\SecurityBundle\Tests\Functional; - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -use Symfony\Bundle\FrameworkBundle\Console\Application; -use Symfony\Bundle\SecurityBundle\Command\SetAclCommand; -use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Exception\NoAceFoundException; -use Symfony\Component\Security\Acl\Permission\BasicPermissionMap; - -/** - * Tests SetAclCommand. - * - * @author Kévin Dunglas - * @requires extension pdo_sqlite - * @group legacy - */ -class SetAclCommandTest extends WebTestCase -{ - const OBJECT_CLASS = 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity\Car'; - const SECURITY_CLASS = 'Symfony\Component\Security\Core\User\User'; - - public function testSetAclUser() - { - $objectId = 1; - $securityUsername1 = 'kevin'; - $securityUsername2 = 'anne'; - $grantedPermission1 = 'VIEW'; - $grantedPermission2 = 'EDIT'; - - $application = $this->getApplication(); - $application->add(new SetAclCommand()); - - $setAclCommand = $application->find('acl:set'); - $setAclCommandTester = new CommandTester($setAclCommand); - $setAclCommandTester->execute(array( - 'command' => 'acl:set', - 'arguments' => array($grantedPermission1, $grantedPermission2, sprintf('%s:%s', self::OBJECT_CLASS, $objectId)), - '--user' => array(sprintf('%s:%s', self::SECURITY_CLASS, $securityUsername1), sprintf('%s:%s', self::SECURITY_CLASS, $securityUsername2)), - )); - - $objectIdentity = new ObjectIdentity($objectId, self::OBJECT_CLASS); - $securityIdentity1 = new UserSecurityIdentity($securityUsername1, self::SECURITY_CLASS); - $securityIdentity2 = new UserSecurityIdentity($securityUsername2, self::SECURITY_CLASS); - $permissionMap = new BasicPermissionMap(); - - /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ - $aclProvider = $application->getKernel()->getContainer()->get('test.security.acl.provider'); - $acl = $aclProvider->findAcl($objectIdentity, array($securityIdentity1)); - - $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission1, null), array($securityIdentity1))); - $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission1, null), array($securityIdentity2))); - $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission2, null), array($securityIdentity2))); - - try { - $acl->isGranted($permissionMap->getMasks('OWNER', null), array($securityIdentity1)); - $this->fail('NoAceFoundException not throwed'); - } catch (NoAceFoundException $e) { - } - - try { - $acl->isGranted($permissionMap->getMasks('OPERATOR', null), array($securityIdentity2)); - $this->fail('NoAceFoundException not throwed'); - } catch (NoAceFoundException $e) { - } - } - - public function testSetAclRole() - { - $objectId = 1; - $securityUsername = 'kevin'; - $grantedPermission = 'VIEW'; - $role = 'ROLE_ADMIN'; - - $application = $this->getApplication(); - $application->add(new SetAclCommand($application->getKernel()->getContainer()->get('test.security.acl.provider'))); - - $setAclCommand = $application->find('acl:set'); - $setAclCommandTester = new CommandTester($setAclCommand); - $setAclCommandTester->execute(array( - 'command' => 'acl:set', - 'arguments' => array($grantedPermission, sprintf('%s:%s', str_replace('\\', '/', self::OBJECT_CLASS), $objectId)), - '--role' => array($role), - )); - - $objectIdentity = new ObjectIdentity($objectId, self::OBJECT_CLASS); - $userSecurityIdentity = new UserSecurityIdentity($securityUsername, self::SECURITY_CLASS); - $roleSecurityIdentity = new RoleSecurityIdentity($role); - $permissionMap = new BasicPermissionMap(); - - /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ - $aclProvider = $application->getKernel()->getContainer()->get('test.security.acl.provider'); - $acl = $aclProvider->findAcl($objectIdentity, array($roleSecurityIdentity, $userSecurityIdentity)); - - $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); - $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); - - try { - $acl->isGranted($permissionMap->getMasks('VIEW', null), array($userSecurityIdentity)); - $this->fail('NoAceFoundException not throwed'); - } catch (NoAceFoundException $e) { - } - - try { - $acl->isGranted($permissionMap->getMasks('OPERATOR', null), array($userSecurityIdentity)); - $this->fail('NoAceFoundException not throwed'); - } catch (NoAceFoundException $e) { - } - } - - public function testSetAclClassScope() - { - $objectId = 1; - $grantedPermission = 'VIEW'; - $role = 'ROLE_USER'; - - $application = $this->getApplication(); - $application->add(new SetAclCommand($application->getKernel()->getContainer()->get('test.security.acl.provider'))); - - $setAclCommand = $application->find('acl:set'); - $setAclCommandTester = new CommandTester($setAclCommand); - $setAclCommandTester->execute(array( - 'command' => 'acl:set', - 'arguments' => array($grantedPermission, sprintf('%s:%s', self::OBJECT_CLASS, $objectId)), - '--class-scope' => true, - '--role' => array($role), - )); - - $objectIdentity1 = new ObjectIdentity($objectId, self::OBJECT_CLASS); - $objectIdentity2 = new ObjectIdentity(2, self::OBJECT_CLASS); - $roleSecurityIdentity = new RoleSecurityIdentity($role); - $permissionMap = new BasicPermissionMap(); - - /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ - $aclProvider = $application->getKernel()->getContainer()->get('test.security.acl.provider'); - - $acl1 = $aclProvider->findAcl($objectIdentity1, array($roleSecurityIdentity)); - $this->assertTrue($acl1->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); - - $acl2 = $aclProvider->createAcl($objectIdentity2); - $this->assertTrue($acl2->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); - } - - private function getApplication() - { - $kernel = $this->createKernel(array('test_case' => 'Acl')); - $kernel->boot(); - - $application = new Application($kernel); - - $initAclCommand = $application->find('init:acl'); - $initAclCommandTester = new CommandTester($initAclCommand); - $initAclCommandTester->execute(array('command' => 'init:acl')); - - return $application; - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php index 01b2ce303acfa..629ff18a0cb6f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php @@ -229,28 +229,6 @@ public function testThrowsExceptionOnNoConfiguredEncoders() ), array('interactive' => false)); } - /** - * @group legacy - * @expectedDeprecation Passing null as the first argument of "Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand::__construct" is deprecated since Symfony 3.3 and will be removed in 4.0. If the command was registered by convention, make it a service instead. - */ - public function testLegacy() - { - $application = new ConsoleApplication(); - $application->add(new UserPasswordEncoderCommand()); - - $passwordEncoderCommand = $application->find('security:encode-password'); - self::bootKernel(array('test_case' => 'PasswordEncode')); - $passwordEncoderCommand->setContainer(self::$kernel->getContainer()); - - $tester = new CommandTester($passwordEncoderCommand); - $tester->execute(array( - 'command' => 'security:encode-password', - 'password' => 'password', - ), array('interactive' => false)); - - $this->assertContains('Encoder used Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder', $tester->getDisplay()); - } - protected function setUp() { putenv('COLUMNS='.(119 + strlen(PHP_EOL))); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php deleted file mode 100644 index 51337913d5370..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return array( - new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), - new Symfony\Bundle\SecurityBundle\SecurityBundle(), - new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\AclBundle(), -); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/config.yml deleted file mode 100644 index 45f8e24d4f81e..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/config.yml +++ /dev/null @@ -1,28 +0,0 @@ -imports: - - { resource: ./../config/framework.yml } - -services: - _defaults: { public: true } - test.security.acl.provider: '@security.acl.provider' - -doctrine: - dbal: - driver: pdo_sqlite - memory: true - charset: UTF8 - -security: - firewalls: - test: - pattern: ^/ - security: false - acl: - connection: default - encoders: - Symfony\Component\Security\Core\User\User: plaintext - providers: - in_memory: - memory: - users: - kevin: { password: test, roles: [ROLE_USER] } - anne: { password: test, roles: [ROLE_ADMIN]} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/doctrine.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/doctrine.yml deleted file mode 100644 index 7a12388398f76..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/doctrine.yml +++ /dev/null @@ -1,5 +0,0 @@ -# to be removed once https://github.com/doctrine/DoctrineBundle/pull/684 is merged -services: - Doctrine\Bundle\DoctrineBundle\Command\: - resource: "@DoctrineBundle/Command/*" - tags: [console.command] diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php index 82c8ad4aa6c60..1aab514f45450 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional\app; -use Doctrine\ORM\Version; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Kernel; @@ -83,11 +82,6 @@ public function getLogDir() public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load($this->rootConfig); - - // to be removed once https://github.com/doctrine/DoctrineBundle/pull/684 is merged - if ('Acl' === $this->testCase && class_exists(Version::class)) { - $loader->load(__DIR__.'/Acl/doctrine.yml'); - } } public function serialize() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml index 6077b0b4a7870..5a00ac329895d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml @@ -28,7 +28,6 @@ security: security: false default: - logout_on_user_change: true form_login: check_path: /login_check default_target_path: /profile diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml index fc032ab5ef0e0..955c41192e26d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml @@ -16,12 +16,10 @@ services: security: firewalls: secure: - logout_on_user_change: true pattern: ^/secure/ http_basic: { realm: "Secure Gateway API" } entry_point: firewall_entry_point.entry_point.stub default: - logout_on_user_change: true anonymous: ~ access_control: - { path: ^/secure/, roles: ROLE_SECURE } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml index 9cf5e8bf9a3a0..cf92920f4bc25 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml @@ -1,5 +1,5 @@ imports: - - { resource: ./../config/framework.yml } + - { resource: ./../config/default.yml } security: encoders: @@ -13,7 +13,6 @@ security: firewalls: main: - logout_on_user_change: true pattern: ^/ anonymous: true json_login: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml index c49f7312e00d5..dff93273e804b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml @@ -1,5 +1,5 @@ imports: - - { resource: ./../config/framework.yml } + - { resource: ./../config/default.yml } security: encoders: @@ -13,7 +13,6 @@ security: firewalls: main: - logout_on_user_change: true pattern: ^/ anonymous: true json_login: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml index d486d63065903..d7b8ac97d9775 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml @@ -15,5 +15,4 @@ security: firewalls: default: - logout_on_user_change: true anonymous: ~ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml index 2276b14dd920c..19b9d8952ec5e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml @@ -20,7 +20,6 @@ security: security: false default: - logout_on_user_change: true form_login: check_path: /login_check default_target_path: /profile @@ -29,7 +28,6 @@ security: # This firewall is here just to check its the logout functionality second_area: - logout_on_user_change: true http_basic: ~ anonymous: ~ logout: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml index e9c266af126dd..e01ed369b1f56 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml @@ -13,7 +13,6 @@ security: firewalls: default: - logout_on_user_change: true form_login: login_path: localized_login_path check_path: localized_check_path diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml index df54ac83f4aab..5251fd1d93de1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml @@ -13,7 +13,6 @@ security: firewalls: default: - logout_on_user_change: true form_login: login_path: localized_login_path check_path: localized_check_path diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php index 0354327690f7e..983e8288214a2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php @@ -37,18 +37,6 @@ public function testGetters() $this->assertEquals($config, $context->getConfig()); } - /** - * @expectedDeprecation Method Symfony\Bundle\SecurityBundle\Security\FirewallContext::getContext() is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Bundle\SecurityBundle\Security\FirewallContext::getListeners/getExceptionListener() instead. - * @group legacy - */ - public function testGetContext() - { - $context = (new FirewallContext($listeners = array(), $exceptionListener = $this->getExceptionListenerMock(), new FirewallConfig('main', 'request_matcher', 'user_checker'))) - ->getContext(); - - $this->assertEquals(array($listeners, $exceptionListener), $context); - } - private function getExceptionListenerMock() { return $this diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php index f9047cfdd233e..3e6f0a1ac74c6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php @@ -18,7 +18,6 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestMatcherInterface; -use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Http\Firewall\ExceptionListener; use Symfony\Component\Security\Http\Firewall\ListenerInterface; @@ -63,7 +62,7 @@ public function testGetListeners() $firewallContext = $this->getMockBuilder(FirewallContext::class)->disableOriginalConstructor()->getMock(); - $firewallConfig = new FirewallConfig('main', $this->getMockBuilder(UserCheckerInterface::class)->getMock()); + $firewallConfig = new FirewallConfig('main', 'user_checker'); $firewallContext->expects($this->once())->method('getConfig')->willReturn($firewallConfig); $listener = $this->getMockBuilder(ListenerInterface::class)->getMock(); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php index 0f9a0fe80b646..a8f005b2e4318 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityUserValueResolverTest.php @@ -21,6 +21,9 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; +/** + * @group legacy + */ class SecurityUserValueResolverTest extends TestCase { public function testResolveNoToken() diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 45e6b3663c185..2eff4d845d11c 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -16,44 +16,39 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "ext-xml": "*", "symfony/security": "~3.4|~4.0", "symfony/dependency-injection": "^3.4.3|^4.0.3", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/polyfill-php70": "~1.0" + "symfony/http-kernel": "~3.4|~4.0" }, "require-dev": { - "symfony/asset": "~2.8|~3.0|~4.0", - "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/asset": "~3.4|~4.0", + "symfony/browser-kit": "~3.4|~4.0", "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~2.8|~3.0|~4.0", - "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~3.4|~4.0", + "symfony/dom-crawler": "~3.4|~4.0", "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/form": "^3.4|~4.0", + "symfony/form": "~3.4|~4.0", "symfony/framework-bundle": "~3.4|~4.0", - "symfony/http-foundation": "~3.3|~4.0", - "symfony/security-acl": "~2.8|~3.0", + "symfony/http-foundation": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", "symfony/twig-bundle": "~3.4|~4.0", "symfony/twig-bridge": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0", - "symfony/validator": "^3.4|~4.0", - "symfony/var-dumper": "~3.3|~4.0", + "symfony/process": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~3.4|~4.0", "doctrine/doctrine-bundle": "~1.5", "twig/twig": "~1.34|~2.4" }, "conflict": { - "symfony/var-dumper": "<3.3", + "symfony/var-dumper": "<3.4", "symfony/event-dispatcher": "<3.4", "symfony/framework-bundle": "<3.4", "symfony/console": "<3.4" }, - "suggest": { - "symfony/security-acl": "For using the ACL functionality of this bundle" - }, "autoload": { "psr-4": { "Symfony\\Bundle\\SecurityBundle\\": "" }, "exclude-from-classmap": [ @@ -63,7 +58,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md index bdf357181f060..78b289092ee70 100644 --- a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md @@ -1,6 +1,17 @@ CHANGELOG ========= +4.1.0 +----- + + * added priority to Twig extensions + * deprecated relying on the default value (`false`) of the `twig.strict_variables` configuration option. The `%kernel.debug%` parameter will be the new default in 5.0 + +4.0.0 +----- + + * removed `ContainerAwareRuntimeLoader` + 3.4.0 ----- diff --git a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php index 65867ffa97853..cdd3a5d740865 100644 --- a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php +++ b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php @@ -28,24 +28,10 @@ class TemplateCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInte private $twig; private $iterator; - /** - * TemplateCacheWarmer constructor. - * - * @param ContainerInterface $container - * @param \Traversable $iterator - */ - public function __construct($container, \Traversable $iterator) + public function __construct(ContainerInterface $container, \Traversable $iterator) { // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. - if ($container instanceof ContainerInterface) { - $this->container = $container; - } elseif ($container instanceof Environment) { - $this->twig = $container; - @trigger_error(sprintf('Using a "%s" as first argument of %s is deprecated since Symfony 3.4 and will be unsupported in version 4.0. Use a %s instead.', Environment::class, __CLASS__, ContainerInterface::class), E_USER_DEPRECATED); - } else { - throw new \InvalidArgumentException(sprintf('%s only accepts instance of Psr\Container\ContainerInterface as first argument.', __CLASS__)); - } - + $this->container = $container; $this->iterator = $iterator; } diff --git a/src/Symfony/Bundle/TwigBundle/Command/DebugCommand.php b/src/Symfony/Bundle/TwigBundle/Command/DebugCommand.php deleted file mode 100644 index 107eece3045d3..0000000000000 --- a/src/Symfony/Bundle/TwigBundle/Command/DebugCommand.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\TwigBundle\Command; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Bridge\Twig\Command\DebugCommand instead.', DebugCommand::class), E_USER_DEPRECATED); - -use Symfony\Bridge\Twig\Command\DebugCommand as BaseDebugCommand; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; - -/** - * Lists twig functions, filters, globals and tests present in the current project. - * - * @author Jordi Boggiano - * - * @deprecated since version 3.4, to be removed in 4.0. - */ -final class DebugCommand extends BaseDebugCommand implements ContainerAwareInterface -{ - use ContainerAwareTrait; - - /** - * {@inheritdoc} - */ - protected function getTwigEnvironment() - { - return $this->container->get('twig'); - } -} diff --git a/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php b/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php index 1474a4de0a3f5..c0b175c3a2ed8 100644 --- a/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php +++ b/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php @@ -12,8 +12,6 @@ namespace Symfony\Bundle\TwigBundle\Command; use Symfony\Bridge\Twig\Command\LintCommand as BaseLintCommand; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\Finder\Finder; /** @@ -22,11 +20,8 @@ * @author Marc Weistroff * @author Jérôme Tamarelle */ -final class LintCommand extends BaseLintCommand implements ContainerAwareInterface +final class LintCommand extends BaseLintCommand { - // BC to be removed in 4.0 - use ContainerAwareTrait; - /** * {@inheritdoc} */ diff --git a/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php b/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php deleted file mode 100644 index 5ba6df59932fd..0000000000000 --- a/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\TwigBundle; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Twig\RuntimeLoader\ContainerRuntimeLoader class instead.', ContainerAwareRuntimeLoader::class), E_USER_DEPRECATED); - -use Psr\Log\LoggerInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Twig\RuntimeLoader\RuntimeLoaderInterface; - -/** - * Loads Twig extension runtimes via the service container. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, will be removed in 4.0. Use \Twig\Loader\ContainerRuntimeLoader instead. - */ -class ContainerAwareRuntimeLoader implements RuntimeLoaderInterface -{ - private $container; - private $mapping; - private $logger; - - public function __construct(ContainerInterface $container, array $mapping, LoggerInterface $logger = null) - { - $this->container = $container; - $this->mapping = $mapping; - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function load($class) - { - if (isset($this->mapping[$class])) { - return $this->container->get($this->mapping[$class]); - } - - if (null !== $this->logger) { - $this->logger->warning(sprintf('Class "%s" is not configured as a Twig runtime. Add the "twig.runtime" tag to the related service in the container.', $class)); - } - } -} diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index b2dfab96ea278..e4ad1b0daaaa0 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -35,7 +35,7 @@ class ExceptionController * @param Environment $twig * @param bool $debug Show error (false) or exception (true) pages by default */ - public function __construct(Environment $twig, $debug) + public function __construct(Environment $twig, bool $debug) { $this->twig = $twig; $this->debug = $debug; diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php index d5e6cb6c4acb2..082870249ba44 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php @@ -11,9 +11,10 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; /** * Adds tagged twig.extension services to twig service. @@ -22,6 +23,8 @@ */ class TwigEnvironmentPass implements CompilerPassInterface { + use PriorityTaggedServiceTrait; + public function process(ContainerBuilder $container) { if (false === $container->hasDefinition('twig')) { @@ -37,9 +40,9 @@ public function process(ContainerBuilder $container) $currentMethodCalls = $definition->getMethodCalls(); $twigBridgeExtensionsMethodCalls = array(); $othersExtensionsMethodCalls = array(); - foreach ($container->findTaggedServiceIds('twig.extension', true) as $id => $attributes) { - $methodCall = array('addExtension', array(new Reference($id))); - $extensionClass = $container->getDefinition($id)->getClass(); + foreach ($this->findAndSortTaggedServices('twig.extension', $container) as $extension) { + $methodCall = array('addExtension', array($extension)); + $extensionClass = $container->getDefinition((string) $extension)->getClass(); if (is_string($extensionClass) && 0 === strpos($extensionClass, 'Symfony\Bridge\Twig\Extension')) { $twigBridgeExtensionsMethodCalls[] = $methodCall; diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php index f4488c0d5ccd4..e2e4c5657963f 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php @@ -127,7 +127,13 @@ private function addTwigOptions(ArrayNodeDefinition $rootNode) ->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end() ->scalarNode('charset')->defaultValue('%kernel.charset%')->end() ->booleanNode('debug')->defaultValue('%kernel.debug%')->end() - ->booleanNode('strict_variables')->end() + ->booleanNode('strict_variables') + ->defaultValue(function () { + @trigger_error('Relying on the default value ("false") of the "twig.strict_variables" configuration option is deprecated since Symfony 4.1. You should use "%kernel.debug%" explicitly instead, which will be the new default in 5.0.', E_USER_DEPRECATED); + + return false; + }) + ->end() ->scalarNode('auto_reload')->end() ->integerNode('optimizations')->min(-1)->end() ->scalarNode('default_path') diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configurator/EnvironmentConfigurator.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configurator/EnvironmentConfigurator.php index 13ad88ef6b635..a1354622b6726 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configurator/EnvironmentConfigurator.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configurator/EnvironmentConfigurator.php @@ -31,7 +31,7 @@ class EnvironmentConfigurator private $decimalPoint; private $thousandsSeparator; - public function __construct($dateFormat, $intervalFormat, $timezone, $decimals, $decimalPoint, $thousandsSeparator) + public function __construct(string $dateFormat, string $intervalFormat, ?string $timezone, int $decimals, string $decimalPoint, string $thousandsSeparator) { $this->dateFormat = $dateFormat; $this->intervalFormat = $intervalFormat; diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index c3012abbd8e53..b44dd2481c631 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -37,15 +37,8 @@ public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('twig.xml'); - $container->getDefinition('twig.profile')->setPrivate(true); - $container->getDefinition('twig.runtime.httpkernel')->setPrivate(true); - $container->getDefinition('twig.translation.extractor')->setPrivate(true); - $container->getDefinition('workflow.twig_extension')->setPrivate(true); - $container->getDefinition('twig.exception_listener')->setPrivate(true); - if (class_exists('Symfony\Component\Form\Form')) { $loader->load('form.xml'); - $container->getDefinition('twig.form.renderer')->setPrivate(true); } if (interface_exists('Symfony\Component\Templating\EngineInterface')) { @@ -112,26 +105,15 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']); $container->getDefinition('twig.template_iterator')->replaceArgument(2, $config['paths']); - $bundleHierarchy = $this->getBundleHierarchy($container, $config); - - foreach ($bundleHierarchy as $name => $bundle) { + foreach ($this->getBundleTemplatePaths($container, $config) as $name => $paths) { $namespace = $this->normalizeBundleName($name); - - foreach ($bundle['children'] as $child) { - foreach ($bundleHierarchy[$child]['paths'] as $path) { - $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace)); - } - } - - foreach ($bundle['paths'] as $path) { + foreach ($paths as $path) { $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace)); } - // add exclusive namespace for root bundles only - // to override a bundle template that also extends itself - if (count($bundle['paths']) > 0 && 0 === count($bundle['parents'])) { + if ($paths) { // the last path must be the bundle views directory - $twigFilesystemLoaderDefinition->addMethodCall('addPath', array(end($bundle['paths']), '!'.$namespace)); + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, '!'.$namespace)); } } @@ -174,73 +156,26 @@ public function load(array $configs, ContainerBuilder $container) $container->registerForAutoconfiguration(ExtensionInterface::class)->addTag('twig.extension'); $container->registerForAutoconfiguration(LoaderInterface::class)->addTag('twig.loader'); $container->registerForAutoconfiguration(RuntimeExtensionInterface::class)->addTag('twig.runtime'); - - if (\PHP_VERSION_ID < 70000) { - $this->addClassesToCompile(array( - 'Twig_Environment', - 'Twig_Extension', - 'Twig_Extension_Core', - 'Twig_Extension_Escaper', - 'Twig_Extension_Optimizer', - 'Twig_LoaderInterface', - 'Twig_Markup', - 'Twig_Template', - )); - } } - private function getBundleHierarchy(ContainerBuilder $container, array $config) + private function getBundleTemplatePaths(ContainerBuilder $container, array $config) { $bundleHierarchy = array(); - foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) { - if (!array_key_exists($name, $bundleHierarchy)) { - $bundleHierarchy[$name] = array( - 'paths' => array(), - 'parents' => array(), - 'children' => array(), - ); - } - if (file_exists($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views')) { - $bundleHierarchy[$name]['paths'][] = $dir; + $bundleHierarchy[$name][] = $dir; } $container->addResource(new FileExistenceResource($dir)); if (file_exists($dir = $container->getParameterBag()->resolveValue($config['default_path']).'/bundles/'.$name)) { - $bundleHierarchy[$name]['paths'][] = $dir; + $bundleHierarchy[$name][] = $dir; } $container->addResource(new FileExistenceResource($dir)); if (file_exists($dir = $bundle['path'].'/Resources/views')) { - $bundleHierarchy[$name]['paths'][] = $dir; + $bundleHierarchy[$name][] = $dir; } $container->addResource(new FileExistenceResource($dir)); - - if (!isset($bundle['parent']) || null === $bundle['parent']) { - continue; - } - - $bundleHierarchy[$name]['parents'][] = $bundle['parent']; - - if (!array_key_exists($bundle['parent'], $bundleHierarchy)) { - $bundleHierarchy[$bundle['parent']] = array( - 'paths' => array(), - 'parents' => array(), - 'children' => array(), - ); - } - - $bundleHierarchy[$bundle['parent']]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$bundle['parent']]['children']); - - foreach ($bundleHierarchy[$bundle['parent']]['parents'] as $parent) { - $bundleHierarchy[$name]['parents'][] = $parent; - $bundleHierarchy[$parent]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$parent]['children']); - } - - foreach ($bundleHierarchy[$name]['children'] as $child) { - $bundleHierarchy[$child]['parents'] = array_merge($bundleHierarchy[$child]['parents'], $bundleHierarchy[$name]['parents']); - } } return $bundleHierarchy; diff --git a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php index a938cb01d9e05..ff092c3d199c4 100644 --- a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php +++ b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php @@ -31,7 +31,7 @@ class FilesystemLoader extends BaseFilesystemLoader /** * @param string|null $rootPath The root path common to all relative paths (null for getcwd()) */ - public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser, $rootPath = null) + public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser, string $rootPath = null) { parent::__construct(array(), $rootPath); diff --git a/src/Symfony/Bundle/TwigBundle/TemplateIterator.php b/src/Symfony/Bundle/TwigBundle/TemplateIterator.php index 63f8da549fdd4..1b700a2d36b24 100644 --- a/src/Symfony/Bundle/TwigBundle/TemplateIterator.php +++ b/src/Symfony/Bundle/TwigBundle/TemplateIterator.php @@ -31,7 +31,7 @@ class TemplateIterator implements \IteratorAggregate * @param string $rootDir The directory where global templates can be stored * @param array $paths Additional Twig paths to warm */ - public function __construct(KernelInterface $kernel, $rootDir, array $paths = array()) + public function __construct(KernelInterface $kernel, string $rootDir, array $paths = array()) { $this->kernel = $kernel; $this->rootDir = $rootDir; diff --git a/src/Symfony/Bundle/TwigBundle/Tests/ContainerAwareRuntimeLoaderTest.php b/src/Symfony/Bundle/TwigBundle/Tests/ContainerAwareRuntimeLoaderTest.php deleted file mode 100644 index 93202ca987b16..0000000000000 --- a/src/Symfony/Bundle/TwigBundle/Tests/ContainerAwareRuntimeLoaderTest.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\TwigBundle\Tests; - -use Psr\Log\LoggerInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Bundle\TwigBundle\ContainerAwareRuntimeLoader; - -/** - * @group legacy - */ -class ContainerAwareRuntimeLoaderTest extends TestCase -{ - public function testLoad() - { - $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); - $container->expects($this->once())->method('get')->with('foo'); - - $loader = new ContainerAwareRuntimeLoader($container, array( - 'FooClass' => 'foo', - )); - $loader->load('FooClass'); - } - - public function testLoadWithoutAMatch() - { - $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); - $logger->expects($this->once())->method('warning')->with('Class "BarClass" is not configured as a Twig runtime. Add the "twig.runtime" tag to the related service in the container.'); - $loader = new ContainerAwareRuntimeLoader($this->getMockBuilder(ContainerInterface::class)->getMock(), array(), $logger); - $this->assertNull($loader->load('BarClass')); - } -} diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php index 0af3fe4b32532..aa6aac242807c 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php @@ -15,10 +15,36 @@ use Symfony\Bridge\Twig\Extension\FormExtension; use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigEnvironmentPass; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; class TwigEnvironmentPassTest extends TestCase { + public function testPassWithTwoExtensionsWithPriority() + { + $twigDefinition = new Definition('twig'); + $twigDefinition->setPublic(true); + $builder = new ContainerBuilder(); + $builder->setDefinition('twig', $twigDefinition); + $pass = new TwigEnvironmentPass(); + + $definition = new Definition('test_extension_1'); + $definition->addTag('twig.extension', array('priority' => 100)); + $builder->setDefinition('test_extension_1', $definition); + + $definition = new Definition('test_extension_2'); + $definition->addTag('twig.extension', array('priority' => 200)); + $builder->setDefinition('test_extension_2', $definition); + + $pass->process($builder); + $calls = $twigDefinition->getMethodCalls(); + $this->assertCount(2, $calls); + $this->assertEquals('addExtension', $calls[0][0]); + $this->assertEquals('addExtension', $calls[1][0]); + $this->assertEquals('test_extension_2', (string) $calls[0][1][0]); + $this->assertEquals('test_extension_1', (string) $calls[1][1][0]); + } + public function testTwigBridgeExtensionsAreRegisteredFirst() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/ConfigurationTest.php index d3f8652c4b6db..bd8482cd9e778 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -20,6 +20,7 @@ class ConfigurationTest extends TestCase public function testDoNoDuplicateDefaultFormResources() { $input = array( + 'strict_variables' => false, // to be removed in 5.0 relying on default 'form_themes' => array('form_div_layout.html.twig'), ); @@ -28,4 +29,16 @@ public function testDoNoDuplicateDefaultFormResources() $this->assertEquals(array('form_div_layout.html.twig'), $config['form_themes']); } + + /** + * @group legacy + * @expectedDeprecation Relying on the default value ("false") of the "twig.strict_variables" configuration option is deprecated since Symfony 4.1. You should use "%kernel.debug%" explicitly instead, which will be the new default in 5.0. + */ + public function testGetStrictVariablesDefaultFalse() + { + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(), array(array())); + + $this->assertFalse($config['strict_variables']); + } } diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views/layout.html.twig b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views/layout.html.twig deleted file mode 100644 index bb07ecfe55a36..0000000000000 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views/layout.html.twig +++ /dev/null @@ -1 +0,0 @@ -This is a layout diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views/layout.html.twig b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views/layout.html.twig deleted file mode 100644 index bb07ecfe55a36..0000000000000 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views/layout.html.twig +++ /dev/null @@ -1 +0,0 @@ -This is a layout diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildTwigBundle/Resources/views/layout.html.twig b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildTwigBundle/Resources/views/layout.html.twig deleted file mode 100644 index bb07ecfe55a36..0000000000000 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildChildTwigBundle/Resources/views/layout.html.twig +++ /dev/null @@ -1 +0,0 @@ -This is a layout diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildTwigBundle/Resources/views/layout.html.twig b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildTwigBundle/Resources/views/layout.html.twig deleted file mode 100644 index bb07ecfe55a36..0000000000000 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Bundle/ChildTwigBundle/Resources/views/layout.html.twig +++ /dev/null @@ -1 +0,0 @@ -This is a layout diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/customTemplateEscapingGuesser.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/customTemplateEscapingGuesser.php index ab429237bb43c..5ef1256663c93 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/customTemplateEscapingGuesser.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/customTemplateEscapingGuesser.php @@ -3,4 +3,5 @@ $container->loadFromExtension('twig', array( 'autoescape_service' => 'my_project.some_bundle.template_escaping_guesser', 'autoescape_service_method' => 'guess', + 'strict_variables' => false, // to be removed in 5.0 relying on default )); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/empty.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/empty.php index efd2df5f47918..674c5d474cc81 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/empty.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/empty.php @@ -1,3 +1,5 @@ loadFromExtension('twig', array()); +$container->loadFromExtension('twig', array( + 'strict_variables' => false, // to be removed in 5.0 relying on default +)); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/extra.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/extra.php index 28b8281a99e87..da234511b1417 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/extra.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/extra.php @@ -1,7 +1,8 @@ loadFromExtension('twig', array( - 'paths' => array( - 'namespaced_path3' => 'namespace3', - ), + 'paths' => array( + 'namespaced_path3' => 'namespace3', + ), + 'strict_variables' => false, // to be removed in 5.0 relying on default )); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/formats.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/formats.php index 630a9a9edc01a..fe4f7c19c1100 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/formats.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/formats.php @@ -11,4 +11,5 @@ 'decimal_point' => ',', 'thousands_separator' => '.', ), + 'strict_variables' => false, // to be removed in 5.0 relying on default )); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/customTemplateEscapingGuesser.xml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/customTemplateEscapingGuesser.xml index fa28361cc8af0..39ab94953edf1 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/customTemplateEscapingGuesser.xml +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/customTemplateEscapingGuesser.xml @@ -6,5 +6,5 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/empty.xml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/empty.xml index 771e382e47002..30a7229aeea0d 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/empty.xml +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/empty.xml @@ -6,5 +6,5 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/formats.xml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/formats.xml index 1ab39e49229cd..ea642a8b8caca 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/formats.xml +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/formats.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/customTemplateEscapingGuesser.yml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/customTemplateEscapingGuesser.yml index eb26e7165bb09..34e301c0957e5 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/customTemplateEscapingGuesser.yml +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/customTemplateEscapingGuesser.yml @@ -1,3 +1,4 @@ twig: autoescape_service: my_project.some_bundle.template_escaping_guesser autoescape_service_method: guess + strict_variables: false # to be removed in 5.0 relying on default diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/empty.yml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/empty.yml index a472b2698e5cd..9b5dbcf35b67b 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/empty.yml +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/empty.yml @@ -1 +1,2 @@ twig: + strict_variables: false # to be removed in 5.0 relying on default diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/extra.yml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/extra.yml index 3c5e6a3b5937a..41a281cc8198c 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/extra.yml +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/extra.yml @@ -1,3 +1,4 @@ twig: + strict_variables: false # to be removed in 5.0 relying on default paths: namespaced_path3: namespace3 diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/formats.yml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/formats.yml index 290921630f9e6..a5c57f383edfe 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/formats.yml +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/formats.yml @@ -1,4 +1,5 @@ twig: + strict_variables: false # to be removed in 5.0 relying on default date: format: Y-m-d interval_format: '%d' diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index 0039dc789ed75..ca0e2387fd75a 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -29,7 +29,9 @@ public function testLoadEmptyConfiguration() { $container = $this->createContainer(); $container->registerExtension(new TwigExtension()); - $container->loadFromExtension('twig', array()); + $container->loadFromExtension('twig', array( + 'strict_variables' => false, // to be removed in 5.0 relying on default + )); $this->compileContainer($container); $this->assertEquals('Twig\Environment', $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file'); @@ -151,7 +153,10 @@ public function testGlobalsWithDifferentTypesAndValues() $container = $this->createContainer(); $container->registerExtension(new TwigExtension()); - $container->loadFromExtension('twig', array('globals' => $globals)); + $container->loadFromExtension('twig', array( + 'globals' => $globals, + 'strict_variables' => false, // // to be removed in 5.0 relying on default + )); $this->compileContainer($container); $calls = $container->getDefinition('twig')->getMethodCalls(); @@ -188,24 +193,10 @@ public function testTwigLoaderPaths($format) array('namespaced_path1', 'namespace1'), array('namespaced_path2', 'namespace2'), array('namespaced_path3', 'namespace3'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildChildChildChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildChildChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'ChildChildChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'Twig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'Twig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views', 'Twig'), - array(__DIR__.'/Fixtures/Bundle/ChildTwigBundle/Resources/views', 'Twig'), array(__DIR__.'/Fixtures/Resources/TwigBundle/views', 'Twig'), array(__DIR__.'/Fixtures/templates/bundles/TwigBundle', 'Twig'), array(realpath(__DIR__.'/../..').'/Resources/views', 'Twig'), array(realpath(__DIR__.'/../..').'/Resources/views', '!Twig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'ChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views', 'ChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildTwigBundle/Resources/views', 'ChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'ChildChildTwig'), - array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views', 'ChildChildTwig'), array(__DIR__.'/Fixtures/Resources/views'), array(__DIR__.'/Fixtures/templates'), ), $paths); @@ -231,7 +222,9 @@ public function testStopwatchExtensionAvailability($debug, $stopwatchEnabled, $e $container->register('debug.stopwatch', 'Symfony\Component\Stopwatch\Stopwatch'); } $container->registerExtension(new TwigExtension()); - $container->loadFromExtension('twig', array()); + $container->loadFromExtension('twig', array( + 'strict_variables' => false, // to be removed in 5.0 relying on default + )); $container->setAlias('test.twig.extension.debug.stopwatch', 'twig.extension.debug.stopwatch')->setPublic(true); $this->compileContainer($container); @@ -256,7 +249,9 @@ public function testRuntimeLoader() { $container = $this->createContainer(); $container->registerExtension(new TwigExtension()); - $container->loadFromExtension('twig', array()); + $container->loadFromExtension('twig', array( + 'strict_variables' => false, // to be removed in 5.0 relying on default + )); $container->setParameter('kernel.environment', 'test'); $container->setParameter('debug.file_link_format', 'test'); $container->setParameter('foo', 'FooClass'); @@ -286,37 +281,12 @@ private function createContainer() 'kernel.debug' => false, 'kernel.bundles' => array( 'TwigBundle' => 'Symfony\\Bundle\\TwigBundle\\TwigBundle', - 'ChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildTwigBundle\\ChildTwigBundle', - 'ChildChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildTwigBundle\\ChildChildTwigBundle', - 'ChildChildChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildTwigBundle\\ChildChildChildTwigBundle', - 'ChildChildChildChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildChildTwigBundle\\ChildChildChildChildTwigBundle', ), 'kernel.bundles_metadata' => array( - 'ChildChildChildChildTwigBundle' => array( - 'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildChildTwigBundle\\ChildChildChildChildTwigBundle', - 'parent' => 'ChildChildChildTwigBundle', - 'path' => __DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle', - ), 'TwigBundle' => array( 'namespace' => 'Symfony\\Bundle\\TwigBundle', - 'parent' => null, 'path' => realpath(__DIR__.'/../..'), ), - 'ChildTwigBundle' => array( - 'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildTwigBundle\\ChildTwigBundle', - 'parent' => 'TwigBundle', - 'path' => __DIR__.'/Fixtures/Bundle/ChildTwigBundle', - ), - 'ChildChildChildTwigBundle' => array( - 'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildTwigBundle\\ChildChildChildTwigBundle', - 'parent' => 'ChildChildTwigBundle', - 'path' => __DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle', - ), - 'ChildChildTwigBundle' => array( - 'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildTwigBundle\\ChildChildTwigBundle', - 'parent' => 'ChildTwigBundle', - 'path' => __DIR__.'/Fixtures/Bundle/ChildChildTwigBundle', - ), ), ))); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php index a21c11adca16d..ce53eb9e96d92 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php @@ -89,10 +89,15 @@ public function registerBundles() public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(function ($container) { - $container->loadFromExtension('framework', array( - 'secret' => '$ecret', - 'form' => array('enabled' => false), - )); + $container + ->loadFromExtension('framework', array( + 'secret' => '$ecret', + 'form' => array('enabled' => false), + )) + ->loadFromExtension('twig', array( // to be removed in 5.0 relying on default + 'strict_variables' => false, + )) + ; }); if ($this->withTemplating) { diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Functional/NoTemplatingEntryTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Functional/NoTemplatingEntryTest.php index 952b5e6ae1937..f3d636c177595 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Functional/NoTemplatingEntryTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Functional/NoTemplatingEntryTest.php @@ -61,10 +61,15 @@ public function registerBundles() public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(function ($container) { - $container->loadFromExtension('framework', array( - 'secret' => '$ecret', - 'form' => array('enabled' => false), - )); + $container + ->loadFromExtension('framework', array( + 'secret' => '$ecret', + 'form' => array('enabled' => false), + )) + ->loadFromExtension('twig', array( // to be removed in 5.0 relying on default + 'strict_variables' => false, + )) + ; }); } diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 10ee36fc6f90c..229a1c99ab889 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -16,31 +16,31 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/config": "~3.2|~4.0", + "php": "^7.1.3", + "symfony/config": "~3.4|~4.0", "symfony/twig-bridge": "^3.4.3|^4.0.3", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "^3.3|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", "twig/twig": "~1.34|~2.4" }, "require-dev": { - "symfony/asset": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/asset": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/form": "~2.8|~3.0|~4.0", - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/yaml": "~2.8|~3.0|~4.0", - "symfony/framework-bundle": "^3.3.11|~4.0", - "symfony/web-link": "~3.3|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/form": "~3.4|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0", + "symfony/framework-bundle": "~3.4|~4.0", + "symfony/web-link": "~3.4|~4.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0" }, "conflict": { "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<3.3.1" + "symfony/event-dispatcher": "<3.4" }, "autoload": { "psr-4": { "Symfony\\Bundle\\TwigBundle\\": "" }, @@ -51,7 +51,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md index c1259b0a20e1d..182a804583313 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md @@ -1,6 +1,19 @@ CHANGELOG ========= +4.1.0 +----- + + * added information about orphaned events + +4.0.0 +----- + + * removed the `WebProfilerExtension::dumpValue()` method + * removed the `getTemplates()` method of the `TemplateManager` class in favor of the ``getNames()`` method + * removed the `web_profiler.position` config option and the + `web_profiler.debug_toolbar.position` container parameter + 3.4.0 ----- diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php index 3f9d873e1d40f..98536397a18ea 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php @@ -30,7 +30,7 @@ class ExceptionController protected $debug; protected $profiler; - public function __construct(Profiler $profiler = null, Environment $twig, $debug) + public function __construct(Profiler $profiler = null, Environment $twig, bool $debug) { $this->profiler = $profiler; $this->twig = $twig; diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 0920a1aa5e18a..ee9e72f7a1355 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -32,26 +32,15 @@ class ProfilerController private $profiler; private $twig; private $templates; - private $toolbarPosition; private $cspHandler; private $baseDir; - /** - * @param UrlGeneratorInterface $generator The URL Generator - * @param Profiler $profiler The profiler - * @param Environment $twig The twig environment - * @param array $templates The templates - * @param string $toolbarPosition The toolbar position (top, bottom, normal, or null -- use the configuration) - * @param ContentSecurityPolicyHandler $cspHandler The Content-Security-Policy handler - * @param string $baseDir The project root directory - */ - public function __construct(UrlGeneratorInterface $generator, Profiler $profiler = null, Environment $twig, array $templates, $toolbarPosition = 'bottom', ContentSecurityPolicyHandler $cspHandler = null, $baseDir = null) + public function __construct(UrlGeneratorInterface $generator, Profiler $profiler = null, Environment $twig, array $templates, ContentSecurityPolicyHandler $cspHandler = null, string $baseDir = null) { $this->generator = $generator; $this->profiler = $profiler; $this->twig = $twig; $this->templates = $templates; - $this->toolbarPosition = $toolbarPosition; $this->cspHandler = $cspHandler; $this->baseDir = $baseDir; } @@ -157,11 +146,6 @@ public function toolbarAction(Request $request, $token) return new Response('', 404, array('Content-Type' => 'text/html')); } - // the toolbar position (top, bottom, normal, or null -- use the configuration) - if (null === $position = $request->query->get('position')) { - $position = $this->toolbarPosition; - } - $url = null; try { $url = $this->generator->generate('_profiler', array('token' => $token)); @@ -171,7 +155,6 @@ public function toolbarAction(Request $request, $token) return $this->renderWithCspNonces($request, '@WebProfiler/Profiler/toolbar.html.twig', array( 'request' => $request, - 'position' => $position, 'profile' => $profile, 'templates' => $this->getTemplateManager()->getNames($profile), 'profiler_url' => $url, diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php index 80946ac428c02..685f08d18f381 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php @@ -77,13 +77,8 @@ public function panelAction($token) /** * Returns the routing traces associated to the given request. - * - * @param RequestDataCollector $request - * @param string $method - * - * @return array */ - private function getTraces(RequestDataCollector $request, $method) + private function getTraces(RequestDataCollector $request, string $method): array { $traceRequest = Request::create( $request->getPathInfo(), diff --git a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php index e07b28925de41..812e311195993 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php @@ -37,14 +37,6 @@ public function getConfigTreeBuilder() $rootNode ->children() ->booleanNode('toolbar')->defaultFalse()->end() - ->scalarNode('position') - ->defaultValue('bottom') - ->setDeprecated('The "web_profiler.position" configuration key has been deprecated in Symfony 3.4 and it will be removed in 4.0.') - ->validate() - ->ifNotInArray(array('bottom', 'top')) - ->thenInvalid('The CSS position %s is not supported') - ->end() - ->end() ->booleanNode('intercept_redirects')->defaultFalse()->end() ->scalarNode('excluded_ajax_paths')->defaultValue('^/(app(_[\\w]+)?\\.php/)?_wdt')->end() ->end() diff --git a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php index 7a17e7fb10e6a..8f74d05f43d05 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php +++ b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php @@ -44,12 +44,10 @@ public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('profiler.xml'); - $container->setParameter('web_profiler.debug_toolbar.position', $config['position']); if ($config['toolbar'] || $config['intercept_redirects']) { $loader->load('toolbar.xml'); - $container->getDefinition('web_profiler.debug_toolbar')->setPrivate(true); - $container->getDefinition('web_profiler.debug_toolbar')->replaceArgument(5, $config['excluded_ajax_paths']); + $container->getDefinition('web_profiler.debug_toolbar')->replaceArgument(4, $config['excluded_ajax_paths']); $container->setParameter('web_profiler.debug_toolbar.intercept_redirects', $config['intercept_redirects']); $container->setParameter('web_profiler.debug_toolbar.mode', $config['toolbar'] ? WebDebugToolbarListener::ENABLED : WebDebugToolbarListener::DISABLED); } diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php index 6ffdbc7ad0764..490e16382fe01 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php +++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php @@ -40,17 +40,15 @@ class WebDebugToolbarListener implements EventSubscriberInterface protected $urlGenerator; protected $interceptRedirects; protected $mode; - protected $position; protected $excludedAjaxPaths; private $cspHandler; - public function __construct(Environment $twig, $interceptRedirects = false, $mode = self::ENABLED, $position = 'bottom', UrlGeneratorInterface $urlGenerator = null, $excludedAjaxPaths = '^/bundles|^/_wdt', ContentSecurityPolicyHandler $cspHandler = null) + public function __construct(Environment $twig, bool $interceptRedirects = false, int $mode = self::ENABLED, UrlGeneratorInterface $urlGenerator = null, string $excludedAjaxPaths = '^/bundles|^/_wdt', ContentSecurityPolicyHandler $cspHandler = null) { $this->twig = $twig; $this->urlGenerator = $urlGenerator; - $this->interceptRedirects = (bool) $interceptRedirects; - $this->mode = (int) $mode; - $this->position = $position; + $this->interceptRedirects = $interceptRedirects; + $this->mode = $mode; $this->excludedAjaxPaths = $excludedAjaxPaths; $this->cspHandler = $cspHandler; } @@ -124,7 +122,6 @@ protected function injectToolbar(Response $response, Request $request, array $no $toolbar = "\n".str_replace("\n", '', $this->twig->render( '@WebProfiler/Profiler/toolbar_js.html.twig', array( - 'position' => $this->position, 'excluded_ajax_paths' => $this->excludedAjaxPaths, 'token' => $response->headers->get('X-Debug-Token'), 'request' => $request, diff --git a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php index 8be572628eabd..3bc642f428ffe 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php @@ -60,24 +60,6 @@ public function getName(Profile $profile, $panel) return $templates[$panel]; } - /** - * Gets the templates for a given profile. - * - * @return Template[] - * - * @deprecated not used anymore internally - */ - public function getTemplates(Profile $profile) - { - $templates = $this->getNames($profile); - - foreach ($templates as $name => $template) { - $templates[$name] = $this->twig->loadTemplate($template); - } - - return $templates; - } - /** * Gets template names of templates that are present in the viewed profile. * diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml index 85634882bd416..e5f9423ce5b86 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml @@ -12,7 +12,6 @@ %data_collector.templates% - %web_profiler.debug_toolbar.position% %kernel.project_dir% diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd index 84cc8ae9a97f3..e22105a178fa7 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd @@ -10,13 +10,5 @@ - - - - - - - - diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml index 16a7022c6d960..e589aa52f5400 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml @@ -12,7 +12,6 @@ %web_profiler.debug_toolbar.intercept_redirects% %web_profiler.debug_toolbar.mode% - %web_profiler.debug_toolbar.position% diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig index 53040a0d9b9b2..3a17380ae59fc 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig @@ -45,6 +45,26 @@ {% endif %} + +
+

Orphaned events {{ collector.orphanedEvents|length }}

+
+ {% if collector.orphanedEvents is empty %} +
+

+ There are no orphaned events. +

+

+ All dispatched events were handled or an error occurred + when trying to collect orphaned events (in which case check the + logs to get more information). +

+
+ {% else %} + {{ helper.render_table(collector.orphanedEvents) }} + {% endif %} +
+
{% endif %} {% endblock %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig index 903d009c98225..f43f1a1c04a70 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -12,9 +12,10 @@ {% endset %} {% endif %} - {% if collector.forward|default(false) %} + {% if collector.forwardtoken %} + {% set forward_profile = profile.childByToken(collector.forwardtoken) %} {% set forward_handler %} - {{ helper.set_handler(collector.forward.controller) }} + {{ helper.set_handler(forward_profile ? forward_profile.collector('request').controller : 'n/a') }} {% endset %} {% endif %} @@ -24,7 +25,7 @@ {{ collector.statuscode }} {% if collector.route %} {% if collector.redirect %}{{ include('@WebProfiler/Icon/redirect.svg') }}{% endif %} - {% if collector.forward|default(false) %}{{ include('@WebProfiler/Icon/forward.svg') }}{% endif %} + {% if collector.forwardtoken %}{{ include('@WebProfiler/Icon/forward.svg') }}{% endif %} {{ 'GET' != collector.method ? collector.method }} @ {{ collector.route }} {% endif %} @@ -49,13 +50,6 @@ {{ request_handler }} - {% if collector.controller.class is defined -%} -
- Controller class - {{ collector.controller.class }} -
- {%- endif %} -
Route name {{ collector.route|default('NONE') }} @@ -88,7 +82,7 @@ Forwarded to {{ forward_handler }} - ({{ collector.forward.token }}) + ({{ collector.forwardtoken }})
@@ -167,7 +161,15 @@ {% endif %}

Server Parameters

- {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestserver }, with_context = false) }} +

Defined in .env

+ {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.dotenvvars }, with_context = false) }} + +

Defined as regular env variables

+ {% set requestserver = [] %} + {% for key, value in collector.requestserver if key not in collector.dotenvvars.keys %} + {% set requestserver = requestserver|merge({(key): value}) %} + {% endfor %} + {{ include('@WebProfiler/Profiler/table.html.twig', { data: requestserver }, with_context = false) }} @@ -287,7 +289,7 @@ {% if controller.class is defined -%} {%- if method|default(false) %}{{ method }}{% endif -%} {%- set link = controller.file|file_link(controller.line) %} - {%- if link %}{% else %}{% endif %} + {%- if link %}{% else %}{% endif %} {%- if route|default(false) -%} @{{ route }} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig index 3f70edb7d2e16..be77f891034c6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -99,7 +99,7 @@ {% if collector.events is empty %}
-

No timing events have been recorded. Are you sure that debugging is enabled in the kernel?

+

No timing events have been recorded. Check that symfony/stopwatch is installed and debugging enabled in the kernel.

{% else %} {{ block('panelContent') }} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig index 822323315e37d..ff1124bfdeb3d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig @@ -44,18 +44,22 @@ {%- endif %} - {% if request_collector and request_collector.forward|default(false) and request_collector.forward.controller.class is defined -%} - {%- set forward = request_collector.forward -%} - {%- set controller = forward.controller -%} + {% if request_collector and request_collector.forwardtoken -%} + {% set forward_profile = profile.childByToken(request_collector.forwardtoken) %} + {% set controller = forward_profile ? forward_profile.collector('request').controller : 'n/a' %}
{%- endif %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index e3a0d8454f5c8..2a2b3793fd845 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -421,34 +421,6 @@ display: none; } -/* Override the setting when the toolbar is on the top */ -{% if position == 'top' %} - .sf-minitoolbar { - border-bottom-left-radius: 4px; - border-top-left-radius: 0; - bottom: auto; - right: 0; - top: 0; - } - - .sf-toolbarreset { - bottom: auto; - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); - top: 0; - } - - .sf-toolbar-block .sf-toolbar-info { - bottom: auto; - top: 36px; - } -{% endif %} - -{% if not floatable %} - .sf-toolbarreset { - position: static; - } -{% endif %} - /* Responsive Design */ .sf-toolbar-icon .sf-toolbar-label, .sf-toolbar-icon .sf-toolbar-value { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig index 2dc3eaff9635d..8df2e844df4dd 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig @@ -1,18 +1,10 @@
{{ include('@WebProfiler/Profiler/base_js.html.twig') }} - {{ include('@WebProfiler/Profiler/toolbar.css.twig', { 'position': position, 'floatable': true }) }} + {{ include('@WebProfiler/Profiler/toolbar.css.twig') }} /*disableOriginalConstructor() ->getMock(); - $controller = new ProfilerController($urlGenerator, $profiler, $twig, array(), 'bottom', null, __DIR__.'/../..'); + $controller = new ProfilerController($urlGenerator, $profiler, $twig, array(), null, __DIR__.'/../..'); try { $response = $controller->openAction(Request::create('/_wdt/open', Request::METHOD_GET, array('file' => $path))); @@ -194,7 +194,7 @@ private function createController($profiler, $twig, $withCSP) if ($withCSP) { $nonceGenerator = $this->getMockBuilder('Symfony\Bundle\WebProfilerBundle\Csp\NonceGenerator')->getMock(); - return new ProfilerController($urlGenerator, $profiler, $twig, array(), 'bottom', new ContentSecurityPolicyHandler($nonceGenerator)); + return new ProfilerController($urlGenerator, $profiler, $twig, array(), new ContentSecurityPolicyHandler($nonceGenerator)); } return new ProfilerController($urlGenerator, $profiler, $twig, array()); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php index afbecf9b381e9..abd9c033d1bf7 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -29,36 +29,14 @@ public function testConfigTree($options, $results) $this->assertEquals($results, $config); } - /** - * @dataProvider getPositionConfig - * @group legacy - * @expectedDeprecation The "web_profiler.position" configuration key has been deprecated in Symfony 3.4 and it will be removed in 4.0. - */ - public function testPositionConfig($options, $results) - { - $processor = new Processor(); - $configuration = new Configuration(); - $config = $processor->processConfiguration($configuration, array($options)); - - $this->assertEquals($results, $config); - } - public function getDebugModes() { return array( - array(array(), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), - array(array('intercept_redirects' => true), array('intercept_redirects' => true, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), - array(array('intercept_redirects' => false), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), - array(array('toolbar' => true), array('intercept_redirects' => false, 'toolbar' => true, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), - array(array('excluded_ajax_paths' => 'test'), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => 'test')), - ); - } - - public function getPositionConfig() - { - return array( - array(array('position' => 'top'), array('intercept_redirects' => false, 'toolbar' => false, 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt', 'position' => 'top')), - array(array('position' => 'bottom'), array('intercept_redirects' => false, 'toolbar' => false, 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt', 'position' => 'bottom')), + array(array(), array('intercept_redirects' => false, 'toolbar' => false, 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('intercept_redirects' => true), array('intercept_redirects' => true, 'toolbar' => false, 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('intercept_redirects' => false), array('intercept_redirects' => false, 'toolbar' => false, 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('toolbar' => true), array('intercept_redirects' => false, 'toolbar' => true, 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('excluded_ajax_paths' => 'test'), array('intercept_redirects' => false, 'toolbar' => false, 'excluded_ajax_paths' => 'test')), ); } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php index 316bd13f2f6ed..4069cdd6c9268 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -32,7 +32,7 @@ public static function assertSaneContainer(Container $container, $message = '', { $errors = array(); foreach ($container->getServiceIds() as $id) { - if (in_array($id, $knownPrivates, true)) { // to be removed in 4.0 + if (in_array($id, $knownPrivates, true)) { // for BC with 3.4 continue; } try { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php index ec420107dfd05..f5d05ff883e73 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php @@ -236,7 +236,7 @@ public function testXDebugUrlHeader() $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); - $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, 'bottom', $urlGenerator); + $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, $urlGenerator); $listener->onKernelResponse($event); $this->assertEquals('http://mydomain.com/_profiler/xxxxxxxx', $response->headers->get('X-Debug-Token-Link')); @@ -257,7 +257,7 @@ public function testThrowingUrlGenerator() $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); - $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, 'bottom', $urlGenerator); + $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, $urlGenerator); $listener->onKernelResponse($event); $this->assertEquals('Exception: foo', $response->headers->get('X-Debug-Error')); @@ -278,7 +278,7 @@ public function testThrowingErrorCleanup() $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); - $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, 'bottom', $urlGenerator); + $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, $urlGenerator); $listener->onKernelResponse($event); $this->assertEquals('Exception: This multiline tabbed text should come out on a single plain line', $response->headers->get('X-Debug-Error')); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php index 7b8f9a8df51c0..ee0ba44fa74d9 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php @@ -79,28 +79,6 @@ public function testGetNameValidTemplate() $this->assertEquals('FooBundle:Collector:foo.html.twig', $this->templateManager->getName($profile, 'foo')); } - /** - * template should be loaded for 'foo' because other collectors are - * missing in profile or in profiler. - */ - public function testGetTemplates() - { - $profile = $this->mockProfile(); - $profile->expects($this->any()) - ->method('hasCollector') - ->will($this->returnCallback(array($this, 'profilerHasCallback'))); - - $this->profiler->expects($this->any()) - ->method('has') - ->withAnyParameters() - ->will($this->returnCallback(array($this, 'profileHasCollectorCallback'))); - - $result = $this->templateManager->getTemplates($profile); - $this->assertArrayHasKey('foo', $result); - $this->assertArrayNotHasKey('bar', $result); - $this->assertArrayNotHasKey('baz', $result); - } - public function profilerHasCallback($panel) { switch ($panel) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php index 61d0781542a5b..c714ff0642472 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\WebProfilerBundle\Twig; -use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Twig\Environment; @@ -26,11 +25,6 @@ */ class WebProfilerExtension extends ProfilerExtension { - /** - * @var ValueExporter - */ - private $valueExporter; - /** * @var HtmlDumper */ @@ -69,12 +63,8 @@ public function leave(Profile $profile) */ public function getFunctions() { - $profilerDump = function (Environment $env, $value, $maxDepth = 0) { - return $value instanceof Data ? $this->dumpData($env, $value, $maxDepth) : twig_escape_filter($env, $this->dumpValue($value)); - }; - return array( - new TwigFunction('profiler_dump', $profilerDump, array('is_safe' => array('html'), 'needs_environment' => true)), + new TwigFunction('profiler_dump', array($this, 'dumpData'), array('is_safe' => array('html'), 'needs_environment' => true)), new TwigFunction('profiler_dump_log', array($this, 'dumpLog'), array('is_safe' => array('html'), 'needs_environment' => true)), ); } @@ -111,20 +101,6 @@ public function dumpLog(Environment $env, $message, Data $context = null) return ''.strtr($message, $replacements).''; } - /** - * @deprecated since 3.2, to be removed in 4.0. Use the dumpData() method instead. - */ - public function dumpValue($value) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use the dumpData() method instead.', __METHOD__), E_USER_DEPRECATED); - - if (null === $this->valueExporter) { - $this->valueExporter = new ValueExporter(); - } - - return $this->valueExporter->exportValue($value); - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index c1efe11a26bb1..df801f033d592 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -16,25 +16,24 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/http-kernel": "~3.3|~4.0", - "symfony/polyfill-php70": "~1.0", - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/twig-bridge": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~3.3|~4.0", + "php": "^7.1.3", + "symfony/http-kernel": "~4.1", + "symfony/routing": "~3.4|~4.0", + "symfony/twig-bridge": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0", "twig/twig": "~1.34|~2.4" }, "require-dev": { "symfony/config": "~3.4|~4.0", - "symfony/console": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0" + "symfony/stopwatch": "~3.4|~4.0" }, "conflict": { "symfony/config": "<3.4", "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<3.3.1", - "symfony/var-dumper": "<3.3" + "symfony/event-dispatcher": "<3.4", + "symfony/var-dumper": "<3.4" }, "autoload": { "psr-4": { "Symfony\\Bundle\\WebProfilerBundle\\": "" }, @@ -45,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerCommand.php deleted file mode 100644 index 1df81a68a173b..0000000000000 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerCommand.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\WebServerBundle\Command; - -use Symfony\Component\Console\Command\Command; - -/** - * Base methods for commands related to a local web server. - * - * @author Christian Flothmann - * - * @internal - */ -abstract class ServerCommand extends Command -{ - /** - * {@inheritdoc} - */ - public function isEnabled() - { - return !defined('HHVM_VERSION') && parent::isEnabled(); - } -} diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerRunCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerRunCommand.php index 5c59a97662ef8..07f66294e2c16 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerRunCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerRunCommand.php @@ -13,6 +13,7 @@ use Symfony\Bundle\WebServerBundle\WebServer; use Symfony\Bundle\WebServerBundle\WebServerConfig; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; @@ -26,14 +27,14 @@ * * @author Michał Pipa */ -class ServerRunCommand extends ServerCommand +class ServerRunCommand extends Command { private $documentRoot; private $environment; protected static $defaultName = 'server:run'; - public function __construct($documentRoot = null, $environment = null) + public function __construct(string $documentRoot = null, string $environment = null) { $this->documentRoot = $documentRoot; $this->environment = $environment; @@ -89,12 +90,6 @@ protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); - // deprecated, logic to be removed in 4.0 - // this allows the commands to work out of the box with web/ and public/ - if ($this->documentRoot && !is_dir($this->documentRoot) && is_dir(dirname($this->documentRoot).'/web')) { - $this->documentRoot = dirname($this->documentRoot).'/web'; - } - if (null === $documentRoot = $input->getOption('docroot')) { if (!$this->documentRoot) { $io->error('The document root directory must be either passed as first argument of the constructor or through the "--docroot" input option.'); diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerStartCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerStartCommand.php index 09d368610ab00..664bce114b643 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerStartCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerStartCommand.php @@ -13,6 +13,7 @@ use Symfony\Bundle\WebServerBundle\WebServer; use Symfony\Bundle\WebServerBundle\WebServerConfig; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -26,14 +27,14 @@ * * @author Christian Flothmann */ -class ServerStartCommand extends ServerCommand +class ServerStartCommand extends Command { private $documentRoot; private $environment; protected static $defaultName = 'server:start'; - public function __construct($documentRoot = null, $environment = null) + public function __construct(string $documentRoot = null, string $environment = null) { $this->documentRoot = $documentRoot; $this->environment = $environment; @@ -102,12 +103,6 @@ protected function execute(InputInterface $input, OutputInterface $output) return 1; } - // deprecated, logic to be removed in 4.0 - // this allows the commands to work out of the box with web/ and public/ - if ($this->documentRoot && !is_dir($this->documentRoot) && is_dir(dirname($this->documentRoot).'/web')) { - $this->documentRoot = dirname($this->documentRoot).'/web'; - } - if (null === $documentRoot = $input->getOption('docroot')) { if (!$this->documentRoot) { $io->error('The document root directory must be either passed as first argument of the constructor or through the "docroot" input option.'); diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php index 6a01ebed79a3d..8a9a1e92c525b 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\WebServerBundle\Command; use Symfony\Bundle\WebServerBundle\WebServer; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -24,7 +25,7 @@ * * @author Christian Flothmann */ -class ServerStatusCommand extends ServerCommand +class ServerStatusCommand extends Command { protected static $defaultName = 'server:status'; diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerStopCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerStopCommand.php index f3f7bb60fe9ee..e63dce89d06d4 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerStopCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerStopCommand.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\WebServerBundle\Command; use Symfony\Bundle\WebServerBundle\WebServer; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; @@ -23,7 +24,7 @@ * * @author Christian Flothmann */ -class ServerStopCommand extends ServerCommand +class ServerStopCommand extends Command { protected static $defaultName = 'server:stop'; diff --git a/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php b/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php index 8b88eaecad654..8a46208eadef2 100644 --- a/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php +++ b/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php @@ -22,7 +22,7 @@ class WebServerConfig private $env; private $router; - public function __construct($documentRoot, $env, $address = null, $router = null) + public function __construct(string $documentRoot, string $env, string $address = null, string $router = null) { if (!is_dir($documentRoot)) { throw new \InvalidArgumentException(sprintf('The document root directory "%s" does not exist.', $documentRoot)); diff --git a/src/Symfony/Bundle/WebServerBundle/composer.json b/src/Symfony/Bundle/WebServerBundle/composer.json index 87272bba7b940..48d8299664697 100644 --- a/src/Symfony/Bundle/WebServerBundle/composer.json +++ b/src/Symfony/Bundle/WebServerBundle/composer.json @@ -16,12 +16,12 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "symfony/config": "~3.4|~4.0", "symfony/console": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/http-kernel": "~3.3|~4.0", - "symfony/process": "~3.3.14|^3.4.2|^4.0.2" + "symfony/http-kernel": "~3.4|~4.0", + "symfony/process": "^3.4.2|^4.0.2" }, "autoload": { "psr-4": { "Symfony\\Bundle\\WebServerBundle\\": "" }, @@ -36,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Asset/Context/RequestStackContext.php b/src/Symfony/Component/Asset/Context/RequestStackContext.php index c18f833264832..b63f4663fc0bf 100644 --- a/src/Symfony/Component/Asset/Context/RequestStackContext.php +++ b/src/Symfony/Component/Asset/Context/RequestStackContext.php @@ -24,12 +24,7 @@ class RequestStackContext implements ContextInterface private $basePath; private $secure; - /** - * @param RequestStack $requestStack - * @param string $basePath - * @param bool $secure - */ - public function __construct(RequestStack $requestStack, $basePath = '', $secure = false) + public function __construct(RequestStack $requestStack, string $basePath = '', bool $secure = false) { $this->requestStack = $requestStack; $this->basePath = $basePath; diff --git a/src/Symfony/Component/Asset/PathPackage.php b/src/Symfony/Component/Asset/PathPackage.php index 31cbc5df507f2..1fd9926b31ead 100644 --- a/src/Symfony/Component/Asset/PathPackage.php +++ b/src/Symfony/Component/Asset/PathPackage.php @@ -33,7 +33,7 @@ class PathPackage extends Package * @param VersionStrategyInterface $versionStrategy The version strategy * @param ContextInterface|null $context The context */ - public function __construct($basePath, VersionStrategyInterface $versionStrategy, ContextInterface $context = null) + public function __construct(string $basePath, VersionStrategyInterface $versionStrategy, ContextInterface $context = null) { parent::__construct($versionStrategy, $context); diff --git a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php index 378ad54346d7f..7bbfa90786ef9 100644 --- a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php +++ b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php @@ -30,7 +30,7 @@ class JsonManifestVersionStrategy implements VersionStrategyInterface /** * @param string $manifestPath Absolute path to the manifest file */ - public function __construct($manifestPath) + public function __construct(string $manifestPath) { $this->manifestPath = $manifestPath; } diff --git a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php index 857cf9432bfa3..e7ce0ec218976 100644 --- a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php +++ b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php @@ -25,7 +25,7 @@ class StaticVersionStrategy implements VersionStrategyInterface * @param string $version Version number * @param string $format Url format */ - public function __construct($version, $format = null) + public function __construct(string $version, string $format = null) { $this->version = $version; $this->format = $format ?: '%s?%s'; diff --git a/src/Symfony/Component/Asset/composer.json b/src/Symfony/Component/Asset/composer.json index ec5dd156fb9e6..bbd74fe239346 100644 --- a/src/Symfony/Component/Asset/composer.json +++ b/src/Symfony/Component/Asset/composer.json @@ -16,14 +16,14 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "suggest": { "symfony/http-foundation": "" }, "require-dev": { - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "~2.8|~3.0|~4.0" + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Asset\\": "" }, @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/BrowserKit/Client.php b/src/Symfony/Component/BrowserKit/Client.php index 63d4be77cff58..14eff1510c239 100644 --- a/src/Symfony/Component/BrowserKit/Client.php +++ b/src/Symfony/Component/BrowserKit/Client.php @@ -272,7 +272,7 @@ public function submit(Form $form, array $values = array()) * * @return Crawler */ - public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true) + public function request(string $method, string $uri, array $parameters = array(), array $files = array(), array $server = array(), string $content = null, bool $changeHistory = true) { if ($this->isMainRequest) { $this->redirectCount = 0; diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php index e6159da74dc0b..a2201c9ce13df 100644 --- a/src/Symfony/Component/BrowserKit/Cookie.php +++ b/src/Symfony/Component/BrowserKit/Cookie.php @@ -53,7 +53,7 @@ class Cookie * @param bool $httponly The cookie httponly flag * @param bool $encodedValue Whether the value is encoded or not */ - public function __construct($name, $value, $expires = null, $path = null, $domain = '', $secure = false, $httponly = true, $encodedValue = false) + public function __construct(string $name, ?string $value, string $expires = null, string $path = null, string $domain = '', bool $secure = false, bool $httponly = true, bool $encodedValue = false) { if ($encodedValue) { $this->value = urldecode($value); @@ -65,8 +65,8 @@ public function __construct($name, $value, $expires = null, $path = null, $domai $this->name = $name; $this->path = empty($path) ? '/' : $path; $this->domain = $domain; - $this->secure = (bool) $secure; - $this->httponly = (bool) $httponly; + $this->secure = $secure; + $this->httponly = $httponly; if (null !== $expires) { $timestampAsDateTime = \DateTime::createFromFormat('U', $expires); diff --git a/src/Symfony/Component/BrowserKit/Request.php b/src/Symfony/Component/BrowserKit/Request.php index d78868e539022..d06868660b2d7 100644 --- a/src/Symfony/Component/BrowserKit/Request.php +++ b/src/Symfony/Component/BrowserKit/Request.php @@ -33,7 +33,7 @@ class Request * @param array $server An array of server parameters * @param string $content The raw body data */ - public function __construct($uri, $method, array $parameters = array(), array $files = array(), array $cookies = array(), array $server = array(), $content = null) + public function __construct(string $uri, string $method, array $parameters = array(), array $files = array(), array $cookies = array(), array $server = array(), string $content = null) { $this->uri = $uri; $this->method = $method; diff --git a/src/Symfony/Component/BrowserKit/Response.php b/src/Symfony/Component/BrowserKit/Response.php index ba4e416bf8048..16b2f5bcfe254 100644 --- a/src/Symfony/Component/BrowserKit/Response.php +++ b/src/Symfony/Component/BrowserKit/Response.php @@ -28,7 +28,7 @@ class Response * @param int $status The response status code * @param array $headers An array of headers */ - public function __construct($content = '', $status = 200, array $headers = array()) + public function __construct(string $content = '', int $status = 200, array $headers = array()) { $this->content = $content; $this->status = $status; diff --git a/src/Symfony/Component/BrowserKit/composer.json b/src/Symfony/Component/BrowserKit/composer.json index 82c0a8ec35014..6c226bed79c55 100644 --- a/src/Symfony/Component/BrowserKit/composer.json +++ b/src/Symfony/Component/BrowserKit/composer.json @@ -16,12 +16,12 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/dom-crawler": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/dom-crawler": "~3.4|~4.0" }, "require-dev": { - "symfony/process": "~2.8|~3.0|~4.0", - "symfony/css-selector": "~2.8|~3.0|~4.0" + "symfony/process": "~3.4|~4.0", + "symfony/css-selector": "~3.4|~4.0" }, "suggest": { "symfony/process": "" @@ -35,7 +35,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index 727fc84c98473..758d3a8ec2e24 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -33,11 +33,7 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface private $createCacheItem; private $mergeByLifetime; - /** - * @param string $namespace - * @param int $defaultLifetime - */ - protected function __construct($namespace = '', $defaultLifetime = 0) + protected function __construct(string $namespace = '', int $defaultLifetime = 0) { $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':'; if (null !== $this->maxIdLength && strlen($namespace) > $this->maxIdLength - 24) { diff --git a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php index 50554ed688309..7db3956588026 100644 --- a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php @@ -18,13 +18,9 @@ class ApcuAdapter extends AbstractAdapter use ApcuTrait; /** - * @param string $namespace - * @param int $defaultLifetime - * @param string|null $version - * * @throws CacheException if APCu is not enabled */ - public function __construct($namespace = '', $defaultLifetime = 0, $version = null) + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null) { $this->init($namespace, $defaultLifetime, $version); } diff --git a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php index 2118e9c6ffa57..fee7ed6d906d5 100644 --- a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php @@ -30,7 +30,7 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, Resettable * @param int $defaultLifetime * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise */ - public function __construct($defaultLifetime = 0, $storeSerialized = true) + public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true) { $this->storeSerialized = $storeSerialized; $this->createCacheItem = \Closure::bind( diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php index 6bdf6b2d54f14..910df0fd38ed5 100644 --- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php @@ -36,7 +36,7 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items * @param int $maxLifetime The max lifetime of items propagated from lower adapters to upper ones */ - public function __construct(array $adapters, $maxLifetime = 0) + public function __construct(array $adapters, int $maxLifetime = 0) { if (!$adapters) { throw new InvalidArgumentException('At least one adapter must be specified.'); diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php index 972d2b41545ef..75ae4cb7015c8 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php @@ -18,12 +18,7 @@ class DoctrineAdapter extends AbstractAdapter { use DoctrineTrait; - /** - * @param CacheProvider $provider - * @param string $namespace - * @param int $defaultLifetime - */ - public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0) + public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0) { parent::__construct('', $defaultLifetime); $this->provider = $provider; diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php index d071964ec2c5c..a08888368347a 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php @@ -18,12 +18,7 @@ class FilesystemAdapter extends AbstractAdapter implements PruneableInterface { use FilesystemTrait; - /** - * @param string $namespace - * @param int $defaultLifetime - * @param string|null $directory - */ - public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) { parent::__construct('', $defaultLifetime); $this->init($namespace, $directory); diff --git a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php index 5637141a77a89..65ab9eda864bf 100644 --- a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php @@ -29,7 +29,7 @@ class MemcachedAdapter extends AbstractAdapter * * Using a MemcachedAdapter as a pure items store is fine. */ - public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0) + public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0) { $this->init($client, $namespace, $defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php index f1a89bea0acec..e74b39b448fd3 100644 --- a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php @@ -36,16 +36,13 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface * * db_password: The password when lazy-connect [default: ''] * * db_connection_options: An array of driver-specific connection options [default: array()] * - * @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null - * @param string $namespace - * @param int $defaultLifetime - * @param array $options An associative array of options + * @param \PDO|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null * * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION * @throws InvalidArgumentException When namespace contains invalid characters */ - public function __construct($connOrDsn, $namespace = '', $defaultLifetime = 0, array $options = array()) + public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = array()) { $this->init($connOrDsn, $namespace, $defaultLifetime, $options); } diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php index d3b9d77d45bb1..96a41fe235d93 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php @@ -36,7 +36,7 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl * @param string $file The PHP file were values are cached * @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit */ - public function __construct($file, AdapterInterface $fallbackPool) + public function __construct(string $file, AdapterInterface $fallbackPool) { $this->file = $file; $this->pool = $fallbackPool; @@ -56,19 +56,17 @@ function ($key, $value, $isHit) { } /** - * This adapter should only be used on PHP 7.0+ to take advantage of how PHP - * stores arrays in its latest versions. This factory method decorates the given - * fallback pool with this adapter only if the current PHP version is supported. + * This adapter takes advantage of how PHP stores arrays in its latest versions. * * @param string $file The PHP file were values are cached - * @param CacheItemPoolInterface $fallbackPool Fallback for old PHP versions or opcache disabled + * @param CacheItemPoolInterface $fallbackPool Fallback when opcache is disabled * * @return CacheItemPoolInterface */ public static function create($file, CacheItemPoolInterface $fallbackPool) { - // Shared memory is available in PHP 7.0+ with OPCache enabled and in HHVM - if ((\PHP_VERSION_ID >= 70000 && ini_get('opcache.enable')) || defined('HHVM_VERSION')) { + // Shared memory is available in PHP 7.0+ with OPCache enabled + if (ini_get('opcache.enable')) { if (!$fallbackPool instanceof AdapterInterface) { $fallbackPool = new ProxyAdapter($fallbackPool); } @@ -226,10 +224,7 @@ public function commit() return $this->pool->commit(); } - /** - * @return \Generator - */ - private function generateItems(array $keys) + private function generateItems(array $keys): \Generator { $f = $this->createCacheItem; $fallbackKeys = array(); diff --git a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php index 528d9c01fb304..41879df266571 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php @@ -20,13 +20,9 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface use PhpFilesTrait; /** - * @param string $namespace - * @param int $defaultLifetime - * @param string|null $directory - * * @throws CacheException if OPcache is not enabled */ - public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) { if (!static::isSupported()) { throw new CacheException('OPcache is not enabled'); diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index 82c95c5b04582..da286dbf173f7 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -30,12 +30,7 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn private $createCacheItem; private $poolHash; - /** - * @param CacheItemPoolInterface $pool - * @param string $namespace - * @param int $defaultLifetime - */ - public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0) + public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0) { $this->pool = $pool; $this->poolHash = $poolHash = spl_object_hash($pool); diff --git a/src/Symfony/Component/Cache/Adapter/RedisAdapter.php b/src/Symfony/Component/Cache/Adapter/RedisAdapter.php index c1e17997fb557..0bb76fcdd4ba6 100644 --- a/src/Symfony/Component/Cache/Adapter/RedisAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/RedisAdapter.php @@ -22,7 +22,7 @@ class RedisAdapter extends AbstractAdapter * @param string $namespace The default namespace * @param int $defaultLifetime The default lifetime */ - public function __construct($redisClient, $namespace = '', $defaultLifetime = 0) + public function __construct($redisClient, string $namespace = '', int $defaultLifetime = 0) { $this->init($redisClient, $namespace, $defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php b/src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php index 24db5d504ab68..2e6d03a1f0b41 100644 --- a/src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php @@ -25,7 +25,7 @@ class SimpleCacheAdapter extends AbstractAdapter implements PruneableInterface, private $miss; - public function __construct(CacheInterface $pool, $namespace = '', $defaultLifetime = 0) + public function __construct(CacheInterface $pool, string $namespace = '', int $defaultLifetime = 0) { parent::__construct($namespace, $defaultLifetime); diff --git a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php index ceef45aa0b185..e4b08790ade4c 100644 --- a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php +++ b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php @@ -104,10 +104,7 @@ public function getCalls() return $this->data['instances']['calls']; } - /** - * @return array - */ - private function calculateStatistics() + private function calculateStatistics(): array { $statistics = array(); foreach ($this->data['instances']['calls'] as $name => $calls) { @@ -159,10 +156,7 @@ private function calculateStatistics() return $statistics; } - /** - * @return array - */ - private function calculateTotalStatistics() + private function calculateTotalStatistics(): array { $statistics = $this->getStatistics(); $totals = array( diff --git a/src/Symfony/Component/Cache/Simple/AbstractCache.php b/src/Symfony/Component/Cache/Simple/AbstractCache.php index e666effaf93f9..51d2d32ba6ed6 100644 --- a/src/Symfony/Component/Cache/Simple/AbstractCache.php +++ b/src/Symfony/Component/Cache/Simple/AbstractCache.php @@ -31,13 +31,9 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re private $defaultLifetime; - /** - * @param string $namespace - * @param int $defaultLifetime - */ - protected function __construct($namespace = '', $defaultLifetime = 0) + protected function __construct(string $namespace = '', int $defaultLifetime = 0) { - $this->defaultLifetime = max(0, (int) $defaultLifetime); + $this->defaultLifetime = max(0, $defaultLifetime); $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':'; if (null !== $this->maxIdLength && strlen($namespace) > $this->maxIdLength - 24) { throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, strlen($namespace), $namespace)); diff --git a/src/Symfony/Component/Cache/Simple/ApcuCache.php b/src/Symfony/Component/Cache/Simple/ApcuCache.php index e583b44341dce..0877c394bbda3 100644 --- a/src/Symfony/Component/Cache/Simple/ApcuCache.php +++ b/src/Symfony/Component/Cache/Simple/ApcuCache.php @@ -17,12 +17,7 @@ class ApcuCache extends AbstractCache { use ApcuTrait; - /** - * @param string $namespace - * @param int $defaultLifetime - * @param string|null $version - */ - public function __construct($namespace = '', $defaultLifetime = 0, $version = null) + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null) { $this->init($namespace, $defaultLifetime, $version); } diff --git a/src/Symfony/Component/Cache/Simple/ArrayCache.php b/src/Symfony/Component/Cache/Simple/ArrayCache.php index 8d027cd2a3722..452b42853ef2c 100644 --- a/src/Symfony/Component/Cache/Simple/ArrayCache.php +++ b/src/Symfony/Component/Cache/Simple/ArrayCache.php @@ -34,9 +34,9 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte * @param int $defaultLifetime * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise */ - public function __construct($defaultLifetime = 0, $storeSerialized = true) + public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true) { - $this->defaultLifetime = (int) $defaultLifetime; + $this->defaultLifetime = $defaultLifetime; $this->storeSerialized = $storeSerialized; } diff --git a/src/Symfony/Component/Cache/Simple/ChainCache.php b/src/Symfony/Component/Cache/Simple/ChainCache.php index 9d0c75870eb7e..64cca2e91c246 100644 --- a/src/Symfony/Component/Cache/Simple/ChainCache.php +++ b/src/Symfony/Component/Cache/Simple/ChainCache.php @@ -35,7 +35,7 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf * @param CacheInterface[] $caches The ordered list of caches used to fetch cached items * @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones */ - public function __construct(array $caches, $defaultLifetime = 0) + public function __construct(array $caches, int $defaultLifetime = 0) { if (!$caches) { throw new InvalidArgumentException('At least one cache must be specified.'); @@ -50,7 +50,7 @@ public function __construct(array $caches, $defaultLifetime = 0) $this->miss = new \stdClass(); $this->caches = array_values($caches); $this->cacheCount = count($this->caches); - $this->defaultLifetime = 0 < $defaultLifetime ? (int) $defaultLifetime : null; + $this->defaultLifetime = 0 < $defaultLifetime ? $defaultLifetime : null; } /** diff --git a/src/Symfony/Component/Cache/Simple/DoctrineCache.php b/src/Symfony/Component/Cache/Simple/DoctrineCache.php index 00f0b9c6fc326..0ba701d7cd9a6 100644 --- a/src/Symfony/Component/Cache/Simple/DoctrineCache.php +++ b/src/Symfony/Component/Cache/Simple/DoctrineCache.php @@ -18,12 +18,7 @@ class DoctrineCache extends AbstractCache { use DoctrineTrait; - /** - * @param CacheProvider $provider - * @param string $namespace - * @param int $defaultLifetime - */ - public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0) + public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0) { parent::__construct('', $defaultLifetime); $this->provider = $provider; diff --git a/src/Symfony/Component/Cache/Simple/FilesystemCache.php b/src/Symfony/Component/Cache/Simple/FilesystemCache.php index ccd579534288e..37b3d3fa611b3 100644 --- a/src/Symfony/Component/Cache/Simple/FilesystemCache.php +++ b/src/Symfony/Component/Cache/Simple/FilesystemCache.php @@ -18,12 +18,7 @@ class FilesystemCache extends AbstractCache implements PruneableInterface { use FilesystemTrait; - /** - * @param string $namespace - * @param int $defaultLifetime - * @param string|null $directory - */ - public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) { parent::__construct('', $defaultLifetime); $this->init($namespace, $directory); diff --git a/src/Symfony/Component/Cache/Simple/MemcachedCache.php b/src/Symfony/Component/Cache/Simple/MemcachedCache.php index 7717740622c5e..0ff521b9c31fb 100644 --- a/src/Symfony/Component/Cache/Simple/MemcachedCache.php +++ b/src/Symfony/Component/Cache/Simple/MemcachedCache.php @@ -19,12 +19,7 @@ class MemcachedCache extends AbstractCache protected $maxIdLength = 250; - /** - * @param \Memcached $client - * @param string $namespace - * @param int $defaultLifetime - */ - public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0) + public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0) { $this->init($client, $namespace, $defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Simple/PdoCache.php b/src/Symfony/Component/Cache/Simple/PdoCache.php index 931a3b1ff7dba..65b9879cd37c4 100644 --- a/src/Symfony/Component/Cache/Simple/PdoCache.php +++ b/src/Symfony/Component/Cache/Simple/PdoCache.php @@ -35,16 +35,13 @@ class PdoCache extends AbstractCache implements PruneableInterface * * db_password: The password when lazy-connect [default: ''] * * db_connection_options: An array of driver-specific connection options [default: array()] * - * @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null - * @param string $namespace - * @param int $defaultLifetime - * @param array $options An associative array of options + * @param \PDO|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null * * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION * @throws InvalidArgumentException When namespace contains invalid characters */ - public function __construct($connOrDsn, $namespace = '', $defaultLifetime = 0, array $options = array()) + public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = array()) { $this->init($connOrDsn, $namespace, $defaultLifetime, $options); } diff --git a/src/Symfony/Component/Cache/Simple/PhpArrayCache.php b/src/Symfony/Component/Cache/Simple/PhpArrayCache.php index 3db8a2ac1c044..0dba1f4895df7 100644 --- a/src/Symfony/Component/Cache/Simple/PhpArrayCache.php +++ b/src/Symfony/Component/Cache/Simple/PhpArrayCache.php @@ -32,7 +32,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt * @param string $file The PHP file were values are cached * @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit */ - public function __construct($file, CacheInterface $fallbackPool) + public function __construct(string $file, CacheInterface $fallbackPool) { $this->file = $file; $this->pool = $fallbackPool; @@ -40,9 +40,7 @@ public function __construct($file, CacheInterface $fallbackPool) } /** - * This adapter should only be used on PHP 7.0+ to take advantage of how PHP - * stores arrays in its latest versions. This factory method decorates the given - * fallback pool with this adapter only if the current PHP version is supported. + * This adapter takes advantage of how PHP stores arrays in its latest versions. * * @param string $file The PHP file were values are cached * @@ -50,8 +48,8 @@ public function __construct($file, CacheInterface $fallbackPool) */ public static function create($file, CacheInterface $fallbackPool) { - // Shared memory is available in PHP 7.0+ with OPCache enabled and in HHVM - if ((\PHP_VERSION_ID >= 70000 && ini_get('opcache.enable')) || defined('HHVM_VERSION')) { + // Shared memory is available in PHP 7.0+ with OPCache enabled + if (ini_get('opcache.enable')) { return new static($file, $fallbackPool); } diff --git a/src/Symfony/Component/Cache/Simple/PhpFilesCache.php b/src/Symfony/Component/Cache/Simple/PhpFilesCache.php index 9231c8cd39634..77239c32eda69 100644 --- a/src/Symfony/Component/Cache/Simple/PhpFilesCache.php +++ b/src/Symfony/Component/Cache/Simple/PhpFilesCache.php @@ -20,13 +20,9 @@ class PhpFilesCache extends AbstractCache implements PruneableInterface use PhpFilesTrait; /** - * @param string $namespace - * @param int $defaultLifetime - * @param string|null $directory - * * @throws CacheException if OPcache is not enabled */ - public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) { if (!static::isSupported()) { throw new CacheException('OPcache is not enabled'); diff --git a/src/Symfony/Component/Cache/Simple/RedisCache.php b/src/Symfony/Component/Cache/Simple/RedisCache.php index e82c0627e241d..45bb5ff799c8a 100644 --- a/src/Symfony/Component/Cache/Simple/RedisCache.php +++ b/src/Symfony/Component/Cache/Simple/RedisCache.php @@ -22,7 +22,7 @@ class RedisCache extends AbstractCache * @param string $namespace * @param int $defaultLifetime */ - public function __construct($redisClient, $namespace = '', $defaultLifetime = 0) + public function __construct($redisClient, string $namespace = '', int $defaultLifetime = 0) { $this->init($redisClient, $namespace, $defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index a0b74b4746bb4..018d149467482 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -21,10 +21,6 @@ protected function setUp() { parent::setUp(); - if (!array_key_exists('testDeferredSaveWithoutCommit', $this->skippedTests) && defined('HHVM_VERSION')) { - $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Destructors are called late on HHVM.'; - } - if (!array_key_exists('testPrune', $this->skippedTests) && !$this->createCachePool() instanceof PruneableInterface) { $this->skippedTests['testPrune'] = 'Not a pruneable cache pool.'; } diff --git a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php index 48b972824c2c2..34cca260f0cfe 100644 --- a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php @@ -28,10 +28,6 @@ protected function setUp() public static function validKeys() { - if (defined('HHVM_VERSION')) { - return parent::validKeys(); - } - return array_merge(parent::validKeys(), array(array("a\0b"))); } diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index e13cd9675160f..9005eeb1462e2 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -20,11 +20,10 @@ "psr/simple-cache-implementation": "1.0" }, "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "psr/cache": "~1.0", "psr/log": "~1.0", - "psr/simple-cache": "^1.0", - "symfony/polyfill-apcu": "~1.1" + "psr/simple-cache": "^1.0" }, "require-dev": { "cache/integration-tests": "dev-master", @@ -33,7 +32,7 @@ "predis/predis": "~1.0" }, "conflict": { - "symfony/var-dumper": "<3.3" + "symfony/var-dumper": "<3.4" }, "autoload": { "psr-4": { "Symfony\\Component\\Cache\\": "" }, @@ -44,7 +43,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/ClassLoader/.gitignore b/src/Symfony/Component/ClassLoader/.gitignore deleted file mode 100644 index c49a5d8df5c65..0000000000000 --- a/src/Symfony/Component/ClassLoader/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/src/Symfony/Component/ClassLoader/ApcClassLoader.php b/src/Symfony/Component/ClassLoader/ApcClassLoader.php deleted file mode 100644 index 04d5835003b12..0000000000000 --- a/src/Symfony/Component/ClassLoader/ApcClassLoader.php +++ /dev/null @@ -1,143 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -@trigger_error('The '.__NAMESPACE__.'\ApcClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); - -/** - * ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3. - * - * It expects an object implementing a findFile method to find the file. This - * allows using it as a wrapper around the other loaders of the component (the - * ClassLoader for instance) but also around any other autoloaders following - * this convention (the Composer one for instance). - * - * // with a Symfony autoloader - * use Symfony\Component\ClassLoader\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // or with a Composer autoloader - * use Composer\Autoload\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * $cachedLoader = new ApcClassLoader('my_prefix', $loader); - * - * // activate the cached autoloader - * $cachedLoader->register(); - * - * // eventually deactivate the non-cached loader if it was registered previously - * // to be sure to use the cached one. - * $loader->unregister(); - * - * @author Fabien Potencier - * @author Kris Wallsmith - * - * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. - */ -class ApcClassLoader -{ - private $prefix; - - /** - * A class loader object that implements the findFile() method. - * - * @var object - */ - protected $decorated; - - /** - * @param string $prefix The APC namespace prefix to use - * @param object $decorated A class loader object that implements the findFile() method - * - * @throws \RuntimeException - * @throws \InvalidArgumentException - */ - public function __construct($prefix, $decorated) - { - if (!function_exists('apcu_fetch')) { - throw new \RuntimeException('Unable to use ApcClassLoader as APC is not installed.'); - } - - if (!method_exists($decorated, 'findFile')) { - throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); - } - - $this->prefix = $prefix; - $this->decorated = $decorated; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds a file by class name while caching lookups to APC. - * - * @param string $class A class name to resolve to file - * - * @return string|null - */ - public function findFile($class) - { - $file = apcu_fetch($this->prefix.$class, $success); - - if (!$success) { - apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null); - } - - return $file; - } - - /** - * Passes through all unknown calls onto the decorated object. - */ - public function __call($method, $args) - { - return call_user_func_array(array($this->decorated, $method), $args); - } -} diff --git a/src/Symfony/Component/ClassLoader/CHANGELOG.md b/src/Symfony/Component/ClassLoader/CHANGELOG.md deleted file mode 100644 index 203ec9eede785..0000000000000 --- a/src/Symfony/Component/ClassLoader/CHANGELOG.md +++ /dev/null @@ -1,41 +0,0 @@ -CHANGELOG -========= - -3.3.0 ------ - - * deprecated the component: use Composer instead - -3.0.0 ------ - - * The DebugClassLoader class has been removed - * The DebugUniversalClassLoader class has been removed - * The UniversalClassLoader class has been removed - * The ApcUniversalClassLoader class has been removed - -2.4.0 ------ - - * deprecated the UniversalClassLoader in favor of the ClassLoader class instead - * deprecated the ApcUniversalClassLoader in favor of the ApcClassLoader class instead - * deprecated the DebugUniversalClassLoader in favor of the DebugClassLoader class from the Debug component - * deprecated the DebugClassLoader as it has been moved to the Debug component instead - -2.3.0 ------ - - * added a WinCacheClassLoader for WinCache - -2.1.0 ------ - - * added a DebugClassLoader able to wrap any autoloader providing a findFile - method - * added a new ApcClassLoader and XcacheClassLoader using composition to wrap - other loaders - * added a new ClassLoader which does not distinguish between namespaced and - pear-like classes (as the PEAR convention is a subset of PSR-0) and - supports using Composer's namespace maps - * added a class map generator - * added support for loading globally-installed PEAR packages diff --git a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php deleted file mode 100644 index 0ea9cef66064e..0000000000000 --- a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php +++ /dev/null @@ -1,448 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -if (\PHP_VERSION_ID >= 70000) { - @trigger_error('The '.__NAMESPACE__.'\ClassCollectionLoader class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); -} - -/** - * ClassCollectionLoader. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class ClassCollectionLoader -{ - private static $loaded; - private static $seen; - private static $useTokenizer = true; - - /** - * Loads a list of classes and caches them in one big file. - * - * @param array $classes An array of classes to load - * @param string $cacheDir A cache directory - * @param string $name The cache name prefix - * @param bool $autoReload Whether to flush the cache when the cache is stale or not - * @param bool $adaptive Whether to remove already declared classes or not - * @param string $extension File extension of the resulting file - * - * @throws \InvalidArgumentException When class can't be loaded - */ - public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php') - { - // each $name can only be loaded once per PHP process - if (isset(self::$loaded[$name])) { - return; - } - - self::$loaded[$name] = true; - - if ($adaptive) { - $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); - - // don't include already declared classes - $classes = array_diff($classes, $declared); - - // the cache is different depending on which classes are already declared - $name = $name.'-'.substr(hash('sha256', implode('|', $classes)), 0, 5); - } - - $classes = array_unique($classes); - - // cache the core classes - if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { - throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); - } - $cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.DIRECTORY_SEPARATOR); - $cache = $cacheDir.'/'.$name.$extension; - - // auto-reload - $reload = false; - if ($autoReload) { - $metadata = $cache.'.meta'; - if (!is_file($metadata) || !is_file($cache)) { - $reload = true; - } else { - $time = filemtime($cache); - $meta = unserialize(file_get_contents($metadata)); - - sort($meta[1]); - sort($classes); - - if ($meta[1] != $classes) { - $reload = true; - } else { - foreach ($meta[0] as $resource) { - if (!is_file($resource) || filemtime($resource) > $time) { - $reload = true; - - break; - } - } - } - } - } - - if (!$reload && file_exists($cache)) { - require_once $cache; - - return; - } - if (!$adaptive) { - $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); - } - - $files = self::inline($classes, $cache, $declared); - - if ($autoReload) { - // save the resources - self::writeCacheFile($metadata, serialize(array(array_values($files), $classes))); - } - } - - /** - * Generates a file where classes and their parents are inlined. - * - * @param array $classes An array of classes to load - * @param string $cache The file where classes are inlined - * @param array $excluded An array of classes that won't be inlined - * - * @return array The source map of inlined classes, with classes as keys and files as values - * - * @throws \RuntimeException When class can't be loaded - */ - public static function inline($classes, $cache, array $excluded) - { - $declared = array(); - foreach (self::getOrderedClasses($excluded) as $class) { - $declared[$class->getName()] = true; - } - - // cache the core classes - $cacheDir = dirname($cache); - if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { - throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); - } - - $spacesRegex = '(?:\s*+(?:(?:\#|//)[^\n]*+\n|/\*(?:(?getName()])) { - continue; - } - $declared[$class->getName()] = true; - - $files[$class->getName()] = $file = $class->getFileName(); - $c = file_get_contents($file); - - if (preg_match($dontInlineRegex, $c)) { - $file = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $file)); - - for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) { - if ($file[$i] !== $cacheDir[$i]) { - break; - } - } - if (1 >= $i) { - $file = var_export(implode('/', $file), true); - } else { - $file = array_slice($file, $i); - $file = str_repeat('../', count($cacheDir) - $i).implode('/', $file); - $file = '__DIR__.'.var_export('/'.$file, true); - } - - $c = "\nnamespace {require $file;}"; - } else { - $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', $c); - - // fakes namespace declaration for global code - if (!$class->inNamespace()) { - $c = "\nnamespace\n{\n".$c."\n}\n"; - } - - $c = self::fixNamespaceDeclarations('= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - unset($tokens, $rawChunk); - gc_mem_caches(); - } - - return $output; - } - - /** - * This method is only useful for testing. - */ - public static function enableTokenizer($bool) - { - self::$useTokenizer = (bool) $bool; - } - - /** - * Strips leading & trailing ws, multiple EOL, multiple ws. - * - * @param string $code Original PHP code - * - * @return string compressed code - */ - private static function compressCode($code) - { - return preg_replace( - array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'), - array('', '', "\n", ' '), - $code - ); - } - - /** - * Writes a cache file. - * - * @param string $file Filename - * @param string $content Temporary file content - * - * @throws \RuntimeException when a cache file cannot be written - */ - private static function writeCacheFile($file, $content) - { - $dir = dirname($file); - if (!is_writable($dir)) { - throw new \RuntimeException(sprintf('Cache directory "%s" is not writable.', $dir)); - } - - $tmpFile = tempnam($dir, basename($file)); - - if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { - @chmod($file, 0666 & ~umask()); - - return; - } - - throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file)); - } - - /** - * Gets an ordered array of passed classes including all their dependencies. - * - * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed) - * - * @throws \InvalidArgumentException When a class can't be loaded - */ - private static function getOrderedClasses(array $classes) - { - $map = array(); - self::$seen = array(); - foreach ($classes as $class) { - try { - $reflectionClass = new \ReflectionClass($class); - } catch (\ReflectionException $e) { - throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); - } - - $map = array_merge($map, self::getClassHierarchy($reflectionClass)); - } - - return $map; - } - - private static function getClassHierarchy(\ReflectionClass $class) - { - if (isset(self::$seen[$class->getName()])) { - return array(); - } - - self::$seen[$class->getName()] = true; - - $classes = array($class); - $parent = $class; - while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) { - self::$seen[$parent->getName()] = true; - - array_unshift($classes, $parent); - } - - $traits = array(); - - foreach ($classes as $c) { - foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) { - if ($trait !== $c) { - $traits[] = $trait; - } - } - } - - return array_merge(self::getInterfaces($class), $traits, $classes); - } - - private static function getInterfaces(\ReflectionClass $class) - { - $classes = array(); - - foreach ($class->getInterfaces() as $interface) { - $classes = array_merge($classes, self::getInterfaces($interface)); - } - - if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) { - self::$seen[$class->getName()] = true; - - $classes[] = $class; - } - - return $classes; - } - - private static function computeTraitDeps(\ReflectionClass $class) - { - $traits = $class->getTraits(); - $deps = array($class->getName() => $traits); - while ($trait = array_pop($traits)) { - if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) { - self::$seen[$trait->getName()] = true; - $traitDeps = $trait->getTraits(); - $deps[$trait->getName()] = $traitDeps; - $traits = array_merge($traits, $traitDeps); - } - } - - return $deps; - } - - /** - * Dependencies resolution. - * - * This function does not check for circular dependencies as it should never - * occur with PHP traits. - * - * @param array $tree The dependency tree - * @param \ReflectionClass $node The node - * @param \ArrayObject $resolved An array of already resolved dependencies - * @param \ArrayObject $unresolved An array of dependencies to be resolved - * - * @return \ArrayObject The dependencies for the given node - * - * @throws \RuntimeException if a circular dependency is detected - */ - private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null) - { - if (null === $resolved) { - $resolved = new \ArrayObject(); - } - if (null === $unresolved) { - $unresolved = new \ArrayObject(); - } - $nodeName = $node->getName(); - - if (isset($tree[$nodeName])) { - $unresolved[$nodeName] = $node; - foreach ($tree[$nodeName] as $dependency) { - if (!$resolved->offsetExists($dependency->getName())) { - self::resolveDependencies($tree, $dependency, $resolved, $unresolved); - } - } - $resolved[$nodeName] = $node; - unset($unresolved[$nodeName]); - } - - return $resolved; - } -} diff --git a/src/Symfony/Component/ClassLoader/ClassLoader.php b/src/Symfony/Component/ClassLoader/ClassLoader.php deleted file mode 100644 index 4afcf405854df..0000000000000 --- a/src/Symfony/Component/ClassLoader/ClassLoader.php +++ /dev/null @@ -1,207 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -@trigger_error('The '.__NAMESPACE__.'\ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); - -/** - * ClassLoader implements an PSR-0 class loader. - * - * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md - * - * $loader = new ClassLoader(); - * - * // register classes with namespaces - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (e.g. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * @author Fabien Potencier - * @author Jordi Boggiano - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class ClassLoader -{ - private $prefixes = array(); - private $fallbackDirs = array(); - private $useIncludePath = false; - - /** - * Returns prefixes. - * - * @return array - */ - public function getPrefixes() - { - return $this->prefixes; - } - - /** - * Returns fallback directories. - * - * @return array - */ - public function getFallbackDirs() - { - return $this->fallbackDirs; - } - - /** - * Adds prefixes. - * - * @param array $prefixes Prefixes to add - */ - public function addPrefixes(array $prefixes) - { - foreach ($prefixes as $prefix => $path) { - $this->addPrefix($prefix, $path); - } - } - - /** - * Registers a set of classes. - * - * @param string $prefix The classes prefix - * @param array|string $paths The location(s) of the classes - */ - public function addPrefix($prefix, $paths) - { - if (!$prefix) { - foreach ((array) $paths as $path) { - $this->fallbackDirs[] = $path; - } - - return; - } - if (isset($this->prefixes[$prefix])) { - if (is_array($paths)) { - $this->prefixes[$prefix] = array_unique(array_merge( - $this->prefixes[$prefix], - $paths - )); - } elseif (!in_array($paths, $this->prefixes[$prefix])) { - $this->prefixes[$prefix][] = $paths; - } - } else { - $this->prefixes[$prefix] = array_unique((array) $paths); - } - } - - /** - * Turns on searching the include for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = (bool) $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|null The path, if found - */ - public function findFile($class) - { - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)).DIRECTORY_SEPARATOR; - $className = substr($class, $pos + 1); - } else { - // PEAR-like class name - $classPath = null; - $className = $class; - } - - $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; - - foreach ($this->prefixes as $prefix => $dirs) { - if ($class === strstr($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) { - return $dir.DIRECTORY_SEPARATOR.$classPath; - } - } - } - } - - foreach ($this->fallbackDirs as $dir) { - if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) { - return $dir.DIRECTORY_SEPARATOR.$classPath; - } - } - - if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { - return $file; - } - } -} diff --git a/src/Symfony/Component/ClassLoader/ClassMapGenerator.php b/src/Symfony/Component/ClassLoader/ClassMapGenerator.php deleted file mode 100644 index a9351367ebb6e..0000000000000 --- a/src/Symfony/Component/ClassLoader/ClassMapGenerator.php +++ /dev/null @@ -1,160 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -@trigger_error('The '.__NAMESPACE__.'\ClassMapGenerator class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); - -/** - * ClassMapGenerator. - * - * @author Gyula Sallai - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class ClassMapGenerator -{ - /** - * Generate a class map file. - * - * @param array|string $dirs Directories or a single path to search in - * @param string $file The name of the class map file - */ - public static function dump($dirs, $file) - { - $dirs = (array) $dirs; - $maps = array(); - - foreach ($dirs as $dir) { - $maps = array_merge($maps, static::createMap($dir)); - } - - file_put_contents($file, sprintf('isFile()) { - continue; - } - - $path = $file->getRealPath() ?: $file->getPathname(); - - if ('php' !== pathinfo($path, PATHINFO_EXTENSION)) { - continue; - } - - $classes = self::findClasses($path); - - if (\PHP_VERSION_ID >= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - gc_mem_caches(); - } - - foreach ($classes as $class) { - $map[$class] = $path; - } - } - - return $map; - } - - /** - * Extract the classes in the given file. - * - * @param string $path The file to check - * - * @return array The found classes - */ - private static function findClasses($path) - { - $contents = file_get_contents($path); - $tokens = token_get_all($contents); - - $classes = array(); - - $namespace = ''; - for ($i = 0; isset($tokens[$i]); ++$i) { - $token = $tokens[$i]; - - if (!isset($token[1])) { - continue; - } - - $class = ''; - - switch ($token[0]) { - case T_NAMESPACE: - $namespace = ''; - // If there is a namespace, extract it - while (isset($tokens[++$i][1])) { - if (in_array($tokens[$i][0], array(T_STRING, T_NS_SEPARATOR))) { - $namespace .= $tokens[$i][1]; - } - } - $namespace .= '\\'; - break; - case T_CLASS: - case T_INTERFACE: - case T_TRAIT: - // Skip usage of ::class constant - $isClassConstant = false; - for ($j = $i - 1; $j > 0; --$j) { - if (!isset($tokens[$j][1])) { - break; - } - - if (T_DOUBLE_COLON === $tokens[$j][0]) { - $isClassConstant = true; - break; - } elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) { - break; - } - } - - if ($isClassConstant) { - break; - } - - // Find the classname - while (isset($tokens[++$i][1])) { - $t = $tokens[$i]; - if (T_STRING === $t[0]) { - $class .= $t[1]; - } elseif ('' !== $class && T_WHITESPACE === $t[0]) { - break; - } - } - - $classes[] = ltrim($namespace.$class, '\\'); - break; - default: - break; - } - } - - return $classes; - } -} diff --git a/src/Symfony/Component/ClassLoader/LICENSE b/src/Symfony/Component/ClassLoader/LICENSE deleted file mode 100644 index 21d7fb9e2f29b..0000000000000 --- a/src/Symfony/Component/ClassLoader/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2018 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/src/Symfony/Component/ClassLoader/MapClassLoader.php b/src/Symfony/Component/ClassLoader/MapClassLoader.php deleted file mode 100644 index 7b0dbba43e213..0000000000000 --- a/src/Symfony/Component/ClassLoader/MapClassLoader.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -@trigger_error('The '.__NAMESPACE__.'\MapClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); - -/** - * A class loader that uses a mapping file to look up paths. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class MapClassLoader -{ - private $map = array(); - - /** - * @param array $map A map where keys are classes and values the absolute file path - */ - public function __construct(array $map) - { - $this->map = $map; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - */ - public function loadClass($class) - { - if (isset($this->map[$class])) { - require $this->map[$class]; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|null The path, if found - */ - public function findFile($class) - { - if (isset($this->map[$class])) { - return $this->map[$class]; - } - } -} diff --git a/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php b/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php deleted file mode 100644 index d1a6522ef836b..0000000000000 --- a/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php +++ /dev/null @@ -1,94 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -@trigger_error('The '.__NAMESPACE__.'\Psr4ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); - -/** - * A PSR-4 compatible class loader. - * - * See http://www.php-fig.org/psr/psr-4/ - * - * @author Alexander M. Turek - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class Psr4ClassLoader -{ - private $prefixes = array(); - - /** - * @param string $prefix - * @param string $baseDir - */ - public function addPrefix($prefix, $baseDir) - { - $prefix = trim($prefix, '\\').'\\'; - $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; - $this->prefixes[] = array($prefix, $baseDir); - } - - /** - * @param string $class - * - * @return string|null - */ - public function findFile($class) - { - $class = ltrim($class, '\\'); - - foreach ($this->prefixes as list($currentPrefix, $currentBaseDir)) { - if (0 === strpos($class, $currentPrefix)) { - $classWithoutPrefix = substr($class, strlen($currentPrefix)); - $file = $currentBaseDir.str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php'; - if (file_exists($file)) { - return $file; - } - } - } - } - - /** - * @param string $class - * - * @return bool - */ - public function loadClass($class) - { - $file = $this->findFile($class); - if (null !== $file) { - require $file; - - return true; - } - - return false; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Removes this instance from the registered autoloaders. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } -} diff --git a/src/Symfony/Component/ClassLoader/README.md b/src/Symfony/Component/ClassLoader/README.md deleted file mode 100644 index d61992b6a80e7..0000000000000 --- a/src/Symfony/Component/ClassLoader/README.md +++ /dev/null @@ -1,14 +0,0 @@ -ClassLoader Component -===================== - -The ClassLoader component provides tools to autoload your classes and cache -their locations for performance. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/class_loader/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/ClassLoader/Tests/ApcClassLoaderTest.php b/src/Symfony/Component/ClassLoader/Tests/ApcClassLoaderTest.php deleted file mode 100644 index bd02f546bbb6c..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/ApcClassLoaderTest.php +++ /dev/null @@ -1,200 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\ClassLoader\ApcClassLoader; -use Symfony\Component\ClassLoader\ClassLoader; - -/** - * @group legacy - */ -class ApcClassLoaderTest extends TestCase -{ - protected function setUp() - { - if (!(ini_get('apc.enabled') && ini_get('apc.enable_cli'))) { - $this->markTestSkipped('The apc extension is not enabled.'); - } else { - apcu_clear_cache(); - } - } - - protected function tearDown() - { - if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) { - apcu_clear_cache(); - } - } - - public function testConstructor() - { - $loader = new ClassLoader(); - $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - - $loader = new ApcClassLoader('test.prefix.', $loader); - - $this->assertEquals($loader->findFile('\Apc\Namespaced\FooBar'), apcu_fetch('test.prefix.\Apc\Namespaced\FooBar'), '__construct() takes a prefix as its first argument'); - } - - /** - * @dataProvider getLoadClassTests - */ - public function testLoadClass($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - - $loader = new ApcClassLoader('test.prefix.', $loader); - $loader->loadClass($testClassName); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassTests() - { - return array( - array('\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'), - array('Apc_Pearlike_Foo', 'Apc_Pearlike_Foo', '->loadClass() loads Apc_Pearlike_Foo class'), - ); - } - - /** - * @dataProvider getLoadClassFromFallbackTests - */ - public function testLoadClassFromFallback($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback')); - - $loader = new ApcClassLoader('test.prefix.fallback', $loader); - $loader->loadClass($testClassName); - - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassFromFallbackTests() - { - return array( - array('\\Apc\\Namespaced\\Baz', 'Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'), - array('Apc_Pearlike_Baz', 'Apc_Pearlike_Baz', '->loadClass() loads Apc_Pearlike_Baz class'), - array('\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'), - array('Apc_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'), - ); - } - - /** - * @dataProvider getLoadClassNamespaceCollisionTests - */ - public function testLoadClassNamespaceCollision($namespaces, $className, $message) - { - $loader = new ClassLoader(); - $loader->addPrefixes($namespaces); - - $loader = new ApcClassLoader('test.prefix.collision.', $loader); - $loader->loadClass($className); - - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassNamespaceCollisionTests() - { - return array( - array( - array( - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - ), - 'Apc\NamespaceCollision\A\Foo', - '->loadClass() loads NamespaceCollision\A\Foo from alpha.', - ), - array( - array( - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - ), - 'Apc\NamespaceCollision\A\Bar', - '->loadClass() loads NamespaceCollision\A\Bar from alpha.', - ), - array( - array( - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - ), - 'Apc\NamespaceCollision\A\B\Foo', - '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', - ), - array( - array( - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - ), - 'Apc\NamespaceCollision\A\B\Bar', - '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', - ), - ); - } - - /** - * @dataProvider getLoadClassPrefixCollisionTests - */ - public function testLoadClassPrefixCollision($prefixes, $className, $message) - { - $loader = new ClassLoader(); - $loader->addPrefixes($prefixes); - - $loader = new ApcClassLoader('test.prefix.collision.', $loader); - $loader->loadClass($className); - - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassPrefixCollisionTests() - { - return array( - array( - array( - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - ), - 'ApcPrefixCollision_A_Foo', - '->loadClass() loads ApcPrefixCollision_A_Foo from alpha.', - ), - array( - array( - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - ), - 'ApcPrefixCollision_A_Bar', - '->loadClass() loads ApcPrefixCollision_A_Bar from alpha.', - ), - array( - array( - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - ), - 'ApcPrefixCollision_A_B_Foo', - '->loadClass() loads ApcPrefixCollision_A_B_Foo from beta.', - ), - array( - array( - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - ), - 'ApcPrefixCollision_A_B_Bar', - '->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.', - ), - ); - } -} diff --git a/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php b/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php deleted file mode 100644 index da34082bea864..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php +++ /dev/null @@ -1,319 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\ClassLoader\ClassCollectionLoader; -use Symfony\Component\ClassLoader\Tests\Fixtures\DeclaredClass; -use Symfony\Component\ClassLoader\Tests\Fixtures\WarmedClass; - -require_once __DIR__.'/Fixtures/ClassesWithParents/GInterface.php'; -require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php'; -require_once __DIR__.'/Fixtures/ClassesWithParents/B.php'; -require_once __DIR__.'/Fixtures/ClassesWithParents/A.php'; - -/** - * @group legacy - */ -class ClassCollectionLoaderTest extends TestCase -{ - public function testTraitDependencies() - { - require_once __DIR__.'/Fixtures/deps/traits.php'; - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, array('CTFoo')); - - $this->assertEquals( - array('TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'), - array_map(function ($class) { return $class->getName(); }, $ordered) - ); - - $ordered = $m->invoke(null, array('CTBar')); - - $this->assertEquals( - array('TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'), - array_map(function ($class) { return $class->getName(); }, $ordered) - ); - } - - /** - * @dataProvider getDifferentOrders - */ - public function testClassReordering(array $classes) - { - $expected = array( - 'ClassesWithParents\\GInterface', - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - ); - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, $classes); - - $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); - } - - public function getDifferentOrders() - { - return array( - array(array( - 'ClassesWithParents\\A', - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\GInterface', - 'ClassesWithParents\\B', - )), - array(array( - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - 'ClassesWithParents\\CInterface', - )), - array(array( - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - )), - array(array( - 'ClassesWithParents\\A', - )), - ); - } - - /** - * @dataProvider getDifferentOrdersForTraits - */ - public function testClassWithTraitsReordering(array $classes) - { - require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/D.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/E.php'; - - $expected = array( - 'ClassesWithParents\\GInterface', - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\ATrait', - 'ClassesWithParents\\BTrait', - 'ClassesWithParents\\CTrait', - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - 'ClassesWithParents\\D', - 'ClassesWithParents\\E', - ); - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, $classes); - - $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); - } - - public function getDifferentOrdersForTraits() - { - return array( - array(array( - 'ClassesWithParents\\E', - 'ClassesWithParents\\ATrait', - )), - array(array( - 'ClassesWithParents\\E', - )), - ); - } - - public function testFixClassWithTraitsOrdering() - { - require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/F.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/G.php'; - - $classes = array( - 'ClassesWithParents\\F', - 'ClassesWithParents\\G', - ); - - $expected = array( - 'ClassesWithParents\\CTrait', - 'ClassesWithParents\\F', - 'ClassesWithParents\\G', - ); - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, $classes); - - $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); - } - - /** - * @dataProvider getFixNamespaceDeclarationsData - */ - public function testFixNamespaceDeclarations($source, $expected) - { - $this->assertEquals('assertEquals('assertEquals(<<<'EOF' -namespace Namespaced -{ -class WithComments -{ -public static $loaded = true; -} -$string ='string should not be modified {$string}'; -$heredoc = (<<assertTrue(class_exists(WarmedClass::class, true)); - - @unlink($cache = sys_get_temp_dir().'/inline.php'); - - $classes = array(WarmedClass::class); - $excluded = array(DeclaredClass::class); - - ClassCollectionLoader::inline($classes, $cache, $excluded); - - $this->assertSame(<<<'EOTXT' - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\ClassLoader\ClassLoader; - -/** - * @group legacy - */ -class ClassLoaderTest extends TestCase -{ - public function testGetPrefixes() - { - $loader = new ClassLoader(); - $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Bar', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Bas', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertArrayNotHasKey('Foo1', $prefixes); - $this->assertArrayHasKey('Bar', $prefixes); - $this->assertArrayHasKey('Bas', $prefixes); - } - - public function testGetFallbackDirs() - { - $loader = new ClassLoader(); - $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $fallback_dirs = $loader->getFallbackDirs(); - $this->assertCount(2, $fallback_dirs); - } - - /** - * @dataProvider getLoadClassTests - */ - public function testLoadClass($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->loadClass($testClassName); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassTests() - { - return array( - array('\\Namespaced2\\Foo', 'Namespaced2\\Foo', '->loadClass() loads Namespaced2\Foo class'), - array('\\Pearlike2_Foo', 'Pearlike2_Foo', '->loadClass() loads Pearlike2_Foo class'), - ); - } - - /** - * @dataProvider getLoadNonexistentClassTests - */ - public function testLoadNonexistentClass($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->loadClass($testClassName); - $this->assertFalse(class_exists($className), $message); - } - - public function getLoadNonexistentClassTests() - { - return array( - array('\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'), - ); - } - - public function testAddPrefixSingle() - { - $loader = new ClassLoader(); - $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertCount(1, $prefixes['Foo']); - } - - public function testAddPrefixesSingle() - { - $loader = new ClassLoader(); - $loader->addPrefixes(array('Foo' => array('foo', 'foo'))); - $loader->addPrefixes(array('Foo' => array('foo'))); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true)); - } - - public function testAddPrefixMulti() - { - $loader = new ClassLoader(); - $loader->addPrefix('Foo', 'foo'); - $loader->addPrefix('Foo', 'bar'); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertCount(2, $prefixes['Foo']); - $this->assertContains('foo', $prefixes['Foo']); - $this->assertContains('bar', $prefixes['Foo']); - } - - public function testUseIncludePath() - { - $loader = new ClassLoader(); - $this->assertFalse($loader->getUseIncludePath()); - - $this->assertNull($loader->findFile('Foo')); - - $includePath = get_include_path(); - - $loader->setUseIncludePath(true); - $this->assertTrue($loader->getUseIncludePath()); - - set_include_path(__DIR__.'/Fixtures/includepath'.PATH_SEPARATOR.$includePath); - - $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'includepath'.DIRECTORY_SEPARATOR.'Foo.php', $loader->findFile('Foo')); - - set_include_path($includePath); - } - - /** - * @dataProvider getLoadClassFromFallbackTests - */ - public function testLoadClassFromFallback($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback')); - $loader->loadClass($testClassName); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassFromFallbackTests() - { - return array( - array('\\Namespaced2\\Baz', 'Namespaced2\\Baz', '->loadClass() loads Namespaced2\Baz class'), - array('\\Pearlike2_Baz', 'Pearlike2_Baz', '->loadClass() loads Pearlike2_Baz class'), - array('\\Namespaced2\\FooBar', 'Namespaced2\\FooBar', '->loadClass() loads Namespaced2\Baz class from fallback dir'), - array('\\Pearlike2_FooBar', 'Pearlike2_FooBar', '->loadClass() loads Pearlike2_Baz class from fallback dir'), - ); - } - - /** - * @dataProvider getLoadClassNamespaceCollisionTests - */ - public function testLoadClassNamespaceCollision($namespaces, $className, $message) - { - $loader = new ClassLoader(); - $loader->addPrefixes($namespaces); - - $loader->loadClass($className); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassNamespaceCollisionTests() - { - return array( - array( - array( - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'NamespaceCollision\C\Foo', - '->loadClass() loads NamespaceCollision\C\Foo from alpha.', - ), - array( - array( - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'NamespaceCollision\C\Bar', - '->loadClass() loads NamespaceCollision\C\Bar from alpha.', - ), - array( - array( - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'NamespaceCollision\C\B\Foo', - '->loadClass() loads NamespaceCollision\C\B\Foo from beta.', - ), - array( - array( - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'NamespaceCollision\C\B\Bar', - '->loadClass() loads NamespaceCollision\C\B\Bar from beta.', - ), - array( - array( - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'PrefixCollision_C_Foo', - '->loadClass() loads PrefixCollision_C_Foo from alpha.', - ), - array( - array( - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'PrefixCollision_C_Bar', - '->loadClass() loads PrefixCollision_C_Bar from alpha.', - ), - array( - array( - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'PrefixCollision_C_B_Foo', - '->loadClass() loads PrefixCollision_C_B_Foo from beta.', - ), - array( - array( - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'PrefixCollision_C_B_Bar', - '->loadClass() loads PrefixCollision_C_B_Bar from beta.', - ), - ); - } -} diff --git a/src/Symfony/Component/ClassLoader/Tests/ClassMapGeneratorTest.php b/src/Symfony/Component/ClassLoader/Tests/ClassMapGeneratorTest.php deleted file mode 100644 index 69b1e61594e35..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/ClassMapGeneratorTest.php +++ /dev/null @@ -1,151 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\ClassLoader\ClassMapGenerator; - -/** - * @group legacy - */ -class ClassMapGeneratorTest extends TestCase -{ - /** - * @var string|null - */ - private $workspace = null; - - public function prepare_workspace() - { - $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); - mkdir($this->workspace, 0777, true); - $this->workspace = realpath($this->workspace); - } - - /** - * @param string $file - */ - private function clean($file) - { - if (is_dir($file) && !is_link($file)) { - $dir = new \FilesystemIterator($file); - foreach ($dir as $childFile) { - $this->clean($childFile); - } - - rmdir($file); - } else { - unlink($file); - } - } - - /** - * @dataProvider getTestCreateMapTests - */ - public function testDump($directory) - { - $this->prepare_workspace(); - - $file = $this->workspace.'/file'; - - $generator = new ClassMapGenerator(); - $generator->dump($directory, $file); - $this->assertFileExists($file); - - $this->clean($this->workspace); - } - - /** - * @dataProvider getTestCreateMapTests - */ - public function testCreateMap($directory, $expected) - { - $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory)); - } - - public function getTestCreateMapTests() - { - $data = array( - array(__DIR__.'/Fixtures/Namespaced', array( - 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', - 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', - 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', - 'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php', - 'Namespaced\\WithStrictTypes' => realpath(__DIR__).'/Fixtures/Namespaced/WithStrictTypes.php', - 'Namespaced\\WithHaltCompiler' => realpath(__DIR__).'/Fixtures/Namespaced/WithHaltCompiler.php', - 'Namespaced\\WithDirMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithDirMagic.php', - 'Namespaced\\WithFileMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithFileMagic.php', - )), - array(__DIR__.'/Fixtures/beta/NamespaceCollision', array( - 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', - 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', - 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', - 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', - )), - array(__DIR__.'/Fixtures/Pearlike', array( - 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', - 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', - 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', - 'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php', - )), - array(__DIR__.'/Fixtures/classmap', array( - 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', - 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', - 'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', - 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', - 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', - )), - array(__DIR__.'/Fixtures/php5.4', array( - 'TFoo' => __DIR__.'/Fixtures/php5.4/traits.php', - 'CFoo' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\TBar' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\IBar' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\TFooBar' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php', - )), - array(__DIR__.'/Fixtures/php5.5', array( - 'ClassCons\\Foo' => __DIR__.'/Fixtures/php5.5/class_cons.php', - )), - ); - - return $data; - } - - public function testCreateMapFinderSupport() - { - $finder = new \Symfony\Component\Finder\Finder(); - $finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision'); - - $this->assertEqualsNormalized(array( - 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', - 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', - 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', - 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', - ), ClassMapGenerator::createMap($finder)); - } - - protected function assertEqualsNormalized($expected, $actual, $message = null) - { - foreach ($expected as $ns => $path) { - $expected[$ns] = str_replace('\\', '/', $path); - } - foreach ($actual as $ns => $path) { - $actual[$ns] = str_replace('\\', '/', $path); - } - $this->assertEquals($expected, $actual, $message); - } -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Bar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Bar.php deleted file mode 100644 index 4259f1451e2c9..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Bar.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class Bar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Baz.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Baz.php deleted file mode 100644 index 3ddb595e25164..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Baz.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class Baz -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Foo.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Foo.php deleted file mode 100644 index cf0a4b741fd9f..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class Foo -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/FooBar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/FooBar.php deleted file mode 100644 index bbbc81515a80f..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/FooBar.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class FooBar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Pearlike/Bar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Pearlike/Bar.php deleted file mode 100644 index e774cb9bfbbae..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Pearlike/Bar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A; - -class Bar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php deleted file mode 100644 index 184a1b1daf159..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A; - -class Foo -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php deleted file mode 100644 index 3892f70683deb..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A\B; - -class Bar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php deleted file mode 100644 index 450eeb50b9e34..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A\B; - -class Foo -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php deleted file mode 100644 index 96f2f76c6f94d..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class FooBar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/A.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/A.php deleted file mode 100644 index b0f9425950f44..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/A.php +++ /dev/null @@ -1,7 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class Bar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Baz.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Baz.php deleted file mode 100644 index 0b0bbd057c44a..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Baz.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class Baz -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Foo.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Foo.php deleted file mode 100644 index df5e1f4ce2ec3..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class Foo -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithComments.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithComments.php deleted file mode 100644 index 361e53de1c4ee..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithComments.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class WithComments -{ - /** @Boolean */ - public static $loaded = true; -} - -$string = 'string should not be modified {$string}'; - -$heredoc = (<< - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -class Pearlike_WithComments -{ - /** @Boolean */ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Pearlike2/Bar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Pearlike2/Bar.php deleted file mode 100644 index 7f5f7977308b7..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Pearlike2/Bar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A; - -class Bar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php deleted file mode 100644 index aee6a080dfb76..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A; - -class Foo -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php deleted file mode 100644 index c1b8dd65ddfa3..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php +++ /dev/null @@ -1,8 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A\B; - -class Bar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php deleted file mode 100644 index f5f2d727ef5e6..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A\B; - -class Foo -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php deleted file mode 100644 index 4bb03dc7fd65a..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php +++ /dev/null @@ -1,8 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ClassMap; - -class SomeClass extends SomeParent implements SomeInterface -{ -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeInterface.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeInterface.php deleted file mode 100644 index 1fe5e09aa1f50..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ClassMap; - -interface SomeInterface -{ -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeParent.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeParent.php deleted file mode 100644 index ce2f9fc6c478c..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeParent.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ClassMap; - -abstract class SomeParent -{ -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/multipleNs.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/multipleNs.php deleted file mode 100644 index c7cec646f5f25..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/multipleNs.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Foo\Bar; - -class A -{ -} -class B -{ -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/deps/traits.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/deps/traits.php deleted file mode 100644 index 82b30a6f9d0b9..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/deps/traits.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class FooBar -{ - public static $loaded = true; -} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/fallback/Namespaced2/FooBar.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/fallback/Namespaced2/FooBar.php deleted file mode 100644 index 1036d43590065..0000000000000 --- a/src/Symfony/Component/ClassLoader/Tests/Fixtures/fallback/Namespaced2/FooBar.php +++ /dev/null @@ -1,8 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\ClassLoader\Psr4ClassLoader; - -/** - * @group legacy - */ -class Psr4ClassLoaderTest extends TestCase -{ - /** - * @param string $className - * @dataProvider getLoadClassTests - */ - public function testLoadClass($className) - { - $loader = new Psr4ClassLoader(); - $loader->addPrefix( - 'Acme\\DemoLib', - __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'psr-4' - ); - $loader->loadClass($className); - $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className)); - } - - /** - * @return array - */ - public function getLoadClassTests() - { - return array( - array('Acme\\DemoLib\\Foo'), - array('Acme\\DemoLib\\Class_With_Underscores'), - array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'), - array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores'), - ); - } - - /** - * @param string $className - * @dataProvider getLoadNonexistentClassTests - */ - public function testLoadNonexistentClass($className) - { - $loader = new Psr4ClassLoader(); - $loader->addPrefix( - 'Acme\\DemoLib', - __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'psr-4' - ); - $loader->loadClass($className); - $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className)); - } - - /** - * @return array - */ - public function getLoadNonexistentClassTests() - { - return array( - array('Acme\\DemoLib\\I_Do_Not_Exist'), - array('UnknownVendor\\SomeLib\\I_Do_Not_Exist'), - ); - } -} diff --git a/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php b/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php deleted file mode 100644 index ee1f521ee7ce0..0000000000000 --- a/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php +++ /dev/null @@ -1,142 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -@trigger_error('The '.__NAMESPACE__.'\WinCacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); - -/** - * WinCacheClassLoader implements a wrapping autoloader cached in WinCache. - * - * It expects an object implementing a findFile method to find the file. This - * allow using it as a wrapper around the other loaders of the component (the - * ClassLoader for instance) but also around any other autoloaders following - * this convention (the Composer one for instance). - * - * // with a Symfony autoloader - * $loader = new ClassLoader(); - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // or with a Composer autoloader - * use Composer\Autoload\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * $cachedLoader = new WinCacheClassLoader('my_prefix', $loader); - * - * // activate the cached autoloader - * $cachedLoader->register(); - * - * // eventually deactivate the non-cached loader if it was registered previously - * // to be sure to use the cached one. - * $loader->unregister(); - * - * @author Fabien Potencier - * @author Kris Wallsmith - * @author Artem Ryzhkov - * - * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. - */ -class WinCacheClassLoader -{ - private $prefix; - - /** - * A class loader object that implements the findFile() method. - * - * @var object - */ - protected $decorated; - - /** - * @param string $prefix The WinCache namespace prefix to use - * @param object $decorated A class loader object that implements the findFile() method - * - * @throws \RuntimeException - * @throws \InvalidArgumentException - */ - public function __construct($prefix, $decorated) - { - if (!extension_loaded('wincache')) { - throw new \RuntimeException('Unable to use WinCacheClassLoader as WinCache is not enabled.'); - } - - if (!method_exists($decorated, 'findFile')) { - throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); - } - - $this->prefix = $prefix; - $this->decorated = $decorated; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds a file by class name while caching lookups to WinCache. - * - * @param string $class A class name to resolve to file - * - * @return string|null - */ - public function findFile($class) - { - $file = wincache_ucache_get($this->prefix.$class, $success); - - if (!$success) { - wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null, 0); - } - - return $file; - } - - /** - * Passes through all unknown calls onto the decorated object. - */ - public function __call($method, $args) - { - return call_user_func_array(array($this->decorated, $method), $args); - } -} diff --git a/src/Symfony/Component/ClassLoader/XcacheClassLoader.php b/src/Symfony/Component/ClassLoader/XcacheClassLoader.php deleted file mode 100644 index 9d30917973989..0000000000000 --- a/src/Symfony/Component/ClassLoader/XcacheClassLoader.php +++ /dev/null @@ -1,137 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -@trigger_error('The '.__NAMESPACE__.'\XcacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); - -/** - * XcacheClassLoader implements a wrapping autoloader cached in XCache for PHP 5.3. - * - * It expects an object implementing a findFile method to find the file. This - * allows using it as a wrapper around the other loaders of the component (the - * ClassLoader for instance) but also around any other autoloaders following - * this convention (the Composer one for instance). - * - * // with a Symfony autoloader - * $loader = new ClassLoader(); - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // or with a Composer autoloader - * use Composer\Autoload\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * $cachedLoader = new XcacheClassLoader('my_prefix', $loader); - * - * // activate the cached autoloader - * $cachedLoader->register(); - * - * // eventually deactivate the non-cached loader if it was registered previously - * // to be sure to use the cached one. - * $loader->unregister(); - * - * @author Fabien Potencier - * @author Kris Wallsmith - * @author Kim Hemsø Rasmussen - * - * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. - */ -class XcacheClassLoader -{ - private $prefix; - private $decorated; - - /** - * @param string $prefix The XCache namespace prefix to use - * @param object $decorated A class loader object that implements the findFile() method - * - * @throws \RuntimeException - * @throws \InvalidArgumentException - */ - public function __construct($prefix, $decorated) - { - if (!extension_loaded('xcache')) { - throw new \RuntimeException('Unable to use XcacheClassLoader as XCache is not enabled.'); - } - - if (!method_exists($decorated, 'findFile')) { - throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); - } - - $this->prefix = $prefix; - $this->decorated = $decorated; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds a file by class name while caching lookups to Xcache. - * - * @param string $class A class name to resolve to file - * - * @return string|null - */ - public function findFile($class) - { - if (xcache_isset($this->prefix.$class)) { - $file = xcache_get($this->prefix.$class); - } else { - $file = $this->decorated->findFile($class) ?: null; - xcache_set($this->prefix.$class, $file); - } - - return $file; - } - - /** - * Passes through all unknown calls onto the decorated object. - */ - public function __call($method, $args) - { - return call_user_func_array(array($this->decorated, $method), $args); - } -} diff --git a/src/Symfony/Component/ClassLoader/composer.json b/src/Symfony/Component/ClassLoader/composer.json deleted file mode 100644 index 97f150fbf1385..0000000000000 --- a/src/Symfony/Component/ClassLoader/composer.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "symfony/class-loader", - "type": "library", - "description": "Symfony ClassLoader Component", - "keywords": [], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "minimum-stability": "dev", - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "require-dev": { - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/polyfill-apcu": "~1.1" - }, - "suggest": { - "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\ClassLoader\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - } -} diff --git a/src/Symfony/Component/ClassLoader/phpunit.xml.dist b/src/Symfony/Component/ClassLoader/phpunit.xml.dist deleted file mode 100644 index 5158b22f27c88..0000000000000 --- a/src/Symfony/Component/ClassLoader/phpunit.xml.dist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index 7813ac53ede47..17319e2b0b3fe 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -1,6 +1,17 @@ CHANGELOG ========= +4.1.0 +----- + + * added `setPathSeparator` method to `NodeBuilder` class + * added third `$pathSeparator` constructor argument to `BaseNode` + +4.0.0 +----- + + * removed `ConfigCachePass` + 3.4.0 ----- diff --git a/src/Symfony/Component/Config/ConfigCache.php b/src/Symfony/Component/Config/ConfigCache.php index 591c89bc4ff02..1d00933c839ca 100644 --- a/src/Symfony/Component/Config/ConfigCache.php +++ b/src/Symfony/Component/Config/ConfigCache.php @@ -31,9 +31,9 @@ class ConfigCache extends ResourceCheckerConfigCache * @param string $file The absolute cache path * @param bool $debug Whether debugging is enabled or not */ - public function __construct($file, $debug) + public function __construct(string $file, bool $debug) { - $this->debug = (bool) $debug; + $this->debug = $debug; $checkers = array(); if (true === $this->debug) { diff --git a/src/Symfony/Component/Config/ConfigCacheFactory.php b/src/Symfony/Component/Config/ConfigCacheFactory.php index 06dbe6c2947ed..e43e1a190be13 100644 --- a/src/Symfony/Component/Config/ConfigCacheFactory.php +++ b/src/Symfony/Component/Config/ConfigCacheFactory.php @@ -27,7 +27,7 @@ class ConfigCacheFactory implements ConfigCacheFactoryInterface /** * @param bool $debug The debug flag to pass to ConfigCache */ - public function __construct($debug) + public function __construct(bool $debug) { $this->debug = $debug; } diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php index ec927e0a97344..b0a32c1b35da5 100644 --- a/src/Symfony/Component/Config/Definition/BaseNode.php +++ b/src/Symfony/Component/Config/Definition/BaseNode.php @@ -23,6 +23,8 @@ */ abstract class BaseNode implements NodeInterface { + const DEFAULT_PATH_SEPARATOR = '.'; + protected $name; protected $parent; protected $normalizationClosures = array(); @@ -32,21 +34,20 @@ abstract class BaseNode implements NodeInterface protected $deprecationMessage = null; protected $equivalentValues = array(); protected $attributes = array(); + protected $pathSeparator; /** - * @param string $name The name of the node - * @param NodeInterface $parent The parent of this node - * * @throws \InvalidArgumentException if the name contains a period */ - public function __construct($name, NodeInterface $parent = null) + public function __construct(?string $name, NodeInterface $parent = null, string $pathSeparator = self::DEFAULT_PATH_SEPARATOR) { - if (false !== strpos($name, '.')) { - throw new \InvalidArgumentException('The name must not contain ".".'); + if (false !== strpos($name, $pathSeparator)) { + throw new \InvalidArgumentException('The name must not contain "'.$pathSeparator.'".'); } $this->name = $name; $this->parent = $parent; + $this->pathSeparator = $pathSeparator; } public function setAttribute($key, $value) @@ -233,13 +234,11 @@ public function getName() */ public function getPath() { - $path = $this->name; - if (null !== $this->parent) { - $path = $this->parent->getPath().'.'.$path; + return $this->parent->getPath().$this->pathSeparator.$this->name; } - return $path; + return $this->name; } /** diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index 170b466984e95..c39bd0ad11ea2 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -39,7 +39,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition /** * {@inheritdoc} */ - public function __construct($name, NodeParentInterface $parent = null) + public function __construct(?string $name, NodeParentInterface $parent = null) { parent::__construct($name, $parent); @@ -405,7 +405,7 @@ protected function getNodeBuilder() protected function createNode() { if (null === $this->prototype) { - $node = new ArrayNode($this->name, $this->parent); + $node = new ArrayNode($this->name, $this->parent, $this->pathSeparator); $this->validateConcreteNode($node); @@ -416,7 +416,7 @@ protected function createNode() $node->addChild($child->getNode()); } } else { - $node = new PrototypedArrayNode($this->name, $this->parent); + $node = new PrototypedArrayNode($this->name, $this->parent, $this->pathSeparator); $this->validatePrototypeNode($node); @@ -424,11 +424,7 @@ protected function createNode() $node->setKeyAttribute($this->key, $this->removeKeyItem); } - if (false === $this->allowEmptyValue) { - @trigger_error(sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), E_USER_DEPRECATED); - } - - if (true === $this->atLeastOne) { + if (true === $this->atLeastOne || false === $this->allowEmptyValue) { $node->setMinNumberOfElements(1); } @@ -490,7 +486,7 @@ protected function validateConcreteNode(ArrayNode $node) } if (false === $this->allowEmptyValue) { - @trigger_error(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), E_USER_DEPRECATED); + throw new InvalidDefinitionException(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s"', $path)); } if (true === $this->atLeastOne) { @@ -547,4 +543,12 @@ protected function validatePrototypeNode(PrototypedArrayNode $node) } } } + + /** + * @return NodeDefinition[] + */ + public function getChildNodeDefinitions() + { + return $this->children; + } } diff --git a/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php index 28e56579ada52..d19324273bff5 100644 --- a/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php @@ -24,7 +24,7 @@ class BooleanNodeDefinition extends ScalarNodeDefinition /** * {@inheritdoc} */ - public function __construct($name, NodeParentInterface $parent = null) + public function __construct(?string $name, NodeParentInterface $parent = null) { parent::__construct($name, $parent); @@ -38,7 +38,7 @@ public function __construct($name, NodeParentInterface $parent = null) */ protected function instantiateNode() { - return new BooleanNode($this->name, $this->parent); + return new BooleanNode($this->name, $this->parent, $this->pathSeparator); } /** diff --git a/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php index 817906f507629..9a9c096e3dcd3 100644 --- a/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php @@ -51,6 +51,6 @@ protected function instantiateNode() throw new \RuntimeException('You must call ->values() on enum nodes.'); } - return new EnumNode($this->name, $this->parent, $this->values); + return new EnumNode($this->name, $this->parent, $this->values, $this->pathSeparator); } } diff --git a/src/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php index c0bed462bf385..7b74271ae498a 100644 --- a/src/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php @@ -27,6 +27,6 @@ class FloatNodeDefinition extends NumericNodeDefinition */ protected function instantiateNode() { - return new FloatNode($this->name, $this->parent, $this->min, $this->max); + return new FloatNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); } } diff --git a/src/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php index f6c3c147f3e6a..0472a9870d9dc 100644 --- a/src/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php @@ -27,6 +27,6 @@ class IntegerNodeDefinition extends NumericNodeDefinition */ protected function instantiateNode() { - return new IntegerNode($this->name, $this->parent, $this->min, $this->max); + return new IntegerNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); } } diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php index 4539b316bf96e..b29cc25156d06 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Config\Definition\Builder; +use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\Config\Definition\NodeInterface; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; @@ -33,14 +34,11 @@ abstract class NodeDefinition implements NodeParentInterface protected $nullEquivalent; protected $trueEquivalent = true; protected $falseEquivalent = false; + protected $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR; protected $parent; protected $attributes = array(); - /** - * @param string $name The name of the node - * @param NodeParentInterface|null $parent The parent - */ - public function __construct($name, NodeParentInterface $parent = null) + public function __construct(?string $name, NodeParentInterface $parent = null) { $this->parent = $parent; $this->name = $name; @@ -350,4 +348,28 @@ protected function normalization() * @throws InvalidDefinitionException When the definition is invalid */ abstract protected function createNode(); + + /** + * Set PathSeparator to use. + * + * @param string $separator + * + * @return $this + */ + public function setPathSeparator(string $separator) + { + if ($this instanceof ParentNodeDefinitionInterface) { + if (method_exists($this, 'getChildNodeDefinitions')) { + foreach ($this->getChildNodeDefinitions() as $child) { + $child->setPathSeparator($separator); + } + } else { + @trigger_error('Passing a ParentNodeDefinitionInterface without getChildNodeDefinitions() is deprecated since version 4.1 and will be removed in 5.0.', E_USER_DEPRECATED); + } + } + + $this->pathSeparator = $separator; + + return $this; + } } diff --git a/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php b/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php index 575495bb684db..fe0d253de2e7b 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php +++ b/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -15,9 +15,14 @@ * An interface that must be implemented by nodes which can have children. * * @author Victor Berchet + * + * @method NodeDefinition[] getChildNodeDefinitions() should be implemented since 4.1 */ interface ParentNodeDefinitionInterface { + /** + * @return NodeBuilder + */ public function children(); public function append(NodeDefinition $node); diff --git a/src/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php index 6170555ccf139..428f61290a063 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php @@ -27,6 +27,6 @@ class ScalarNodeDefinition extends VariableNodeDefinition */ protected function instantiateNode() { - return new ScalarNode($this->name, $this->parent); + return new ScalarNode($this->name, $this->parent, $this->pathSeparator); } } diff --git a/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php index 384477c501082..6da510ec1c2dd 100644 --- a/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php @@ -23,11 +23,6 @@ class TreeBuilder implements NodeParentInterface protected $tree; protected $root; - /** - * @deprecated since 3.4. To be removed in 4.0 - */ - protected $builder; - /** * Creates the root node. * @@ -55,13 +50,31 @@ public function root($name, $type = 'array', NodeBuilder $builder = null) */ public function buildTree() { - if (null === $this->root) { - throw new \RuntimeException('The configuration tree has no root node.'); - } + $this->assertTreeHasRootNode(); if (null !== $this->tree) { return $this->tree; } return $this->tree = $this->root->getNode(true); } + + public function setPathSeparator(string $separator) + { + $this->assertTreeHasRootNode(); + + // unset last built as changing path separator changes all nodes + $this->tree = null; + + $this->root->setPathSeparator($separator); + } + + /** + * @throws \RuntimeException if root node is not defined + */ + private function assertTreeHasRootNode() + { + if (null === $this->root) { + throw new \RuntimeException('The configuration tree has no root node.'); + } + } } diff --git a/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php index 26565e1771d84..39a564f4cdb76 100644 --- a/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php @@ -27,7 +27,7 @@ class VariableNodeDefinition extends NodeDefinition */ protected function instantiateNode() { - return new VariableNode($this->name, $this->parent); + return new VariableNode($this->name, $this->parent, $this->pathSeparator); } /** diff --git a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php index f78bc7c3a258b..b87acc93d4c7c 100644 --- a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php +++ b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php @@ -41,13 +41,7 @@ public function dumpNode(NodeInterface $node, $namespace = null) return $ref; } - /** - * @param NodeInterface $node - * @param int $depth - * @param bool $root If the node is the root node - * @param string $namespace The namespace of the node - */ - private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null) + private function writeNode(NodeInterface $node, int $depth = 0, bool $root = false, string $namespace = null) { $rootName = ($root ? 'config' : $node->getName()); $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); @@ -259,11 +253,8 @@ private function writeNode(NodeInterface $node, $depth = 0, $root = false, $name /** * Outputs a single config reference line. - * - * @param string $text - * @param int $indent */ - private function writeLine($text, $indent = 0) + private function writeLine(string $text, int $indent = 0) { $indent = strlen($text) + $indent; $format = '%'.$indent.'s'; @@ -275,10 +266,8 @@ private function writeLine($text, $indent = 0) * Renders the string conversion of the value. * * @param mixed $value - * - * @return string */ - private function writeValue($value) + private function writeValue($value): string { if ('%%%%not_defined%%%%' === $value) { return ''; diff --git a/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php index 5a0e76c5255a3..960d136231079 100644 --- a/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php +++ b/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php @@ -69,13 +69,7 @@ public function dumpNode(NodeInterface $node) return $ref; } - /** - * @param NodeInterface $node - * @param NodeInterface|null $parentNode - * @param int $depth - * @param bool $prototypedArray - */ - private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, $depth = 0, $prototypedArray = false) + private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, int $depth = 0, bool $prototypedArray = false) { $comments = array(); $default = ''; @@ -179,11 +173,8 @@ private function writeNode(NodeInterface $node, NodeInterface $parentNode = null /** * Outputs a single config reference line. - * - * @param string $text - * @param int $indent */ - private function writeLine($text, $indent = 0) + private function writeLine(string $text, int $indent = 0) { $indent = strlen($text) + $indent; $format = '%'.$indent.'s'; @@ -214,12 +205,7 @@ private function writeArray(array $array, $depth) } } - /** - * @param PrototypedArrayNode $node - * - * @return array - */ - private function getPrototypeChildren(PrototypedArrayNode $node) + private function getPrototypeChildren(PrototypedArrayNode $node): array { $prototype = $node->getPrototype(); $key = $node->getKeyAttribute(); diff --git a/src/Symfony/Component/Config/Definition/EnumNode.php b/src/Symfony/Component/Config/Definition/EnumNode.php index 9b4c4156e311e..72b806bc0b80a 100644 --- a/src/Symfony/Component/Config/Definition/EnumNode.php +++ b/src/Symfony/Component/Config/Definition/EnumNode.php @@ -22,14 +22,14 @@ class EnumNode extends ScalarNode { private $values; - public function __construct($name, NodeInterface $parent = null, array $values = array()) + public function __construct(?string $name, NodeInterface $parent = null, array $values = array(), string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR) { $values = array_unique($values); if (empty($values)) { throw new \InvalidArgumentException('$values must contain at least one element.'); } - parent::__construct($name, $parent); + parent::__construct($name, $parent, $pathSeparator); $this->values = $values; } diff --git a/src/Symfony/Component/Config/Definition/NumericNode.php b/src/Symfony/Component/Config/Definition/NumericNode.php index 439935e4559f8..19c96e8af764c 100644 --- a/src/Symfony/Component/Config/Definition/NumericNode.php +++ b/src/Symfony/Component/Config/Definition/NumericNode.php @@ -23,9 +23,9 @@ class NumericNode extends ScalarNode protected $min; protected $max; - public function __construct($name, NodeInterface $parent = null, $min = null, $max = null) + public function __construct(?string $name, NodeInterface $parent = null, $min = null, $max = null, string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR) { - parent::__construct($name, $parent); + parent::__construct($name, $parent, $pathSeparator); $this->min = $min; $this->max = $max; } diff --git a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php index 08f335a015f30..c576198b54089 100644 --- a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php +++ b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php @@ -371,11 +371,9 @@ protected function mergeValues($leftSide, $rightSide) * Now, the key becomes 'name001' and the child node becomes 'value001' and * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance. * - * @param string $key The key of the child node - * * @return mixed The prototype instance */ - private function getPrototypeForChild($key) + private function getPrototypeForChild(string $key) { $prototype = isset($this->valuePrototypes[$key]) ? $this->valuePrototypes[$key] : $this->prototype; $prototype->setName($key); diff --git a/src/Symfony/Component/Config/DependencyInjection/ConfigCachePass.php b/src/Symfony/Component/Config/DependencyInjection/ConfigCachePass.php deleted file mode 100644 index 128bf7e28cd21..0000000000000 --- a/src/Symfony/Component/Config/DependencyInjection/ConfigCachePass.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\DependencyInjection; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED); - -use Symfony\Component\DependencyInjection\Argument\IteratorArgument; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. - * - * @author Matthias Pigulla - * @author Benjamin Klotz - * - * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead. - */ -class ConfigCachePass implements CompilerPassInterface -{ - use PriorityTaggedServiceTrait; - - private $factoryServiceId; - private $resourceCheckerTag; - - public function __construct($factoryServiceId = 'config_cache_factory', $resourceCheckerTag = 'config_cache.resource_checker') - { - $this->factoryServiceId = $factoryServiceId; - $this->resourceCheckerTag = $resourceCheckerTag; - } - - public function process(ContainerBuilder $container) - { - $resourceCheckers = $this->findAndSortTaggedServices($this->resourceCheckerTag, $container); - - if (empty($resourceCheckers)) { - return; - } - - $container->getDefinition($this->factoryServiceId)->replaceArgument(0, new IteratorArgument($resourceCheckers)); - } -} diff --git a/src/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php b/src/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php index 6a3b01cfbe097..d75860ecf0086 100644 --- a/src/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php +++ b/src/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php @@ -18,7 +18,7 @@ */ class FileLoaderImportCircularReferenceException extends FileLoaderLoadException { - public function __construct(array $resources, $code = null, $previous = null) + public function __construct(array $resources, int $code = null, \Exception $previous = null) { $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]); diff --git a/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php b/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php index a19069a6aeb0e..62e56622f3d4a 100644 --- a/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php +++ b/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php @@ -25,7 +25,7 @@ class FileLoaderLoadException extends \Exception * @param \Exception $previous A previous exception * @param string $type The type of resource */ - public function __construct($resource, $sourceResource = null, $code = null, $previous = null, $type = null) + public function __construct(string $resource, string $sourceResource = null, int $code = null, \Exception $previous = null, string $type = null) { $message = ''; if ($previous) { diff --git a/src/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php b/src/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php index af764eb4718d8..09b71606732d7 100644 --- a/src/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php +++ b/src/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php @@ -20,7 +20,7 @@ class FileLocatorFileNotFoundException extends \InvalidArgumentException { private $paths; - public function __construct($message = '', $code = 0, $previous = null, array $paths = array()) + public function __construct(string $message = '', int $code = 0, \Exception $previous = null, array $paths = array()) { parent::__construct($message, $code, $previous); diff --git a/src/Symfony/Component/Config/FileLocator.php b/src/Symfony/Component/Config/FileLocator.php index f5593cbbe2550..a3e8fd1104377 100644 --- a/src/Symfony/Component/Config/FileLocator.php +++ b/src/Symfony/Component/Config/FileLocator.php @@ -23,7 +23,7 @@ class FileLocator implements FileLocatorInterface protected $paths; /** - * @param string|array $paths A path or an array of paths where to look for resources + * @param string|string[] $paths A path or an array of paths where to look for resources */ public function __construct($paths = array()) { diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php index 0dfd30ba988a8..c06171992aad6 100644 --- a/src/Symfony/Component/Config/Loader/FileLoader.php +++ b/src/Symfony/Component/Config/Loader/FileLoader.php @@ -93,7 +93,7 @@ public function import($resource, $type = null, $ignoreErrors = false, $sourceRe /** * @internal */ - protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false) + protected function glob(string $pattern, bool $recursive, &$resource = null, bool $ignoreErrors = false) { if (strlen($pattern) === $i = strcspn($pattern, '*?{[')) { $prefix = $pattern; @@ -127,7 +127,7 @@ protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = } } - private function doImport($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + private function doImport($resource, $type = null, bool $ignoreErrors = false, $sourceResource = null) { try { $loader = $this->resolve($resource, $type); diff --git a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php index e3fd095b6008d..a4dd74b1f9db5 100644 --- a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php +++ b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php @@ -32,12 +32,10 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ * @param string $resource The fully-qualified class name * @param bool|null $exists Boolean when the existency check has already been done */ - public function __construct($resource, $exists = null) + public function __construct(string $resource, bool $exists = null) { $this->resource = $resource; - if (null !== $exists) { - $this->exists = (bool) $exists; - } + $this->exists = $exists; } /** diff --git a/src/Symfony/Component/Config/Resource/DirectoryResource.php b/src/Symfony/Component/Config/Resource/DirectoryResource.php index b1786397d1865..b6d1f9a24a863 100644 --- a/src/Symfony/Component/Config/Resource/DirectoryResource.php +++ b/src/Symfony/Component/Config/Resource/DirectoryResource.php @@ -27,7 +27,7 @@ class DirectoryResource implements SelfCheckingResourceInterface, \Serializable * * @throws \InvalidArgumentException */ - public function __construct($resource, $pattern = null) + public function __construct(string $resource, string $pattern = null) { $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); $this->pattern = $pattern; diff --git a/src/Symfony/Component/Config/Resource/FileExistenceResource.php b/src/Symfony/Component/Config/Resource/FileExistenceResource.php index 6396ddd524852..a4eb8f354449e 100644 --- a/src/Symfony/Component/Config/Resource/FileExistenceResource.php +++ b/src/Symfony/Component/Config/Resource/FileExistenceResource.php @@ -28,9 +28,9 @@ class FileExistenceResource implements SelfCheckingResourceInterface, \Serializa /** * @param string $resource The file path to the resource */ - public function __construct($resource) + public function __construct(string $resource) { - $this->resource = (string) $resource; + $this->resource = $resource; $this->exists = file_exists($resource); } diff --git a/src/Symfony/Component/Config/Resource/FileResource.php b/src/Symfony/Component/Config/Resource/FileResource.php index 1d373a623acfc..78d552a5ab63b 100644 --- a/src/Symfony/Component/Config/Resource/FileResource.php +++ b/src/Symfony/Component/Config/Resource/FileResource.php @@ -30,7 +30,7 @@ class FileResource implements SelfCheckingResourceInterface, \Serializable * * @throws \InvalidArgumentException */ - public function __construct($resource) + public function __construct(string $resource) { $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index 1edd7cd22e44a..c188f80ce4eca 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -35,7 +35,7 @@ class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, * * @throws \InvalidArgumentException */ - public function __construct($prefix, $pattern, $recursive) + public function __construct(?string $prefix, string $pattern, bool $recursive) { $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false); $this->pattern = $pattern; diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php index 149789aa367da..c5b7abf8960e7 100644 --- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php +++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php @@ -25,7 +25,7 @@ class ReflectionClassResource implements SelfCheckingResourceInterface, \Seriali private $excludedVendors = array(); private $hash; - public function __construct(\ReflectionClass $classReflector, $excludedVendors = array()) + public function __construct(\ReflectionClass $classReflector, array $excludedVendors = array()) { $this->className = $classReflector->name; $this->classReflector = $classReflector; @@ -138,21 +138,14 @@ private function generateSignature(\ReflectionClass $class) } } - if (defined('HHVM_VERSION')) { - foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { - // workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762 - yield preg_replace('/^ @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name)); - } - } else { - foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { - yield preg_replace('/^ @@.*/m', '', $m); + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + yield preg_replace('/^ @@.*/m', '', $m); - $defaults = array(); - foreach ($m->getParameters() as $p) { - $defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null; - } - yield print_r($defaults, true); + $defaults = array(); + foreach ($m->getParameters() as $p) { + $defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null; } + yield print_r($defaults, true); } if ($class->isSubclassOf(EventSubscriberInterface::class)) { @@ -166,31 +159,3 @@ private function generateSignature(\ReflectionClass $class) } } } - -/** - * @internal - */ -class ReflectionMethodHhvmWrapper extends \ReflectionMethod -{ - public function getParameters() - { - $params = array(); - - foreach (parent::getParameters() as $i => $p) { - $params[] = new ReflectionParameterHhvmWrapper(array($this->class, $this->name), $i); - } - - return $params; - } -} - -/** - * @internal - */ -class ReflectionParameterHhvmWrapper extends \ReflectionParameter -{ - public function getDefaultValue() - { - return array($this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null); - } -} diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php index 0a79b84c1c7b3..ebacb7a4c0be9 100644 --- a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php +++ b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php @@ -37,7 +37,7 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface * @param string $file The absolute cache path * @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check */ - public function __construct($file, $resourceCheckers = array()) + public function __construct(string $file, iterable $resourceCheckers = array()) { $this->file = $file; $this->resourceCheckers = $resourceCheckers; diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php b/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php index 2493f34663e75..845eabdee8764 100644 --- a/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php +++ b/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php @@ -24,7 +24,7 @@ class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface /** * @param iterable|ResourceCheckerInterface[] $resourceCheckers */ - public function __construct($resourceCheckers = array()) + public function __construct(iterable $resourceCheckers = array()) { $this->resourceCheckers = $resourceCheckers; } diff --git a/src/Symfony/Component/Config/Tests/Definition/BaseNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/BaseNodeTest.php new file mode 100644 index 0000000000000..a606bf407d69b --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Definition/BaseNodeTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\BaseNode; +use Symfony\Component\Config\Definition\NodeInterface; + +class BaseNodeTest extends TestCase +{ + /** + * @dataProvider providePath + */ + public function testGetPathForChildNode($expected, array $params) + { + $constructorArgs = array(); + $constructorArgs[] = $params[0]; + + if (isset($params[1])) { + // Handle old PHPUnit version for PHP 5.5 + $parent = method_exists($this, 'createMock') + ? $this->createMock(NodeInterface::class) + : $this->getMock(NodeInterface::class); + $parent->method('getPath')->willReturn($params[1]); + + $constructorArgs[] = $parent; + + if (isset($params[2])) { + $constructorArgs[] = $params[2]; + } + } + + $node = $this->getMockForAbstractClass(BaseNode::class, $constructorArgs); + + $this->assertSame($expected, $node->getPath()); + } + + public function providePath() + { + return array( + 'name only' => array('root', array('root')), + 'name and parent' => array('foo.bar.baz.bim', array('bim', 'foo.bar.baz')), + 'name and separator' => array('foo', array('foo', null, '/')), + 'name, parent and separator' => array('foo.bar/baz/bim', array('bim', 'foo.bar/baz', '/')), + ); + } +} diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index 0fd4af976784a..2bd55575d0542 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -54,6 +54,7 @@ public function providePrototypeNodeSpecificCalls() array('defaultValue', array(array())), array('addDefaultChildrenIfNoneSet', array()), array('requiresAtLeastOneElement', array()), + array('cannotBeEmpty', array()), array('useAttributeAsKey', array('foo')), ); } @@ -298,8 +299,8 @@ public function testRequiresAtLeastOneElement() } /** - * @group legacy - * @expectedDeprecation Using Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition::cannotBeEmpty() at path "root" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same. + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The path "root" should have at least 1 element(s) defined. */ public function testCannotBeEmpty() { @@ -326,8 +327,8 @@ public function testSetDeprecated() } /** - * @group legacy - * @expectedDeprecation ->cannotBeEmpty() is not applicable to concrete nodes at path "root". In 4.0 it will throw an exception. + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to concrete nodes at path "root" */ public function testCannotBeEmptyOnConcreteNode() { diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/NodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/NodeDefinitionTest.php new file mode 100644 index 0000000000000..1a602bb9dab52 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/NodeDefinitionTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; + +class NodeDefinitionTest extends TestCase +{ + public function testDefaultPathSeparatorIsDot() + { + $node = $this->getMockForAbstractClass(NodeDefinition::class, array('foo')); + + $this->assertAttributeSame('.', 'pathSeparator', $node); + } + + public function testSetPathSeparatorChangesChildren() + { + $node = new ArrayNodeDefinition('foo'); + $scalar = new ScalarNodeDefinition('bar'); + $node->append($scalar); + + $node->setPathSeparator('/'); + + $this->assertAttributeSame('/', 'pathSeparator', $node); + $this->assertAttributeSame('/', 'pathSeparator', $scalar); + } +} diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php index 0a9312346e989..18201c911053f 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php @@ -131,4 +131,65 @@ public function testDefinitionExampleGetsTransferredToNode() $this->assertInternalType('array', $tree->getExample()); $this->assertEquals('example', $children['child']->getExample()); } + + public function testDefaultPathSeparatorIsDot() + { + $builder = new TreeBuilder(); + + $builder->root('propagation') + ->children() + ->node('foo', 'variable')->end() + ->arrayNode('child') + ->children() + ->node('foo', 'variable') + ->end() + ->end() + ->end() + ->end(); + + $node = $builder->buildTree(); + $children = $node->getChildren(); + + $this->assertArrayHasKey('foo', $children); + $this->assertInstanceOf('Symfony\Component\Config\Definition\BaseNode', $children['foo']); + $this->assertSame('propagation.foo', $children['foo']->getPath()); + + $this->assertArrayHasKey('child', $children); + $childChildren = $children['child']->getChildren(); + + $this->assertArrayHasKey('foo', $childChildren); + $this->assertInstanceOf('Symfony\Component\Config\Definition\BaseNode', $childChildren['foo']); + $this->assertSame('propagation.child.foo', $childChildren['foo']->getPath()); + } + + public function testPathSeparatorIsPropagatedToChildren() + { + $builder = new TreeBuilder(); + + $builder->root('propagation') + ->children() + ->node('foo', 'variable')->end() + ->arrayNode('child') + ->children() + ->node('foo', 'variable') + ->end() + ->end() + ->end() + ->end(); + + $builder->setPathSeparator('/'); + $node = $builder->buildTree(); + $children = $node->getChildren(); + + $this->assertArrayHasKey('foo', $children); + $this->assertInstanceOf('Symfony\Component\Config\Definition\BaseNode', $children['foo']); + $this->assertSame('propagation/foo', $children['foo']->getPath()); + + $this->assertArrayHasKey('child', $children); + $childChildren = $children['child']->getChildren(); + + $this->assertArrayHasKey('foo', $childChildren); + $this->assertInstanceOf('Symfony\Component\Config\Definition\BaseNode', $childChildren['foo']); + $this->assertSame('propagation/child/foo', $childChildren['foo']->getPath()); + } } diff --git a/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php index e3e7ef05946fc..48468db2604c0 100644 --- a/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php @@ -43,6 +43,12 @@ public function testConstructionWithOneDistinctValue() $this->assertSame('foo', $node->finalize('foo')); } + public function testConstructionWithNullName() + { + $node = new EnumNode(null, null, array('foo')); + $this->assertSame('foo', $node->finalize('foo')); + } + /** * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException * @expectedExceptionMessage The value "foobar" is not allowed for path "foo". Permissible values: "foo", "bar" diff --git a/src/Symfony/Component/Config/Tests/DependencyInjection/ConfigCachePassTest.php b/src/Symfony/Component/Config/Tests/DependencyInjection/ConfigCachePassTest.php deleted file mode 100644 index 4f9c12f8c4ff0..0000000000000 --- a/src/Symfony/Component/Config/Tests/DependencyInjection/ConfigCachePassTest.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\DependencyInjection; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Argument\IteratorArgument; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\Config\DependencyInjection\ConfigCachePass; - -/** - * @group legacy - */ -class ConfigCachePassTest extends TestCase -{ - public function testThatCheckersAreProcessedInPriorityOrder() - { - $container = new ContainerBuilder(); - - $definition = $container->register('config_cache_factory')->addArgument(null); - $container->register('checker_2')->addTag('config_cache.resource_checker', array('priority' => 100)); - $container->register('checker_1')->addTag('config_cache.resource_checker', array('priority' => 200)); - $container->register('checker_3')->addTag('config_cache.resource_checker'); - - $pass = new ConfigCachePass(); - $pass->process($container); - - $expected = new IteratorArgument(array( - new Reference('checker_1'), - new Reference('checker_2'), - new Reference('checker_3'), - )); - $this->assertEquals($expected, $definition->getArgument(0)); - } - - public function testThatCheckersCanBeMissing() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('findTaggedServiceIds'))->getMock(); - - $container->expects($this->atLeastOnce()) - ->method('findTaggedServiceIds') - ->will($this->returnValue(array())); - - $pass = new ConfigCachePass(); - $pass->process($container); - } -} diff --git a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php index 61b0fdf7345b1..229e83e89bbcb 100644 --- a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php @@ -126,12 +126,8 @@ public function provideHashedSignature() yield array(0, 8, '/** priv docblock */'); yield array(0, 9, 'private $priv = 123;'); yield array(1, 10, '/** pub docblock */'); - if (\PHP_VERSION_ID >= 50600) { - yield array(1, 11, 'public function pub(...$arg) {}'); - } - if (\PHP_VERSION_ID >= 70000) { - yield array(1, 11, 'public function pub($arg = null): Foo {}'); - } + yield array(1, 11, 'public function pub(...$arg) {}'); + yield array(1, 11, 'public function pub($arg = null): Foo {}'); yield array(0, 11, "public function pub(\$arg = null) {\nreturn 123;\n}"); yield array(1, 12, '/** prot docblock */'); yield array(1, 13, 'protected function prot($a = array(123)) {}'); diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index ab26402819a28..68418bd1b85f6 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -16,18 +16,17 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/filesystem": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/filesystem": "~3.4|~4.0" }, "require-dev": { - "symfony/finder": "~3.3|~4.0", - "symfony/yaml": "~3.0|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/event-dispatcher": "~3.3|~4.0" + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" }, "conflict": { - "symfony/finder": "<3.3", - "symfony/dependency-injection": "<3.3" + "symfony/finder": "<3.4" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -41,7 +40,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 107c1ed6caaa9..00e941f13238a 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -36,7 +36,6 @@ use Symfony\Component\Console\Helper\FormatterHelper; use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleErrorEvent; -use Symfony\Component\Console\Event\ConsoleExceptionEvent; use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\LogicException; @@ -81,7 +80,7 @@ class Application * @param string $name The name of the application * @param string $version The version of the application */ - public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN') { $this->name = $name; $this->version = $version; @@ -138,10 +137,6 @@ public function run(InputInterface $input = null, OutputInterface $output = null } } - if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) { - @trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED); - } - $this->configureIO($input, $output); try { @@ -224,21 +219,19 @@ public function doRun(InputInterface $input, OutputInterface $output) } try { - $e = $this->runningCommand = null; + $this->runningCommand = null; // the command name MUST be the first element of the input $command = $this->find($name); - } catch (\Exception $e) { } catch (\Throwable $e) { - } - if (null !== $e) { if (null !== $this->dispatcher) { $event = new ConsoleErrorEvent($input, $output, $e); $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event); - $e = $event->getError(); if (0 === $event->getExitCode()) { return 0; } + + $e = $event->getError(); } throw $e; @@ -746,10 +739,6 @@ protected function doRenderException(\Exception $e, OutputInterface $output) } $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX; - // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 - if (defined('HHVM_VERSION') && $width > 1 << 31) { - $width = 1 << 31; - } $lines = array(); foreach ('' !== $message ? preg_split('/\r?\n/', $message) : array() as $line) { foreach ($this->splitStringByWidth($line, $width - 4) as $line) { @@ -798,70 +787,6 @@ protected function doRenderException(\Exception $e, OutputInterface $output) } while ($e = $e->getPrevious()); } - /** - * Tries to figure out the terminal width in which this application runs. - * - * @return int|null - * - * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. - */ - protected function getTerminalWidth() - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); - - return $this->terminal->getWidth(); - } - - /** - * Tries to figure out the terminal height in which this application runs. - * - * @return int|null - * - * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. - */ - protected function getTerminalHeight() - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); - - return $this->terminal->getHeight(); - } - - /** - * Tries to figure out the terminal dimensions based on the current environment. - * - * @return array Array containing width and height - * - * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. - */ - public function getTerminalDimensions() - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); - - return array($this->terminal->getWidth(), $this->terminal->getHeight()); - } - - /** - * Sets terminal dimensions. - * - * Can be useful to force terminal dimensions for functional tests. - * - * @param int $width The width - * @param int $height The height - * - * @return $this - * - * @deprecated since version 3.2, to be removed in 4.0. Set the COLUMNS and LINES env vars instead. - */ - public function setTerminalDimensions($width, $height) - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Set the COLUMNS and LINES env vars instead.', __METHOD__), E_USER_DEPRECATED); - - putenv('COLUMNS='.$width); - putenv('LINES='.$height); - - return $this; - } - /** * Configures the input and output instances based on the user arguments and options. */ @@ -882,12 +807,6 @@ protected function configureIO(InputInterface $input, OutputInterface $output) $inputStream = $input->getStream(); } - // This check ensures that calling QuestionHelper::setInputStream() works - // To be removed in 4.0 (in the same time as QuestionHelper::setInputStream) - if (!$inputStream && $this->getHelperSet()->has('question')) { - $inputStream = $this->getHelperSet()->get('question')->getInputStream(false); - } - if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { $input->setInteractive(false); } @@ -965,19 +884,7 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI } else { $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; } - } catch (\Exception $e) { } catch (\Throwable $e) { - } - if (null !== $e) { - if ($this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) { - $x = $e instanceof \Exception ? $e : new FatalThrowableError($e); - $event = new ConsoleExceptionEvent($command, $input, $output, $x, $x->getCode()); - $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); - - if ($x !== $event->getException()) { - $e = $event->getException(); - } - } $event = new ConsoleErrorEvent($input, $output, $e, $command); $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event); $e = $event->getError(); diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 946ff1e0ba08f..2437748ae56e6 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -1,6 +1,16 @@ CHANGELOG ========= +4.0.0 +----- + + * `OutputFormatter` throws an exception when unknown options are used + * removed `QuestionHelper::setInputStream()/getInputStream()` + * removed `Application::getTerminalWidth()/getTerminalHeight()` and + `Application::setTerminalDimensions()/getTerminalDimensions()` +* removed `ConsoleExceptionEvent` +* removed `ConsoleEvents::EXCEPTION` + 3.4.0 ----- diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 069304251617c..f42ee945f5111 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -66,7 +66,7 @@ public static function getDefaultName() * * @throws LogicException When the command name is empty */ - public function __construct($name = null) + public function __construct(string $name = null) { $this->definition = new InputDefinition(); @@ -274,15 +274,7 @@ public function setCode(callable $code) if ($code instanceof \Closure) { $r = new \ReflectionFunction($code); if (null === $r->getClosureThis()) { - if (\PHP_VERSION_ID < 70000) { - // Bug in PHP5: https://bugs.php.net/bug.php?id=64761 - // This means that we cannot bind static closures and therefore we must - // ignore any errors here. There is no way to test if the closure is - // bindable. - $code = @\Closure::bind($code, $this); - } else { - $code = \Closure::bind($code, $this); - } + $code = \Closure::bind($code, $this); } } @@ -644,11 +636,9 @@ public function getHelper($name) * * It must be non-empty and parts can optionally be separated by ":". * - * @param string $name - * * @throws InvalidArgumentException When the name is invalid */ - private function validateName($name) + private function validateName(string $name) { if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); diff --git a/src/Symfony/Component/Console/Command/LockableTrait.php b/src/Symfony/Component/Console/Command/LockableTrait.php index 308ebf28c045a..6c634ce44667a 100644 --- a/src/Symfony/Component/Console/Command/LockableTrait.php +++ b/src/Symfony/Component/Console/Command/LockableTrait.php @@ -43,7 +43,7 @@ private function lock($name = null, $blocking = false) throw new LogicException('A lock is already in place.'); } - if (SemaphoreStore::isSupported($blocking)) { + if (SemaphoreStore::isSupported()) { $store = new SemaphoreStore(); } else { $store = new FlockStore(); diff --git a/src/Symfony/Component/Console/ConsoleEvents.php b/src/Symfony/Component/Console/ConsoleEvents.php index bf6cab9a19ab4..4975643aedf2b 100644 --- a/src/Symfony/Component/Console/ConsoleEvents.php +++ b/src/Symfony/Component/Console/ConsoleEvents.php @@ -35,19 +35,6 @@ final class ConsoleEvents */ const TERMINATE = 'console.terminate'; - /** - * The EXCEPTION event occurs when an uncaught exception appears - * while executing Command#run(). - * - * This event allows you to deal with the exception or - * to modify the thrown exception. - * - * @Event("Symfony\Component\Console\Event\ConsoleExceptionEvent") - * - * @deprecated The console.exception event is deprecated since version 3.3 and will be removed in 4.0. Use the console.error event instead. - */ - const EXCEPTION = 'console.exception'; - /** * The ERROR event occurs when an uncaught exception or error appears. * diff --git a/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php b/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php index 39d53ef8e37d3..bf14ba2d17582 100644 --- a/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php +++ b/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php @@ -29,7 +29,7 @@ class AddConsoleCommandPass implements CompilerPassInterface private $commandLoaderServiceId; private $commandTag; - public function __construct($commandLoaderServiceId = 'console.command_loader', $commandTag = 'console.command') + public function __construct(string $commandLoaderServiceId = 'console.command_loader', string $commandTag = 'console.command') { $this->commandLoaderServiceId = $commandLoaderServiceId; $this->commandTag = $commandTag; @@ -41,14 +41,11 @@ public function process(ContainerBuilder $container) $lazyCommandMap = array(); $lazyCommandRefs = array(); $serviceIds = array(); - $lazyServiceIds = array(); foreach ($commandServices as $id => $tags) { $definition = $container->getDefinition($id); $class = $container->getParameterBag()->resolveValue($definition->getClass()); - $commandId = 'console.command.'.strtolower(str_replace('\\', '_', $class)); - if (isset($tags[0]['command'])) { $commandName = $tags[0]['command']; } else { @@ -62,20 +59,16 @@ public function process(ContainerBuilder $container) } if (null === $commandName) { - if (isset($serviceIds[$commandId]) || $container->hasAlias($commandId)) { - $commandId = $commandId.'_'.$id; - } if (!$definition->isPublic() || $definition->isPrivate()) { + $commandId = 'console.command.public_alias.'.$id; $container->setAlias($commandId, $id)->setPublic(true); $id = $commandId; } - $serviceIds[$commandId] = $id; + $serviceIds[] = $id; continue; } - $serviceIds[$commandId] = $id; - $lazyServiceIds[$id] = true; unset($tags[0]); $lazyCommandMap[$commandName] = $id; $lazyCommandRefs[$id] = new TypedReference($id, $class); @@ -101,6 +94,5 @@ public function process(ContainerBuilder $container) ->setArguments(array(ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap)); $container->setParameter('console.command.ids', $serviceIds); - $container->setParameter('console.lazy_command.ids', $lazyServiceIds); } } diff --git a/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php b/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php index ef4c673b7bcf3..5d34778621774 100644 --- a/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php +++ b/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php @@ -43,12 +43,7 @@ class ApplicationDescription */ private $aliases; - /** - * @param Application $application - * @param string|null $namespace - * @param bool $showHidden - */ - public function __construct(Application $application, $namespace = null, $showHidden = false) + public function __construct(Application $application, string $namespace = null, bool $showHidden = false) { $this->application = $application; $this->namespace = $namespace; @@ -123,10 +118,7 @@ private function inspectApplication() } } - /** - * @return array - */ - private function sortCommands(array $commands) + private function sortCommands(array $commands): array { $namespacedCommands = array(); $globalCommands = array(); diff --git a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php index a79df7e230d85..ac848184c0323 100644 --- a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php @@ -252,10 +252,8 @@ private function writeText($content, array $options = array()) /** * Formats command aliases to show them in the command description. - * - * @return string */ - private function getCommandAliasesText(Command $command) + private function getCommandAliasesText(Command $command): string { $text = ''; $aliases = $command->getAliases(); @@ -271,10 +269,8 @@ private function getCommandAliasesText(Command $command) * Formats input option/argument default value. * * @param mixed $default - * - * @return string */ - private function formatDefaultValue($default) + private function formatDefaultValue($default): string { if (INF === $default) { return 'INF'; @@ -295,10 +291,8 @@ private function formatDefaultValue($default) /** * @param (Command|string)[] $commands - * - * @return int */ - private function getColumnWidth(array $commands) + private function getColumnWidth(array $commands): int { $widths = array(); @@ -318,10 +312,8 @@ private function getColumnWidth(array $commands) /** * @param InputOption[] $options - * - * @return int */ - private function calculateTotalWidthForOptions(array $options) + private function calculateTotalWidthForOptions(array $options): int { $totalWidth = 0; foreach ($options as $option) { diff --git a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php index f05756ca309da..0d239868c4c99 100644 --- a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php @@ -188,10 +188,7 @@ private function writeDocument(\DOMDocument $dom) $this->write($dom->saveXML()); } - /** - * @return \DOMDocument - */ - private function getInputArgumentDocument(InputArgument $argument) + private function getInputArgumentDocument(InputArgument $argument): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); @@ -212,10 +209,7 @@ private function getInputArgumentDocument(InputArgument $argument) return $dom; } - /** - * @return \DOMDocument - */ - private function getInputOptionDocument(InputOption $option) + private function getInputOptionDocument(InputOption $option): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); diff --git a/src/Symfony/Component/Console/Event/ConsoleErrorEvent.php b/src/Symfony/Component/Console/Event/ConsoleErrorEvent.php index 0d05b9dad5bd0..3665da77d9159 100644 --- a/src/Symfony/Component/Console/Event/ConsoleErrorEvent.php +++ b/src/Symfony/Component/Console/Event/ConsoleErrorEvent.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Console\Event; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -26,57 +25,33 @@ final class ConsoleErrorEvent extends ConsoleEvent private $error; private $exitCode; - public function __construct(InputInterface $input, OutputInterface $output, $error, Command $command = null) + public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, Command $command = null) { parent::__construct($command, $input, $output); - $this->setError($error); + $this->error = $error; } - /** - * Returns the thrown error/exception. - * - * @return \Throwable - */ - public function getError() + public function getError(): \Throwable { return $this->error; } - /** - * Replaces the thrown error/exception. - * - * @param \Throwable $error - */ - public function setError($error) + public function setError(\Throwable $error): void { - if (!$error instanceof \Throwable && !$error instanceof \Exception) { - throw new InvalidArgumentException(sprintf('The error passed to ConsoleErrorEvent must be an instance of \Throwable or \Exception, "%s" was passed instead.', is_object($error) ? get_class($error) : gettype($error))); - } - $this->error = $error; } - /** - * Sets the exit code. - * - * @param int $exitCode The command exit code - */ - public function setExitCode($exitCode) + public function setExitCode(int $exitCode): void { - $this->exitCode = (int) $exitCode; + $this->exitCode = $exitCode; $r = new \ReflectionProperty($this->error, 'code'); $r->setAccessible(true); $r->setValue($this->error, $this->exitCode); } - /** - * Gets the exit code. - * - * @return int The command exit code - */ - public function getExitCode() + public function getExitCode(): int { return null !== $this->exitCode ? $this->exitCode : (is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1); } diff --git a/src/Symfony/Component/Console/Event/ConsoleExceptionEvent.php b/src/Symfony/Component/Console/Event/ConsoleExceptionEvent.php deleted file mode 100644 index 0adfaf9ac938f..0000000000000 --- a/src/Symfony/Component/Console/Event/ConsoleExceptionEvent.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Event; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ConsoleErrorEvent instead.', ConsoleExceptionEvent::class), E_USER_DEPRECATED); - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Allows to handle exception thrown in a command. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, to be removed in 4.0. Use ConsoleErrorEvent instead. - */ -class ConsoleExceptionEvent extends ConsoleEvent -{ - private $exception; - private $exitCode; - - public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) - { - parent::__construct($command, $input, $output); - - $this->setException($exception); - $this->exitCode = (int) $exitCode; - } - - /** - * Returns the thrown exception. - * - * @return \Exception The thrown exception - */ - public function getException() - { - return $this->exception; - } - - /** - * Replaces the thrown exception. - * - * This exception will be thrown if no response is set in the event. - * - * @param \Exception $exception The thrown exception - */ - public function setException(\Exception $exception) - { - $this->exception = $exception; - } - - /** - * Gets the exit code. - * - * @return int The command exit code - */ - public function getExitCode() - { - return $this->exitCode; - } -} diff --git a/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php b/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php index b6a5d7c0dc48c..ff0c749d1dc3a 100644 --- a/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php +++ b/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php @@ -22,14 +22,9 @@ */ class ConsoleTerminateEvent extends ConsoleEvent { - /** - * The exit code of the command. - * - * @var int - */ private $exitCode; - public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $exitCode) { parent::__construct($command, $input, $output); diff --git a/src/Symfony/Component/Console/Exception/CommandNotFoundException.php b/src/Symfony/Component/Console/Exception/CommandNotFoundException.php index cb7d1131fa776..2f6e644b99a9c 100644 --- a/src/Symfony/Component/Console/Exception/CommandNotFoundException.php +++ b/src/Symfony/Component/Console/Exception/CommandNotFoundException.php @@ -26,7 +26,7 @@ class CommandNotFoundException extends \InvalidArgumentException implements Exce * @param int $code Exception code * @param \Exception $previous Previous exception used for the exception chaining */ - public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null) + public function __construct(string $message, array $alternatives = array(), int $code = 0, \Exception $previous = null) { parent::__construct($message, $code, $previous); diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index 6ece9e90684e1..a78bd952051c7 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -65,9 +65,9 @@ public static function escapeTrailingBackslash($text) * @param bool $decorated Whether this formatter should actually decorate strings * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances */ - public function __construct($decorated = false, array $styles = array()) + public function __construct(bool $decorated = false, array $styles = array()) { - $this->decorated = (bool) $decorated; + $this->decorated = $decorated; $this->setStyle('error', new OutputFormatterStyle('white', 'red')); $this->setStyle('info', new OutputFormatterStyle('green')); @@ -186,11 +186,9 @@ public function getStyleStack() /** * Tries to create new style instance from string. * - * @param string $string - * - * @return OutputFormatterStyle|false false if string is not format string + * @return OutputFormatterStyle|false False if string is not format string */ - private function createStyleFromString($string) + private function createStyleFromString(string $string) { if (isset($this->styles[$string])) { return $this->styles[$string]; @@ -212,13 +210,7 @@ private function createStyleFromString($string) preg_match_all('([^,;]+)', $match[1], $options); $options = array_shift($options); foreach ($options as $option) { - try { - $style->setOption($option); - } catch (\InvalidArgumentException $e) { - @trigger_error(sprintf('Unknown style options are deprecated since Symfony 3.2 and will be removed in 4.0. Exception "%s".', $e->getMessage()), E_USER_DEPRECATED); - - return false; - } + $style->setOption($option); } } else { return false; @@ -230,12 +222,8 @@ private function createStyleFromString($string) /** * Applies current style from stack to text, if must be applied. - * - * @param string $text Input text - * - * @return string Styled text */ - private function applyCurrentStyle($text) + private function applyCurrentStyle(string $text): string { return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; } diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php index 7ada54f4c11b9..900f2ef31b058 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -61,7 +61,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface * @param string|null $background The style background color name * @param array $options The style options */ - public function __construct($foreground = null, $background = null, array $options = array()) + public function __construct(string $foreground = null, string $background = null, array $options = array()) { if (null !== $foreground) { $this->setForeground($foreground); diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php index 247b59133b6a6..7c0c09a97d375 100644 --- a/src/Symfony/Component/Console/Helper/ProgressBar.php +++ b/src/Symfony/Component/Console/Helper/ProgressBar.php @@ -50,7 +50,7 @@ final class ProgressBar * @param OutputInterface $output An OutputInterface instance * @param int $max Maximum steps (0 if unknown) */ - public function __construct(OutputInterface $output, $max = 0) + public function __construct(OutputInterface $output, int $max = 0) { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); @@ -79,7 +79,7 @@ public function __construct(OutputInterface $output, $max = 0) * @param string $name The placeholder name (including the delimiter char like %) * @param callable $callable A PHP callable */ - public static function setPlaceholderFormatterDefinition($name, callable $callable) + public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); @@ -95,7 +95,7 @@ public static function setPlaceholderFormatterDefinition($name, callable $callab * * @return callable|null A PHP callable */ - public static function getPlaceholderFormatterDefinition($name) + public static function getPlaceholderFormatterDefinition(string $name): ?callable { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); @@ -112,7 +112,7 @@ public static function getPlaceholderFormatterDefinition($name) * @param string $name The format name * @param string $format A format string */ - public static function setFormatDefinition($name, $format) + public static function setFormatDefinition(string $name, string $format): void { if (!self::$formats) { self::$formats = self::initFormats(); @@ -128,7 +128,7 @@ public static function setFormatDefinition($name, $format) * * @return string|null A format string */ - public static function getFormatDefinition($name) + public static function getFormatDefinition(string $name): ?string { if (!self::$formats) { self::$formats = self::initFormats(); @@ -147,102 +147,57 @@ public static function getFormatDefinition($name) * @param string $message The text to associate with the placeholder * @param string $name The name of the placeholder */ - public function setMessage($message, $name = 'message') + public function setMessage(string $message, string $name = 'message') { $this->messages[$name] = $message; } - public function getMessage($name = 'message') + public function getMessage(string $name = 'message') { return $this->messages[$name]; } - /** - * Gets the progress bar start time. - * - * @return int The progress bar start time - */ - public function getStartTime() + public function getStartTime(): int { return $this->startTime; } - /** - * Gets the progress bar maximal steps. - * - * @return int The progress bar max steps - */ - public function getMaxSteps() + public function getMaxSteps(): int { return $this->max; } - /** - * Gets the current step position. - * - * @return int The progress bar step - */ - public function getProgress() + public function getProgress(): int { return $this->step; } - /** - * Gets the progress bar step width. - * - * @return int The progress bar step width - */ - private function getStepWidth() + private function getStepWidth(): int { return $this->stepWidth; } - /** - * Gets the current progress bar percent. - * - * @return float The current progress bar percent - */ - public function getProgressPercent() + public function getProgressPercent(): float { return $this->percent; } - /** - * Sets the progress bar width. - * - * @param int $size The progress bar size - */ - public function setBarWidth($size) + public function setBarWidth(int $size) { - $this->barWidth = max(1, (int) $size); + $this->barWidth = max(1, $size); } - /** - * Gets the progress bar width. - * - * @return int The progress bar size - */ - public function getBarWidth() + public function getBarWidth(): int { return $this->barWidth; } - /** - * Sets the bar character. - * - * @param string $char A character - */ - public function setBarCharacter($char) + public function setBarCharacter(string $char) { $this->barChar = $char; } - /** - * Gets the bar character. - * - * @return string A character - */ - public function getBarCharacter() + public function getBarCharacter(): string { if (null === $this->barChar) { return $this->max ? '=' : $this->emptyBarChar; @@ -251,52 +206,27 @@ public function getBarCharacter() return $this->barChar; } - /** - * Sets the empty bar character. - * - * @param string $char A character - */ - public function setEmptyBarCharacter($char) + public function setEmptyBarCharacter(string $char) { $this->emptyBarChar = $char; } - /** - * Gets the empty bar character. - * - * @return string A character - */ - public function getEmptyBarCharacter() + public function getEmptyBarCharacter(): string { return $this->emptyBarChar; } - /** - * Sets the progress bar character. - * - * @param string $char A character - */ - public function setProgressCharacter($char) + public function setProgressCharacter(string $char) { $this->progressChar = $char; } - /** - * Gets the progress bar character. - * - * @return string A character - */ - public function getProgressCharacter() + public function getProgressCharacter(): string { return $this->progressChar; } - /** - * Sets the progress bar format. - * - * @param string $format The format - */ - public function setFormat($format) + public function setFormat(string $format) { $this->format = null; $this->internalFormat = $format; @@ -307,9 +237,9 @@ public function setFormat($format) * * @param int|float $freq The frequency in steps */ - public function setRedrawFrequency($freq) + public function setRedrawFrequency(int $freq) { - $this->redrawFreq = max((int) $freq, 1); + $this->redrawFreq = max($freq, 1); } /** @@ -317,7 +247,7 @@ public function setRedrawFrequency($freq) * * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged */ - public function start($max = null) + public function start(int $max = null) { $this->startTime = time(); $this->step = 0; @@ -335,30 +265,21 @@ public function start($max = null) * * @param int $step Number of steps to advance */ - public function advance($step = 1) + public function advance(int $step = 1) { $this->setProgress($this->step + $step); } /** * Sets whether to overwrite the progressbar, false for new line. - * - * @param bool $overwrite */ - public function setOverwrite($overwrite) + public function setOverwrite(bool $overwrite) { - $this->overwrite = (bool) $overwrite; + $this->overwrite = $overwrite; } - /** - * Sets the current progress. - * - * @param int $step The current progress - */ - public function setProgress($step) + public function setProgress(int $step) { - $step = (int) $step; - if ($this->max && $step > $this->max) { $this->max = $step; } elseif ($step < 0) { @@ -377,7 +298,7 @@ public function setProgress($step) /** * Finishes the progress output. */ - public function finish() + public function finish(): void { if (!$this->max) { $this->max = $this->step; @@ -394,7 +315,7 @@ public function finish() /** * Outputs the current progress string. */ - public function display() + public function display(): void { if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { return; @@ -414,7 +335,7 @@ public function display() * while a progress bar is running. * Call display() to show the progress bar again. */ - public function clear() + public function clear(): void { if (!$this->overwrite) { return; @@ -427,12 +348,7 @@ public function clear() $this->overwrite(''); } - /** - * Sets the progress bar format. - * - * @param string $format The format - */ - private function setRealFormat($format) + private function setRealFormat(string $format) { // try to use the _nomax variant if available if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { @@ -446,23 +362,16 @@ private function setRealFormat($format) $this->formatLineCount = substr_count($this->format, "\n"); } - /** - * Sets the progress bar maximal steps. - * - * @param int $max The progress bar max steps - */ - private function setMaxSteps($max) + private function setMaxSteps(int $max) { - $this->max = max(0, (int) $max); - $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; + $this->max = max(0, $max); + $this->stepWidth = $this->max ? Helper::strlen((string) $this->max) : 4; } /** * Overwrites a previous message to the output. - * - * @param string $message The message */ - private function overwrite($message) + private function overwrite(string $message): void { if ($this->overwrite) { if (!$this->firstRun) { @@ -486,7 +395,7 @@ private function overwrite($message) $this->output->write($message); } - private function determineBestFormat() + private function determineBestFormat(): string { switch ($this->output->getVerbosity()) { // OutputInterface::VERBOSITY_QUIET: display is disabled anyway @@ -501,7 +410,7 @@ private function determineBestFormat() } } - private static function initPlaceholderFormatters() + private static function initPlaceholderFormatters(): array { return array( 'bar' => function (ProgressBar $bar, OutputInterface $output) { @@ -558,7 +467,7 @@ private static function initPlaceholderFormatters() ); } - private static function initFormats() + private static function initFormats(): array { return array( 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', @@ -575,10 +484,7 @@ private static function initFormats() ); } - /** - * @return string - */ - private function buildLine() + private function buildLine(): string { $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; $callback = function ($matches) { diff --git a/src/Symfony/Component/Console/Helper/ProgressIndicator.php b/src/Symfony/Component/Console/Helper/ProgressIndicator.php index d441accd46e11..e6094647fa312 100644 --- a/src/Symfony/Component/Console/Helper/ProgressIndicator.php +++ b/src/Symfony/Component/Console/Helper/ProgressIndicator.php @@ -39,7 +39,7 @@ class ProgressIndicator * @param int $indicatorChangeInterval Change interval in milliseconds * @param array|null $indicatorValues Animated indicator characters */ - public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null) { $this->output = $output; @@ -219,10 +219,8 @@ private function determineBestFormat() /** * Overwrites a previous message to the output. - * - * @param string $message The message */ - private function overwrite($message) + private function overwrite(string $message) { if ($this->output->isDecorated()) { $this->output->write("\x0D\x1B[2K"); diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 5bcd96b340313..dc088a0565ae8 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Console\Helper; -use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatterStyle; @@ -71,46 +70,6 @@ public function ask(InputInterface $input, OutputInterface $output, Question $qu return $this->validateAttempts($interviewer, $output, $question); } - /** - * Sets the input stream to read from when interacting with the user. - * - * This is mainly useful for testing purpose. - * - * @deprecated since version 3.2, to be removed in 4.0. Use - * StreamableInputInterface::setStream() instead. - * - * @param resource $stream The input stream - * - * @throws InvalidArgumentException In case the stream is not a resource - */ - public function setInputStream($stream) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::setStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); - - if (!is_resource($stream)) { - throw new InvalidArgumentException('Input stream must be a valid resource.'); - } - - $this->inputStream = $stream; - } - - /** - * Returns the helper's input stream. - * - * @deprecated since version 3.2, to be removed in 4.0. Use - * StreamableInputInterface::getStream() instead. - * - * @return resource - */ - public function getInputStream() - { - if (0 === func_num_args() || func_get_arg(0)) { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::getStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); - } - - return $this->inputStream; - } - /** * {@inheritdoc} */ @@ -217,11 +176,8 @@ protected function writeError(OutputInterface $output, \Exception $error) * @param OutputInterface $output * @param Question $question * @param resource $inputStream - * @param array $autocomplete - * - * @return string */ - private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete) + private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete): string { $ret = ''; @@ -336,11 +292,9 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu * @param OutputInterface $output An Output instance * @param resource $inputStream The handler resource * - * @return string The answer - * * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ - private function getHiddenResponse(OutputInterface $output, $inputStream) + private function getHiddenResponse(OutputInterface $output, $inputStream): string { if ('\\' === DIRECTORY_SEPARATOR) { $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; @@ -451,10 +405,8 @@ private function getShell() /** * Returns whether Stty is available or not. - * - * @return bool */ - private function hasSttyAvailable() + private function hasSttyAvailable(): bool { if (null !== self::$stty) { return self::$stty; diff --git a/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php b/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php index cf071d594d44a..79016b9f2d90b 100644 --- a/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Console\Helper; -use Symfony\Component\Console\Exception\LogicException; -use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion; @@ -27,32 +25,6 @@ */ class SymfonyQuestionHelper extends QuestionHelper { - /** - * {@inheritdoc} - * - * To be removed in 4.0 - */ - public function ask(InputInterface $input, OutputInterface $output, Question $question) - { - $validator = $question->getValidator(); - $question->setValidator(function ($value) use ($validator) { - if (null !== $validator) { - $value = $validator($value); - } else { - // make required - if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { - @trigger_error('The default question validator is deprecated since Symfony 3.3 and will not be used anymore in version 4.0. Set a custom question validator if needed.', E_USER_DEPRECATED); - - throw new LogicException('A value is required.'); - } - } - - return $value; - }); - - return parent::ask($input, $output, $question); - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 81d0e427f473d..d54f63684a410 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -275,29 +275,40 @@ public function setRow($column, array $row) */ public function render() { - $this->calculateNumberOfColumns(); - $rows = $this->buildTableRows($this->rows); - $headers = $this->buildTableRows($this->headers); + $rows = array_merge($this->headers, array($divider = new TableSeparator()), $this->rows); + $this->calculateNumberOfColumns($rows); - $this->calculateColumnsWidth(array_merge($headers, $rows)); + $rows = $this->buildTableRows($rows); + $this->calculateColumnsWidth($rows); - $this->renderRowSeparator(); - if (!empty($headers)) { - foreach ($headers as $header) { - $this->renderRow($header, $this->style->getCellHeaderFormat()); - $this->renderRowSeparator(); - } - } + $isHeader = true; + $isFirstRow = false; foreach ($rows as $row) { + if ($divider === $row) { + $isHeader = false; + $isFirstRow = true; + + continue; + } if ($row instanceof TableSeparator) { $this->renderRowSeparator(); - } else { - $this->renderRow($row, $this->style->getCellRowFormat()); + + continue; } + if (!$row) { + continue; + } + + if ($isHeader || $isFirstRow) { + $this->renderRowSeparator(); + if ($isFirstRow) { + $isFirstRow = false; + } + } + + $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat()); } - if (!empty($rows)) { - $this->renderRowSeparator(); - } + $this->renderRowSeparator(); $this->cleanup(); } @@ -337,16 +348,9 @@ private function renderColumnSeparator() * Renders table row. * * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | - * - * @param array $row - * @param string $cellFormat */ - private function renderRow(array $row, $cellFormat) + private function renderRow(array $row, string $cellFormat) { - if (empty($row)) { - return; - } - $rowContent = $this->renderColumnSeparator(); foreach ($this->getRowColumns($row) as $column) { $rowContent .= $this->renderCell($row, $column, $cellFormat); @@ -357,12 +361,8 @@ private function renderRow(array $row, $cellFormat) /** * Renders table cell with padding. - * - * @param array $row - * @param int $column - * @param string $cellFormat */ - private function renderCell(array $row, $column, $cellFormat) + private function renderCell(array $row, int $column, string $cellFormat) { $cell = isset($row[$column]) ? $row[$column] : ''; $width = $this->effectiveColumnWidths[$column]; @@ -393,14 +393,10 @@ private function renderCell(array $row, $column, $cellFormat) /** * Calculate number of columns for this table. */ - private function calculateNumberOfColumns() + private function calculateNumberOfColumns($rows) { - if (null !== $this->numberOfColumns) { - return; - } - $columns = array(0); - foreach (array_merge($this->headers, $this->rows) as $row) { + foreach ($rows as $row) { if ($row instanceof TableSeparator) { continue; } @@ -436,28 +432,25 @@ private function buildTableRows($rows) } } - $tableRows = array(); - foreach ($rows as $rowKey => $row) { - $tableRows[] = $this->fillCells($row); - if (isset($unmergedRows[$rowKey])) { - $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); - } - } + return new TableRows(function () use ($rows, $unmergedRows) { + foreach ($rows as $rowKey => $row) { + yield $this->fillCells($row); - return $tableRows; + if (isset($unmergedRows[$rowKey])) { + foreach ($unmergedRows[$rowKey] as $row) { + yield $row; + } + } + } + }); } /** * fill rows that contains rowspan > 1. * - * @param array $rows - * @param int $line - * - * @return array - * * @throws InvalidArgumentException */ - private function fillNextRows(array $rows, $line) + private function fillNextRows(array $rows, int $line): array { $unmergedRows = array(); foreach ($rows[$line] as $column => $cell) { @@ -510,8 +503,6 @@ private function fillNextRows(array $rows, $line) /** * fill cells for a row that contains colspan > 1. - * - * @return array */ private function fillCells($row) { @@ -529,13 +520,7 @@ private function fillCells($row) return $newRow ?: $row; } - /** - * @param array $rows - * @param int $line - * - * @return array - */ - private function copyRow(array $rows, $line) + private function copyRow(array $rows, int $line): array { $row = $rows[$line]; foreach ($row as $cellKey => $cellValue) { @@ -550,10 +535,8 @@ private function copyRow(array $rows, $line) /** * Gets number of columns by row. - * - * @return int */ - private function getNumberOfColumns(array $row) + private function getNumberOfColumns(array $row): int { $columns = count($row); foreach ($row as $column) { @@ -565,10 +548,8 @@ private function getNumberOfColumns(array $row) /** * Gets list of columns for the given row. - * - * @return array */ - private function getRowColumns(array $row) + private function getRowColumns(array $row): array { $columns = range(0, $this->numberOfColumns - 1); foreach ($row as $cellKey => $cell) { @@ -584,7 +565,7 @@ private function getRowColumns(array $row) /** * Calculates columns widths. */ - private function calculateColumnsWidth(array $rows) + private function calculateColumnsWidth(iterable $rows) { for ($column = 0; $column < $this->numberOfColumns; ++$column) { $lengths = array(); @@ -613,25 +594,12 @@ private function calculateColumnsWidth(array $rows) } } - /** - * Gets column width. - * - * @return int - */ - private function getColumnSeparatorWidth() + private function getColumnSeparatorWidth(): int { return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); } - /** - * Gets cell width. - * - * @param array $row - * @param int $column - * - * @return int - */ - private function getCellWidth(array $row, $column) + private function getCellWidth(array $row, int $column): int { $cellWidth = 0; @@ -679,11 +647,18 @@ private static function initStyles() ->setCellHeaderFormat('%s') ; + $box = (new TableStyle()) + ->setHorizontalBorderChar('─') + ->setVerticalBorderChar('│') + ->setCrossingChar('┼') + ; + return array( 'default' => new TableStyle(), 'borderless' => $borderless, 'compact' => $compact, 'symfony-style-guide' => $styleGuide, + 'box' => $box, ); } diff --git a/src/Symfony/Component/Console/Helper/TableCell.php b/src/Symfony/Component/Console/Helper/TableCell.php index 6fc7d3b913b62..f800fb44e30d4 100644 --- a/src/Symfony/Component/Console/Helper/TableCell.php +++ b/src/Symfony/Component/Console/Helper/TableCell.php @@ -24,16 +24,8 @@ class TableCell 'colspan' => 1, ); - /** - * @param string $value - * @param array $options - */ - public function __construct($value = '', array $options = array()) + public function __construct(string $value = '', array $options = array()) { - if (is_numeric($value) && !is_string($value)) { - $value = (string) $value; - } - $this->value = $value; // check option names diff --git a/src/Symfony/Component/Console/Helper/TableRows.php b/src/Symfony/Component/Console/Helper/TableRows.php new file mode 100644 index 0000000000000..4809daf1cac80 --- /dev/null +++ b/src/Symfony/Component/Console/Helper/TableRows.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * @internal + */ +class TableRows implements \IteratorAggregate +{ + private $generator; + + public function __construct(callable $generator) + { + $this->generator = $generator; + } + + public function getIterator() + { + $g = $this->generator; + + return $g(); + } +} diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index 6bcfcf4a903e4..0cf7381c8aee9 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -146,11 +146,6 @@ private function parseLongOption($token) if (false !== $pos = strpos($name, '=')) { if (0 === strlen($value = substr($name, $pos + 1))) { - // if no value after "=" then substr() returns "" since php7 only, false before - // see http://php.net/manual/fr/migration70.incompatible.php#119151 - if (\PHP_VERSION_ID < 70000 && false === $value) { - $value = ''; - } array_unshift($this->parsed, $value); } $this->addLongOption(substr($name, 0, $pos), $value); diff --git a/src/Symfony/Component/Console/Input/InputArgument.php b/src/Symfony/Component/Console/Input/InputArgument.php index a969d2c5adc0a..b5cbbaf1b476e 100644 --- a/src/Symfony/Component/Console/Input/InputArgument.php +++ b/src/Symfony/Component/Console/Input/InputArgument.php @@ -38,11 +38,11 @@ class InputArgument * * @throws InvalidArgumentException When argument mode is not valid */ - public function __construct($name, $mode = null, $description = '', $default = null) + public function __construct(string $name, int $mode = null, string $description = '', $default = null) { if (null === $mode) { $mode = self::OPTIONAL; - } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + } elseif ($mode > 7 || $mode < 1) { throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); } diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index 3af8077c94b81..e4c855cae1706 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -41,7 +41,7 @@ class InputOption * * @throws InvalidArgumentException If option mode is invalid or incompatible */ - public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null) { if (0 === strpos($name, '--')) { $name = substr($name, 2); @@ -70,7 +70,7 @@ public function __construct($name, $shortcut = null, $mode = null, $description if (null === $mode) { $mode = self::VALUE_NONE; - } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + } elseif ($mode > 15 || $mode < 1) { throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); } diff --git a/src/Symfony/Component/Console/Input/StringInput.php b/src/Symfony/Component/Console/Input/StringInput.php index 969b9da747433..d1394ececa3e5 100644 --- a/src/Symfony/Component/Console/Input/StringInput.php +++ b/src/Symfony/Component/Console/Input/StringInput.php @@ -30,7 +30,7 @@ class StringInput extends ArgvInput /** * @param string $input A string representing the parameters from the CLI */ - public function __construct($input) + public function __construct(string $input) { parent::__construct(array()); diff --git a/src/Symfony/Component/Console/Logger/ConsoleLogger.php b/src/Symfony/Component/Console/Logger/ConsoleLogger.php index 05dd3b966ee62..9f07e1fb03f4c 100644 --- a/src/Symfony/Component/Console/Logger/ConsoleLogger.php +++ b/src/Symfony/Component/Console/Logger/ConsoleLogger.php @@ -99,13 +99,8 @@ public function hasErrored() * Interpolates context values into the message placeholders. * * @author PHP Framework Interoperability Group - * - * @param string $message - * @param array $context - * - * @return string */ - private function interpolate($message, array $context) + private function interpolate(string $message, array $context): string { if (false === strpos($message, '{')) { return $message; diff --git a/src/Symfony/Component/Console/Output/ConsoleOutput.php b/src/Symfony/Component/Console/Output/ConsoleOutput.php index edef356c4e2bf..cff6cd5a4a420 100644 --- a/src/Symfony/Component/Console/Output/ConsoleOutput.php +++ b/src/Symfony/Component/Console/Output/ConsoleOutput.php @@ -36,7 +36,7 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) */ - public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null) { parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); diff --git a/src/Symfony/Component/Console/Output/Output.php b/src/Symfony/Component/Console/Output/Output.php index 371735e142c80..20635a5f730d3 100644 --- a/src/Symfony/Component/Console/Output/Output.php +++ b/src/Symfony/Component/Console/Output/Output.php @@ -37,7 +37,7 @@ abstract class Output implements OutputInterface * @param bool $decorated Whether to decorate messages * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) */ - public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null) { $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; $this->formatter = $formatter ?: new OutputFormatter(); diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index d9e6bd119f28b..0a9f50843fb16 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -40,7 +40,7 @@ class StreamOutput extends Output * * @throws InvalidArgumentException When first argument is not a real stream */ - public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null) { if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); diff --git a/src/Symfony/Component/Console/Question/ChoiceQuestion.php b/src/Symfony/Component/Console/Question/ChoiceQuestion.php index 46cc72a368ee7..b66f2c685bf10 100644 --- a/src/Symfony/Component/Console/Question/ChoiceQuestion.php +++ b/src/Symfony/Component/Console/Question/ChoiceQuestion.php @@ -30,7 +30,7 @@ class ChoiceQuestion extends Question * @param array $choices The list of available choices * @param mixed $default The default answer to return */ - public function __construct($question, array $choices, $default = null) + public function __construct(string $question, array $choices, $default = null) { if (!$choices) { throw new \LogicException('Choice question must have at least 1 choice available.'); @@ -121,12 +121,7 @@ public function setErrorMessage($errorMessage) return $this; } - /** - * Returns the default answer validator. - * - * @return callable - */ - private function getDefaultValidator() + private function getDefaultValidator(): callable { $choices = $this->choices; $errorMessage = $this->errorMessage; diff --git a/src/Symfony/Component/Console/Question/ConfirmationQuestion.php b/src/Symfony/Component/Console/Question/ConfirmationQuestion.php index 40f54b4e9b8d8..7a15e5afb5ae9 100644 --- a/src/Symfony/Component/Console/Question/ConfirmationQuestion.php +++ b/src/Symfony/Component/Console/Question/ConfirmationQuestion.php @@ -25,9 +25,9 @@ class ConfirmationQuestion extends Question * @param bool $default The default answer to return, true or false * @param string $trueAnswerRegex A regex to match the "yes" answer */ - public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i') { - parent::__construct($question, (bool) $default); + parent::__construct($question, $default); $this->trueAnswerRegex = $trueAnswerRegex; $this->setNormalizer($this->getDefaultNormalizer()); diff --git a/src/Symfony/Component/Console/Question/Question.php b/src/Symfony/Component/Console/Question/Question.php index 63afbc4c0e4d0..683998542ceea 100644 --- a/src/Symfony/Component/Console/Question/Question.php +++ b/src/Symfony/Component/Console/Question/Question.php @@ -34,7 +34,7 @@ class Question * @param string $question The question to ask to the user * @param mixed $default The default answer to return if the user enters nothing */ - public function __construct($question, $default = null) + public function __construct(string $question, $default = null) { $this->question = $question; $this->default = $default; diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 6cf53cd924255..311d0675b552e 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -338,10 +338,7 @@ public function getErrorStyle() return new self($this->input, $this->getErrorOutput()); } - /** - * @return ProgressBar - */ - private function getProgressBar() + private function getProgressBar(): ProgressBar { if (!$this->progressBar) { throw new RuntimeException('The ProgressBar is not started.'); @@ -350,18 +347,20 @@ private function getProgressBar() return $this->progressBar; } - private function autoPrependBlock() + private function autoPrependBlock(): void { $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); if (!isset($chars[0])) { - return $this->newLine(); //empty history, so we should start with a new line. + $this->newLine(); //empty history, so we should start with a new line. + + return; } //Prepend new line for each non LF chars (This means no blank line was output before) $this->newLine(2 - substr_count($chars, "\n")); } - private function autoPrependText() + private function autoPrependText(): void { $fetched = $this->bufferedOutput->fetch(); //Prepend new line if last char isn't EOL: @@ -370,7 +369,7 @@ private function autoPrependText() } } - private function reduceBuffer($messages) + private function reduceBuffer($messages): array { // We need to know if the two last chars are PHP_EOL // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer @@ -379,7 +378,7 @@ private function reduceBuffer($messages) }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages)); } - private function createBlock($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = false) + private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false) { $indentLength = 0; $prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix); diff --git a/src/Symfony/Component/Console/Tester/ApplicationTester.php b/src/Symfony/Component/Console/Tester/ApplicationTester.php index c0f8c7207f2a8..3ae31152503cb 100644 --- a/src/Symfony/Component/Console/Tester/ApplicationTester.php +++ b/src/Symfony/Component/Console/Tester/ApplicationTester.php @@ -13,9 +13,7 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\StreamOutput; /** @@ -30,13 +28,11 @@ */ class ApplicationTester { + use TesterTrait; + private $application; private $input; private $statusCode; - /** - * @var OutputInterface - */ - private $output; private $captureStreamsIndependently = false; public function __construct(Application $application) @@ -66,6 +62,13 @@ public function run(array $input, $options = array()) $this->input->setInteractive($options['interactive']); } + $shellInteractive = getenv('SHELL_INTERACTIVE'); + + if ($this->inputs) { + $this->input->setStream(self::createStream($this->inputs)); + putenv('SHELL_INTERACTIVE=1'); + } + $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; if (!$this->captureStreamsIndependently) { $this->output = new StreamOutput(fopen('php://memory', 'w', false)); @@ -97,27 +100,11 @@ public function run(array $input, $options = array()) $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); } - return $this->statusCode = $this->application->run($this->input, $this->output); - } - - /** - * Gets the display returned by the last execution of the application. - * - * @param bool $normalize Whether to normalize end of lines to \n or not - * - * @return string The display - */ - public function getDisplay($normalize = false) - { - rewind($this->output->getStream()); - - $display = stream_get_contents($this->output->getStream()); + $this->statusCode = $this->application->run($this->input, $this->output); - if ($normalize) { - $display = str_replace(PHP_EOL, "\n", $display); - } + putenv($shellInteractive ? "SHELL_INTERACTIVE=$shellInteractive" : 'SHELL_INTERACTIVE'); - return $display; + return $this->statusCode; } /** @@ -143,34 +130,4 @@ public function getErrorOutput($normalize = false) return $display; } - - /** - * Gets the input instance used by the last execution of the application. - * - * @return InputInterface The current input instance - */ - public function getInput() - { - return $this->input; - } - - /** - * Gets the output instance used by the last execution of the application. - * - * @return OutputInterface The current output instance - */ - public function getOutput() - { - return $this->output; - } - - /** - * Gets the status code returned by the last execution of the application. - * - * @return int The status code - */ - public function getStatusCode() - { - return $this->statusCode; - } } diff --git a/src/Symfony/Component/Console/Tester/CommandTester.php b/src/Symfony/Component/Console/Tester/CommandTester.php index 39723b2613c5a..ecdc40c046adc 100644 --- a/src/Symfony/Component/Console/Tester/CommandTester.php +++ b/src/Symfony/Component/Console/Tester/CommandTester.php @@ -14,8 +14,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\StreamOutput; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; /** * Eases the testing of console commands. @@ -25,10 +23,10 @@ */ class CommandTester { + use TesterTrait; + private $command; private $input; - private $output; - private $inputs = array(); private $statusCode; public function __construct(Command $command) @@ -78,79 +76,4 @@ public function execute(array $input, array $options = array()) return $this->statusCode = $this->command->run($this->input, $this->output); } - - /** - * Gets the display returned by the last execution of the command. - * - * @param bool $normalize Whether to normalize end of lines to \n or not - * - * @return string The display - */ - public function getDisplay($normalize = false) - { - rewind($this->output->getStream()); - - $display = stream_get_contents($this->output->getStream()); - - if ($normalize) { - $display = str_replace(PHP_EOL, "\n", $display); - } - - return $display; - } - - /** - * Gets the input instance used by the last execution of the command. - * - * @return InputInterface The current input instance - */ - public function getInput() - { - return $this->input; - } - - /** - * Gets the output instance used by the last execution of the command. - * - * @return OutputInterface The current output instance - */ - public function getOutput() - { - return $this->output; - } - - /** - * Gets the status code returned by the last execution of the application. - * - * @return int The status code - */ - public function getStatusCode() - { - return $this->statusCode; - } - - /** - * Sets the user inputs. - * - * @param array $inputs An array of strings representing each input - * passed to the command input stream - * - * @return CommandTester - */ - public function setInputs(array $inputs) - { - $this->inputs = $inputs; - - return $this; - } - - private static function createStream(array $inputs) - { - $stream = fopen('php://memory', 'r+', false); - - fwrite($stream, implode(PHP_EOL, $inputs)); - rewind($stream); - - return $stream; - } } diff --git a/src/Symfony/Component/Console/Tester/TesterTrait.php b/src/Symfony/Component/Console/Tester/TesterTrait.php new file mode 100644 index 0000000000000..4e1e0795ca2c7 --- /dev/null +++ b/src/Symfony/Component/Console/Tester/TesterTrait.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @author Amrouche Hamza + * + * @internal + */ +trait TesterTrait +{ + /** @var StreamOutput */ + private $output; + private $inputs = array(); + + /** + * Gets the display returned by the last execution of the command or application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command or application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command or application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the command or application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Sets the user inputs. + * + * @param $inputs array An array of strings representing each input + * passed to the command input stream + * + * @return self + */ + public function setInputs(array $inputs) + { + $this->inputs = $inputs; + + return $this; + } + + private static function createStream(array $inputs) + { + $stream = fopen('php://memory', 'r+', false); + + fwrite($stream, implode(PHP_EOL, $inputs)); + rewind($stream); + + return $stream; + } +} diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 38389b1b4b14c..e9432c522d6f7 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -31,7 +31,6 @@ use Symfony\Component\Console\Tester\ApplicationTester; use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleErrorEvent; -use Symfony\Component\Console\Event\ConsoleExceptionEvent; use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -1164,9 +1163,6 @@ public function testRunDispatchesAllEventsWithExceptionInListener() $this->assertContains('before.error.after.', $tester->getDisplay()); } - /** - * @requires PHP 7 - */ public function testRunWithError() { $application = new Application(); @@ -1235,36 +1231,6 @@ public function testConsoleErrorEventIsTriggeredOnCommandNotFound() $this->assertEquals(1, $tester->getStatusCode()); } - /** - * @group legacy - * @expectedDeprecation The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead. - */ - public function testLegacyExceptionListenersAreStillTriggered() - { - $dispatcher = $this->getDispatcher(); - $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { - $event->getOutput()->write('caught.'); - - $event->setException(new \RuntimeException('replaced in caught.')); - }); - - $application = new Application(); - $application->setDispatcher($dispatcher); - $application->setAutoExit(false); - - $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { - throw new \RuntimeException('foo'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo')); - $this->assertContains('before.caught.error.after.', $tester->getDisplay()); - $this->assertContains('replaced in caught.', $tester->getDisplay()); - } - - /** - * @requires PHP 7 - */ public function testErrorIsRethrownIfNotHandledByConsoleErrorEvent() { $application = new Application(); @@ -1287,7 +1253,6 @@ public function testErrorIsRethrownIfNotHandledByConsoleErrorEvent() } /** - * @requires PHP 7 * @expectedException \LogicException * @expectedExceptionMessage error */ @@ -1309,9 +1274,6 @@ public function testRunWithErrorAndDispatcher() $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); } - /** - * @requires PHP 7 - */ public function testRunDispatchesAllEventsWithError() { $application = new Application(); @@ -1329,9 +1291,6 @@ public function testRunDispatchesAllEventsWithError() $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); } - /** - * @requires PHP 7 - */ public function testRunWithErrorFailingStatusCode() { $application = new Application(); @@ -1422,24 +1381,6 @@ public function testRunWithDispatcherAddingInputOptions() $this->assertEquals('some test value', $extraValue); } - /** - * @group legacy - */ - public function testTerminalDimensions() - { - $application = new Application(); - $originalDimensions = $application->getTerminalDimensions(); - $this->assertCount(2, $originalDimensions); - - $width = 80; - if ($originalDimensions[0] == $width) { - $width = 100; - } - - $application->setTerminalDimensions($width, 80); - $this->assertSame(array($width, 80), $application->getTerminalDimensions()); - } - public function testSetRunCustomDefaultCommand() { $command = new \FooCommand(); @@ -1592,9 +1533,6 @@ protected function getDispatcher($skipCommand = false) return $dispatcher; } - /** - * @requires PHP 7 - */ public function testErrorIsRethrownIfNotHandledByConsoleErrorEventWithCatchingEnabled() { $application = new Application(); @@ -1615,6 +1553,34 @@ public function testErrorIsRethrownIfNotHandledByConsoleErrorEventWithCatchingEn } } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage foo + */ + public function testThrowingErrorListener() + { + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) { + throw new \RuntimeException('foo'); + }); + + $dispatcher->addListener('console.command', function () { + throw new \RuntimeException('bar'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + } + protected function tearDown() { putenv('SHELL_VERBOSITY'); diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php index 4fcbf95753bb8..0e51632162a1f 100644 --- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -392,13 +392,7 @@ public function testSetCodeWithStaticClosure() $tester = new CommandTester($command); $tester->execute(array()); - if (\PHP_VERSION_ID < 70000) { - // Cannot bind static closures in PHP 5 - $this->assertEquals('interact called'.PHP_EOL.'not bound'.PHP_EOL, $tester->getDisplay()); - } else { - // Can bind static closures in PHP 7 - $this->assertEquals('interact called'.PHP_EOL.'bound'.PHP_EOL, $tester->getDisplay()); - } + $this->assertEquals('interact called'.PHP_EOL.'bound'.PHP_EOL, $tester->getDisplay()); } private static function createClosure() diff --git a/src/Symfony/Component/Console/Tests/Command/LockableTraitTest.php b/src/Symfony/Component/Console/Tests/Command/LockableTraitTest.php index a622d1b4895f5..f5d62d73f2849 100644 --- a/src/Symfony/Component/Console/Tests/Command/LockableTraitTest.php +++ b/src/Symfony/Component/Console/Tests/Command/LockableTraitTest.php @@ -41,7 +41,7 @@ public function testLockReturnsFalseIfAlreadyLockedByAnotherCommand() { $command = new \FooLockCommand(); - if (SemaphoreStore::isSupported(false)) { + if (SemaphoreStore::isSupported()) { $store = new SemaphoreStore(); } else { $store = new FlockStore(); diff --git a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php index 34f648610836a..2a0136d7974c7 100644 --- a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php +++ b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php @@ -31,28 +31,27 @@ public function testProcess($public) $container->addCompilerPass(new AddConsoleCommandPass()); $container->setParameter('my-command.class', 'Symfony\Component\Console\Tests\DependencyInjection\MyCommand'); + $id = 'my-command'; $definition = new Definition('%my-command.class%'); $definition->setPublic($public); $definition->addTag('console.command'); - $container->setDefinition('my-command', $definition); + $container->setDefinition($id, $definition); $container->compile(); - $alias = 'console.command.symfony_component_console_tests_dependencyinjection_mycommand'; + $alias = 'console.command.public_alias.my-command'; if ($public) { $this->assertFalse($container->hasAlias($alias)); - $id = 'my-command'; } else { - $id = $alias; // The alias is replaced by a Definition by the ReplaceAliasByActualDefinitionPass // in case the original service is private - $this->assertFalse($container->hasDefinition('my-command')); + $this->assertFalse($container->hasDefinition($id)); $this->assertTrue($container->hasDefinition($alias)); } $this->assertTrue($container->hasParameter('console.command.ids')); - $this->assertSame(array($alias => $id), $container->getParameter('console.command.ids')); + $this->assertSame(array($public ? $id : $alias), $container->getParameter('console.command.ids')); } public function testProcessRegistersLazyCommands() @@ -73,8 +72,7 @@ public function testProcessRegistersLazyCommands() $this->assertSame(ContainerCommandLoader::class, $commandLoader->getClass()); $this->assertSame(array('my:command' => 'my-command', 'my:alias' => 'my-command'), $commandLoader->getArgument(1)); $this->assertEquals(array(array('my-command' => new ServiceClosureArgument(new TypedReference('my-command', MyCommand::class)))), $commandLocator->getArguments()); - $this->assertSame(array('console.command.symfony_component_console_tests_dependencyinjection_mycommand' => 'my-command'), $container->getParameter('console.command.ids')); - $this->assertSame(array('my-command' => true), $container->getParameter('console.lazy_command.ids')); + $this->assertSame(array(), $container->getParameter('console.command.ids')); $this->assertSame(array(array('setName', array('my:command')), array('setAliases', array(array('my:alias')))), $command->getMethodCalls()); } @@ -96,8 +94,7 @@ public function testProcessFallsBackToDefaultName() $this->assertSame(ContainerCommandLoader::class, $commandLoader->getClass()); $this->assertSame(array('default' => 'with-default-name'), $commandLoader->getArgument(1)); $this->assertEquals(array(array('with-default-name' => new ServiceClosureArgument(new TypedReference('with-default-name', NamedCommand::class)))), $commandLocator->getArguments()); - $this->assertSame(array('console.command.symfony_component_console_tests_dependencyinjection_namedcommand' => 'with-default-name'), $container->getParameter('console.command.ids')); - $this->assertSame(array('with-default-name' => true), $container->getParameter('console.lazy_command.ids')); + $this->assertSame(array(), $container->getParameter('console.command.ids')); $container = new ContainerBuilder(); $container @@ -170,10 +167,9 @@ public function testProcessPrivateServicesWithSameCommand() (new AddConsoleCommandPass())->process($container); - $alias1 = 'console.command.symfony_component_console_tests_dependencyinjection_mycommand'; - $alias2 = $alias1.'_my-command2'; - $this->assertTrue($container->hasAlias($alias1)); - $this->assertTrue($container->hasAlias($alias2)); + $aliasPrefix = 'console.command.public_alias.'; + $this->assertTrue($container->hasAlias($aliasPrefix.'my-command1')); + $this->assertTrue($container->hasAlias($aliasPrefix.'my-command2')); } } diff --git a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php index 4b715c680d3d3..c1addbab9bb9a 100644 --- a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php +++ b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php @@ -196,17 +196,6 @@ public function provideInlineStyleOptionsCases() ); } - /** - * @group legacy - * @dataProvider provideInlineStyleTagsWithUnknownOptions - * @expectedDeprecation Unknown style options are deprecated since Symfony 3.2 and will be removed in 4.0. Exception "Invalid option specified: "%s". Expected one of (bold, underscore, blink, reverse, conceal)". - */ - public function testInlineStyleOptionsUnknownAreDeprecated($tag, $option) - { - $formatter = new OutputFormatter(true); - $formatter->format($tag); - } - public function provideInlineStyleTagsWithUnknownOptions() { return array( diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 78ec1d3c7f6fb..3b8c07d39379b 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -467,357 +467,6 @@ public function testChoiceOutputFormattingQuestionForUtf8Keys() $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("\n")), $output, $question); } - /** - * @group legacy - */ - public function testLegacyAskChoice() - { - $questionHelper = new QuestionHelper(); - - $helperSet = new HelperSet(array(new FormatterHelper())); - $questionHelper->setHelperSet($helperSet); - - $heroes = array('Superman', 'Batman', 'Spiderman'); - - $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); - $question->setMaxAttempts(1); - // first answer is an empty answer, we're supposed to receive the default value - $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setMaxAttempts(1); - $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setErrorMessage('Input "%s" is not a superhero!'); - $question->setMaxAttempts(2); - $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); - - rewind($output->getStream()); - $stream = stream_get_contents($output->getStream()); - $this->assertContains('Input "Fabien" is not a superhero!', $stream); - - try { - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); - $question->setMaxAttempts(1); - $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); - } - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAsk() - { - $dialog = new QuestionHelper(); - - $dialog->setInputStream($this->getInputStream("\n8AM\n")); - - $question = new Question('What time is it?', '2PM'); - $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new Question('What time is it?', '2PM'); - $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); - - rewind($output->getStream()); - $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); - } - - /** - * @group legacy - */ - public function testLegacyAskWithAutocomplete() - { - if (!$this->hasSttyAvailable()) { - $this->markTestSkipped('`stty` is required to test autocomplete functionality'); - } - - // Acm - // AcsTest - // - // - // Test - // - // S - // F00oo - $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($inputStream); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new Question('Please select a bundle', 'FrameworkBundle'); - $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); - - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAskWithAutocompleteWithNonSequentialKeys() - { - if (!$this->hasSttyAvailable()) { - $this->markTestSkipped('`stty` is required to test autocomplete functionality'); - } - - // - $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($inputStream); - $dialog->setHelperSet(new HelperSet(array(new FormatterHelper()))); - - $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle')); - $question->setMaxAttempts(1); - - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAskHiddenResponse() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test is not supported on Windows'); - } - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream("8AM\n")); - - $question = new Question('What time is it?'); - $question->setHidden(true); - - $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - * @dataProvider getAskConfirmationData - */ - public function testLegacyAskConfirmation($question, $expected, $default = true) - { - $dialog = new QuestionHelper(); - - $dialog->setInputStream($this->getInputStream($question."\n")); - $question = new ConfirmationQuestion('Do you like French fries?', $default); - $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); - } - - /** - * @group legacy - */ - public function testLegacyAskConfirmationWithCustomTrueAnswer() - { - $dialog = new QuestionHelper(); - - $dialog->setInputStream($this->getInputStream("j\ny\n")); - $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); - $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); - $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAskAndValidate() - { - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $error = 'This is not a color!'; - $validator = function ($color) use ($error) { - if (!in_array($color, array('white', 'black'))) { - throw new \InvalidArgumentException($error); - } - - return $color; - }; - - $question = new Question('What color was the white horse of Henry IV?', 'white'); - $question->setValidator($validator); - $question->setMaxAttempts(2); - - $dialog->setInputStream($this->getInputStream("\nblack\n")); - $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); - try { - $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertEquals($error, $e->getMessage()); - } - } - - /** - * @group legacy - * @dataProvider simpleAnswerProvider - */ - public function testLegacySelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) - { - $possibleChoices = array( - 'My environment 1', - 'My environment 2', - 'My environment 3', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - /** - * @group legacy - * @dataProvider mixedKeysChoiceListAnswerProvider - */ - public function testLegacyChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) - { - $possibleChoices = array( - '0' => 'No environment', - '1' => 'My environment 1', - 'env_2' => 'My environment 2', - 3 => 'My environment 3', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - /** - * @group legacy - * @dataProvider answerProvider - */ - public function testLegacySelectChoiceFromChoiceList($providedAnswer, $expectedValue) - { - $possibleChoices = array( - 'env_1' => 'My environment 1', - 'env_2' => 'My environment', - 'env_3' => 'My environment', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - /** - * @group legacy - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. - */ - public function testLegacyAmbiguousChoiceFromChoicelist() - { - $possibleChoices = array( - 'env_1' => 'My first environment', - 'env_2' => 'My environment', - 'env_3' => 'My environment', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream("My environment\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - - $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - } - - /** - * @requires function mb_strwidth - * @group legacy - */ - public function testLegacyChoiceOutputFormattingQuestionForUtf8Keys() - { - $question = 'Lorem ipsum?'; - $possibleChoices = array( - 'foo' => 'foo', - 'żółw' => 'bar', - 'łabądź' => 'baz', - ); - $outputShown = array( - $question, - ' [foo ] foo', - ' [żółw ] bar', - ' [łabądź] baz', - ); - $output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock(); - $output->method('getFormatter')->willReturn(new OutputFormatter()); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream("\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); - - $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); - $dialog->ask($this->createInputInterfaceMock(), $output, $question); - } - /** * @expectedException \Symfony\Component\Console\Exception\RuntimeException * @expectedExceptionMessage Aborted diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index b195e09acb8b0..380699528d941 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -136,6 +136,22 @@ public function renderProvider() 80-902734-1-6 And Then There Were None Agatha Christie =============== ========================== ================== +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + $books, + 'box', + <<<'TABLE' +┼───────────────┼──────────────────────────┼──────────────────┼ +│ ISBN │ Title │ Author │ +┼───────────────┼──────────────────────────┼──────────────────┼ +│ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri │ +│ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens │ +│ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien │ +│ 80-902734-1-6 │ And Then There Were None │ Agatha Christie │ +┼───────────────┼──────────────────────────┼──────────────────┼ + TABLE ), array( diff --git a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php index 66af98b33b666..881300c2576f3 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php @@ -38,26 +38,12 @@ public function testModes() } /** - * @dataProvider provideInvalidModes + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Argument mode "-1" is not valid. */ - public function testInvalidModes($mode) + public function testInvalidModes() { - if (method_exists($this, 'expectException')) { - $this->expectException('InvalidArgumentException'); - $this->expectExceptionMessage(sprintf('Argument mode "%s" is not valid.', $mode)); - } else { - $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode)); - } - - new InputArgument('foo', $mode); - } - - public function provideInvalidModes() - { - return array( - array('ANOTHER_ONE'), - array(-1), - ); + new InputArgument('foo', '-1'); } public function testIsArray() diff --git a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php index 943bf607f5e5a..66a6dd5ac32f9 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php @@ -74,26 +74,12 @@ public function testModes() } /** - * @dataProvider provideInvalidModes + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Option mode "-1" is not valid. */ - public function testInvalidModes($mode) - { - if (method_exists($this, 'expectException')) { - $this->expectException('InvalidArgumentException'); - $this->expectExceptionMessage(sprintf('Option mode "%s" is not valid.', $mode)); - } else { - $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode)); - } - - new InputOption('foo', 'f', $mode); - } - - public function provideInvalidModes() + public function testInvalidModes() { - return array( - array('ANOTHER_ONE'), - array(-1), - ); + new InputOption('foo', 'f', '-1'); } /** diff --git a/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php b/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php index 57e7136d5580e..71547e7b798ec 100644 --- a/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php +++ b/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php @@ -13,7 +13,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Tester\ApplicationTester; class ApplicationTesterTest extends TestCase @@ -27,7 +29,9 @@ protected function setUp() $this->application->setAutoExit(false); $this->application->register('foo') ->addArgument('foo') - ->setCode(function ($input, $output) { $output->writeln('foo'); }) + ->setCode(function ($input, $output) { + $output->writeln('foo'); + }) ; $this->tester = new ApplicationTester($this->application); @@ -63,6 +67,25 @@ public function testGetDisplay() $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); } + public function testSetInputs() + { + $application = new Application(); + $application->setAutoExit(false); + $application->register('foo')->setCode(function ($input, $output) { + $helper = new QuestionHelper(); + $helper->ask($input, $output, new Question('Q1')); + $helper->ask($input, $output, new Question('Q2')); + $helper->ask($input, $output, new Question('Q3')); + }); + $tester = new ApplicationTester($application); + + $tester->setInputs(array('I1', 'I2', 'I3')); + $tester->run(array('command' => 'foo')); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertEquals('Q1Q2Q3', $tester->getDisplay(true)); + } + public function testGetStatusCode() { $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index e9d10e0c7f887..59d9cda191083 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -16,16 +16,15 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/debug": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/config": "~3.3|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0", + "symfony/process": "~3.4|~4.0", "psr/log": "~1.0" }, "suggest": { @@ -47,7 +46,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/CssSelector/CssSelectorConverter.php b/src/Symfony/Component/CssSelector/CssSelectorConverter.php index 8d66dbd0e18f2..d1aeb7eb1702e 100644 --- a/src/Symfony/Component/CssSelector/CssSelectorConverter.php +++ b/src/Symfony/Component/CssSelector/CssSelectorConverter.php @@ -31,7 +31,7 @@ class CssSelectorConverter /** * @param bool $html Whether HTML support should be enabled. Disable it for XML documents */ - public function __construct($html = true) + public function __construct(bool $html = true) { $this->translator = new Translator(); diff --git a/src/Symfony/Component/CssSelector/Node/AbstractNode.php b/src/Symfony/Component/CssSelector/Node/AbstractNode.php index 7477e9119df32..123ef1b117a67 100644 --- a/src/Symfony/Component/CssSelector/Node/AbstractNode.php +++ b/src/Symfony/Component/CssSelector/Node/AbstractNode.php @@ -31,7 +31,7 @@ abstract class AbstractNode implements NodeInterface /** * @return string */ - public function getNodeName() + public function getNodeName(): string { if (null === $this->nodeName) { $this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', get_called_class()); diff --git a/src/Symfony/Component/CssSelector/Node/AttributeNode.php b/src/Symfony/Component/CssSelector/Node/AttributeNode.php index 1caccb6bfeefb..bf702d9ce44e4 100644 --- a/src/Symfony/Component/CssSelector/Node/AttributeNode.php +++ b/src/Symfony/Component/CssSelector/Node/AttributeNode.php @@ -29,14 +29,7 @@ class AttributeNode extends AbstractNode private $operator; private $value; - /** - * @param NodeInterface $selector - * @param string $namespace - * @param string $attribute - * @param string $operator - * @param string $value - */ - public function __construct(NodeInterface $selector, $namespace, $attribute, $operator, $value) + public function __construct(NodeInterface $selector, ?string $namespace, string $attribute, string $operator, ?string $value) { $this->selector = $selector; $this->namespace = $namespace; @@ -45,42 +38,27 @@ public function __construct(NodeInterface $selector, $namespace, $attribute, $op $this->value = $value; } - /** - * @return NodeInterface - */ - public function getSelector() + public function getSelector(): NodeInterface { return $this->selector; } - /** - * @return string - */ - public function getNamespace() + public function getNamespace(): ?string { return $this->namespace; } - /** - * @return string - */ - public function getAttribute() + public function getAttribute(): string { return $this->attribute; } - /** - * @return string - */ - public function getOperator() + public function getOperator(): string { return $this->operator; } - /** - * @return string - */ - public function getValue() + public function getValue(): ?string { return $this->value; } @@ -88,7 +66,7 @@ public function getValue() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } @@ -96,7 +74,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { $attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute; diff --git a/src/Symfony/Component/CssSelector/Node/ClassNode.php b/src/Symfony/Component/CssSelector/Node/ClassNode.php index 69462e8e71091..1998b4bd5b0ec 100644 --- a/src/Symfony/Component/CssSelector/Node/ClassNode.php +++ b/src/Symfony/Component/CssSelector/Node/ClassNode.php @@ -26,28 +26,18 @@ class ClassNode extends AbstractNode private $selector; private $name; - /** - * @param NodeInterface $selector - * @param string $name - */ - public function __construct(NodeInterface $selector, $name) + public function __construct(NodeInterface $selector, string $name) { $this->selector = $selector; $this->name = $name; } - /** - * @return NodeInterface - */ - public function getSelector() + public function getSelector(): NodeInterface { return $this->selector; } - /** - * @return string - */ - public function getName() + public function getName(): string { return $this->name; } @@ -55,7 +45,7 @@ public function getName() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } @@ -63,7 +53,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); } diff --git a/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php b/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php index 2aa583aaf6928..f97fd21aebba7 100644 --- a/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php +++ b/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php @@ -27,38 +27,24 @@ class CombinedSelectorNode extends AbstractNode private $combinator; private $subSelector; - /** - * @param NodeInterface $selector - * @param string $combinator - * @param NodeInterface $subSelector - */ - public function __construct(NodeInterface $selector, $combinator, NodeInterface $subSelector) + public function __construct(NodeInterface $selector, string $combinator, NodeInterface $subSelector) { $this->selector = $selector; $this->combinator = $combinator; $this->subSelector = $subSelector; } - /** - * @return NodeInterface - */ - public function getSelector() + public function getSelector(): NodeInterface { return $this->selector; } - /** - * @return string - */ - public function getCombinator() + public function getCombinator(): string { return $this->combinator; } - /** - * @return NodeInterface - */ - public function getSubSelector() + public function getSubSelector(): NodeInterface { return $this->subSelector; } @@ -66,7 +52,7 @@ public function getSubSelector() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); } @@ -74,7 +60,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { $combinator = ' ' === $this->combinator ? '' : $this->combinator; diff --git a/src/Symfony/Component/CssSelector/Node/ElementNode.php b/src/Symfony/Component/CssSelector/Node/ElementNode.php index bcdce7a7a5207..7d405b31f77ce 100644 --- a/src/Symfony/Component/CssSelector/Node/ElementNode.php +++ b/src/Symfony/Component/CssSelector/Node/ElementNode.php @@ -30,7 +30,7 @@ class ElementNode extends AbstractNode * @param string|null $namespace * @param string|null $element */ - public function __construct($namespace = null, $element = null) + public function __construct(string $namespace = null, string $element = null) { $this->namespace = $namespace; $this->element = $element; @@ -55,7 +55,7 @@ public function getElement() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return new Specificity(0, 0, $this->element ? 1 : 0); } @@ -63,7 +63,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { $element = $this->element ?: '*'; diff --git a/src/Symfony/Component/CssSelector/Node/FunctionNode.php b/src/Symfony/Component/CssSelector/Node/FunctionNode.php index 50268255ce525..89b6437801d82 100644 --- a/src/Symfony/Component/CssSelector/Node/FunctionNode.php +++ b/src/Symfony/Component/CssSelector/Node/FunctionNode.php @@ -34,25 +34,19 @@ class FunctionNode extends AbstractNode * @param string $name * @param Token[] $arguments */ - public function __construct(NodeInterface $selector, $name, array $arguments = array()) + public function __construct(NodeInterface $selector, string $name, array $arguments = array()) { $this->selector = $selector; $this->name = strtolower($name); $this->arguments = $arguments; } - /** - * @return NodeInterface - */ - public function getSelector() + public function getSelector(): NodeInterface { return $this->selector; } - /** - * @return string - */ - public function getName() + public function getName(): string { return $this->name; } @@ -68,7 +62,7 @@ public function getArguments() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } @@ -76,7 +70,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { $arguments = implode(', ', array_map(function (Token $token) { return "'".$token->getValue()."'"; diff --git a/src/Symfony/Component/CssSelector/Node/HashNode.php b/src/Symfony/Component/CssSelector/Node/HashNode.php index ebf9a9872a7d1..f73fa2e7402bd 100644 --- a/src/Symfony/Component/CssSelector/Node/HashNode.php +++ b/src/Symfony/Component/CssSelector/Node/HashNode.php @@ -26,28 +26,18 @@ class HashNode extends AbstractNode private $selector; private $id; - /** - * @param NodeInterface $selector - * @param string $id - */ - public function __construct(NodeInterface $selector, $id) + public function __construct(NodeInterface $selector, string $id) { $this->selector = $selector; $this->id = $id; } - /** - * @return NodeInterface - */ - public function getSelector() + public function getSelector(): NodeInterface { return $this->selector; } - /** - * @return string - */ - public function getId() + public function getId(): string { return $this->id; } @@ -55,7 +45,7 @@ public function getId() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0)); } @@ -63,7 +53,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); } diff --git a/src/Symfony/Component/CssSelector/Node/NegationNode.php b/src/Symfony/Component/CssSelector/Node/NegationNode.php index bf97caeb58683..9d3e9bc7a6e0c 100644 --- a/src/Symfony/Component/CssSelector/Node/NegationNode.php +++ b/src/Symfony/Component/CssSelector/Node/NegationNode.php @@ -51,7 +51,7 @@ public function getSubSelector() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); } @@ -59,7 +59,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); } diff --git a/src/Symfony/Component/CssSelector/Node/NodeInterface.php b/src/Symfony/Component/CssSelector/Node/NodeInterface.php index d919e20c7107a..b078d26d4de31 100644 --- a/src/Symfony/Component/CssSelector/Node/NodeInterface.php +++ b/src/Symfony/Component/CssSelector/Node/NodeInterface.php @@ -23,24 +23,9 @@ */ interface NodeInterface { - /** - * Returns node's name. - * - * @return string - */ - public function getNodeName(); + public function getNodeName(): string; - /** - * Returns node's specificity. - * - * @return Specificity - */ - public function getSpecificity(); + public function getSpecificity(): Specificity; - /** - * Returns node's string representation. - * - * @return string - */ - public function __toString(); + public function __toString(): string; } diff --git a/src/Symfony/Component/CssSelector/Node/PseudoNode.php b/src/Symfony/Component/CssSelector/Node/PseudoNode.php index 3842c695e852b..7d4a011e1faf3 100644 --- a/src/Symfony/Component/CssSelector/Node/PseudoNode.php +++ b/src/Symfony/Component/CssSelector/Node/PseudoNode.php @@ -26,28 +26,18 @@ class PseudoNode extends AbstractNode private $selector; private $identifier; - /** - * @param NodeInterface $selector - * @param string $identifier - */ - public function __construct(NodeInterface $selector, $identifier) + public function __construct(NodeInterface $selector, string $identifier) { $this->selector = $selector; $this->identifier = strtolower($identifier); } - /** - * @return NodeInterface - */ - public function getSelector() + public function getSelector(): NodeInterface { return $this->selector; } - /** - * @return string - */ - public function getIdentifier() + public function getIdentifier(): string { return $this->identifier; } @@ -55,7 +45,7 @@ public function getIdentifier() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } @@ -63,7 +53,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); } diff --git a/src/Symfony/Component/CssSelector/Node/SelectorNode.php b/src/Symfony/Component/CssSelector/Node/SelectorNode.php index 5ef2be62a41b4..a76aa5bb5f48a 100644 --- a/src/Symfony/Component/CssSelector/Node/SelectorNode.php +++ b/src/Symfony/Component/CssSelector/Node/SelectorNode.php @@ -26,28 +26,18 @@ class SelectorNode extends AbstractNode private $tree; private $pseudoElement; - /** - * @param NodeInterface $tree - * @param null|string $pseudoElement - */ - public function __construct(NodeInterface $tree, $pseudoElement = null) + public function __construct(NodeInterface $tree, string $pseudoElement = null) { $this->tree = $tree; $this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null; } - /** - * @return NodeInterface - */ - public function getTree() + public function getTree(): NodeInterface { return $this->tree; } - /** - * @return null|string - */ - public function getPseudoElement() + public function getPseudoElement(): ?string { return $this->pseudoElement; } @@ -55,7 +45,7 @@ public function getPseudoElement() /** * {@inheritdoc} */ - public function getSpecificity() + public function getSpecificity(): Specificity { return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0)); } @@ -63,7 +53,7 @@ public function getSpecificity() /** * {@inheritdoc} */ - public function __toString() + public function __toString(): string { return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); } diff --git a/src/Symfony/Component/CssSelector/Node/Specificity.php b/src/Symfony/Component/CssSelector/Node/Specificity.php index 6aa70d781cae2..11228f7246d3a 100644 --- a/src/Symfony/Component/CssSelector/Node/Specificity.php +++ b/src/Symfony/Component/CssSelector/Node/Specificity.php @@ -33,32 +33,19 @@ class Specificity private $b; private $c; - /** - * @param int $a - * @param int $b - * @param int $c - */ - public function __construct($a, $b, $c) + public function __construct(int $a, int $b, int $c) { $this->a = $a; $this->b = $b; $this->c = $c; } - /** - * @return self - */ - public function plus(Specificity $specificity) + public function plus(Specificity $specificity): Specificity { return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c); } - /** - * Returns global specificity value. - * - * @return int - */ - public function getValue() + public function getValue(): int { return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR; } diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php index a29775cab370f..93f318844a5bd 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php @@ -29,7 +29,7 @@ class CommentHandler implements HandlerInterface /** * {@inheritdoc} */ - public function handle(Reader $reader, TokenStream $stream) + public function handle(Reader $reader, TokenStream $stream): bool { if ('/*' !== $reader->getSubstring(2)) { return false; diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php b/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php index de931f6797080..d7ac4d332bf05 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php @@ -29,5 +29,5 @@ interface HandlerInterface /** * @return bool */ - public function handle(Reader $reader, TokenStream $stream); + public function handle(Reader $reader, TokenStream $stream): bool; } diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php index 5f7f4d23be70e..f40807cdfbf07 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php @@ -41,7 +41,7 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca /** * {@inheritdoc} */ - public function handle(Reader $reader, TokenStream $stream) + public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern($this->patterns->getHashPattern()); diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php index aa6804ed477d6..5ea0d5fd9935d 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php @@ -41,7 +41,7 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca /** * {@inheritdoc} */ - public function handle(Reader $reader, TokenStream $stream) + public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern($this->patterns->getIdentifierPattern()); diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php index 7e561d1dc302d..41e5844ea81e7 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php @@ -38,7 +38,7 @@ public function __construct(TokenizerPatterns $patterns) /** * {@inheritdoc} */ - public function handle(Reader $reader, TokenStream $stream) + public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern($this->patterns->getNumberPattern()); diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php index f842e1ce20eaf..fe92c1fcc7daf 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php @@ -43,7 +43,7 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca /** * {@inheritdoc} */ - public function handle(Reader $reader, TokenStream $stream) + public function handle(Reader $reader, TokenStream $stream): bool { $quote = $reader->getSubstring(1); diff --git a/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php b/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php index 4c2d3354fb83e..ebf8a19fe2605 100644 --- a/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php +++ b/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php @@ -30,7 +30,7 @@ class WhitespaceHandler implements HandlerInterface /** * {@inheritdoc} */ - public function handle(Reader $reader, TokenStream $stream) + public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern('~^[ \t\r\n\f]+~'); diff --git a/src/Symfony/Component/CssSelector/Parser/Parser.php b/src/Symfony/Component/CssSelector/Parser/Parser.php index 195e107159d98..dc84cc1826197 100644 --- a/src/Symfony/Component/CssSelector/Parser/Parser.php +++ b/src/Symfony/Component/CssSelector/Parser/Parser.php @@ -37,7 +37,7 @@ public function __construct(Tokenizer $tokenizer = null) /** * {@inheritdoc} */ - public function parse($source) + public function parse(string $source): array { $reader = new Reader($source); $stream = $this->tokenizer->tokenize($reader); @@ -50,11 +50,9 @@ public function parse($source) * * @param Token[] $tokens * - * @return array - * * @throws SyntaxErrorException */ - public static function parseSeries(array $tokens) + public static function parseSeries(array $tokens): array { foreach ($tokens as $token) { if ($token->isString()) { @@ -94,12 +92,7 @@ public static function parseSeries(array $tokens) ); } - /** - * Parses selector nodes. - * - * @return array - */ - private function parseSelectorList(TokenStream $stream) + private function parseSelectorList(TokenStream $stream): array { $stream->skipWhitespace(); $selectors = array(); @@ -118,14 +111,7 @@ private function parseSelectorList(TokenStream $stream) return $selectors; } - /** - * Parses next selector or combined node. - * - * @return Node\SelectorNode - * - * @throws SyntaxErrorException - */ - private function parserSelectorNode(TokenStream $stream) + private function parserSelectorNode(TokenStream $stream): Node\SelectorNode { list($result, $pseudoElement) = $this->parseSimpleSelector($stream); @@ -158,14 +144,9 @@ private function parserSelectorNode(TokenStream $stream) /** * Parses next simple node (hash, class, pseudo, negation). * - * @param TokenStream $stream - * @param bool $insideNegation - * - * @return array - * * @throws SyntaxErrorException */ - private function parseSimpleSelector(TokenStream $stream, $insideNegation = false) + private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = false): array { $stream->skipWhitespace(); @@ -279,12 +260,7 @@ private function parseSimpleSelector(TokenStream $stream, $insideNegation = fals return array($result, $pseudoElement); } - /** - * Parses next element node. - * - * @return Node\ElementNode - */ - private function parseElementNode(TokenStream $stream) + private function parseElementNode(TokenStream $stream): Node\ElementNode { $peek = $stream->getPeek(); @@ -310,14 +286,7 @@ private function parseElementNode(TokenStream $stream) return new Node\ElementNode($namespace, $element); } - /** - * Parses next attribute node. - * - * @return Node\AttributeNode - * - * @throws SyntaxErrorException - */ - private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream) + private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream): Node\AttributeNode { $stream->skipWhitespace(); $attribute = $stream->getNextIdentifierOrStar(); diff --git a/src/Symfony/Component/CssSelector/Parser/ParserInterface.php b/src/Symfony/Component/CssSelector/Parser/ParserInterface.php index c5af20367de8c..51c3d935069aa 100644 --- a/src/Symfony/Component/CssSelector/Parser/ParserInterface.php +++ b/src/Symfony/Component/CssSelector/Parser/ParserInterface.php @@ -28,9 +28,7 @@ interface ParserInterface /** * Parses given selector source into an array of tokens. * - * @param string $source - * * @return SelectorNode[] */ - public function parse($source); + public function parse(string $source): array; } diff --git a/src/Symfony/Component/CssSelector/Parser/Reader.php b/src/Symfony/Component/CssSelector/Parser/Reader.php index 5a1be30cd957a..adfd3c2113940 100644 --- a/src/Symfony/Component/CssSelector/Parser/Reader.php +++ b/src/Symfony/Component/CssSelector/Parser/Reader.php @@ -27,56 +27,33 @@ class Reader private $length; private $position = 0; - /** - * @param string $source - */ - public function __construct($source) + public function __construct(string $source) { $this->source = $source; $this->length = strlen($source); } - /** - * @return bool - */ - public function isEOF() + public function isEOF(): bool { return $this->position >= $this->length; } - /** - * @return int - */ - public function getPosition() + public function getPosition(): int { return $this->position; } - /** - * @return int - */ - public function getRemainingLength() + public function getRemainingLength(): int { return $this->length - $this->position; } - /** - * @param int $length - * @param int $offset - * - * @return string - */ - public function getSubstring($length, $offset = 0) + public function getSubstring(int $length, int $offset = 0): string { return substr($this->source, $this->position + $offset, $length); } - /** - * @param string $string - * - * @return int - */ - public function getOffset($string) + public function getOffset(string $string) { $position = strpos($this->source, $string, $this->position); @@ -84,11 +61,9 @@ public function getOffset($string) } /** - * @param string $pattern - * * @return array|false */ - public function findPattern($pattern) + public function findPattern(string $pattern) { $source = substr($this->source, $this->position); @@ -99,10 +74,7 @@ public function findPattern($pattern) return false; } - /** - * @param int $length - */ - public function moveForward($length) + public function moveForward(int $length) { $this->position += $length; } diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php index c513de5ff12ee..ce7b20ef2e768 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php @@ -31,7 +31,7 @@ class ClassParser implements ParserInterface /** * {@inheritdoc} */ - public function parse($source) + public function parse(string $source): array { // Matches an optional namespace, optional element, and required class // $source = 'test|input.ab6bd_field'; diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php index c29f5e442e739..3b93f0068f5d4 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php @@ -30,7 +30,7 @@ class ElementParser implements ParserInterface /** * {@inheritdoc} */ - public function parse($source) + public function parse(string $source): array { // Matches an optional namespace, required element or `*` // $source = 'testns|testel'; diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php index 16d374af30ccc..e8a89cc4dbb5c 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php @@ -34,7 +34,7 @@ class EmptyStringParser implements ParserInterface /** * {@inheritdoc} */ - public function parse($source) + public function parse(string $source): array { // Matches an empty string if ('' == $source) { diff --git a/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php b/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php index 3f3883bb8d2e9..e94ce0a46afd4 100644 --- a/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php +++ b/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php @@ -31,7 +31,7 @@ class HashParser implements ParserInterface /** * {@inheritdoc} */ - public function parse($source) + public function parse(string $source): array { // Matches an optional namespace, optional element, and required id // $source = 'test|input#ab6bd_field'; diff --git a/src/Symfony/Component/CssSelector/Parser/Token.php b/src/Symfony/Component/CssSelector/Parser/Token.php index 9baaa6dd8d165..554deb8e4aefd 100644 --- a/src/Symfony/Component/CssSelector/Parser/Token.php +++ b/src/Symfony/Component/CssSelector/Parser/Token.php @@ -35,54 +35,34 @@ class Token private $value; private $position; - /** - * @param int $type - * @param string $value - * @param int $position - */ - public function __construct($type, $value, $position) + public function __construct(?string $type, ?string $value, ?int $position) { $this->type = $type; $this->value = $value; $this->position = $position; } - /** - * @return int - */ - public function getType() + public function getType(): ?int { return $this->type; } - /** - * @return string - */ - public function getValue() + public function getValue(): ?string { return $this->value; } - /** - * @return int - */ - public function getPosition() + public function getPosition(): ?int { return $this->position; } - /** - * @return bool - */ - public function isFileEnd() + public function isFileEnd(): bool { return self::TYPE_FILE_END === $this->type; } - /** - * @return bool - */ - public function isDelimiter(array $values = array()) + public function isDelimiter(array $values = array()): bool { if (self::TYPE_DELIMITER !== $this->type) { return false; @@ -95,50 +75,32 @@ public function isDelimiter(array $values = array()) return in_array($this->value, $values); } - /** - * @return bool - */ - public function isWhitespace() + public function isWhitespace(): bool { return self::TYPE_WHITESPACE === $this->type; } - /** - * @return bool - */ - public function isIdentifier() + public function isIdentifier(): bool { return self::TYPE_IDENTIFIER === $this->type; } - /** - * @return bool - */ - public function isHash() + public function isHash(): bool { return self::TYPE_HASH === $this->type; } - /** - * @return bool - */ - public function isNumber() + public function isNumber(): bool { return self::TYPE_NUMBER === $this->type; } - /** - * @return bool - */ - public function isString() + public function isString(): bool { return self::TYPE_STRING === $this->type; } - /** - * @return string - */ - public function __toString() + public function __toString(): string { if ($this->value) { return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position); diff --git a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php index c6f289aa11439..2df63f51621d1 100644 --- a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php +++ b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php @@ -30,36 +30,21 @@ public function __construct(TokenizerPatterns $patterns) $this->patterns = $patterns; } - /** - * @param string $value - * - * @return string - */ - public function escapeUnicode($value) + public function escapeUnicode(string $value): string { $value = $this->replaceUnicodeSequences($value); return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value); } - /** - * @param string $value - * - * @return string - */ - public function escapeUnicodeAndNewLine($value) + public function escapeUnicodeAndNewLine(string $value): string { $value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value); return $this->escapeUnicode($value); } - /** - * @param string $value - * - * @return string - */ - private function replaceUnicodeSequences($value) + private function replaceUnicodeSequences(string $value): string { return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) { $c = hexdec($match[1]); diff --git a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php index 30584ca92ee85..dffb079d75059 100644 --- a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php +++ b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php @@ -52,60 +52,37 @@ public function __construct() $this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*'; } - /** - * @return string - */ - public function getNewLineEscapePattern() + public function getNewLineEscapePattern(): string { return '~^'.$this->newLineEscapePattern.'~'; } - /** - * @return string - */ - public function getSimpleEscapePattern() + public function getSimpleEscapePattern(): string { return '~^'.$this->simpleEscapePattern.'~'; } - /** - * @return string - */ - public function getUnicodeEscapePattern() + public function getUnicodeEscapePattern(): string { return '~^'.$this->unicodeEscapePattern.'~i'; } - /** - * @return string - */ - public function getIdentifierPattern() + public function getIdentifierPattern(): string { return '~^'.$this->identifierPattern.'~i'; } - /** - * @return string - */ - public function getHashPattern() + public function getHashPattern(): string { return '~^'.$this->hashPattern.'~i'; } - /** - * @return string - */ - public function getNumberPattern() + public function getNumberPattern(): string { return '~^'.$this->numberPattern.'~'; } - /** - * @param string $quote - * - * @return string - */ - public function getQuotedStringPattern($quote) + public function getQuotedStringPattern(string $quote): string { return '~^'.sprintf($this->quotedStringPattern, $quote).'~i'; } diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php index 6ace8b5925969..ee8976fdd03fa 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php @@ -43,38 +43,17 @@ public function getAttributeMatchingTranslators() ); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translateExists(XPathExpr $xpath, $attribute, $value) + public function translateExists(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition($attribute); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translateEquals(XPathExpr $xpath, $attribute, $value) + public function translateEquals(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translateIncludes(XPathExpr $xpath, $attribute, $value) + public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition($value ? sprintf( '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)', @@ -83,14 +62,7 @@ public function translateIncludes(XPathExpr $xpath, $attribute, $value) ) : '0'); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translateDashMatch(XPathExpr $xpath, $attribute, $value) + public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition(sprintf( '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))', @@ -100,14 +72,7 @@ public function translateDashMatch(XPathExpr $xpath, $attribute, $value) )); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translatePrefixMatch(XPathExpr $xpath, $attribute, $value) + public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition($value ? sprintf( '%1$s and starts-with(%1$s, %2$s)', @@ -116,14 +81,7 @@ public function translatePrefixMatch(XPathExpr $xpath, $attribute, $value) ) : '0'); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translateSuffixMatch(XPathExpr $xpath, $attribute, $value) + public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition($value ? sprintf( '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s', @@ -133,14 +91,7 @@ public function translateSuffixMatch(XPathExpr $xpath, $attribute, $value) ) : '0'); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translateSubstringMatch(XPathExpr $xpath, $attribute, $value) + public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition($value ? sprintf( '%1$s and contains(%1$s, %2$s)', @@ -149,14 +100,7 @@ public function translateSubstringMatch(XPathExpr $xpath, $attribute, $value) ) : '0'); } - /** - * @param XPathExpr $xpath - * @param string $attribute - * @param string $value - * - * @return XPathExpr - */ - public function translateDifferent(XPathExpr $xpath, $attribute, $value) + public function translateDifferent(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { return $xpath->addCondition(sprintf( $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s', diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php index 0c9cc0320c3c2..85181fd93e876 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php @@ -28,7 +28,7 @@ class CombinationExtension extends AbstractExtension /** * {@inheritdoc} */ - public function getCombinationTranslators() + public function getCombinationTranslators(): array { return array( ' ' => array($this, 'translateDescendant'), @@ -41,7 +41,7 @@ public function getCombinationTranslators() /** * @return XPathExpr */ - public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath) + public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr { return $xpath->join('/descendant-or-self::*/', $combinedXpath); } diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php index c2606b5ad4308..52b9c2f63cfaa 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php @@ -46,16 +46,9 @@ public function getFunctionTranslators() } /** - * @param XPathExpr $xpath - * @param FunctionNode $function - * @param bool $last - * @param bool $addNameTest - * - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $last = false, $addNameTest = true) + public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr { try { list($a, $b) = Parser::parseSeries($function->getArguments()); @@ -110,28 +103,20 @@ public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $las // -1n+6 means elements 6 and previous } - /** - * @return XPathExpr - */ - public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function) + public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr { return $this->translateNthChild($xpath, $function, true); } - /** - * @return XPathExpr - */ - public function translateNthOfType(XPathExpr $xpath, FunctionNode $function) + public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr { return $this->translateNthChild($xpath, $function, false, false); } /** - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function) + public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr { if ('*' === $xpath->getElement()) { throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.'); @@ -141,11 +126,9 @@ public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function) } /** - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function translateContains(XPathExpr $xpath, FunctionNode $function) + public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr { $arguments = $function->getArguments(); foreach ($arguments as $token) { @@ -164,11 +147,9 @@ public function translateContains(XPathExpr $xpath, FunctionNode $function) } /** - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function translateLang(XPathExpr $xpath, FunctionNode $function) + public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr { $arguments = $function->getArguments(); foreach ($arguments as $token) { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php index 715d9611a8267..61442b6f7a530 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php @@ -33,21 +33,15 @@ class NodeExtension extends AbstractExtension private $flags; - /** - * @param int $flags - */ - public function __construct($flags = 0) + public function __construct(int $flags = 0) { $this->flags = $flags; } /** - * @param int $flag - * @param bool $on - * * @return $this */ - public function setFlag($flag, $on) + public function setFlag(int $flag, bool $on) { if ($on && !$this->hasFlag($flag)) { $this->flags += $flag; @@ -60,12 +54,7 @@ public function setFlag($flag, $on) return $this; } - /** - * @param int $flag - * - * @return bool - */ - public function hasFlag($flag) + public function hasFlag(int $flag): bool { return (bool) ($this->flags & $flag); } @@ -88,26 +77,17 @@ public function getNodeTranslators() ); } - /** - * @return XPathExpr - */ - public function translateSelector(Node\SelectorNode $node, Translator $translator) + public function translateSelector(Node\SelectorNode $node, Translator $translator): XPathExpr { return $translator->nodeToXPath($node->getTree()); } - /** - * @return XPathExpr - */ - public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator) + public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator): XPathExpr { return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); } - /** - * @return XPathExpr - */ - public function translateNegation(Node\NegationNode $node, Translator $translator) + public function translateNegation(Node\NegationNode $node, Translator $translator): XPathExpr { $xpath = $translator->nodeToXPath($node->getSelector()); $subXpath = $translator->nodeToXPath($node->getSubSelector()); @@ -120,30 +100,21 @@ public function translateNegation(Node\NegationNode $node, Translator $translato return $xpath->addCondition('0'); } - /** - * @return XPathExpr - */ - public function translateFunction(Node\FunctionNode $node, Translator $translator) + public function translateFunction(Node\FunctionNode $node, Translator $translator): XPathExpr { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addFunction($xpath, $node); } - /** - * @return XPathExpr - */ - public function translatePseudo(Node\PseudoNode $node, Translator $translator) + public function translatePseudo(Node\PseudoNode $node, Translator $translator): XPathExpr { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addPseudoClass($xpath, $node->getIdentifier()); } - /** - * @return XPathExpr - */ - public function translateAttribute(Node\AttributeNode $node, Translator $translator) + public function translateAttribute(Node\AttributeNode $node, Translator $translator): XPathExpr { $name = $node->getAttribute(); $safe = $this->isSafeName($name); @@ -168,30 +139,21 @@ public function translateAttribute(Node\AttributeNode $node, Translator $transla return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); } - /** - * @return XPathExpr - */ - public function translateClass(Node\ClassNode $node, Translator $translator) + public function translateClass(Node\ClassNode $node, Translator $translator): XPathExpr { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); } - /** - * @return XPathExpr - */ - public function translateHash(Node\HashNode $node, Translator $translator) + public function translateHash(Node\HashNode $node, Translator $translator): XPathExpr { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); } - /** - * @return XPathExpr - */ - public function translateElement(Node\ElementNode $node) + public function translateElement(Node\ElementNode $node): XPathExpr { $element = $node->getElement(); @@ -228,14 +190,7 @@ public function getName() return 'node'; } - /** - * Tests if given name is safe. - * - * @param string $name - * - * @return bool - */ - private function isSafeName($name) + private function isSafeName(string $name): bool { return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name); } diff --git a/src/Symfony/Component/CssSelector/XPath/Translator.php b/src/Symfony/Component/CssSelector/XPath/Translator.php index d20ad7dc3401d..28478b3db7e47 100644 --- a/src/Symfony/Component/CssSelector/XPath/Translator.php +++ b/src/Symfony/Component/CssSelector/XPath/Translator.php @@ -61,12 +61,7 @@ public function __construct(ParserInterface $parser = null) ; } - /** - * @param string $element - * - * @return string - */ - public static function getXpathLiteral($element) + public static function getXpathLiteral(string $element): string { if (false === strpos($element, "'")) { return "'".$element."'"; @@ -95,7 +90,7 @@ public static function getXpathLiteral($element) /** * {@inheritdoc} */ - public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') + public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string { $selectors = $this->parseSelectors($cssExpr); @@ -114,17 +109,12 @@ public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') /** * {@inheritdoc} */ - public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::') + public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string { return ($prefix ?: '').$this->nodeToXPath($selector); } - /** - * Registers an extension. - * - * @return $this - */ - public function registerExtension(Extension\ExtensionInterface $extension) + public function registerExtension(Extension\ExtensionInterface $extension): Translator { $this->extensions[$extension->getName()] = $extension; @@ -138,13 +128,9 @@ public function registerExtension(Extension\ExtensionInterface $extension) } /** - * @param string $name - * - * @return Extension\ExtensionInterface - * * @throws ExpressionErrorException */ - public function getExtension($name) + public function getExtension(string $name): Extension\ExtensionInterface { if (!isset($this->extensions[$name])) { throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name)); @@ -153,12 +139,7 @@ public function getExtension($name) return $this->extensions[$name]; } - /** - * Registers a shortcut parser. - * - * @return $this - */ - public function registerParserShortcut(ParserInterface $shortcut) + public function registerParserShortcut(ParserInterface $shortcut): Translator { $this->shortcutParsers[] = $shortcut; @@ -166,11 +147,9 @@ public function registerParserShortcut(ParserInterface $shortcut) } /** - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function nodeToXPath(NodeInterface $node) + public function nodeToXPath(NodeInterface $node): XPathExpr { if (!isset($this->nodeTranslators[$node->getNodeName()])) { throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); @@ -180,15 +159,9 @@ public function nodeToXPath(NodeInterface $node) } /** - * @param string $combiner - * @param NodeInterface $xpath - * @param NodeInterface $combinedXpath - * - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function addCombination($combiner, NodeInterface $xpath, NodeInterface $combinedXpath) + public function addCombination(string $combiner, NodeInterface $xpath, NodeInterface $combinedXpath): XPathExpr { if (!isset($this->combinationTranslators[$combiner])) { throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); @@ -198,11 +171,9 @@ public function addCombination($combiner, NodeInterface $xpath, NodeInterface $c } /** - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function addFunction(XPathExpr $xpath, FunctionNode $function) + public function addFunction(XPathExpr $xpath, FunctionNode $function): XPathExpr { if (!isset($this->functionTranslators[$function->getName()])) { throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); @@ -212,14 +183,9 @@ public function addFunction(XPathExpr $xpath, FunctionNode $function) } /** - * @param XPathExpr $xpath - * @param string $pseudoClass - * - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function addPseudoClass(XPathExpr $xpath, $pseudoClass) + public function addPseudoClass(XPathExpr $xpath, string $pseudoClass): XPathExpr { if (!isset($this->pseudoClassTranslators[$pseudoClass])) { throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); @@ -229,16 +195,9 @@ public function addPseudoClass(XPathExpr $xpath, $pseudoClass) } /** - * @param XPathExpr $xpath - * @param string $operator - * @param string $attribute - * @param string $value - * - * @return XPathExpr - * * @throws ExpressionErrorException */ - public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $value) + public function addAttributeMatching(XPathExpr $xpath, string $operator, string $attribute, $value): XPathExpr { if (!isset($this->attributeMatchingTranslators[$operator])) { throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); @@ -248,11 +207,9 @@ public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $v } /** - * @param string $css - * * @return SelectorNode[] */ - private function parseSelectors($css) + private function parseSelectors(string $css) { foreach ($this->shortcutParsers as $shortcut) { $tokens = $shortcut->parse($css); diff --git a/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php b/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php index 0b5de83d57124..c19eefb9c99d0 100644 --- a/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php +++ b/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php @@ -27,21 +27,11 @@ interface TranslatorInterface { /** * Translates a CSS selector to an XPath expression. - * - * @param string $cssExpr - * @param string $prefix - * - * @return string */ - public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::'); + public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string; /** * Translates a parsed selector node to an XPath expression. - * - * @param SelectorNode $selector - * @param string $prefix - * - * @return string */ - public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::'); + public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string; } diff --git a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php index 63e3ac36bf820..8090df99075fa 100644 --- a/src/Symfony/Component/CssSelector/XPath/XPathExpr.php +++ b/src/Symfony/Component/CssSelector/XPath/XPathExpr.php @@ -27,13 +27,7 @@ class XPathExpr private $element; private $condition; - /** - * @param string $path - * @param string $element - * @param string $condition - * @param bool $starPrefix - */ - public function __construct($path = '', $element = '*', $condition = '', $starPrefix = false) + public function __construct(string $path = '', string $element = '*', string $condition = '', bool $starPrefix = false) { $this->path = $path; $this->element = $element; @@ -44,38 +38,24 @@ public function __construct($path = '', $element = '*', $condition = '', $starPr } } - /** - * @return string - */ - public function getElement() + public function getElement(): string { return $this->element; } - /** - * @param $condition - * - * @return $this - */ - public function addCondition($condition) + public function addCondition(string $condition): XPathExpr { $this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition; return $this; } - /** - * @return string - */ - public function getCondition() + public function getCondition(): string { return $this->condition; } - /** - * @return $this - */ - public function addNameTest() + public function addNameTest(): XPathExpr { if ('*' !== $this->element) { $this->addCondition('name() = '.Translator::getXpathLiteral($this->element)); @@ -85,10 +65,7 @@ public function addNameTest() return $this; } - /** - * @return $this - */ - public function addStarPrefix() + public function addStarPrefix(): XPathExpr { $this->path .= '*/'; @@ -98,12 +75,9 @@ public function addStarPrefix() /** * Joins another XPathExpr with a combiner. * - * @param string $combiner - * @param XPathExpr $expr - * * @return $this */ - public function join($combiner, self $expr) + public function join(string $combiner, self $expr): self { $path = $this->__toString().$combiner; @@ -118,10 +92,7 @@ public function join($combiner, self $expr) return $this; } - /** - * @return string - */ - public function __toString() + public function __toString(): string { $path = $this->path.$this->element; $condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']'; diff --git a/src/Symfony/Component/CssSelector/composer.json b/src/Symfony/Component/CssSelector/composer.json index 56ab35b81408a..e2ed078e364af 100644 --- a/src/Symfony/Component/CssSelector/composer.json +++ b/src/Symfony/Component/CssSelector/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Debug/CHANGELOG.md b/src/Symfony/Component/Debug/CHANGELOG.md index 31c67eb60cce9..122af73174bf4 100644 --- a/src/Symfony/Component/Debug/CHANGELOG.md +++ b/src/Symfony/Component/Debug/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.0.0 +----- + +* removed the symfony_debug extension +* removed `ContextErrorException` + 3.4.0 ----- diff --git a/src/Symfony/Component/Debug/Debug.php b/src/Symfony/Component/Debug/Debug.php index e3665ae5f40c8..09a3e7502226a 100644 --- a/src/Symfony/Component/Debug/Debug.php +++ b/src/Symfony/Component/Debug/Debug.php @@ -25,9 +25,6 @@ class Debug * * This method registers an error handler and an exception handler. * - * If the Symfony ClassLoader component is available, a special - * class loader is also registered. - * * @param int $errorReportingLevel The level of error reporting you want * @param bool $displayErrors Whether to display errors (for development) or just log them (for production) */ diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 33e715a869737..f9c80d041bcfb 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -34,7 +34,6 @@ class DebugClassLoader private static $deprecated = array(); private static $internal = array(); private static $internalMethods = array(); - private static $php7Reserved = array('int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1); private static $darwinCache = array('/' => array('/', array())); public function __construct(callable $classLoader) @@ -278,10 +277,6 @@ private function checkClass($class, $file = null) } } } - - if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) { - @trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); - } } if ($file) { diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 07e0861122a5d..00149b38e53d4 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -13,7 +13,6 @@ use Psr\Log\LogLevel; use Psr\Log\LoggerInterface; -use Symfony\Component\Debug\Exception\ContextErrorException; use Symfony\Component\Debug\Exception\FatalErrorException; use Symfony\Component\Debug\Exception\FatalThrowableError; use Symfony\Component\Debug\Exception\OutOfMemoryException; @@ -97,8 +96,6 @@ class ErrorHandler private $bootstrappingLogger; private static $reservedMemory; - private static $stackedErrors = array(); - private static $stackedErrorLevels = array(); private static $toStringException = null; private static $silencedErrorCache = array(); private static $silencedErrorCount = 0; @@ -396,10 +393,8 @@ public function handleError($type, $message, $file, $line) if (4 < $numArgs = func_num_args()) { $context = $scope ? (func_get_arg(4) ?: array()) : array(); - $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM } else { $context = array(); - $backtrace = null; } if (isset($context['GLOBALS']) && $scope) { @@ -408,15 +403,6 @@ public function handleError($type, $message, $file, $line) $context = $e; } - if (null !== $backtrace && $type & E_ERROR) { - // E_ERROR fatal errors are triggered on HHVM when - // hhvm.error_handling.call_user_handler_on_fatals=1 - // which is the way to get their backtrace. - $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace')); - - return true; - } - $logMessage = $this->levels[$type].': '.$message; if (null !== self::$toStringException) { @@ -446,19 +432,16 @@ public function handleError($type, $message, $file, $line) return; } } else { - if ($scope) { - $errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context); - } else { - $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); - } + $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); // Clean the trace by removing function arguments and the first frames added by the error handler itself. if ($throw || $this->tracedErrors & $type) { - $backtrace = $backtrace ?: $errorAsException->getTrace(); + $backtrace = $errorAsException->getTrace(); $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw); $this->traceReflector->setValue($errorAsException, $lightTrace); } else { $this->traceReflector->setValue($errorAsException, array()); + $backtrace = array(); } } @@ -472,32 +455,25 @@ public function handleError($type, $message, $file, $line) && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) ) { // Here, we know trigger_error() has been called from __toString(). - // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. + // PHP triggers a fatal error when throwing from __toString(). // A small convention allows working around the limitation: // given a caught $e exception in __toString(), quitting the method with // `return trigger_error($e, E_USER_ERROR);` allows this error handler // to make $e get through the __toString() barrier. foreach ($context as $e) { - if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { - if (1 === $i) { - // On HHVM - $errorAsException = $e; - break; - } + if ($e instanceof \Throwable && $e->__toString() === $message) { self::$toStringException = $e; return true; } } - if (1 < $i) { - // On PHP (not on HHVM), display the original error message instead of the default one. - $this->handleException($errorAsException); + // Display the original error message instead of the default one. + $this->handleException($errorAsException); - // Stop the process by giving back the error to the native handler. - return false; - } + // Stop the process by giving back the error to the native handler. + return false; } } } @@ -507,13 +483,6 @@ public function handleError($type, $message, $file, $line) if ($this->isRecursive) { $log = 0; - } elseif (self::$stackedErrorLevels) { - self::$stackedErrors[] = array( - $this->loggers[$type][0], - ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, - $logMessage, - $errorAsException ? array('exception' => $errorAsException) : array(), - ); } else { try { $this->isRecursive = true; @@ -567,7 +536,6 @@ public function handleException($exception, array $error = null) if ($this->loggedErrors & $type) { try { $this->loggers[$type][0]->log($this->loggers[$type][1], $message, array('exception' => $exception)); - } catch (\Exception $handlerException) { } catch (\Throwable $handlerException) { } } @@ -584,7 +552,6 @@ public function handleException($exception, array $error = null) return \call_user_func($this->exceptionHandler, $exception); } $handlerException = $handlerException ?: $exception; - } catch (\Exception $handlerException) { } catch (\Throwable $handlerException) { } $this->exceptionHandler = null; @@ -646,16 +613,6 @@ public static function handleFatalError(array $error = null) $error = error_get_last(); } - try { - while (self::$stackedErrorLevels) { - static::unstackErrors(); - } - } catch (\Exception $exception) { - // Handled below - } catch (\Throwable $exception) { - // Handled below - } - if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { // Let's not throw anymore but keep logging $handler->throwAt(0, true); @@ -666,10 +623,12 @@ public static function handleFatalError(array $error = null) } else { $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); } + } else { + $exception = null; } try { - if (isset($exception)) { + if (null !== $exception) { self::$exitCode = 255; $handler->handleException($exception, $error); } @@ -683,55 +642,6 @@ public static function handleFatalError(array $error = null) } } - /** - * Configures the error handler for delayed handling. - * Ensures also that non-catchable fatal errors are never silenced. - * - * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 - * PHP has a compile stage where it behaves unusually. To workaround it, - * we plug an error handler that only stacks errors for later. - * - * The most important feature of this is to prevent - * autoloading until unstackErrors() is called. - * - * @deprecated since version 3.4, to be removed in 4.0. - */ - public static function stackErrors() - { - @trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); - - self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); - } - - /** - * Unstacks stacked errors and forwards to the logger. - * - * @deprecated since version 3.4, to be removed in 4.0. - */ - public static function unstackErrors() - { - @trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); - - $level = array_pop(self::$stackedErrorLevels); - - if (null !== $level) { - $errorReportingLevel = error_reporting($level); - if ($errorReportingLevel !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) { - // If the user changed the error level, do not overwrite it - error_reporting($errorReportingLevel); - } - } - - if (empty(self::$stackedErrorLevels)) { - $errors = self::$stackedErrors; - self::$stackedErrors = array(); - - foreach ($errors as $error) { - $error[0]->log($error[1], $error[2], $error[3]); - } - } - } - /** * Gets the fatal error handlers. * diff --git a/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php b/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php index b91bf46631bbb..45d4c253c992a 100644 --- a/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php +++ b/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php @@ -18,7 +18,7 @@ */ class ClassNotFoundException extends FatalErrorException { - public function __construct($message, \ErrorException $previous) + public function __construct(string $message, \ErrorException $previous) { parent::__construct( $message, diff --git a/src/Symfony/Component/Debug/Exception/ContextErrorException.php b/src/Symfony/Component/Debug/Exception/ContextErrorException.php deleted file mode 100644 index 554139da3bf1d..0000000000000 --- a/src/Symfony/Component/Debug/Exception/ContextErrorException.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Error Exception with Variable Context. - * - * @author Christian Sciberras - * - * @deprecated since version 3.3. Instead, \ErrorException will be used directly in 4.0. - */ -class ContextErrorException extends \ErrorException -{ - private $context = array(); - - public function __construct($message, $code, $severity, $filename, $lineno, $context = array()) - { - parent::__construct($message, $code, $severity, $filename, $lineno); - $this->context = $context; - } - - /** - * @return array Array of variables that existed when the exception occurred - */ - public function getContext() - { - @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - - return $this->context; - } -} diff --git a/src/Symfony/Component/Debug/Exception/FatalErrorException.php b/src/Symfony/Component/Debug/Exception/FatalErrorException.php index f24a54e77a6ac..46c7ad72b5f16 100644 --- a/src/Symfony/Component/Debug/Exception/FatalErrorException.php +++ b/src/Symfony/Component/Debug/Exception/FatalErrorException.php @@ -18,7 +18,7 @@ */ class FatalErrorException extends \ErrorException { - public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null) + public function __construct(string $message, int $code, int $severity, string $filename, int $lineno, int $traceOffset = null, bool $traceArgs = true, array $trace = null) { parent::__construct($message, $code, $severity, $filename, $lineno); @@ -60,11 +60,6 @@ public function __construct($message, $code, $severity, $filename, $lineno, $tra unset($frame); $trace = array_reverse($trace); - } elseif (function_exists('symfony_debug_backtrace')) { - $trace = symfony_debug_backtrace(); - if (0 < $traceOffset) { - array_splice($trace, 0, $traceOffset); - } } else { $trace = array(); } diff --git a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php b/src/Symfony/Component/Debug/Exception/FatalThrowableError.php index 34f43b17b13b4..dd0d902466244 100644 --- a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php +++ b/src/Symfony/Component/Debug/Exception/FatalThrowableError.php @@ -18,21 +18,22 @@ */ class FatalThrowableError extends FatalErrorException { + private $originalClassName; + public function __construct(\Throwable $e) { + $this->originalClassName = \get_class($e); + if ($e instanceof \ParseError) { - $message = 'Parse error: '.$e->getMessage(); $severity = E_PARSE; } elseif ($e instanceof \TypeError) { - $message = 'Type error: '.$e->getMessage(); $severity = E_RECOVERABLE_ERROR; } else { - $message = $e->getMessage(); $severity = E_ERROR; } \ErrorException::__construct( - $message, + $e->getMessage(), $e->getCode(), $severity, $e->getFile(), @@ -41,4 +42,9 @@ public function __construct(\Throwable $e) $this->setTrace($e->getTrace()); } + + public function getOriginalClassName(): string + { + return $this->originalClassName; + } } diff --git a/src/Symfony/Component/Debug/Exception/FlattenException.php b/src/Symfony/Component/Debug/Exception/FlattenException.php index 24679dcaab242..aa6e47333b01c 100644 --- a/src/Symfony/Component/Debug/Exception/FlattenException.php +++ b/src/Symfony/Component/Debug/Exception/FlattenException.php @@ -53,7 +53,7 @@ public static function create(\Exception $exception, $statusCode = null, array $ $e->setStatusCode($statusCode); $e->setHeaders($headers); $e->setTraceFromException($exception); - $e->setClass(get_class($exception)); + $e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception)); $e->setFile($exception->getFile()); $e->setLine($exception->getLine()); @@ -157,7 +157,7 @@ public function getPrevious() return $this->previous; } - public function setPrevious(FlattenException $previous) + public function setPrevious(self $previous) { $this->previous = $previous; } diff --git a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php b/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php index 4be83491b9df7..6f84617c466f1 100644 --- a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php +++ b/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php @@ -25,7 +25,7 @@ class SilencedErrorContext implements \JsonSerializable private $line; private $trace; - public function __construct($severity, $file, $line, array $trace = array(), $count = 1) + public function __construct(int $severity, string $file, int $line, array $trace = array(), int $count = 1) { $this->severity = $severity; $this->file = $file; diff --git a/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php b/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php index a66ae2a3879c9..89710671d1d33 100644 --- a/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php +++ b/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php @@ -18,7 +18,7 @@ */ class UndefinedFunctionException extends FatalErrorException { - public function __construct($message, \ErrorException $previous) + public function __construct(string $message, \ErrorException $previous) { parent::__construct( $message, diff --git a/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php b/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php index 350dc3187f475..e895274711988 100644 --- a/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php +++ b/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php @@ -18,7 +18,7 @@ */ class UndefinedMethodException extends FatalErrorException { - public function __construct($message, \ErrorException $previous) + public function __construct(string $message, \ErrorException $previous) { parent::__construct( $message, diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index 97470cb6b4d01..a6ae71f8f1091 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -36,7 +36,7 @@ class ExceptionHandler private $caughtLength; private $fileLinkFormat; - public function __construct($debug = true, $charset = null, $fileLinkFormat = null) + public function __construct(bool $debug = true, string $charset = null, $fileLinkFormat = null) { $this->debug = $debug; $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php index 32ba9a09c5777..25030e5606d28 100644 --- a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php +++ b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -83,7 +83,7 @@ public function handleError(array $error, FatalErrorException $exception) * * @return array An array of possible fully qualified class names */ - private function getClassCandidates($class) + private function getClassCandidates(string $class): array { if (!is_array($functions = spl_autoload_functions())) { return array(); @@ -124,14 +124,7 @@ private function getClassCandidates($class) return array_unique($classes); } - /** - * @param string $path - * @param string $class - * @param string $prefix - * - * @return array - */ - private function findClassInPath($path, $class, $prefix) + private function findClassInPath(string $path, string $class, string $prefix): array { if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { return array(); @@ -148,14 +141,7 @@ private function findClassInPath($path, $class, $prefix) return $classes; } - /** - * @param string $path - * @param string $file - * @param string $prefix - * - * @return string|null - */ - private function convertFileToClass($path, $file, $prefix) + private function convertFileToClass(string $path, string $file, string $prefix): ?string { $candidates = array( // namespaced class @@ -192,14 +178,11 @@ private function convertFileToClass($path, $file, $prefix) return $candidate; } } + + return null; } - /** - * @param string $class - * - * @return bool - */ - private function classExists($class) + private function classExists(string $class): bool { return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); } diff --git a/src/Symfony/Component/Debug/Resources/ext/README.md b/src/Symfony/Component/Debug/Resources/ext/README.md deleted file mode 100644 index 25dccf0766470..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/README.md +++ /dev/null @@ -1,134 +0,0 @@ -Symfony Debug Extension for PHP 5 -================================= - -This extension publishes several functions to help building powerful debugging tools. -It is compatible with PHP 5.3, 5.4, 5.5 and 5.6; with ZTS and non-ZTS modes. -It is not required thus not provided for PHP 7. - -symfony_zval_info() -------------------- - -- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP, -- does work with references, preventing memory copying. - -Its behavior is about the same as: - -```php - gettype($array[$key]), - 'zval_hash' => /* hashed memory address of $array[$key] */, - 'zval_refcount' => /* internal zval refcount of $array[$key] */, - 'zval_isref' => /* is_ref status of $array[$key] */, - ); - - switch ($info['type']) { - case 'object': - $info += array( - 'object_class' => get_class($array[$key]), - 'object_refcount' => /* internal object refcount of $array[$key] */, - 'object_hash' => spl_object_hash($array[$key]), - 'object_handle' => /* internal object handle $array[$key] */, - ); - break; - - case 'resource': - $info += array( - 'resource_handle' => (int) $array[$key], - 'resource_type' => get_resource_type($array[$key]), - 'resource_refcount' => /* internal resource refcount of $array[$key] */, - ); - break; - - case 'array': - $info += array( - 'array_count' => count($array[$key]), - ); - break; - - case 'string': - $info += array( - 'strlen' => strlen($array[$key]), - ); - break; - } - - return $info; -} -``` - -symfony_debug_backtrace() -------------------------- - -This function works like debug_backtrace(), except that it can fetch the full backtrace in case of fatal errors: - -```php -function foo() { fatal(); } -function bar() { foo(); } - -function sd() { var_dump(symfony_debug_backtrace()); } - -register_shutdown_function('sd'); - -bar(); - -/* Will output -Fatal error: Call to undefined function fatal() in foo.php on line 42 -array(3) { - [0]=> - array(2) { - ["function"]=> - string(2) "sd" - ["args"]=> - array(0) { - } - } - [1]=> - array(4) { - ["file"]=> - string(7) "foo.php" - ["line"]=> - int(1) - ["function"]=> - string(3) "foo" - ["args"]=> - array(0) { - } - } - [2]=> - array(4) { - ["file"]=> - string(102) "foo.php" - ["line"]=> - int(2) - ["function"]=> - string(3) "bar" - ["args"]=> - array(0) { - } - } -} -*/ -``` - -Usage ------ - -To enable the extension from source, run: - -``` - phpize - ./configure - make - sudo make install -``` diff --git a/src/Symfony/Component/Debug/Resources/ext/config.m4 b/src/Symfony/Component/Debug/Resources/ext/config.m4 deleted file mode 100644 index 3c56047150569..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/config.m4 +++ /dev/null @@ -1,63 +0,0 @@ -dnl $Id$ -dnl config.m4 for extension symfony_debug - -dnl Comments in this file start with the string 'dnl'. -dnl Remove where necessary. This file will not work -dnl without editing. - -dnl If your extension references something external, use with: - -dnl PHP_ARG_WITH(symfony_debug, for symfony_debug support, -dnl Make sure that the comment is aligned: -dnl [ --with-symfony_debug Include symfony_debug support]) - -dnl Otherwise use enable: - -PHP_ARG_ENABLE(symfony_debug, whether to enable symfony_debug support, -dnl Make sure that the comment is aligned: -[ --enable-symfony_debug Enable symfony_debug support]) - -if test "$PHP_SYMFONY_DEBUG" != "no"; then - dnl Write more examples of tests here... - - dnl # --with-symfony_debug -> check with-path - dnl SEARCH_PATH="/usr/local /usr" # you might want to change this - dnl SEARCH_FOR="/include/symfony_debug.h" # you most likely want to change this - dnl if test -r $PHP_SYMFONY_DEBUG/$SEARCH_FOR; then # path given as parameter - dnl SYMFONY_DEBUG_DIR=$PHP_SYMFONY_DEBUG - dnl else # search default path list - dnl AC_MSG_CHECKING([for symfony_debug files in default path]) - dnl for i in $SEARCH_PATH ; do - dnl if test -r $i/$SEARCH_FOR; then - dnl SYMFONY_DEBUG_DIR=$i - dnl AC_MSG_RESULT(found in $i) - dnl fi - dnl done - dnl fi - dnl - dnl if test -z "$SYMFONY_DEBUG_DIR"; then - dnl AC_MSG_RESULT([not found]) - dnl AC_MSG_ERROR([Please reinstall the symfony_debug distribution]) - dnl fi - - dnl # --with-symfony_debug -> add include path - dnl PHP_ADD_INCLUDE($SYMFONY_DEBUG_DIR/include) - - dnl # --with-symfony_debug -> check for lib and symbol presence - dnl LIBNAME=symfony_debug # you may want to change this - dnl LIBSYMBOL=symfony_debug # you most likely want to change this - - dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, - dnl [ - dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SYMFONY_DEBUG_DIR/lib, SYMFONY_DEBUG_SHARED_LIBADD) - dnl AC_DEFINE(HAVE_SYMFONY_DEBUGLIB,1,[ ]) - dnl ],[ - dnl AC_MSG_ERROR([wrong symfony_debug lib version or lib not found]) - dnl ],[ - dnl -L$SYMFONY_DEBUG_DIR/lib -lm - dnl ]) - dnl - dnl PHP_SUBST(SYMFONY_DEBUG_SHARED_LIBADD) - - PHP_NEW_EXTENSION(symfony_debug, symfony_debug.c, $ext_shared) -fi diff --git a/src/Symfony/Component/Debug/Resources/ext/config.w32 b/src/Symfony/Component/Debug/Resources/ext/config.w32 deleted file mode 100644 index 487e6913891cf..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/config.w32 +++ /dev/null @@ -1,13 +0,0 @@ -// $Id$ -// vim:ft=javascript - -// If your extension references something external, use ARG_WITH -// ARG_WITH("symfony_debug", "for symfony_debug support", "no"); - -// Otherwise, use ARG_ENABLE -// ARG_ENABLE("symfony_debug", "enable symfony_debug support", "no"); - -if (PHP_SYMFONY_DEBUG != "no") { - EXTENSION("symfony_debug", "symfony_debug.c"); -} - diff --git a/src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h b/src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h deleted file mode 100644 index 26d0e8c012030..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#ifndef PHP_SYMFONY_DEBUG_H -#define PHP_SYMFONY_DEBUG_H - -extern zend_module_entry symfony_debug_module_entry; -#define phpext_symfony_debug_ptr &symfony_debug_module_entry - -#define PHP_SYMFONY_DEBUG_VERSION "2.7" - -#ifdef PHP_WIN32 -# define PHP_SYMFONY_DEBUG_API __declspec(dllexport) -#elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_SYMFONY_DEBUG_API __attribute__ ((visibility("default"))) -#else -# define PHP_SYMFONY_DEBUG_API -#endif - -#ifdef ZTS -#include "TSRM.h" -#endif - -ZEND_BEGIN_MODULE_GLOBALS(symfony_debug) - intptr_t req_rand_init; - void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); - zval *debug_bt; -ZEND_END_MODULE_GLOBALS(symfony_debug) - -PHP_MINIT_FUNCTION(symfony_debug); -PHP_MSHUTDOWN_FUNCTION(symfony_debug); -PHP_RINIT_FUNCTION(symfony_debug); -PHP_RSHUTDOWN_FUNCTION(symfony_debug); -PHP_MINFO_FUNCTION(symfony_debug); -PHP_GINIT_FUNCTION(symfony_debug); -PHP_GSHUTDOWN_FUNCTION(symfony_debug); - -PHP_FUNCTION(symfony_zval_info); -PHP_FUNCTION(symfony_debug_backtrace); - -static char *_symfony_debug_memory_address_hash(void * TSRMLS_DC); -static const char *_symfony_debug_zval_type(zval *); -static const char* _symfony_debug_get_resource_type(long TSRMLS_DC); -static int _symfony_debug_get_resource_refcount(long TSRMLS_DC); - -void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); - -#ifdef ZTS -#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v) -#else -#define SYMFONY_DEBUG_G(v) (symfony_debug_globals.v) -#endif - -#endif /* PHP_SYMFONY_DEBUG_H */ diff --git a/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c b/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c deleted file mode 100644 index 0d7cb602320f9..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#ifdef ZTS -#include "TSRM.h" -#endif -#include "php_ini.h" -#include "ext/standard/info.h" -#include "php_symfony_debug.h" -#include "ext/standard/php_rand.h" -#include "ext/standard/php_lcg.h" -#include "ext/spl/php_spl.h" -#include "Zend/zend_gc.h" -#include "Zend/zend_builtin_functions.h" -#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ -#include "ext/standard/php_array.h" -#include "Zend/zend_interfaces.h" -#include "SAPI.h" - -#define IS_PHP_53 ZEND_EXTENSION_API_NO == 220090626 - -ZEND_DECLARE_MODULE_GLOBALS(symfony_debug) - -ZEND_BEGIN_ARG_INFO_EX(symfony_zval_arginfo, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, array, 0) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -const zend_function_entry symfony_debug_functions[] = { - PHP_FE(symfony_zval_info, symfony_zval_arginfo) - PHP_FE(symfony_debug_backtrace, NULL) - PHP_FE_END -}; - -PHP_FUNCTION(symfony_debug_backtrace) -{ - if (zend_parse_parameters_none() == FAILURE) { - return; - } -#if IS_PHP_53 - zend_fetch_debug_backtrace(return_value, 1, 0 TSRMLS_CC); -#else - zend_fetch_debug_backtrace(return_value, 1, 0, 0 TSRMLS_CC); -#endif - - if (!SYMFONY_DEBUG_G(debug_bt)) { - return; - } - - php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(SYMFONY_DEBUG_G(debug_bt)), 0 TSRMLS_CC); -} - -PHP_FUNCTION(symfony_zval_info) -{ - zval *key = NULL, *arg = NULL; - zval **data = NULL; - HashTable *array = NULL; - long options = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zh|l", &key, &array, &options) == FAILURE) { - return; - } - - switch (Z_TYPE_P(key)) { - case IS_STRING: - if (zend_symtable_find(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&data) == FAILURE) { - return; - } - break; - case IS_LONG: - if (zend_hash_index_find(array, Z_LVAL_P(key), (void **)&data)) { - return; - } - break; - } - - arg = *data; - - array_init(return_value); - - add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1); - add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg TSRMLS_CC), 16, 0); - add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg)); - add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg)); - - if (Z_TYPE_P(arg) == IS_OBJECT) { - char hash[33] = {0}; - - php_spl_object_hash(arg, (char *)hash TSRMLS_CC); - add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1); - add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount); - add_assoc_string(return_value, "object_hash", hash, 1); - add_assoc_long(return_value, "object_handle", Z_OBJ_HANDLE_P(arg)); - } else if (Z_TYPE_P(arg) == IS_ARRAY) { - add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg))); - } else if(Z_TYPE_P(arg) == IS_RESOURCE) { - add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg)); - add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg) TSRMLS_CC), 1); - add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg) TSRMLS_CC)); - } else if (Z_TYPE_P(arg) == IS_STRING) { - add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg)); - } -} - -void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) -{ - TSRMLS_FETCH(); - zval *retval; - - switch (type) { - case E_ERROR: - case E_PARSE: - case E_CORE_ERROR: - case E_CORE_WARNING: - case E_COMPILE_ERROR: - case E_COMPILE_WARNING: - ALLOC_INIT_ZVAL(retval); -#if IS_PHP_53 - zend_fetch_debug_backtrace(retval, 1, 0 TSRMLS_CC); -#else - zend_fetch_debug_backtrace(retval, 1, 0, 0 TSRMLS_CC); -#endif - SYMFONY_DEBUG_G(debug_bt) = retval; - } - - SYMFONY_DEBUG_G(old_error_cb)(type, error_filename, error_lineno, format, args); -} - -static const char* _symfony_debug_get_resource_type(long rsid TSRMLS_DC) -{ - const char *res_type; - res_type = zend_rsrc_list_get_rsrc_type(rsid TSRMLS_CC); - - if (!res_type) { - return "Unknown"; - } - - return res_type; -} - -static int _symfony_debug_get_resource_refcount(long rsid TSRMLS_DC) -{ - zend_rsrc_list_entry *le; - - if (zend_hash_index_find(&EG(regular_list), rsid, (void **) &le)==SUCCESS) { - return le->refcount; - } - - return 0; -} - -static char *_symfony_debug_memory_address_hash(void *address TSRMLS_DC) -{ - char *result = NULL; - intptr_t address_rand; - - if (!SYMFONY_DEBUG_G(req_rand_init)) { - if (!BG(mt_rand_is_seeded)) { - php_mt_srand(GENERATE_SEED() TSRMLS_CC); - } - SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand(TSRMLS_C); - } - - address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init); - - spprintf(&result, 17, "%016zx", address_rand); - - return result; -} - -static const char *_symfony_debug_zval_type(zval *zv) -{ - switch (Z_TYPE_P(zv)) { - case IS_NULL: - return "NULL"; - break; - - case IS_BOOL: - return "boolean"; - break; - - case IS_LONG: - return "integer"; - break; - - case IS_DOUBLE: - return "double"; - break; - - case IS_STRING: - return "string"; - break; - - case IS_ARRAY: - return "array"; - break; - - case IS_OBJECT: - return "object"; - - case IS_RESOURCE: - return "resource"; - - default: - return "unknown type"; - } -} - -zend_module_entry symfony_debug_module_entry = { - STANDARD_MODULE_HEADER, - "symfony_debug", - symfony_debug_functions, - PHP_MINIT(symfony_debug), - PHP_MSHUTDOWN(symfony_debug), - PHP_RINIT(symfony_debug), - PHP_RSHUTDOWN(symfony_debug), - PHP_MINFO(symfony_debug), - PHP_SYMFONY_DEBUG_VERSION, - PHP_MODULE_GLOBALS(symfony_debug), - PHP_GINIT(symfony_debug), - PHP_GSHUTDOWN(symfony_debug), - NULL, - STANDARD_MODULE_PROPERTIES_EX -}; - -#ifdef COMPILE_DL_SYMFONY_DEBUG -ZEND_GET_MODULE(symfony_debug) -#endif - -PHP_GINIT_FUNCTION(symfony_debug) -{ - memset(symfony_debug_globals, 0 , sizeof(*symfony_debug_globals)); -} - -PHP_GSHUTDOWN_FUNCTION(symfony_debug) -{ - -} - -PHP_MINIT_FUNCTION(symfony_debug) -{ - SYMFONY_DEBUG_G(old_error_cb) = zend_error_cb; - zend_error_cb = symfony_debug_error_cb; - - return SUCCESS; -} - -PHP_MSHUTDOWN_FUNCTION(symfony_debug) -{ - zend_error_cb = SYMFONY_DEBUG_G(old_error_cb); - - return SUCCESS; -} - -PHP_RINIT_FUNCTION(symfony_debug) -{ - return SUCCESS; -} - -PHP_RSHUTDOWN_FUNCTION(symfony_debug) -{ - return SUCCESS; -} - -PHP_MINFO_FUNCTION(symfony_debug) -{ - php_info_print_table_start(); - php_info_print_table_header(2, "Symfony Debug support", "enabled"); - php_info_print_table_header(2, "Symfony Debug version", PHP_SYMFONY_DEBUG_VERSION); - php_info_print_table_end(); -} diff --git a/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt b/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt deleted file mode 100644 index 4a87cd3180b03..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt +++ /dev/null @@ -1,155 +0,0 @@ ---TEST-- -Test symfony_zval_info API ---SKIPIF-- - ---FILE-- - $int, - 'float' => $float, - 'str' => $str, - 'object' => $object, - 'array' => $array, - 'resource' => $resource, - 'null' => $null, - 'bool' => $bool, - 'refcount' => &$refcount2, -); - -var_dump(symfony_zval_info('int', $var)); -var_dump(symfony_zval_info('float', $var)); -var_dump(symfony_zval_info('str', $var)); -var_dump(symfony_zval_info('object', $var)); -var_dump(symfony_zval_info('array', $var)); -var_dump(symfony_zval_info('resource', $var)); -var_dump(symfony_zval_info('null', $var)); -var_dump(symfony_zval_info('bool', $var)); - -var_dump(symfony_zval_info('refcount', $var)); -var_dump(symfony_zval_info('not-exist', $var)); -?> ---EXPECTF-- -array(4) { - ["type"]=> - string(7) "integer" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(4) { - ["type"]=> - string(6) "double" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(5) { - ["type"]=> - string(6) "string" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["strlen"]=> - int(6) -} -array(8) { - ["type"]=> - string(6) "object" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["object_class"]=> - string(8) "stdClass" - ["object_refcount"]=> - int(1) - ["object_hash"]=> - string(32) "%s" - ["object_handle"]=> - int(%d) -} -array(5) { - ["type"]=> - string(5) "array" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["array_count"]=> - int(2) -} -array(7) { - ["type"]=> - string(8) "resource" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["resource_handle"]=> - int(%d) - ["resource_type"]=> - string(6) "stream" - ["resource_refcount"]=> - int(1) -} -array(4) { - ["type"]=> - string(4) "NULL" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(4) { - ["type"]=> - string(7) "boolean" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(4) { - ["type"]=> - string(7) "integer" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(3) - ["zval_isref"]=> - bool(true) -} -NULL diff --git a/src/Symfony/Component/Debug/Resources/ext/tests/002.phpt b/src/Symfony/Component/Debug/Resources/ext/tests/002.phpt deleted file mode 100644 index afc7bb49024d9..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/tests/002.phpt +++ /dev/null @@ -1,65 +0,0 @@ ---TEST-- -Test symfony_debug_backtrace in case of fatal error ---SKIPIF-- - ---FILE-- - ---EXPECTF-- -Fatal error: Call to undefined function notexist() in %s on line %d -Array -( - [0] => Array - ( - [function] => bt - [args] => Array - ( - ) - - ) - - [1] => Array - ( - [file] => %s - [line] => %d - [function] => foo - [args] => Array - ( - ) - - ) - - [2] => Array - ( - [file] => %s - [line] => %d - [function] => bar - [args] => Array - ( - ) - - ) - -) diff --git a/src/Symfony/Component/Debug/Resources/ext/tests/002_1.phpt b/src/Symfony/Component/Debug/Resources/ext/tests/002_1.phpt deleted file mode 100644 index 86de3e177d086..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/tests/002_1.phpt +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -Test symfony_debug_backtrace in case of non fatal error ---SKIPIF-- - ---FILE-- - ---EXPECTF-- -Array -( - [0] => Array - ( - [file] => %s - [line] => %d - [function] => bt - [args] => Array - ( - ) - - ) - - [1] => Array - ( - [file] => %s - [line] => %d - [function] => bar - [args] => Array - ( - ) - - ) - -) diff --git a/src/Symfony/Component/Debug/Resources/ext/tests/003.phpt b/src/Symfony/Component/Debug/Resources/ext/tests/003.phpt deleted file mode 100644 index ce3c4e0ab5647..0000000000000 --- a/src/Symfony/Component/Debug/Resources/ext/tests/003.phpt +++ /dev/null @@ -1,87 +0,0 @@ ---TEST-- -Test ErrorHandler in case of fatal error ---SKIPIF-- - ---FILE-- -setExceptionHandler('print_r'); - -if (function_exists('xdebug_disable')) { - xdebug_disable(); -} - -bar(); -?> ---EXPECTF-- -Fatal error: Call to undefined function Symfony\Component\Debug\notexist() in %s on line %d -Symfony\Component\Debug\Exception\UndefinedFunctionException Object -( - [message:protected] => Attempted to call function "notexist" from namespace "Symfony\Component\Debug". - [string:Exception:private] => - [code:protected] => 0 - [file:protected] => %s - [line:protected] => %d - [trace:Exception:private] => Array - ( - [0] => Array - ( -%A [function] => Symfony\Component\Debug\foo -%A [args] => Array - ( - ) - - ) - - [1] => Array - ( -%A [function] => Symfony\Component\Debug\bar -%A [args] => Array - ( - ) - - ) -%A - ) - - [previous:Exception:private] => - [severity:protected] => 1 -) diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php index 0219f53350869..25a7d3b19673c 100644 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Debug\DebugClassLoader; -use Symfony\Component\Debug\ErrorHandler; class DebugClassLoaderTest extends TestCase { @@ -76,72 +75,8 @@ class_exists(__NAMESPACE__.'\Fixtures\Throwing'); class_exists(__NAMESPACE__.'\Fixtures\Throwing'); } - public function testUnsilencing() - { - if (\PHP_VERSION_ID >= 70000) { - $this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.'); - } - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM is not handled in this test case.'); - } - - ob_start(); - - $this->iniSet('log_errors', 0); - $this->iniSet('display_errors', 1); - - // See below: this will fail with parse error - // but this should not be @-silenced. - @class_exists(__NAMESPACE__.'\TestingUnsilencing', true); - - $output = ob_get_clean(); - - $this->assertStringMatchesFormat('%aParse error%a', $output); - } - - public function testStacking() - { - // the ContextErrorException must not be loaded to test the workaround - // for https://bugs.php.net/65322. - if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) { - $this->markTestSkipped('The ContextErrorException class is already loaded.'); - } - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM is not handled in this test case.'); - } - - ErrorHandler::register(); - - try { - // Trigger autoloading + E_STRICT at compile time - // which in turn triggers $errorHandler->handle() - // that again triggers autoloading for ContextErrorException. - // Error stacking works around the bug above and everything is fine. - - eval(' - namespace '.__NAMESPACE__.'; - class ChildTestingStacking extends TestingStacking { function foo($bar) {} } - '); - $this->fail('ContextErrorException expected'); - } catch (\ErrorException $exception) { - // if an exception is thrown, the test passed - $this->assertStringStartsWith(__FILE__, $exception->getFile()); - if (\PHP_VERSION_ID < 70000) { - $this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage()); - $this->assertEquals(E_STRICT, $exception->getSeverity()); - } else { - $this->assertRegExp('/^Warning: Declaration/', $exception->getMessage()); - $this->assertEquals(E_WARNING, $exception->getSeverity()); - } - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - /** * @expectedException \RuntimeException - * @expectedExceptionMessage Case mismatch between loaded and declared class names */ public function testNameCaseMismatch() { @@ -163,7 +98,6 @@ class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); /** * @expectedException \RuntimeException - * @expectedExceptionMessage Case mismatch between loaded and declared class names */ public function testPsr4CaseMismatch() { @@ -262,32 +196,6 @@ class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true $this->assertSame($xError, $lastError); } - public function testReservedForPhp7() - { - if (\PHP_VERSION_ID >= 70000) { - $this->markTestSkipped('PHP7 already prevents using reserved names.'); - } - - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); - - class_exists('Test\\'.__NAMESPACE__.'\\Float', true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = array( - 'type' => E_USER_DEPRECATED, - 'message' => 'The "Test\Symfony\Component\Debug\Tests\Float" class uses the reserved name "Float", it will break on PHP 7 and higher', - ); - - $this->assertSame($xError, $lastError); - } - public function testExtendedFinalClass() { set_error_handler(function () { return false; }); diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 03dd8074121a7..0d57204481e13 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -299,9 +299,6 @@ public function testHandleDeprecation() @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array()); } - /** - * @group no-hhvm - */ public function testHandleException() { try { @@ -343,38 +340,6 @@ public function testHandleException() } } - /** - * @group legacy - */ - public function testErrorStacking() - { - try { - $handler = ErrorHandler::register(); - $handler->screamAt(E_USER_WARNING); - - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - - $logger - ->expects($this->exactly(2)) - ->method('log') - ->withConsecutive( - array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')), - array($this->equalTo(LogLevel::DEBUG), $this->equalTo('User Warning: Silenced warning')) - ) - ; - - $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING)); - - ErrorHandler::stackErrors(); - @trigger_error('Silenced warning', E_USER_WARNING); - $logger->log(LogLevel::WARNING, 'Dummy log'); - ErrorHandler::unstackErrors(); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - public function testBootstrappingLogger() { $bootLogger = new BufferingLogger(); @@ -426,9 +391,6 @@ public function testBootstrappingLogger() $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING))); } - /** - * @group no-hhvm - */ public function testSettingLoggerWhenExceptionIsBuffered() { $bootLogger = new BufferingLogger(); @@ -448,9 +410,6 @@ public function testSettingLoggerWhenExceptionIsBuffered() $handler->handleException($exception); } - /** - * @group no-hhvm - */ public function testHandleFatalError() { try { @@ -491,9 +450,6 @@ public function testHandleFatalError() } } - /** - * @requires PHP 7 - */ public function testHandleErrorException() { $exception = new \Error("Class 'Foo' not found"); @@ -508,41 +464,4 @@ public function testHandleErrorException() $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); } - - /** - * @group no-hhvm - */ - public function testHandleFatalErrorOnHHVM() - { - try { - $handler = ErrorHandler::register(); - - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $logger - ->expects($this->once()) - ->method('log') - ->with( - $this->equalTo(LogLevel::CRITICAL), - $this->equalTo('Fatal Error: foo') - ) - ; - - $handler->setDefaultLogger($logger, E_ERROR); - - $error = array( - 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors - 'message' => 'foo', - 'file' => 'bar', - 'line' => 123, - 'context' => array(123), - 'backtrace' => array(456), - ); - - call_user_func_array(array($handler, 'handleError'), $error); - $handler->handleFatalError($error); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } } diff --git a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php b/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php index 66171e3ae71ff..6c9b7f21386b6 100644 --- a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php +++ b/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Debug\Tests\Exception; use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\FatalThrowableError; use Symfony\Component\Debug\Exception\FlattenException; use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -123,6 +124,16 @@ public function testFlattenHttpException(\Exception $exception) $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception'); } + public function testThrowable() + { + $exception = new FatalThrowableError(new \DivisionByZeroError('Ouch', 42)); + $flattened = FlattenException::create($exception); + + $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.'); + $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.'); + $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error'); + } + /** * @dataProvider flattenDataProvider */ @@ -138,18 +149,15 @@ public function testPrevious(\Exception $exception) $this->assertSame(array($flattened2), $flattened->getAllPrevious()); } - /** - * @requires PHP 7.0 - */ public function testPreviousError() { $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42)); $flattened = FlattenException::create($exception)->getPrevious(); - $this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.'); + $this->assertEquals($flattened->getMessage(), 'Oh noes!', 'The message is copied from the original exception.'); $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.'); - $this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception'); + $this->assertEquals($flattened->getClass(), 'ParseError', 'The class is set to the class of the original exception'); } /** @@ -236,7 +244,7 @@ function () {}, $this->assertSame(array('object', 'stdClass'), $array[$i++]); $this->assertSame(array('object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'), $array[$i++]); $this->assertSame(array('incomplete-object', 'BogusTestClass'), $array[$i++]); - $this->assertSame(array('resource', defined('HHVM_VERSION') ? 'Directory' : 'stream'), $array[$i++]); + $this->assertSame(array('resource', 'stream'), $array[$i++]); $this->assertSame(array('resource', 'stream'), $array[$i++]); $args = $array[$i++]; diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php index 1dc2120045c2c..60153fc5ec2f2 100644 --- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php @@ -26,7 +26,7 @@ public function testUndefinedFunction($error, $translatedMessage) $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception); - // class names are case insensitive and PHP/HHVM do not return the same + // class names are case insensitive and PHP do not return the same $this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage())); $this->assertSame($error['type'], $exception->getSeverity()); $this->assertSame($error['file'], $exception->getFile()); diff --git a/src/Symfony/Component/Debug/composer.json b/src/Symfony/Component/Debug/composer.json index f98a5d07b5d6c..9b2deebe6864a 100644 --- a/src/Symfony/Component/Debug/composer.json +++ b/src/Symfony/Component/Debug/composer.json @@ -16,14 +16,14 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "psr/log": "~1.0" }, "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + "symfony/http-kernel": "<3.4" }, "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" + "symfony/http-kernel": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Debug\\": "" }, @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/DependencyInjection/Alias.php b/src/Symfony/Component/DependencyInjection/Alias.php index 8773b8389110f..55e385f674e48 100644 --- a/src/Symfony/Component/DependencyInjection/Alias.php +++ b/src/Symfony/Component/DependencyInjection/Alias.php @@ -17,13 +17,9 @@ class Alias private $public; private $private; - /** - * @param string $id Alias identifier - * @param bool $public If this alias is public - */ - public function __construct($id, $public = true) + public function __construct(string $id, bool $public = true) { - $this->id = (string) $id; + $this->id = $id; $this->public = $public; $this->private = 2 > func_num_args(); } diff --git a/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php index 19e0d2b97152c..3a6fab8b995ff 100644 --- a/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php +++ b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php @@ -20,14 +20,11 @@ class TaggedIteratorArgument extends IteratorArgument { private $tag; - /** - * @param string $tag - */ - public function __construct($tag) + public function __construct(string $tag) { parent::__construct(array()); - $this->tag = (string) $tag; + $this->tag = $tag; } public function getTag() diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index a004161b963d9..3ba2fd634746f 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,78 @@ CHANGELOG ========= +4.1.0 +----- + + * added support for variadics in named arguments + * added PSR-11 `ContainerBagInterface` and its `ContainerBag` implementation to access parameters as-a-service + +4.0.0 +----- + + * Relying on service auto-registration while autowiring is not supported anymore. + Explicitly inject your dependencies or create services whose ids are + their fully-qualified class name. + + Before: + + ```php + namespace App\Controller; + + use App\Mailer; + + class DefaultController + { + public function __construct(Mailer $mailer) { + // ... + } + + // ... + } + ``` + ```yml + services: + App\Controller\DefaultController: + autowire: true + ``` + + After: + + ```php + // same PHP code + ``` + ```yml + services: + App\Controller\DefaultController: + autowire: true + + # or + # App\Controller\DefaultController: + # arguments: { $mailer: "@App\Mailer" } + + App\Mailer: + autowire: true + ``` + * removed autowiring services based on the types they implement + * added a third `$methodName` argument to the `getProxyFactoryCode()` method + of the `DumperInterface` + * removed support for autowiring types + * removed `Container::isFrozen` + * removed support for dumping an ucompiled container in `PhpDumper` + * removed support for generating a dumped `Container` without populating the method map + * removed support for case insensitive service identifiers + * removed the `DefinitionDecorator` class, replaced by `ChildDefinition` + * removed the `AutowireServiceResource` class and related `AutowirePass::createResourceForClass()` method + * removed `LoggingFormatter`, `Compiler::getLoggingFormatter()` and `addLogMessage()` class and methods, use the `ContainerBuilder::log()` method instead + * removed `FactoryReturnTypePass` + * removed `ContainerBuilder::addClassResource()`, use the `addObjectResource()` or the `getReflectionClass()` method instead. + * removed support for top-level anonymous services + * removed silent behavior for unused attributes and elements + * removed support for setting and accessing private services in `Container` + * removed support for setting pre-defined services in `Container` + * removed support for case insensitivity of parameter names + * removed `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead + 3.4.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php index f363a64829c24..bcb7ff4838f64 100644 --- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php @@ -27,7 +27,7 @@ class ChildDefinition extends Definition /** * @param string $parent The id of Definition instance to decorate */ - public function __construct($parent) + public function __construct(string $parent) { $this->parent = $parent; $this->setPrivate(false); @@ -130,5 +130,3 @@ public function setBindings(array $bindings) throw new BadMethodCallException('A ChildDefinition cannot have bindings set on it.'); } } - -class_alias(ChildDefinition::class, DefinitionDecorator::class); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index 23a5abc9f58ea..95a1e5a9284aa 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -40,9 +40,9 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe /** * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls */ - public function __construct($onlyConstructorArguments = false) + public function __construct(bool $onlyConstructorArguments = false) { - $this->onlyConstructorArguments = (bool) $onlyConstructorArguments; + $this->onlyConstructorArguments = $onlyConstructorArguments; } /** @@ -125,28 +125,21 @@ protected function processValue($value, $isRoot = false) return $value; } - /** - * Returns a service definition given the full name or an alias. - * - * @param string $id A full id or alias for a service definition - * - * @return Definition|null The definition related to the supplied id - */ - private function getDefinition($id) + private function getDefinition(string $id): ?Definition { $id = $this->getDefinitionId($id); return null === $id ? null : $this->container->getDefinition($id); } - private function getDefinitionId($id) + private function getDefinitionId(string $id): ?string { while ($this->container->hasAlias($id)) { $id = (string) $this->container->getAlias($id); } if (!$this->container->hasDefinition($id)) { - return; + return null; } return $id; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php deleted file mode 100644 index 9a4c74d3590ed..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -@trigger_error('The '.__NAMESPACE__.'\AutowireExceptionPass class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the DefinitionErrorExceptionPass class instead.', E_USER_DEPRECATED); - -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * Throws autowire exceptions from AutowirePass for definitions that still exist. - * - * @deprecated since version 3.4, will be removed in 4.0. - * - * @author Ryan Weaver - */ -class AutowireExceptionPass implements CompilerPassInterface -{ - private $autowirePass; - private $inlineServicePass; - - public function __construct(AutowirePass $autowirePass, InlineServiceDefinitionsPass $inlineServicePass) - { - $this->autowirePass = $autowirePass; - $this->inlineServicePass = $inlineServicePass; - } - - public function process(ContainerBuilder $container) - { - // the pass should only be run once - if (null === $this->autowirePass || null === $this->inlineServicePass) { - return; - } - - $inlinedIds = $this->inlineServicePass->getInlinedServiceIds(); - $exceptions = $this->autowirePass->getAutowiringExceptions(); - - // free up references - $this->autowirePass = null; - $this->inlineServicePass = null; - - foreach ($exceptions as $exception) { - if ($this->doesServiceExistInTheContainer($exception->getServiceId(), $container, $inlinedIds)) { - throw $exception; - } - } - } - - private function doesServiceExistInTheContainer($serviceId, ContainerBuilder $container, array $inlinedIds) - { - if ($container->hasDefinition($serviceId)) { - return true; - } - - // was the service inlined? Of so, does its parent service exist? - if (isset($inlinedIds[$serviceId])) { - foreach ($inlinedIds[$serviceId] as $parentId) { - if ($this->doesServiceExistInTheContainer($parentId, $container, $inlinedIds)) { - return true; - } - } - } - - return false; - } -} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index ae58f62bd0813..cd6ad8721827e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -12,7 +12,6 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\Config\Resource\ClassExistenceResource; -use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; @@ -28,78 +27,31 @@ */ class AutowirePass extends AbstractRecursivePass { - private $definedTypes = array(); private $types; private $ambiguousServiceTypes = array(); private $autowired = array(); private $lastFailure; private $throwOnAutowiringException; - private $autowiringExceptions = array(); - private $strictMode; - /** - * @param bool $throwOnAutowireException Errors can be retrieved via Definition::getErrors() - */ - public function __construct($throwOnAutowireException = true) + public function __construct(bool $throwOnAutowireException = true) { $this->throwOnAutowiringException = $throwOnAutowireException; } - /** - * @deprecated since version 3.4, to be removed in 4.0. - * - * @return AutowiringFailedException[] - */ - public function getAutowiringExceptions() - { - @trigger_error('Calling AutowirePass::getAutowiringExceptions() is deprecated since Symfony 3.4 and will be removed in 4.0. Use Definition::getErrors() instead.', E_USER_DEPRECATED); - - return $this->autowiringExceptions; - } - /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { - // clear out any possibly stored exceptions from before - $this->autowiringExceptions = array(); - $this->strictMode = $container->hasParameter('container.autowiring.strict_mode') && $container->getParameter('container.autowiring.strict_mode'); - try { parent::process($container); } finally { - $this->definedTypes = array(); $this->types = null; $this->ambiguousServiceTypes = array(); $this->autowired = array(); } } - /** - * Creates a resource to help know if this service has changed. - * - * @param \ReflectionClass $reflectionClass - * - * @return AutowireServiceResource - * - * @deprecated since version 3.3, to be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead. - */ - public static function createResourceForClass(\ReflectionClass $reflectionClass) - { - @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.', E_USER_DEPRECATED); - - $metadata = array(); - - foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { - if (!$reflectionMethod->isStatic()) { - $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod); - } - } - - return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata); - } - /** * {@inheritdoc} */ @@ -112,7 +64,6 @@ protected function processValue($value, $isRoot = false) throw $e; } - $this->autowiringExceptions[] = $e; $this->container->getDefinition($this->currentId)->addError($e->getMessage()); return parent::processValue($value, $isRoot); @@ -197,19 +148,16 @@ private function autowireCalls(\ReflectionClass $reflectionClass, array $methodC /** * Autowires the constructor or a method. * - * @param \ReflectionFunctionAbstract $reflectionMethod - * @param array $arguments - * * @return array The autowired arguments * * @throws AutowiringFailedException */ - private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments) + private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments): array { $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; $method = $reflectionMethod->name; $parameters = $reflectionMethod->getParameters(); - if (method_exists('ReflectionMethod', 'isVariadic') && $reflectionMethod->isVariadic()) { + if ($reflectionMethod->isVariadic()) { array_pop($parameters); } @@ -285,38 +233,21 @@ private function getAutowiredReference(TypedReference $reference, $deprecationMe return $reference; } - if (null === $this->types) { - $this->populateAvailableTypes(); - } - - if (isset($this->definedTypes[$type])) { - return new TypedReference($this->types[$type], $type); + if (!$reference->canBeAutoregistered()) { + return; } - if (!$this->strictMode && isset($this->types[$type])) { - $message = 'Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won\'t be supported in version 4.0.'; - if ($aliasSuggestion = $this->getAliasesSuggestionForType($type = $reference->getType(), $deprecationMessage)) { - $message .= ' '.$aliasSuggestion; - } else { - $message .= sprintf(' You should %s the "%s" service to "%s" instead.', isset($this->types[$this->types[$type]]) ? 'alias' : 'rename (or alias)', $this->types[$type], $type); - } - - @trigger_error($message, E_USER_DEPRECATED); - - return new TypedReference($this->types[$type], $type); + if (null === $this->types) { + $this->populateAvailableTypes(); } - if (!$reference->canBeAutoregistered() || isset($this->types[$type]) || isset($this->ambiguousServiceTypes[$type])) { + if (isset($this->types[$type]) || isset($this->ambiguousServiceTypes[$type])) { return; } if (isset($this->autowired[$type])) { return $this->autowired[$type] ? new TypedReference($this->autowired[$type], $type) : null; } - - if (!$this->strictMode) { - return $this->createAutowiredDefinition($type); - } } /** @@ -333,23 +264,14 @@ private function populateAvailableTypes() /** * Populates the list of available types for a given definition. - * - * @param string $id - * @param Definition $definition */ - private function populateAvailableType($id, Definition $definition) + private function populateAvailableType(string $id, Definition $definition) { // Never use abstract services if ($definition->isAbstract()) { return; } - foreach ($definition->getAutowiringTypes(false) as $type) { - $this->definedTypes[$type] = true; - $this->types[$type] = $id; - unset($this->ambiguousServiceTypes[$type]); - } - if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id) || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) { return; } @@ -365,16 +287,9 @@ private function populateAvailableType($id, Definition $definition) /** * Associates a type and a service id if applicable. - * - * @param string $type - * @param string $id */ - private function set($type, $id) + private function set(string $type, string $id) { - if (isset($this->definedTypes[$type])) { - return; - } - // is this already a type/class that is known to match multiple services? if (isset($this->ambiguousServiceTypes[$type])) { $this->ambiguousServiceTypes[$type][] = $id; @@ -397,49 +312,6 @@ private function set($type, $id) $this->ambiguousServiceTypes[$type][] = $id; } - /** - * Registers a definition for the type if possible or throws an exception. - * - * @param string $type - * - * @return TypedReference|null A reference to the registered definition - */ - private function createAutowiredDefinition($type) - { - if (!($typeHint = $this->container->getReflectionClass($type, false)) || !$typeHint->isInstantiable()) { - return; - } - - $currentId = $this->currentId; - $this->currentId = $type; - $this->autowired[$type] = $argumentId = sprintf('autowired.%s', $type); - $argumentDefinition = new Definition($type); - $argumentDefinition->setPublic(false); - $argumentDefinition->setAutowired(true); - - try { - $originalThrowSetting = $this->throwOnAutowiringException; - $this->throwOnAutowiringException = true; - $this->processValue($argumentDefinition, true); - $this->container->setDefinition($argumentId, $argumentDefinition); - } catch (AutowiringFailedException $e) { - $this->autowired[$type] = false; - $this->lastFailure = $e->getMessage(); - $this->container->log($this, $this->lastFailure); - - return; - } finally { - $this->throwOnAutowiringException = $originalThrowSetting; - $this->currentId = $currentId; - } - - @trigger_error(sprintf('Relying on service auto-registration for type "%s" is deprecated since Symfony 3.4 and won\'t be supported in 4.0. Create a service named "%s" instead.', $type, $type), E_USER_DEPRECATED); - - $this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId)); - - return new TypedReference($argumentId, $type); - } - private function createTypeNotFoundMessage(TypedReference $reference, $label) { if (!$r = $this->container->getReflectionClass($type = $reference->getType(), false)) { @@ -480,7 +352,10 @@ private function createTypeAlternatives(TypedReference $reference) return ' '.$message; } - if (isset($this->ambiguousServiceTypes[$type])) { + $servicesAndAliases = $this->container->getServiceIds(); + if (!$this->container->has($type) && false !== $key = array_search(strtolower($type), array_map('strtolower', $servicesAndAliases))) { + return sprintf(' Did you mean "%s"?', $servicesAndAliases[$key]); + } elseif (isset($this->ambiguousServiceTypes[$type])) { $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type])); } elseif (isset($this->types[$type])) { $message = sprintf('the existing "%s" service', $this->types[$type]); @@ -493,31 +368,6 @@ private function createTypeAlternatives(TypedReference $reference) return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message); } - /** - * @deprecated since version 3.3, to be removed in 4.0. - */ - private static function getResourceMetadataForMethod(\ReflectionMethod $method) - { - $methodArgumentsMetadata = array(); - foreach ($method->getParameters() as $parameter) { - try { - $class = $parameter->getClass(); - } catch (\ReflectionException $e) { - // type-hint is against a non-existent class - $class = false; - } - - $isVariadic = method_exists($parameter, 'isVariadic') && $parameter->isVariadic(); - $methodArgumentsMetadata[] = array( - 'class' => $class, - 'isOptional' => $parameter->isOptional(), - 'defaultValue' => ($parameter->isOptional() && !$isVariadic) ? $parameter->getDefaultValue() : null, - ); - } - - return $methodArgumentsMetadata; - } - private function getAliasesSuggestionForType($type, $extraContext = null) { $aliases = array(); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php index 7f032058ab139..6e5e9042c8c20 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php @@ -24,7 +24,7 @@ class CheckArgumentsValidityPass extends AbstractRecursivePass { private $throwExceptions; - public function __construct($throwExceptions = true) + public function __construct(bool $throwExceptions = true) { $this->throwExceptions = $throwExceptions; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php index d7d44d90ce6d9..ba6356f4aceb9 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php @@ -38,7 +38,7 @@ public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $id => $definition) { // synthetic service is public - if ($definition->isSynthetic() && !($definition->isPublic() || $definition->isPrivate())) { + if ($definition->isSynthetic() && !$definition->isPublic()) { throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id)); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php index 2a0d13641d9d2..7c797a92b3d54 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php @@ -23,7 +23,6 @@ class Compiler { private $passConfig; private $log = array(); - private $loggingFormatter; private $serviceReferenceGraph; public function __construct() @@ -52,24 +51,6 @@ public function getServiceReferenceGraph() return $this->serviceReferenceGraph; } - /** - * Returns the logging formatter which can be used by compilation passes. - * - * @return LoggingFormatter - * - * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead. - */ - public function getLoggingFormatter() - { - if (null === $this->loggingFormatter) { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', __METHOD__), E_USER_DEPRECATED); - - $this->loggingFormatter = new LoggingFormatter(); - } - - return $this->loggingFormatter; - } - /** * Adds a pass to the PassConfig. * @@ -77,42 +58,15 @@ public function getLoggingFormatter() * @param string $type The type of the pass * @param int $priority Used to sort the passes */ - public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/) + public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { - if (func_num_args() >= 3) { - $priority = func_get_arg(2); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED); - } - } - - $priority = 0; - } - $this->passConfig->addPass($pass, $type, $priority); } - /** - * Adds a log message. - * - * @param string $string The log message - * - * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead. - */ - public function addLogMessage($string) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', __METHOD__), E_USER_DEPRECATED); - - $this->log[] = $string; - } - /** * @final */ - public function log(CompilerPassInterface $pass, $message) + public function log(CompilerPassInterface $pass, string $message) { if (false !== strpos($message, "\n")) { $message = str_replace("\n", "\n".get_class($pass).': ', trim($message)); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index 99234812d65ca..f36293c6cff2a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -54,16 +54,10 @@ public function process(ContainerBuilder $container) } else { $decoratedDefinition = $container->getDefinition($inner); $definition->setTags(array_merge($decoratedDefinition->getTags(), $definition->getTags())); - if ($types = array_merge($decoratedDefinition->getAutowiringTypes(false), $definition->getAutowiringTypes(false))) { - $definition->setAutowiringTypes($types); - } $public = $decoratedDefinition->isPublic(); $private = $decoratedDefinition->isPrivate(); $decoratedDefinition->setPublic(false); $decoratedDefinition->setTags(array()); - if ($decoratedDefinition->getAutowiringTypes(false)) { - $decoratedDefinition->setAutowiringTypes(array()); - } $container->setDefinition($renamedId, $decoratedDefinition); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/FactoryReturnTypePass.php b/src/Symfony/Component/DependencyInjection/Compiler/FactoryReturnTypePass.php deleted file mode 100644 index 91497086c21de..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Compiler/FactoryReturnTypePass.php +++ /dev/null @@ -1,112 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -/** - * @author Guilhem N. - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class FactoryReturnTypePass implements CompilerPassInterface -{ - private $resolveClassPass; - - public function __construct(ResolveClassPass $resolveClassPass = null) - { - if (null === $resolveClassPass) { - @trigger_error('The '.__CLASS__.' class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); - } - $this->resolveClassPass = $resolveClassPass; - } - - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) - { - // works only since php 7.0 and hhvm 3.11 - if (!method_exists(\ReflectionMethod::class, 'getReturnType')) { - return; - } - $resolveClassPassChanges = null !== $this->resolveClassPass ? $this->resolveClassPass->getChanges() : array(); - - foreach ($container->getDefinitions() as $id => $definition) { - $this->updateDefinition($container, $id, $definition, $resolveClassPassChanges); - } - } - - private function updateDefinition(ContainerBuilder $container, $id, Definition $definition, array $resolveClassPassChanges, array $previous = array()) - { - // circular reference - $lcId = strtolower($id); - if (isset($previous[$lcId])) { - return; - } - - $factory = $definition->getFactory(); - if (null === $factory || (!isset($resolveClassPassChanges[$lcId]) && null !== $definition->getClass())) { - return; - } - - $class = null; - if (is_string($factory)) { - try { - $m = new \ReflectionFunction($factory); - if (false !== $m->getFileName() && file_exists($m->getFileName())) { - $container->fileExists($m->getFileName()); - } - } catch (\ReflectionException $e) { - return; - } - } else { - if ($factory[0] instanceof Reference) { - $previous[$lcId] = true; - $factoryDefinition = $container->findDefinition((string) $factory[0]); - $this->updateDefinition($container, $factory[0], $factoryDefinition, $resolveClassPassChanges, $previous); - $class = $factoryDefinition->getClass(); - } else { - $class = $factory[0]; - } - - if (!$m = $container->getReflectionClass($class, false)) { - return; - } - try { - $m = $m->getMethod($factory[1]); - } catch (\ReflectionException $e) { - return; - } - } - - $returnType = $m->getReturnType(); - if (null !== $returnType && !$returnType->isBuiltin()) { - $returnType = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : $returnType->__toString(); - if (null !== $class) { - $declaringClass = $m->getDeclaringClass()->getName(); - if ('self' === strtolower($returnType)) { - $returnType = $declaringClass; - } elseif ('parent' === strtolower($returnType)) { - $returnType = get_parent_class($declaringClass) ?: null; - } - } - - if (null !== $returnType && (!isset($resolveClassPassChanges[$lcId]) || $returnType !== $resolveClassPassChanges[$lcId])) { - @trigger_error(sprintf('Relying on its factory\'s return-type to define the class of service "%s" is deprecated since Symfony 3.3 and won\'t work in 4.0. Set the "class" attribute to "%s" on the service definition instead.', $id, $returnType), E_USER_DEPRECATED); - } - $definition->setClass($returnType); - } - } -} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index c704891856f9e..00a1e1f99fced 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -24,7 +24,6 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface { private $cloningIds = array(); - private $inlinedServiceIds = array(); /** * {@inheritdoc} @@ -34,22 +33,6 @@ public function setRepeatedPass(RepeatedPass $repeatedPass) // no-op for BC } - /** - * Returns an array of all services inlined by this pass. - * - * The key is the inlined service id and its value is the list of services it was inlined into. - * - * @deprecated since version 3.4, to be removed in 4.0. - * - * @return array - */ - public function getInlinedServiceIds() - { - @trigger_error('Calling InlineServiceDefinitionsPass::getInlinedServiceIds() is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); - - return $this->inlinedServiceIds; - } - /** * {@inheritdoc} */ @@ -78,7 +61,6 @@ protected function processValue($value, $isRoot = false) } $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); - $this->inlinedServiceIds[$id][] = $this->currentId; if ($definition->isShared()) { return $definition; @@ -110,7 +92,7 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe return true; } - if ($definition->isDeprecated() || $definition->isPublic() || $definition->isPrivate() || $definition->isLazy()) { + if ($definition->isDeprecated() || $definition->isPublic() || $definition->isLazy()) { return false; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php b/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php deleted file mode 100644 index a9d8258de9926..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -@trigger_error('The '.__NAMESPACE__.'\LoggingFormatter class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', E_USER_DEPRECATED); - -/** - * Used to format logging messages during the compilation. - * - * @author Johannes M. Schmitt - * - * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead. - */ -class LoggingFormatter -{ - public function formatRemoveService(CompilerPassInterface $pass, $id, $reason) - { - return $this->format($pass, sprintf('Removed service "%s"; reason: %s.', $id, $reason)); - } - - public function formatInlineService(CompilerPassInterface $pass, $id, $target) - { - return $this->format($pass, sprintf('Inlined service "%s" to "%s".', $id, $target)); - } - - public function formatUpdateReference(CompilerPassInterface $pass, $serviceId, $oldDestId, $newDestId) - { - return $this->format($pass, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $serviceId, $oldDestId, $newDestId)); - } - - public function formatResolveInheritance(CompilerPassInterface $pass, $childId, $parentId) - { - return $this->format($pass, sprintf('Resolving inheritance for "%s" (parent: %s).', $childId, $parentId)); - } - - public function formatUnusedAutowiringPatterns(CompilerPassInterface $pass, $id, array $patterns) - { - return $this->format($pass, sprintf('Autowiring\'s patterns "%s" for service "%s" don\'t match any method.', implode('", "', $patterns), $id)); - } - - public function format(CompilerPassInterface $pass, $message) - { - return sprintf('%s: %s', get_class($pass), $message); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php index c36535927f8c6..e077529b59185 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php @@ -153,7 +153,7 @@ public function __construct(ExtensionInterface $extension, ParameterBagInterface /** * {@inheritdoc} */ - public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/) + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { throw new LogicException(sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', get_class($pass), $this->extensionClass)); } @@ -169,7 +169,7 @@ public function registerExtension(ExtensionInterface $extension) /** * {@inheritdoc} */ - public function compile($resolveEnvPlaceholders = false) + public function compile(bool $resolveEnvPlaceholders = false) { throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass)); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index d1a308328a260..3a930134799c6 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -41,7 +41,7 @@ public function __construct() $this->beforeOptimizationPasses = array( 100 => array( - $resolveClassPass = new ResolveClassPass(), + new ResolveClassPass(), new ResolveInstanceofConditionalsPass(), new RegisterEnvVarProcessorsPass(), ), @@ -54,7 +54,6 @@ public function __construct() new DecoratorServicePass(), new ResolveParameterPlaceHoldersPass(false), new ResolveFactoryClassPass(), - new FactoryReturnTypePass($resolveClassPass), new CheckDefinitionValidityPass(), new RegisterServiceSubscribersPass(), new ResolveNamedArgumentsPass(), @@ -119,21 +118,8 @@ public function getPasses() * * @throws InvalidArgumentException when a pass type doesn't exist */ - public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/) + public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { - if (func_num_args() >= 3) { - $priority = func_get_arg(2); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED); - } - } - - $priority = 0; - } - $property = $type.'Passes'; if (!isset($this->$property)) { throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php index 03d9e1d8a59ab..75b36d227eab3 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php @@ -28,7 +28,7 @@ class RemovePrivateAliasesPass implements CompilerPassInterface public function process(ContainerBuilder $container) { foreach ($container->getAliases() as $id => $alias) { - if ($alias->isPublic() || $alias->isPrivate()) { + if ($alias->isPublic()) { continue; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php index c0771890a453c..ec2eed27edbc8 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php @@ -39,7 +39,7 @@ public function process(ContainerBuilder $container) $hasChanged = false; foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isPublic() || $definition->isPrivate()) { + if ($definition->isPublic()) { continue; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php index bc86e5ab9d11e..9a27b371b247c 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php @@ -56,7 +56,7 @@ public function process(ContainerBuilder $container) } catch (InvalidArgumentException $e) { throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e); } - if ($definition->isPublic() || $definition->isPrivate()) { + if ($definition->isPublic()) { continue; } // Remove private definition and schedule for replacement diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php index a124e472aca81..0ad3fe1dcd509 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php @@ -89,9 +89,6 @@ private function doResolveDefinition(ChildDefinition $definition) $def->setArguments($parentDef->getArguments()); $def->setMethodCalls($parentDef->getMethodCalls()); $def->setProperties($parentDef->getProperties()); - if ($parentDef->getAutowiringTypes(false)) { - $def->setAutowiringTypes($parentDef->getAutowiringTypes(false)); - } if ($parentDef->isDeprecated()) { $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%')); } @@ -166,11 +163,6 @@ private function doResolveDefinition(ChildDefinition $definition) $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); } - // merge autowiring types - foreach ($definition->getAutowiringTypes(false) as $autowiringType) { - $def->addAutowiringType($autowiringType); - } - // these attributes are always taken from the child $def->setAbstract($definition->isAbstract()); $def->setTags($definition->getTags()); @@ -181,5 +173,3 @@ private function doResolveDefinition(ChildDefinition $definition) return $def; } } - -class_alias(ResolveChildDefinitionsPass::class, ResolveDefinitionTemplatesPass::class); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php index c40d6f5d3d0f1..0235e5abb8e3b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php @@ -20,8 +20,6 @@ */ class ResolveClassPass implements CompilerPassInterface { - private $changes = array(); - /** * {@inheritdoc} */ @@ -35,22 +33,8 @@ public function process(ContainerBuilder $container) if ($definition instanceof ChildDefinition && !class_exists($id)) { throw new InvalidArgumentException(sprintf('Service definition "%s" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id)); } - $this->changes[strtolower($id)] = $id; $definition->setClass($id); } } } - - /** - * @internal - * - * @deprecated since 3.3, to be removed in 4.0. - */ - public function getChanges() - { - $changes = $this->changes; - $this->changes = array(); - - return $changes; - } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php deleted file mode 100644 index d48eff2214635..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -@trigger_error('The '.__NAMESPACE__.'\ResolveDefinitionTemplatesPass class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the ResolveChildDefinitionsPass class instead.', E_USER_DEPRECATED); - -class_exists(ResolveChildDefinitionsPass::class); - -if (false) { - /** - * This definition decorates another definition. - * - * @author Johannes M. Schmitt - * - * @deprecated The ResolveDefinitionTemplatesPass class is deprecated since version 3.4 and will be removed in 4.0. Use the ResolveChildDefinitionsPass class instead. - */ - class ResolveDefinitionTemplatesPass extends AbstractRecursivePass - { - } -} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php index cd6bb6fe8c58a..2dc53da89a5ec 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php @@ -55,7 +55,13 @@ protected function processValue($value, $isRoot = false) if (isset($key[0]) && '$' === $key[0]) { foreach ($parameters as $j => $p) { if ($key === '$'.$p->name) { - $resolvedArguments[$j] = $argument; + if ($p->isVariadic() && \is_array($argument)) { + foreach ($argument as $variadicArgument) { + $resolvedArguments[$j++] = $variadicArgument; + } + } else { + $resolvedArguments[$j] = $argument; + } continue 2; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php index 9733e5d094333..3988f6dc2783e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php @@ -25,7 +25,7 @@ class ResolveParameterPlaceHoldersPass extends AbstractRecursivePass private $bag; private $resolveArrays; - public function __construct($resolveArrays = true) + public function __construct(bool $resolveArrays = true) { $this->resolveArrays = $resolveArrays; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php index a46c74fbb65f1..83677a0dd3096 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php @@ -53,15 +53,7 @@ protected function processValue($value, $isRoot = false) return parent::processValue($value); } - /** - * Resolves an alias into a definition id. - * - * @param string $id The definition or alias id to resolve - * @param ContainerBuilder $container - * - * @return string The definition id with aliases resolved - */ - private function getDefinitionId($id, ContainerBuilder $container) + private function getDefinitionId(string $id, ContainerBuilder $container): string { $seen = array(); while ($container->hasAlias($id)) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php index d040e66c4dac7..5a370398408dc 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php @@ -30,14 +30,7 @@ class ServiceReferenceGraph */ private $nodes = array(); - /** - * Checks if the graph has a specific node. - * - * @param string $id Id to check - * - * @return bool - */ - public function hasNode($id) + public function hasNode(string $id): bool { return isset($this->nodes[$id]); } @@ -45,13 +38,9 @@ public function hasNode($id) /** * Gets a node by identifier. * - * @param string $id The id to retrieve - * - * @return ServiceReferenceGraphNode - * * @throws InvalidArgumentException if no node matches the supplied identifier */ - public function getNode($id) + public function getNode(string $id): ServiceReferenceGraphNode { if (!isset($this->nodes[$id])) { throw new InvalidArgumentException(sprintf('There is no node with id "%s".', $id)); @@ -65,7 +54,7 @@ public function getNode($id) * * @return ServiceReferenceGraphNode[] */ - public function getNodes() + public function getNodes(): array { return $this->nodes; } @@ -83,20 +72,9 @@ public function clear() /** * Connects 2 nodes together in the Graph. - * - * @param string $sourceId - * @param mixed $sourceValue - * @param string $destId - * @param mixed $destValue - * @param string $reference - * @param bool $lazy - * @param bool $weak */ - public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, bool $lazy = false, bool $weak = false*/) + public function connect(?string $sourceId, $sourceValue, ?string $destId, $destValue = null, $reference = null, bool $lazy = false, bool $weak = false) { - $lazy = func_num_args() >= 6 ? func_get_arg(5) : false; - $weak = func_num_args() >= 7 ? func_get_arg(6) : false; - if (null === $sourceId || null === $destId) { return; } @@ -109,15 +87,7 @@ public function connect($sourceId, $sourceValue, $destId, $destValue = null, $re $destNode->addInEdge($edge); } - /** - * Creates a graph node. - * - * @param string $id - * @param mixed $value - * - * @return ServiceReferenceGraphNode - */ - private function createNode($id, $value) + private function createNode(string $id, $value): ServiceReferenceGraphNode { if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) { return $this->nodes[$id]; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php index 018905394f0cf..3e5b9c92c5433 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php @@ -26,14 +26,7 @@ class ServiceReferenceGraphEdge private $lazy; private $weak; - /** - * @param ServiceReferenceGraphNode $sourceNode - * @param ServiceReferenceGraphNode $destNode - * @param mixed $value - * @param bool $lazy - * @param bool $weak - */ - public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, $lazy = false, $weak = false) + public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, bool $lazy = false, bool $weak = false) { $this->sourceNode = $sourceNode; $this->destNode = $destNode; @@ -45,7 +38,7 @@ public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceRefere /** * Returns the value of the edge. * - * @return string + * @return mixed */ public function getValue() { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php index 1052d2c6ae59e..b7274c425b707 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php @@ -32,7 +32,7 @@ class ServiceReferenceGraphNode * @param string $id The node identifier * @param mixed $value The node value */ - public function __construct($id, $value) + public function __construct(string $id, $value) { $this->id = $id; $this->value = $value; diff --git a/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php b/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php deleted file mode 100644 index 0eac93964b94c..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Config; - -@trigger_error('The '.__NAMESPACE__.'\AutowireServiceResource class is deprecated since Symfony 3.3 and will be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead.', E_USER_DEPRECATED); - -use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; -use Symfony\Component\DependencyInjection\Compiler\AutowirePass; - -/** - * @deprecated since version 3.3, to be removed in 4.0. Use ContainerBuilder::getReflectionClass() instead. - */ -class AutowireServiceResource implements SelfCheckingResourceInterface, \Serializable -{ - private $class; - private $filePath; - private $autowiringMetadata = array(); - - public function __construct($class, $path, array $autowiringMetadata) - { - $this->class = $class; - $this->filePath = $path; - $this->autowiringMetadata = $autowiringMetadata; - } - - public function isFresh($timestamp) - { - if (!file_exists($this->filePath)) { - return false; - } - - // has the file *not* been modified? Definitely fresh - if (@filemtime($this->filePath) <= $timestamp) { - return true; - } - - try { - $reflectionClass = new \ReflectionClass($this->class); - } catch (\ReflectionException $e) { - // the class does not exist anymore! - return false; - } - - return (array) $this === (array) AutowirePass::createResourceForClass($reflectionClass); - } - - public function __toString() - { - return 'service.autowire.'.$this->class; - } - - public function serialize() - { - return serialize(array($this->class, $this->filePath, $this->autowiringMetadata)); - } - - public function unserialize($serialized) - { - if (\PHP_VERSION_ID >= 70000) { - list($this->class, $this->filePath, $this->autowiringMetadata) = unserialize($serialized, array('allowed_classes' => false)); - } else { - list($this->class, $this->filePath, $this->autowiringMetadata) = unserialize($serialized); - } - } - - /** - * @deprecated Implemented for compatibility with Symfony 2.8 - */ - public function getResource() - { - return $this->filePath; - } -} diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index aa714728cd33a..c5ada9ee20bb2 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -43,22 +43,12 @@ class Container implements ResettableContainerInterface protected $services = array(); protected $fileMap = array(); protected $methodMap = array(); + protected $factories = array(); protected $aliases = array(); protected $loading = array(); protected $resolving = array(); protected $syntheticIds = array(); - /** - * @internal - */ - protected $privates = array(); - - /** - * @internal - */ - protected $normalizedIds = array(); - - private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_'); private $envCache = array(); private $compiled = false; private $getEnv; @@ -95,20 +85,6 @@ public function isCompiled() return $this->compiled; } - /** - * Returns true if the container parameter bag are frozen. - * - * @deprecated since version 3.3, to be removed in 4.0. - * - * @return bool true if the container parameter bag are frozen, false otherwise - */ - public function isFrozen() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return $this->parameterBag instanceof FrozenParameterBag; - } - /** * Gets the service container parameter bag. * @@ -174,27 +150,20 @@ public function set($id, $service) $initialize(); } - $id = $this->normalizeId($id); - if ('service_container' === $id) { throw new InvalidArgumentException('You cannot set service "service_container".'); } - if (isset($this->privates[$id]) || !(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { - if (!isset($this->privates[$id]) && !isset($this->getRemovedIds()[$id])) { + if (!(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { + if (isset($this->syntheticIds[$id]) || !isset($this->getRemovedIds()[$id])) { // no-op } elseif (null === $service) { - @trigger_error(sprintf('The "%s" service is private, unsetting it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED); - unset($this->privates[$id]); + throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot unset it.', $id)); } else { - @trigger_error(sprintf('The "%s" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED); + throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot replace it.', $id)); } } elseif (isset($this->services[$id])) { - if (null === $service) { - @trigger_error(sprintf('The "%s" service is already initialized, unsetting it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), E_USER_DEPRECATED); - } else { - @trigger_error(sprintf('The "%s" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), E_USER_DEPRECATED); - } + throw new InvalidArgumentException(sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); } if (isset($this->aliases[$id])) { @@ -219,47 +188,22 @@ public function set($id, $service) */ public function has($id) { - for ($i = 2;;) { - if (isset($this->privates[$id])) { - @trigger_error(sprintf('The "%s" service is private, checking for its existence is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED); - } - if (isset($this->aliases[$id])) { - $id = $this->aliases[$id]; - } - if (isset($this->services[$id])) { - return true; - } - if ('service_container' === $id) { - return true; - } - - if (isset($this->fileMap[$id]) || isset($this->methodMap[$id])) { - return true; - } - - if (--$i && $id !== $normalizedId = $this->normalizeId($id)) { - $id = $normalizedId; - continue; - } - - // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, - // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) - if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service')) { - @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED); - - return true; - } - - return false; + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + if (isset($this->services[$id])) { + return true; + } + if ('service_container' === $id) { + return true; } + + return isset($this->fileMap[$id]) || isset($this->methodMap[$id]); } /** * Gets a service. * - * If a service is defined both through a set() method and - * with a get{$id}Service() method, the former has always precedence. - * * @param string $id The service identifier * @param int $invalidBehavior The behavior when the service does not exist * @@ -273,57 +217,39 @@ public function has($id) */ public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1) { - // Attempt to retrieve the service by checking first aliases then - // available services. Service IDs are case insensitive, however since - // this method can be called thousands of times during a request, avoid - // calling $this->normalizeId($id) unless necessary. - for ($i = 2;;) { - if (isset($this->privates[$id])) { - @trigger_error(sprintf('The "%s" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.', $id), E_USER_DEPRECATED); - } - if (isset($this->aliases[$id])) { - $id = $this->aliases[$id]; - } - - // Re-use shared service instance if it exists. - if (isset($this->services[$id])) { - return $this->services[$id]; - } - if ('service_container' === $id) { - return $this; - } + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } - if (isset($this->loading[$id])) { - throw new ServiceCircularReferenceException($id, array_keys($this->loading)); - } + // Re-use shared service instance if it exists. + if (isset($this->services[$id])) { + return $this->services[$id]; + } + if ('service_container' === $id) { + return $this; + } + if (isset($this->factories[$id])) { + return $this->factories[$id](); + } - $this->loading[$id] = true; - - try { - if (isset($this->fileMap[$id])) { - return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]); - } elseif (isset($this->methodMap[$id])) { - return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}(); - } elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) { - unset($this->loading[$id]); - $id = $normalizedId; - continue; - } elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) { - // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, - // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) - @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED); - - return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$method}(); - } + if (isset($this->loading[$id])) { + throw new ServiceCircularReferenceException($id, array_keys($this->loading)); + } - break; - } catch (\Exception $e) { - unset($this->services[$id]); + $this->loading[$id] = true; - throw $e; - } finally { - unset($this->loading[$id]); + try { + if (isset($this->fileMap[$id])) { + return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]); + } elseif (isset($this->methodMap[$id])) { + return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}(); } + } catch (\Exception $e) { + unset($this->services[$id]); + + throw $e; + } finally { + unset($this->loading[$id]); } if (/* self::EXCEPTION_ON_INVALID_REFERENCE */ 1 === $invalidBehavior) { @@ -358,12 +284,6 @@ public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERE */ public function initialized($id) { - $id = $this->normalizeId($id); - - if (isset($this->privates[$id])) { - @trigger_error(sprintf('Checking for the initialization of the "%s" private service is deprecated since Symfony 3.4 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); - } - if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } @@ -380,7 +300,7 @@ public function initialized($id) */ public function reset() { - $this->services = array(); + $this->services = $this->factories = array(); } /** @@ -390,22 +310,7 @@ public function reset() */ public function getServiceIds() { - $ids = array(); - - if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class) { - // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, - // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) - @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED); - - foreach (get_class_methods($this) as $method) { - if (preg_match('/^get(.+)Service$/', $method, $match)) { - $ids[] = self::underscore($match[1]); - } - } - } - $ids[] = 'service_container'; - - return array_unique(array_merge($ids, array_keys($this->methodMap), array_keys($this->fileMap), array_keys($this->services))); + return array_unique(array_merge(array('service_container'), array_keys($this->fileMap), array_keys($this->methodMap), array_keys($this->services))); } /** @@ -496,32 +401,6 @@ protected function getEnv($name) } } - /** - * Returns the case sensitive id used at registration time. - * - * @param string $id - * - * @return string - * - * @internal - */ - public function normalizeId($id) - { - if (!is_string($id)) { - $id = (string) $id; - } - if (isset($this->normalizedIds[$normalizedId = strtolower($id)])) { - $normalizedId = $this->normalizedIds[$normalizedId]; - if ($id !== $normalizedId) { - @trigger_error(sprintf('Service identifiers will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.3.', $id, $normalizedId), E_USER_DEPRECATED); - } - } else { - $normalizedId = $this->normalizedIds[$normalizedId] = $id; - } - - return $normalizedId; - } - private function __clone() { } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 6e025387f5c8b..6bd7e1974466c 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -39,7 +39,6 @@ use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; -use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; @@ -310,36 +309,17 @@ public function addObjectResource($object) return $this; } - /** - * Adds the given class hierarchy as resources. - * - * @return $this - * - * @deprecated since version 3.3, to be removed in 4.0. Use addObjectResource() or getReflectionClass() instead. - */ - public function addClassResource(\ReflectionClass $class) - { - @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the addObjectResource() or the getReflectionClass() method instead.', E_USER_DEPRECATED); - - return $this->addObjectResource($class->name); - } - /** * Retrieves the requested reflection class and registers it for resource tracking. * - * @param string $class - * @param bool $throw - * - * @return \ReflectionClass|null - * * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true * * @final */ - public function getReflectionClass($class, $throw = true) + public function getReflectionClass(?string $class, bool $throw = true): ?\ReflectionClass { if (!$class = $this->getParameterBag()->resolveValue($class)) { - return; + return null; } $resource = null; @@ -384,7 +364,7 @@ public function getReflectionClass($class, $throw = true) * * @final */ - public function fileExists($path, $trackContents = true) + public function fileExists(string $path, $trackContents = true): bool { $exists = file_exists($path); @@ -448,21 +428,8 @@ public function loadFromExtension($extension, array $values = null) * * @return $this */ - public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/) + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { - if (func_num_args() >= 3) { - $priority = func_get_arg(2); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED); - } - } - - $priority = 0; - } - $this->getCompiler()->addPass($pass, $type, $priority); $this->addObjectResource($pass); @@ -504,7 +471,7 @@ public function getCompiler() */ public function set($id, $service) { - $id = $this->normalizeId($id); + $id = (string) $id; if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { // setting a synthetic service on a compiled container is alright @@ -523,7 +490,7 @@ public function set($id, $service) */ public function removeDefinition($id) { - if (isset($this->definitions[$id = $this->normalizeId($id)])) { + if (isset($this->definitions[$id = (string) $id])) { unset($this->definitions[$id]); $this->removedIds[$id] = true; } @@ -538,7 +505,7 @@ public function removeDefinition($id) */ public function has($id) { - $id = $this->normalizeId($id); + $id = (string) $id; return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); } @@ -560,8 +527,8 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - if ($this->isCompiled() && isset($this->removedIds[$id = $this->normalizeId($id)])) { - @trigger_error(sprintf('Fetching the "%s" private service or alias is deprecated since Symfony 3.4 and will fail in 4.0. Make it public instead.', $id), E_USER_DEPRECATED); + if ($this->isCompiled() && isset($this->removedIds[$id = (string) $id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + return parent::get($id); } return $this->doGet($id, $invalidBehavior); @@ -569,8 +536,6 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = array()) { - $id = $this->normalizeId($id); - if (isset($inlineServices[$id])) { return $inlineServices[$id]; } @@ -728,19 +693,8 @@ public function prependExtensionConfig($name, array $config) * Set to "true" when you want to use the current ContainerBuilder * directly, keep to "false" when the container is dumped instead. */ - public function compile(/*$resolveEnvPlaceholders = false*/) + public function compile(bool $resolveEnvPlaceholders = false) { - if (1 <= func_num_args()) { - $resolveEnvPlaceholders = func_get_arg(0); - } else { - if (__CLASS__ !== static::class) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName() && (1 > $r->getNumberOfParameters() || 'resolveEnvPlaceholders' !== $r->getParameters()[0]->name)) { - @trigger_error(sprintf('The %s::compile() method expects a first "$resolveEnvPlaceholders" argument since Symfony 3.3. It will be made mandatory in 4.0.', static::class), E_USER_DEPRECATED); - } - } - $resolveEnvPlaceholders = false; - } $compiler = $this->getCompiler(); if ($this->trackResources) { @@ -833,10 +787,10 @@ public function setAliases(array $aliases) */ public function setAlias($alias, $id) { - $alias = $this->normalizeId($alias); + $alias = (string) $alias; if (is_string($id)) { - $id = new Alias($this->normalizeId($id)); + $id = new Alias($id); } elseif (!$id instanceof Alias) { throw new InvalidArgumentException('$id must be a string, or an Alias object.'); } @@ -857,7 +811,7 @@ public function setAlias($alias, $id) */ public function removeAlias($alias) { - if (isset($this->aliasDefinitions[$alias = $this->normalizeId($alias)])) { + if (isset($this->aliasDefinitions[$alias = (string) $alias])) { unset($this->aliasDefinitions[$alias]); $this->removedIds[$alias] = true; } @@ -872,7 +826,7 @@ public function removeAlias($alias) */ public function hasAlias($id) { - return isset($this->aliasDefinitions[$this->normalizeId($id)]); + return isset($this->aliasDefinitions[$id = (string) $id]); } /** @@ -896,7 +850,7 @@ public function getAliases() */ public function getAlias($id) { - $id = $this->normalizeId($id); + $id = (string) $id; if (!isset($this->aliasDefinitions[$id])) { throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); @@ -986,7 +940,7 @@ public function setDefinition($id, Definition $definition) throw new BadMethodCallException('Adding definition to a compiled container is not allowed'); } - $id = $this->normalizeId($id); + $id = (string) $id; unset($this->aliasDefinitions[$id], $this->removedIds[$id]); @@ -1002,7 +956,7 @@ public function setDefinition($id, Definition $definition) */ public function hasDefinition($id) { - return isset($this->definitions[$this->normalizeId($id)]); + return isset($this->definitions[(string) $id]); } /** @@ -1016,7 +970,7 @@ public function hasDefinition($id) */ public function getDefinition($id) { - $id = $this->normalizeId($id); + $id = (string) $id; if (!isset($this->definitions[$id])) { throw new ServiceNotFoundException($id); @@ -1038,7 +992,7 @@ public function getDefinition($id) */ public function findDefinition($id) { - $id = $this->normalizeId($id); + $id = (string) $id; $seen = array(); while (isset($this->aliasDefinitions[$id])) { @@ -1136,11 +1090,8 @@ private function createService(Definition $definition, array &$inlineServices, $ $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass())); $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); - // don't trigger deprecations for internal uses - // @deprecated since version 3.3, to be removed in 4.0 along with the deprecated class - $deprecationWhitelist = array('event_dispatcher' => ContainerAwareEventDispatcher::class); - if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ") && (!isset($deprecationWhitelist[$id]) || $deprecationWhitelist[$id] !== $class)) { + if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED); } } @@ -1431,26 +1382,10 @@ public function getEnvCounters() return $this->envCounters; } - /** - * @internal - */ - public function getNormalizedIds() - { - $normalizedIds = array(); - - foreach ($this->normalizedIds as $k => $v) { - if ($v !== (string) $k) { - $normalizedIds[$k] = $v; - } - } - - return $normalizedIds; - } - /** * @final */ - public function log(CompilerPassInterface $pass, $message) + public function log(CompilerPassInterface $pass, string $message) { $this->getCompiler()->log($pass, $message); } @@ -1514,7 +1449,7 @@ public static function hash($value) { $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7); - return str_replace(array('/', '+'), array('.', '_'), strtolower($hash)); + return str_replace(array('/', '+'), array('.', '_'), $hash); } /** @@ -1547,10 +1482,8 @@ protected function getEnv($name) /** * Retrieves the currently set proxy instantiator or instantiates one. - * - * @return InstantiatorInterface */ - private function getProxyInstantiator() + private function getProxyInstantiator(): InstantiatorInterface { if (!$this->proxyInstantiator) { $this->proxyInstantiator = new RealServiceInstantiator(); diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 99f2d6a22ac9d..9865adbd7a94f 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -41,7 +41,6 @@ class Definition private $lazy = false; private $decoratedService; private $autowired = false; - private $autowiringTypes = array(); private $changes = array(); private $bindings = array(); private $errors = array(); @@ -803,28 +802,6 @@ public function getConfigurator() return $this->configurator; } - /** - * Sets types that will default to this definition. - * - * @param string[] $types - * - * @return $this - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function setAutowiringTypes(array $types) - { - @trigger_error('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead.', E_USER_DEPRECATED); - - $this->autowiringTypes = array(); - - foreach ($types as $type) { - $this->autowiringTypes[$type] = true; - } - - return $this; - } - /** * Is the definition autowired? * @@ -851,74 +828,6 @@ public function setAutowired($autowired) return $this; } - /** - * Gets autowiring types that will default to this definition. - * - * @return string[] - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function getAutowiringTypes(/*$triggerDeprecation = true*/) - { - if (1 > func_num_args() || func_get_arg(0)) { - @trigger_error('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead.', E_USER_DEPRECATED); - } - - return array_keys($this->autowiringTypes); - } - - /** - * Adds a type that will default to this definition. - * - * @param string $type - * - * @return $this - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function addAutowiringType($type) - { - @trigger_error(sprintf('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead for "%s".', $type), E_USER_DEPRECATED); - - $this->autowiringTypes[$type] = true; - - return $this; - } - - /** - * Removes a type. - * - * @param string $type - * - * @return $this - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function removeAutowiringType($type) - { - @trigger_error(sprintf('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead for "%s".', $type), E_USER_DEPRECATED); - - unset($this->autowiringTypes[$type]); - - return $this; - } - - /** - * Will this definition default for the given type? - * - * @param string $type - * - * @return bool - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function hasAutowiringType($type) - { - @trigger_error(sprintf('Autowiring-types are deprecated since Symfony 3.3 and will be removed in 4.0. Use aliases instead for "%s".', $type), E_USER_DEPRECATED); - - return isset($this->autowiringTypes[$type]); - } - /** * Gets bindings. * diff --git a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php deleted file mode 100644 index 99af39e89dbfd..0000000000000 --- a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection; - -@trigger_error('The '.__NAMESPACE__.'\DefinitionDecorator class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.', E_USER_DEPRECATED); - -class_exists(ChildDefinition::class); - -if (false) { - /** - * This definition decorates another definition. - * - * @author Johannes M. Schmitt - * - * @deprecated The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead. - */ - class DefinitionDecorator extends Definition - { - } -} diff --git a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php index b41bf7e0d065b..84cc1f59a5ef7 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php @@ -83,12 +83,7 @@ public function dump(array $options = array()) return $this->container->resolveEnvPlaceholders($this->startDot().$this->addNodes().$this->addEdges().$this->endDot(), '__ENV_%s__'); } - /** - * Returns all nodes. - * - * @return string A string representation of all nodes - */ - private function addNodes() + private function addNodes(): string { $code = ''; foreach ($this->nodes as $id => $node) { @@ -100,12 +95,7 @@ private function addNodes() return $code; } - /** - * Returns all edges. - * - * @return string A string representation of all edges - */ - private function addEdges() + private function addEdges(): string { $code = ''; foreach ($this->edges as $id => $edges) { @@ -119,15 +109,8 @@ private function addEdges() /** * Finds all edges belonging to a specific service id. - * - * @param string $id The service id used to find edges - * @param array $arguments An array of arguments - * @param bool $required - * @param string $name - * - * @return array An array of edges */ - private function findEdges($id, array $arguments, $required, $name, $lazy = false) + private function findEdges(string $id, array $arguments, bool $required, string $name, bool $lazy = false): array { $edges = array(); foreach ($arguments as $argument) { @@ -157,12 +140,7 @@ private function findEdges($id, array $arguments, $required, $name, $lazy = fals return $edges; } - /** - * Finds all nodes. - * - * @return array An array of all nodes - */ - private function findNodes() + private function findNodes(): array { $nodes = array(); @@ -212,12 +190,7 @@ private function cloneContainer() return $container; } - /** - * Returns the start dot. - * - * @return string The string representation of a start dot - */ - private function startDot() + private function startDot(): string { return sprintf("digraph sc {\n %s\n node [%s];\n edge [%s];\n\n", $this->addOptions($this->options['graph']), @@ -226,24 +199,12 @@ private function startDot() ); } - /** - * Returns the end dot. - * - * @return string - */ - private function endDot() + private function endDot(): string { return "}\n"; } - /** - * Adds attributes. - * - * @param array $attributes An array of attributes - * - * @return string A comma separated list of attributes - */ - private function addAttributes(array $attributes) + private function addAttributes(array $attributes): string { $code = array(); foreach ($attributes as $k => $v) { @@ -253,14 +214,7 @@ private function addAttributes(array $attributes) return $code ? ', '.implode(', ', $code) : ''; } - /** - * Adds options. - * - * @param array $options An array of options - * - * @return string A space separated list of options - */ - private function addOptions(array $options) + private function addOptions(array $options): string { $code = array(); foreach ($options as $k => $v) { @@ -270,26 +224,12 @@ private function addOptions(array $options) return implode(' ', $code); } - /** - * Dotizes an identifier. - * - * @param string $id The identifier to dotize - * - * @return string A dotized string - */ - private function dotize($id) + private function dotize(string $id): string { - return strtolower(preg_replace('/\W/i', '_', $id)); + return preg_replace('/\W/i', '_', $id); } - /** - * Compiles an array of aliases for a specified service id. - * - * @param string $id A service id - * - * @return array An array of aliases - */ - private function getAliases($id) + private function getAliases(string $id): array { $aliases = array(); foreach ($this->container->getAliases() as $alias => $origin) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 9461e1ccb53f1..088d0a6c5a05c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Exception\EnvParameterException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; @@ -79,7 +80,7 @@ class PhpDumper extends Dumper public function __construct(ContainerBuilder $container) { if (!$container->isCompiled()) { - @trigger_error('Dumping an uncompiled ContainerBuilder is deprecated since Symfony 3.3 and will not be supported anymore in 4.0. Compile the container beforehand.', E_USER_DEPRECATED); + throw new LogicException('Cannot dump an uncompiled container.'); } parent::__construct($container); @@ -192,7 +193,13 @@ public function dump(array $options = array()) EOF; $files = array(); - if ($ids = array_keys($this->container->getRemovedIds())) { + $ids = $this->container->getRemovedIds(); + foreach ($this->container->getDefinitions() as $id => $definition) { + if (!$definition->isPublic()) { + $ids[$id] = true; + } + } + if ($ids = array_keys($ids)) { sort($ids); $c = "proxyDumper) { $this->proxyDumper = new NullDumper(); @@ -281,12 +286,7 @@ private function getProxyDumper() return $this->proxyDumper; } - /** - * Generates Service local temp variables. - * - * @return string - */ - private function addServiceLocalTempVariables($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, \SplObjectStorage $allInlinedDefinitions) + private function addServiceLocalTempVariables(string $cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, \SplObjectStorage $allInlinedDefinitions): string { $allCalls = $calls = $behavior = array(); @@ -326,13 +326,14 @@ private function addServiceLocalTempVariables($cId, Definition $definition, \Spl if ('' !== $code) { if ($isPreInstance) { - $code .= <<services['$cId'])) { - return \$this->services['$cId']; + if (isset($this->%s['%s'])) { + return $this->%1$s['%2$s']; } -EOTXT; +EOTXT + , $definition->isPublic() ? 'services' : 'privates', $cId); } $code .= "\n"; @@ -415,12 +416,7 @@ private function generateProxyClasses() } } - /** - * Generates the require_once statement for service includes. - * - * @return string - */ - private function addServiceInclude($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions) + private function addServiceInclude(string $cId, Definition $definition, \SplObjectStorage $inlinedDefinitions): string { $code = ''; @@ -466,12 +462,10 @@ private function addServiceInclude($cId, Definition $definition, \SplObjectStora /** * Generates the inline definition of a service. * - * @return string - * * @throws RuntimeException When the factory definition is incomplete * @throws ServiceCircularReferenceException When a circular reference is detected */ - private function addServiceInlinedDefinitions($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, &$isSimpleInstance) + private function addServiceInlinedDefinitions(string $id, Definition $definition, \SplObjectStorage $inlinedDefinitions, bool &$isSimpleInstance): string { $code = ''; @@ -517,18 +511,10 @@ private function addServiceInlinedDefinitions($id, Definition $definition, \SplO } /** - * Generates the service instance. - * - * @param string $id - * @param Definition $definition - * @param bool $isSimpleInstance - * - * @return string - * * @throws InvalidArgumentException * @throws RuntimeException */ - private function addServiceInstance($id, Definition $definition, $isSimpleInstance) + private function addServiceInstance(string $id, Definition $definition, string $isSimpleInstance): string { $class = $this->dumpValue($definition->getClass()); @@ -540,7 +526,7 @@ private function addServiceInstance($id, Definition $definition, $isSimpleInstan $instantiation = ''; if (!$isProxyCandidate && $definition->isShared()) { - $instantiation = "\$this->services['$id'] = ".($isSimpleInstance ? '' : '$instance'); + $instantiation = sprintf('$this->%s[\'%s\'] = %s', $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $id, $isSimpleInstance ? '' : '$instance'); } elseif (!$isSimpleInstance) { $instantiation = '$instance'; } @@ -561,14 +547,7 @@ private function addServiceInstance($id, Definition $definition, $isSimpleInstan return $code; } - /** - * Checks if the definition is a trivial instance. - * - * @param Definition $definition - * - * @return bool - */ - private function isTrivialInstance(Definition $definition) + private function isTrivialInstance(Definition $definition): bool { if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) { return false; @@ -610,15 +589,7 @@ private function isTrivialInstance(Definition $definition) return true; } - /** - * Adds method calls to a service definition. - * - * @param Definition $definition - * @param string $variableName - * - * @return string - */ - private function addServiceMethodCalls(Definition $definition, $variableName = 'instance') + private function addServiceMethodCalls(Definition $definition, string $variableName = 'instance'): string { $calls = ''; foreach ($definition->getMethodCalls() as $call) { @@ -644,13 +615,9 @@ private function addServiceProperties(Definition $definition, $variableName = 'i } /** - * Generates the inline definition setup. - * - * @return string - * * @throws ServiceCircularReferenceException when the container contains a circular reference */ - private function addServiceInlinedDefinitionsSetup($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, $isSimpleInstance) + private function addServiceInlinedDefinitionsSetup(string $id, Definition $definition, \SplObjectStorage $inlinedDefinitions, bool $isSimpleInstance): string { $this->referenceVariables[$id] = new Variable('instance'); @@ -679,15 +646,7 @@ private function addServiceInlinedDefinitionsSetup($id, Definition $definition, return $code; } - /** - * Adds configurator definition. - * - * @param Definition $definition - * @param string $variableName - * - * @return string - */ - private function addServiceConfigurator(Definition $definition, $variableName = 'instance') + private function addServiceConfigurator(Definition $definition, string $variableName = 'instance'): string { if (!$callable = $definition->getConfigurator()) { return ''; @@ -700,7 +659,7 @@ private function addServiceConfigurator(Definition $definition, $variableName = } $class = $this->dumpValue($callable[0]); - // If the class is a string we can optimize call_user_func away + // If the class is a string we can optimize away if (0 === strpos($class, "'") && false === strpos($class, '$')) { return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName); } @@ -709,22 +668,13 @@ private function addServiceConfigurator(Definition $definition, $variableName = return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } - return sprintf(" \\call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + return sprintf(" [%s, '%s'](\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } return sprintf(" %s(\$%s);\n", $callable, $variableName); } - /** - * Adds a service. - * - * @param string $id - * @param Definition $definition - * @param string &$file - * - * @return string - */ - private function addService($id, Definition $definition, &$file = null) + private function addService(string $id, Definition $definition, string &$file = null): string { $this->definitionVariables = new \SplObjectStorage(); $this->referenceVariables = array(); @@ -769,7 +719,7 @@ private function addService($id, Definition $definition, &$file = null) $lazyInitialization = ''; } - $asFile = $this->asFiles && $definition->isShared() && !$this->isHotPath($definition); + $asFile = $this->asFiles && !$this->isHotPath($definition); $methodName = $this->generateMethodName($id); if ($asFile) { $file = $methodName.'.php'; @@ -788,15 +738,6 @@ protected function {$methodName}($lazyInitialization) EOF; } - if ($this->getProxyDumper()->isProxyCandidate($definition)) { - $factoryCode = $asFile ? "\$this->load(__DIR__.'/%s.php', false)" : '$this->%s(false)'; - $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName)); - } - - if ($definition->isDeprecated()) { - $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); - } - $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); $constructorDefinitions = $this->getDefinitionsFromArguments(array($definition->getArguments(), $definition->getFactory())); $otherDefinitions = new \SplObjectStorage(); @@ -811,8 +752,18 @@ protected function {$methodName}($lazyInitialization) $isSimpleInstance = !$definition->getProperties() && !$definition->getMethodCalls() && !$definition->getConfigurator(); + $code .= $this->addServiceInclude($id, $definition, $inlinedDefinitions); + + if ($this->getProxyDumper()->isProxyCandidate($definition)) { + $factoryCode = $asFile ? "\$this->load(__DIR__.'/%s.php', false)" : '$this->%s(false)'; + $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName)); + } + + if ($definition->isDeprecated()) { + $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); + } + $code .= - $this->addServiceInclude($id, $definition, $inlinedDefinitions). $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $inlinedDefinitions). $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance). $this->addServiceInstance($id, $definition, $isSimpleInstance). @@ -837,23 +788,18 @@ protected function {$methodName}($lazyInitialization) return $code; } - /** - * Adds multiple services. - * - * @return string - */ - private function addServices() + private function addServices(): string { $publicServices = $privateServices = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if ($definition->isSynthetic() || ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition))) { + if ($definition->isSynthetic() || ($this->asFiles && !$this->isHotPath($definition))) { continue; } if ($definition->isPublic()) { $publicServices .= $this->addService($id, $definition); - } else { + } elseif (!$this->isTrivialInstance($definition)) { $privateServices .= $this->addService($id, $definition); } } @@ -866,8 +812,22 @@ private function generateServiceFiles() $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) { + if (!$definition->isSynthetic() && !$this->isHotPath($definition)) { $code = $this->addService($id, $definition, $file); + + if (!$definition->isShared()) { + $i = strpos($code, "\n\ninclude_once "); + if (false !== $i && false !== $i = strpos($code, "\n\n", 2 + $i)) { + $code = array(substr($code, 0, 2 + $i), substr($code, 2 + $i)); + } else { + $code = array("\n", $code); + } + $code[1] = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code[1]))); + $factory = sprintf('$this->factories%s[\'%s\']', $definition->isPublic() ? '' : "['service_container']", $id); + $code[1] = sprintf("%s = function () {\n%s};\n\nreturn %1\$s();\n", $factory, $code[1]); + $code = $code[0].$code[1]; + } + yield $file => $code; } } @@ -896,7 +856,7 @@ private function addNewInstance(Definition $definition, $return, $instantiation, } $class = $this->dumpValue($callable[0]); - // If the class is a string we can optimize call_user_func away + // If the class is a string we can optimize away if (0 === strpos($class, "'") && false === strpos($class, '$')) { if ("''" === $class) { throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id)); @@ -909,7 +869,7 @@ private function addNewInstance(Definition $definition, $return, $instantiation, return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); } - return $return.sprintf("\\call_user_func(array(%s, '%s')%s);\n", $class, $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); + return $return.sprintf("[%s, '%s'](%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); } return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : ''); @@ -922,18 +882,8 @@ private function addNewInstance(Definition $definition, $return, $instantiation, return $return.sprintf("new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments)); } - /** - * Adds the class headers. - * - * @param string $class Class name - * @param string $baseClass The name of the base class - * @param string $baseClassWithNamespace Fully qualified base class name - * - * @return string - */ - private function startClass($class, $baseClass, $baseClassWithNamespace) + private function startClass(string $class, string $baseClass, string $baseClassWithNamespace): string { - $bagClass = $this->container->isCompiled() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;'; $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; $code = <<docStar} * This class has been auto-generated @@ -958,6 +908,11 @@ class $class extends $baseClass private \$parameters; private \$targetDirs = array(); + /*{$this->docStar} + * @internal but protected for BC on cache:clear + */ + protected \$privates = array(); + public function __construct() { @@ -978,40 +933,32 @@ public function __construct() $code .= " \$this->buildParameters = \$buildParameters;\n"; } - if ($this->container->isCompiled()) { - if (Container::class !== $baseClassWithNamespace) { - $r = $this->container->getReflectionClass($baseClassWithNamespace, false); - - if (null !== $r && (null !== $constructor = $r->getConstructor()) && 0 === $constructor->getNumberOfRequiredParameters()) { - $code .= " parent::__construct();\n\n"; - } - } + if (Container::class !== $baseClassWithNamespace) { + $r = $this->container->getReflectionClass($baseClassWithNamespace, false); - if ($this->container->getParameterBag()->all()) { - $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n"; + if (null !== $r && (null !== $constructor = $r->getConstructor()) && 0 === $constructor->getNumberOfRequiredParameters()) { + $code .= " parent::__construct();\n\n"; } + } - $code .= " \$this->services = array();\n"; - } else { - $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; - $code .= " parent::__construct($arguments);\n"; + if ($this->container->getParameterBag()->all()) { + $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n"; } + $code .= " \$this->services = \$this->privates = array();\n"; - $code .= $this->addNormalizedIds(); $code .= $this->addSyntheticIds(); $code .= $this->addMethodMap(); $code .= $this->asFiles ? $this->addFileMap() : ''; - $code .= $this->addPrivateServices(); $code .= $this->addAliases(); $code .= $this->addInlineRequires(); - $code .= <<<'EOF' + $code .= <<addRemovedIds(); - - if ($this->container->isCompiled()) { - $code .= <<privates = array(); + parent::reset(); + } public function compile() { @@ -1023,15 +970,8 @@ public function isCompiled() return true; } - public function isFrozen() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; - } - EOF; - } + $code .= $this->addRemovedIds(); if ($this->asFiles) { $code .= <<container->getNormalizedIds(); - ksort($normalizedIds); - foreach ($normalizedIds as $id => $normalizedId) { - if ($this->container->has($normalizedId)) { - $code .= ' '.$this->doExport($id).' => '.$this->doExport($normalizedId).",\n"; - } - } - - return $code ? " \$this->normalizedIds = array(\n".$code." );\n" : ''; - } - - /** - * Adds the syntheticIds definition. - * - * @return string - */ - private function addSyntheticIds() + private function addSyntheticIds(): string { $code = ''; $definitions = $this->container->getDefinitions(); @@ -1111,14 +1027,15 @@ private function addSyntheticIds() return $code ? " \$this->syntheticIds = array(\n{$code} );\n" : ''; } - /** - * Adds the removedIds definition. - * - * @return string - */ - private function addRemovedIds() + private function addRemovedIds(): string { - if (!$ids = $this->container->getRemovedIds()) { + $ids = $this->container->getRemovedIds(); + foreach ($this->container->getDefinitions() as $id => $definition) { + if (!$definition->isPublic()) { + $ids[$id] = true; + } + } + if (!$ids) { return ''; } if ($this->asFiles) { @@ -1144,18 +1061,13 @@ public function getRemovedIds() EOF; } - /** - * Adds the methodMap property definition. - * - * @return string - */ - private function addMethodMap() + private function addMethodMap(): string { $code = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if (!$definition->isSynthetic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) { + if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || $this->isHotPath($definition))) { $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n"; } } @@ -1163,18 +1075,13 @@ private function addMethodMap() return $code ? " \$this->methodMap = array(\n{$code} );\n" : ''; } - /** - * Adds the fileMap property definition. - * - * @return string - */ - private function addFileMap() + private function addFileMap(): string { $code = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) { + if (!$definition->isSynthetic() && $definition->isPublic() && !$this->isHotPath($definition)) { $code .= sprintf(" %s => __DIR__.'/%s.php',\n", $this->doExport($id), $this->generateMethodName($id)); } } @@ -1182,51 +1089,10 @@ private function addFileMap() return $code ? " \$this->fileMap = array(\n{$code} );\n" : ''; } - /** - * Adds the privates property definition. - * - * @return string - */ - private function addPrivateServices() - { - $code = ''; - - $aliases = $this->container->getAliases(); - ksort($aliases); - foreach ($aliases as $id => $alias) { - if ($alias->isPrivate()) { - $code .= ' '.$this->doExport($id)." => true,\n"; - } - } - - $definitions = $this->container->getDefinitions(); - ksort($definitions); - foreach ($definitions as $id => $definition) { - if (!$definition->isPublic()) { - $code .= ' '.$this->doExport($id)." => true,\n"; - } - } - - if (empty($code)) { - return ''; - } - - $out = " \$this->privates = array(\n"; - $out .= $code; - $out .= " );\n"; - - return $out; - } - - /** - * Adds the aliases property definition. - * - * @return string - */ - private function addAliases() + private function addAliases(): string { if (!$aliases = $this->container->getAliases()) { - return $this->container->isCompiled() ? "\n \$this->aliases = array();\n" : ''; + return "\n \$this->aliases = array();\n"; } $code = " \$this->aliases = array(\n"; @@ -1242,7 +1108,7 @@ private function addAliases() return $code." );\n"; } - private function addInlineRequires() + private function addInlineRequires(): string { if (!$this->hotPathTag || !$this->inlineRequires) { return ''; @@ -1273,12 +1139,7 @@ private function addInlineRequires() return $code ? sprintf("\n \$this->privates['service_container'] = function () {%s\n };\n", $code) : ''; } - /** - * Adds default parameters method. - * - * @return string - */ - private function addDefaultParametersMethod() + private function addDefaultParametersMethod(): string { if (!$this->container->getParameterBag()->all()) { return ''; @@ -1286,15 +1147,11 @@ private function addDefaultParametersMethod() $php = array(); $dynamicPhp = array(); - $normalizedParams = array(); foreach ($this->container->getParameterBag()->all() as $key => $value) { if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) { throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey)); } - if ($key !== $lcKey = strtolower($key)) { - $normalizedParams[] = sprintf(' %s => %s,', $this->export($lcKey), $this->export($key)); - } $export = $this->exportParameters(array($value)); $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2); @@ -1306,9 +1163,7 @@ private function addDefaultParametersMethod() } $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8)); - $code = ''; - if ($this->container->isCompiled()) { - $code .= <<<'EOF' + $code = <<<'EOF' public function getParameter($name) { @@ -1316,12 +1171,9 @@ public function getParameter($name) if (isset($this->buildParameters[$name])) { return $this->buildParameters[$name]; } - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -1336,7 +1188,6 @@ public function hasParameter($name) if (isset($this->buildParameters[$name])) { return true; } - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -1363,13 +1214,13 @@ public function getParameterBag() } EOF; - if (!$this->asFiles) { - $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code); - } + if (!$this->asFiles) { + $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code); + } - if ($dynamicPhp) { - $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8); - $getDynamicParameter = <<<'EOF' + if ($dynamicPhp) { + $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8); + $getDynamicParameter = <<<'EOF' switch ($name) { %s default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name)); @@ -1378,13 +1229,13 @@ public function getParameterBag() return $this->dynamicParameters[$name] = $value; EOF; - $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); - } else { - $loadedDynamicParameters = 'array()'; - $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));'; - } + $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); + } else { + $loadedDynamicParameters = 'array()'; + $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));'; + } - $code .= <<normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - -EOF; - } elseif ($dynamicPhp) { - throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.'); - } - - $code .= <<docStar} * Gets the default parameters. * @@ -1446,17 +1270,9 @@ protected function getDefaultParameters() } /** - * Exports parameters. - * - * @param array $parameters - * @param string $path - * @param int $indent - * - * @return string - * * @throws InvalidArgumentException */ - private function exportParameters(array $parameters, $path = '', $indent = 12) + private function exportParameters(array $parameters, string $path = '', int $indent = 12): string { $php = array(); foreach ($parameters as $key => $value) { @@ -1482,12 +1298,7 @@ private function exportParameters(array $parameters, $path = '', $indent = 12) return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4)); } - /** - * Ends the class definition. - * - * @return string - */ - private function endClass() + private function endClass(): string { return <<<'EOF' } @@ -1495,15 +1306,7 @@ private function endClass() EOF; } - /** - * Wraps the service conditionals. - * - * @param string $value - * @param string $code - * - * @return string - */ - private function wrapServiceConditionals($value, $code) + private function wrapServiceConditionals($value, string $code): string { if (!$condition = $this->getServiceConditionals($value)) { return $code; @@ -1515,21 +1318,14 @@ private function wrapServiceConditionals($value, $code) return sprintf(" if (%s) {\n%s }\n", $condition, $code); } - /** - * Get the conditions to execute for conditional services. - * - * @param string $value - * - * @return null|string - */ - private function getServiceConditionals($value) + private function getServiceConditionals($value): string { $conditions = array(); foreach (ContainerBuilder::getInitializedConditionals($value) as $service) { if (!$this->container->hasDefinition($service)) { return 'false'; } - $conditions[] = sprintf("isset(\$this->services['%s'])", $service); + $conditions[] = sprintf("isset(\$this->%s['%s'])", $this->container->getDefinition($service)->isPublic() ? 'services' : 'privates', $service); } foreach (ContainerBuilder::getServiceConditionals($value) as $service) { if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) { @@ -1546,10 +1342,7 @@ private function getServiceConditionals($value) return implode(' && ', $conditions); } - /** - * Builds service calls from arguments. - */ - private function getServiceCallsFromArguments(array $arguments, array &$calls, $isPreInstance, $callerId, array &$behavior = array(), $step = 1) + private function getServiceCallsFromArguments(array $arguments, array &$calls, bool $isPreInstance, string $callerId, array &$behavior = array(), int $step = 1) { foreach ($arguments as $argument) { if (is_array($argument)) { @@ -1571,7 +1364,7 @@ private function getServiceCallsFromArguments(array $arguments, array &$calls, $ } } - private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null) + private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null): \SplObjectStorage { if (null === $definitions) { $definitions = new \SplObjectStorage(); @@ -1601,17 +1394,7 @@ private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage return $definitions; } - /** - * Checks if a service id has a reference. - * - * @param string $id - * @param array $arguments - * @param bool $deep - * @param array $visited - * - * @return bool - */ - private function hasReference($id, array $arguments, $deep = false, array &$visited = array()) + private function hasReference(string $id, array $arguments, bool $deep = false, array &$visited = array()): bool { if (!isset($this->circularReferences[$id])) { return false; @@ -1658,16 +1441,9 @@ private function hasReference($id, array $arguments, $deep = false, array &$visi } /** - * Dumps values. - * - * @param mixed $value - * @param bool $interpolate - * - * @return string - * * @throws RuntimeException */ - private function dumpValue($value, $interpolate = true) + private function dumpValue($value, bool $interpolate = true): string { if (is_array($value)) { if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) { @@ -1688,13 +1464,14 @@ private function dumpValue($value, $interpolate = true) $value = $value->getValues()[0]; $code = $this->dumpValue($value, $interpolate); + $returnedType = ''; if ($value instanceof TypedReference) { - $code = sprintf('$f = function (\\%s $v%s) { return $v; }; return $f(%s);', $value->getType(), ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $value->getInvalidBehavior() ? ' = null' : '', $code); - } else { - $code = sprintf('return %s;', $code); + $returnedType = sprintf(': %s\%s', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior() ? '' : '?', $value->getType()); } - return sprintf("function () {\n %s\n }", $code); + $code = sprintf('return %s;', $code); + + return sprintf("function ()%s {\n %s\n }", $returnedType, $code); } if ($value instanceof IteratorArgument) { @@ -1770,7 +1547,7 @@ private function dumpValue($value, $interpolate = true) return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments)); } - return sprintf("\\call_user_func(array(%s, '%s')%s)", $class, $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + return sprintf("[%s, '%s'](%s)", $class, $factory[1], implode(', ', $arguments)); } if ($factory[0] instanceof Reference) { @@ -1823,13 +1600,9 @@ private function dumpValue($value, $interpolate = true) /** * Dumps a string to a literal (aka PHP Code) class value. * - * @param string $class - * - * @return string - * * @throws RuntimeException */ - private function dumpLiteralClass($class) + private function dumpLiteralClass(string $class): string { if (false !== strpos($class, '$')) { return sprintf('${($_ = %s) && false ?: "_"}', $class); @@ -1843,14 +1616,7 @@ private function dumpLiteralClass($class) return 0 === strpos($class, '\\') ? $class : '\\'.$class; } - /** - * Dumps a parameter. - * - * @param string $name - * - * @return string - */ - private function dumpParameter($name) + private function dumpParameter(string $name): string { if ($this->container->isCompiled() && $this->container->hasParameter($name)) { $value = $this->container->getParameter($name); @@ -1868,15 +1634,7 @@ private function dumpParameter($name) return sprintf("\$this->getParameter('%s')", $name); } - /** - * Gets a service call. - * - * @param string $id - * @param Reference $reference - * - * @return string - */ - private function getServiceCall($id, Reference $reference = null) + private function getServiceCall(string $id, Reference $reference = null): string { while ($this->container->hasAlias($id)) { $id = (string) $this->container->getAlias($id); @@ -1892,32 +1650,39 @@ private function getServiceCall($id, Reference $reference = null) } elseif ($this->isTrivialInstance($definition)) { $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2); if ($definition->isShared()) { - $code = sprintf('$this->services[\'%s\'] = %s', $id, $code); + $code = sprintf('$this->%s[\'%s\'] = %s', $definition->isPublic() ? 'services' : 'privates', $id, $code); } - } elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) { + } elseif ($this->asFiles && !$this->isHotPath($definition)) { $code = sprintf("\$this->load(__DIR__.'/%s.php')", $this->generateMethodName($id)); + if (!$definition->isShared()) { + $factory = sprintf('$this->factories%s[\'%s\']', $definition->isPublic() ? '' : "['service_container']", $id); + $code = sprintf('(isset(%s) ? %1$s() : %s)', $factory, $code); + } } else { $code = sprintf('$this->%s()', $this->generateMethodName($id)); } - } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { + if ($definition->isShared()) { + $code = sprintf('($this->%s[\'%s\'] ?? %s)', $definition->isPublic() ? 'services' : 'privates', $id, $code); + } + + return $code; + } + if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { return 'null'; - } elseif (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { + } + if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { $code = sprintf('$this->get(\'%s\', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $id, ContainerInterface::NULL_ON_INVALID_REFERENCE); } else { $code = sprintf('$this->get(\'%s\')', $id); } - // The following is PHP 5.5 syntax for what could be written as "(\$this->services['$id'] ?? $code)" on PHP>=7.0 - - return "\${(\$_ = isset(\$this->services['$id']) ? \$this->services['$id'] : $code) && false ?: '_'}"; + return sprintf('($this->services[\'%s\'] ?? %s)', $id, $code); } /** * Initializes the method names map to avoid conflicts with the Container methods. - * - * @param string $class the container base class */ - private function initializeMethodNamesMap($class) + private function initializeMethodNamesMap(string $class) { $this->serviceIdToMethodNameMap = array(); $this->usedMethodNames = array(); @@ -1930,15 +1695,9 @@ private function initializeMethodNamesMap($class) } /** - * Convert a service id to a valid PHP method name. - * - * @param string $id - * - * @return string - * * @throws InvalidArgumentException */ - private function generateMethodName($id) + private function generateMethodName(string $id): string { if (isset($this->serviceIdToMethodNameMap[$id])) { return $this->serviceIdToMethodNameMap[$id]; @@ -1961,12 +1720,7 @@ private function generateMethodName($id) return $methodName; } - /** - * Returns the next name to use. - * - * @return string - */ - private function getNextVariableName() + private function getNextVariableName(): string { $firstChars = self::FIRST_CHARS; $firstCharsLength = strlen($firstChars); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 4410a14704aca..defbc7ae89da4 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -185,13 +185,6 @@ private function addService($definition, $id, \DOMElement $parent) $service->setAttribute('autowire', 'true'); } - foreach ($definition->getAutowiringTypes(false) as $autowiringTypeValue) { - $autowiringType = $this->document->createElement('autowiring-type'); - $autowiringType->appendChild($this->document->createTextNode($autowiringTypeValue)); - - $service->appendChild($autowiringType); - } - if ($definition->isAutoconfigured()) { $service->setAttribute('autoconfigure', 'true'); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index f76857224a444..25830e0bc506b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -54,15 +54,7 @@ public function dump(array $options = array()) return $this->container->resolveEnvPlaceholders($this->addParameters()."\n".$this->addServices()); } - /** - * Adds a service. - * - * @param string $id - * @param Definition $definition - * - * @return string - */ - private function addService($id, Definition $definition) + private function addService(string $id, Definition $definition): string { $code = " $id:\n"; if ($class = $definition->getClass()) { @@ -109,14 +101,6 @@ private function addService($id, Definition $definition) $code .= " autowire: true\n"; } - $autowiringTypesCode = ''; - foreach ($definition->getAutowiringTypes(false) as $autowiringType) { - $autowiringTypesCode .= sprintf(" - %s\n", $this->dumper->dump($autowiringType)); - } - if ($autowiringTypesCode) { - $code .= sprintf(" autowiring_types:\n%s", $autowiringTypesCode); - } - if ($definition->isAutoconfigured()) { $code .= " autoconfigure: true\n"; } @@ -167,15 +151,7 @@ private function addService($id, Definition $definition) return $code; } - /** - * Adds a service alias. - * - * @param string $alias - * @param Alias $id - * - * @return string - */ - private function addServiceAlias($alias, Alias $id) + private function addServiceAlias(string $alias, Alias $id): string { if ($id->isPrivate()) { return sprintf(" %s: '@%s'\n", $alias, $id); @@ -184,12 +160,7 @@ private function addServiceAlias($alias, Alias $id) return sprintf(" %s:\n alias: %s\n public: %s\n", $alias, $id, $id->isPublic() ? 'true' : 'false'); } - /** - * Adds services. - * - * @return string - */ - private function addServices() + private function addServices(): string { if (!$this->container->getDefinitions()) { return ''; @@ -211,12 +182,7 @@ private function addServices() return $code; } - /** - * Adds parameters. - * - * @return string - */ - private function addParameters() + private function addParameters(): string { if (!$this->container->getParameterBag()->all()) { return ''; @@ -296,15 +262,7 @@ private function dumpValue($value) return $value; } - /** - * Gets the service call. - * - * @param string $id - * @param Reference $reference - * - * @return string - */ - private function getServiceCall($id, Reference $reference = null) + private function getServiceCall(string $id, Reference $reference = null): string { if (null !== $reference) { switch ($reference->getInvalidBehavior()) { @@ -317,14 +275,7 @@ private function getServiceCall($id, Reference $reference = null) return sprintf('@%s', $id); } - /** - * Gets parameter call. - * - * @param string $id - * - * @return string - */ - private function getParameterCall($id) + private function getParameterCall(string $id): string { return sprintf('%%%s%%', $id); } @@ -334,15 +285,7 @@ private function getExpressionCall($expression) return sprintf('@=%s', $expression); } - /** - * Prepares parameters. - * - * @param array $parameters - * @param bool $escape - * - * @return array - */ - private function prepareParameters(array $parameters, $escape = true) + private function prepareParameters(array $parameters, bool $escape = true): array { $filtered = array(); foreach ($parameters as $key => $value) { @@ -358,12 +301,7 @@ private function prepareParameters(array $parameters, $escape = true) return $escape ? $this->escape($filtered) : $filtered; } - /** - * Escapes arguments. - * - * @return array - */ - private function escape(array $arguments) + private function escape(array $arguments): array { $args = array(); foreach ($arguments as $k => $v) { diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php index d4c60a8606a13..1bb68a98cd923 100644 --- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php +++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php @@ -36,6 +36,7 @@ public static function getProvidedTypes() 'base64' => 'string', 'bool' => 'bool', 'const' => 'bool|int|float|string|array', + 'csv' => 'array', 'file' => 'string', 'float' => 'float', 'int' => 'int', @@ -149,6 +150,10 @@ public function getEnv($prefix, $name, \Closure $getEnv) }, $env); } + if ('csv' === $prefix) { + return str_getcsv($env); + } + throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix)); } diff --git a/src/Symfony/Component/DependencyInjection/Exception/AutowiringFailedException.php b/src/Symfony/Component/DependencyInjection/Exception/AutowiringFailedException.php index 145cd8cbdcf24..f198cd2800289 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/AutowiringFailedException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/AutowiringFailedException.php @@ -18,7 +18,7 @@ class AutowiringFailedException extends RuntimeException { private $serviceId; - public function __construct($serviceId, $message = '', $code = 0, \Exception $previous = null) + public function __construct(string $serviceId, string $message = '', int $code = 0, \Exception $previous = null) { $this->serviceId = $serviceId; diff --git a/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php b/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php index 577095e88b493..6ed18e06807f5 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php @@ -18,7 +18,7 @@ */ class EnvNotFoundException extends InvalidArgumentException { - public function __construct($name) + public function __construct(string $name) { parent::__construct(sprintf('Environment variable not found: "%s".', $name)); } diff --git a/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php b/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php index 3839a4633be40..c758ce143824a 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/EnvParameterException.php @@ -18,7 +18,7 @@ */ class EnvParameterException extends InvalidArgumentException { - public function __construct(array $envs, \Exception $previous = null, $message = 'Incompatible use of dynamic environment variables "%s" found in parameters.') + public function __construct(array $envs, \Exception $previous = null, string $message = 'Incompatible use of dynamic environment variables "%s" found in parameters.') { parent::__construct(sprintf($message, implode('", "', $envs)), 0, $previous); } diff --git a/src/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php b/src/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php index 29151765dc5b6..e17e4024fdb4d 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php @@ -20,7 +20,7 @@ class ParameterCircularReferenceException extends RuntimeException { private $parameters; - public function __construct($parameters, \Exception $previous = null) + public function __construct(array $parameters, \Exception $previous = null) { parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0]), 0, $previous); diff --git a/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php b/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php index 40c01b05081d8..07066a92841ef 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php @@ -11,12 +11,14 @@ namespace Symfony\Component\DependencyInjection\Exception; +use Psr\Container\NotFoundExceptionInterface; + /** * This exception is thrown when a non-existent parameter is used. * * @author Fabien Potencier */ -class ParameterNotFoundException extends InvalidArgumentException +class ParameterNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface { private $key; private $sourceId; @@ -32,7 +34,7 @@ class ParameterNotFoundException extends InvalidArgumentException * @param string[] $alternatives Some parameter name alternatives * @param string|null $nonNestedAlternative The alternative parameter name when the user expected dot notation for nested parameters */ - public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = array(), $nonNestedAlternative = null) + public function __construct(string $key, string $sourceId = null, string $sourceKey = null, \Exception $previous = null, array $alternatives = array(), string $nonNestedAlternative = null) { $this->key = $key; $this->sourceId = $sourceId; diff --git a/src/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php b/src/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php index 26e3fb34bf3cb..5936b3a6d7944 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php @@ -21,7 +21,7 @@ class ServiceCircularReferenceException extends RuntimeException private $serviceId; private $path; - public function __construct($serviceId, array $path, \Exception $previous = null) + public function __construct(string $serviceId, array $path, \Exception $previous = null) { parent::__construct(sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, implode(' -> ', $path)), 0, $previous); diff --git a/src/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php b/src/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php index 59567074ccb2c..dabd9da4a41e4 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php @@ -24,7 +24,7 @@ class ServiceNotFoundException extends InvalidArgumentException implements NotFo private $sourceId; private $alternatives; - public function __construct($id, $sourceId = null, \Exception $previous = null, array $alternatives = array(), $msg = null) + public function __construct(string $id, string $sourceId = null, \Exception $previous = null, array $alternatives = array(), string $msg = null) { if (null !== $msg) { // no-op diff --git a/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php b/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php index 051d41b84ba4b..a64561c3a9715 100644 --- a/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php +++ b/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection; +use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; /** @@ -25,7 +26,7 @@ class ExpressionLanguage extends BaseExpressionLanguage /** * {@inheritdoc} */ - public function __construct($cache = null, array $providers = array(), callable $serviceCompiler = null) + public function __construct(CacheItemPoolInterface $cache = null, array $providers = array(), callable $serviceCompiler = null) { // prepend the default provider to let users override it easily array_unshift($providers, new ExpressionLanguageProvider($serviceCompiler)); diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php index f2d0476f6e4a4..60787b7743440 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php @@ -32,11 +32,11 @@ public function isProxyCandidate(Definition $definition); * * @param Definition $definition * @param string $id Service identifier - * @param string $factoryCode The code to execute to create the service, will be added to the interface in 4.0 + * @param string $factoryCode The code to execute to create the service * * @return string */ - public function getProxyFactoryCode(Definition $definition, $id/**, $factoryCode = null */); + public function getProxyFactoryCode(Definition $definition, $id, $factoryCode); /** * Generates the code for the lazy proxy. diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php index 84686efff5d6a..6e3d75bb0b9de 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php @@ -24,23 +24,15 @@ class ProxyHelper public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionParameter $p = null, $noBuiltin = false) { if ($p instanceof \ReflectionParameter) { - if (method_exists($p, 'getType')) { - $type = $p->getType(); - } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $p, $type)) { - $name = $type = $type[1]; - - if ('callable' === $name || 'array' === $name) { - return $noBuiltin ? null : $name; - } - } + $type = $p->getType(); } else { - $type = method_exists($r, 'getReturnType') ? $r->getReturnType() : null; + $type = $r->getReturnType(); } if (!$type) { return; } if (!is_string($type)) { - $name = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); + $name = $type->getName(); if ($type->isBuiltin()) { return $noBuiltin ? null : $name; diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php index 40b6c2f389723..3d89e2a99e8e7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php @@ -20,7 +20,7 @@ abstract class AbstractServiceConfigurator extends AbstractConfigurator protected $id; private $defaultTags = array(); - public function __construct(ServicesConfigurator $parent, Definition $definition, $id = null, array $defaultTags = array()) + public function __construct(ServicesConfigurator $parent, Definition $definition, string $id = null, array $defaultTags = array()) { $this->parent = $parent; $this->definition = $definition; @@ -41,13 +41,8 @@ public function __destruct() /** * Registers a service. - * - * @param string $id - * @param string|null $class - * - * @return ServiceConfigurator */ - final public function set($id, $class = null) + final public function set(string $id, string $class = null): ServiceConfigurator { $this->__destruct(); @@ -56,13 +51,8 @@ final public function set($id, $class = null) /** * Creates an alias. - * - * @param string $id - * @param string $referencedId - * - * @return AliasConfigurator */ - final public function alias($id, $referencedId) + final public function alias(string $id, string $referencedId): AliasConfigurator { $this->__destruct(); @@ -71,13 +61,8 @@ final public function alias($id, $referencedId) /** * Registers a PSR-4 namespace using a glob pattern. - * - * @param string $namespace - * @param string $resource - * - * @return PrototypeConfigurator */ - final public function load($namespace, $resource) + final public function load(string $namespace, string $resource): PrototypeConfigurator { $this->__destruct(); @@ -87,13 +72,9 @@ final public function load($namespace, $resource) /** * Gets an already defined service definition. * - * @param string $id - * - * @return ServiceConfigurator - * * @throws ServiceNotFoundException if the service definition does not exist */ - final public function get($id) + final public function get(string $id): ServiceConfigurator { $this->__destruct(); @@ -102,13 +83,8 @@ final public function get($id) /** * Registers a service. - * - * @param string $id - * @param string|null $class - * - * @return ServiceConfigurator */ - final public function __invoke($id, $class = null) + final public function __invoke(string $id, string $class = null): ServiceConfigurator { $this->__destruct(); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php index a68f83d8f583c..22fc92727a993 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -31,8 +31,9 @@ class ContainerConfigurator extends AbstractConfigurator private $instanceof; private $path; private $file; + private $anonymousCount = 0; - public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, $path, $file) + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file) { $this->container = $container; $this->loader = $loader; @@ -41,7 +42,7 @@ public function __construct(ContainerBuilder $container, PhpFileLoader $loader, $this->file = $file; } - final public function extension($namespace, array $config) + final public function extension(string $namespace, array $config) { if (!$this->container->hasExtension($namespace)) { $extensions = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions())); @@ -57,49 +58,35 @@ final public function extension($namespace, array $config) $this->container->loadFromExtension($namespace, static::processValue($config)); } - final public function import($resource, $type = null, $ignoreErrors = false) + final public function import(string $resource, string $type = null, bool $ignoreErrors = false) { $this->loader->setCurrentDir(dirname($this->path)); $this->loader->import($resource, $type, $ignoreErrors, $this->file); } - /** - * @return ParametersConfigurator - */ - final public function parameters() + final public function parameters(): ParametersConfigurator { return new ParametersConfigurator($this->container); } - /** - * @return ServicesConfigurator - */ - final public function services() + final public function services(): ServicesConfigurator { - return new ServicesConfigurator($this->container, $this->loader, $this->instanceof); + return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount); } } /** * Creates a service reference. - * - * @param string $id - * - * @return ReferenceConfigurator */ -function ref($id) +function ref(string $id): ReferenceConfigurator { return new ReferenceConfigurator($id); } /** * Creates an inline service. - * - * @param string|null $class - * - * @return InlineServiceConfigurator */ -function inline($class = null) +function inline(string $class = null): InlineServiceConfigurator { return new InlineServiceConfigurator(new Definition($class)); } @@ -108,34 +95,24 @@ function inline($class = null) * Creates a lazy iterator. * * @param ReferenceConfigurator[] $values - * - * @return IteratorArgument */ -function iterator(array $values) +function iterator(array $values): IteratorArgument { return new IteratorArgument(AbstractConfigurator::processValue($values, true)); } /** * Creates a lazy iterator by tag name. - * - * @param string $tag - * - * @return TaggedIteratorArgument */ -function tagged($tag) +function tagged(string $tag): TaggedIteratorArgument { return new TaggedIteratorArgument($tag); } /** * Creates an expression. - * - * @param string $expression an expression - * - * @return Expression */ -function expr($expression) +function expr(string $expression): Expression { return new Expression($expression); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php index adf294b9928d9..aec0b2bd21032 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php @@ -15,8 +15,6 @@ /** * @author Nicolas Grekas - * - * @method InstanceofConfigurator instanceof(string $fqcn) */ class DefaultsConfigurator extends AbstractServiceConfigurator { @@ -30,14 +28,11 @@ class DefaultsConfigurator extends AbstractServiceConfigurator /** * Adds a tag for this definition. * - * @param string $name The tag name - * @param array $attributes An array of attributes - * * @return $this * * @throws InvalidArgumentException when an invalid tag name or attribute is provided */ - final public function tag($name, array $attributes = array()) + final public function tag(string $name, array $attributes = array()) { if (!is_string($name) || '' === $name) { throw new InvalidArgumentException('The tag name in "_defaults" must be a non-empty string.'); @@ -57,11 +52,9 @@ final public function tag($name, array $attributes = array()) /** * Defines an instanceof-conditional to be applied to following service definitions. * - * @param string $fqcn - * * @return InstanceofConfigurator */ - final protected function setInstanceof($fqcn) + final public function instanceof(string $fqcn) { return $this->parent->instanceof($fqcn); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php index 1d3975bf8794e..9ecb2a21c3deb 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InstanceofConfigurator.php @@ -13,8 +13,6 @@ /** * @author Nicolas Grekas - * - * @method InstanceofConfigurator instanceof(string $fqcn) */ class InstanceofConfigurator extends AbstractServiceConfigurator { @@ -31,12 +29,8 @@ class InstanceofConfigurator extends AbstractServiceConfigurator /** * Defines an instanceof-conditional to be applied to following service definitions. - * - * @param string $fqcn - * - * @return InstanceofConfigurator */ - final protected function setInstanceof($fqcn) + final public function instanceof(string $fqcn): InstanceofConfigurator { return $this->parent->instanceof($fqcn); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ParametersConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ParametersConfigurator.php index 9585b1a4b5c34..bacc60f05146d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ParametersConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ParametersConfigurator.php @@ -30,12 +30,9 @@ public function __construct(ContainerBuilder $container) /** * Creates a parameter. * - * @param string $name - * @param mixed $value - * * @return $this */ - final public function set($name, $value) + final public function set(string $name, $value) { $this->container->setParameter($name, static::processValue($value, true)); @@ -45,12 +42,9 @@ final public function set($name, $value) /** * Creates a parameter. * - * @param string $name - * @param mixed $value - * * @return $this */ - final public function __invoke($name, $value) + final public function __invoke(string $name, $value) { return $this->set($name, $value); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php index 76e7829e8fe99..573dcc51e8204 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php @@ -42,7 +42,7 @@ class PrototypeConfigurator extends AbstractServiceConfigurator private $exclude; private $allowParent; - public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, $namespace, $resource, $allowParent) + public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, string $namespace, string $resource, bool $allowParent) { $definition = new Definition(); $definition->setPublic($defaults->isPublic()); @@ -71,11 +71,9 @@ public function __destruct() /** * Excludes files from registration using a glob pattern. * - * @param string $exclude - * * @return $this */ - final public function exclude($exclude) + final public function exclude(string $exclude) { $this->exclude = $exclude; diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ReferenceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ReferenceConfigurator.php index 1585c0872a694..590b60a54a390 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ReferenceConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ReferenceConfigurator.php @@ -24,7 +24,7 @@ class ReferenceConfigurator extends AbstractConfigurator /** @internal */ protected $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; - public function __construct($id) + public function __construct(string $id) { $this->id = $id; } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php index 12054cad0c021..8fc60fbd376bd 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php @@ -46,7 +46,7 @@ class ServiceConfigurator extends AbstractServiceConfigurator private $instanceof; private $allowParent; - public function __construct(ContainerBuilder $container, array $instanceof, $allowParent, ServicesConfigurator $parent, Definition $definition, $id, array $defaultTags) + public function __construct(ContainerBuilder $container, array $instanceof, bool $allowParent, ServicesConfigurator $parent, Definition $definition, $id, array $defaultTags) { $this->container = $container; $this->instanceof = $instanceof; diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php index c9673b6c7a730..ce122dcec96bf 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php @@ -20,8 +20,6 @@ /** * @author Nicolas Grekas - * - * @method InstanceofConfigurator instanceof($fqcn) */ class ServicesConfigurator extends AbstractConfigurator { @@ -31,34 +29,32 @@ class ServicesConfigurator extends AbstractConfigurator private $container; private $loader; private $instanceof; + private $anonymousHash; + private $anonymousCount; - public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof) + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path = null, int &$anonymousCount = 0) { $this->defaults = new Definition(); $this->container = $container; $this->loader = $loader; $this->instanceof = &$instanceof; + $this->anonymousHash = ContainerBuilder::hash($path ?: mt_rand()); + $this->anonymousCount = &$anonymousCount; $instanceof = array(); } /** * Defines a set of defaults for following service definitions. - * - * @return DefaultsConfigurator */ - final public function defaults() + final public function defaults(): DefaultsConfigurator { return new DefaultsConfigurator($this, $this->defaults = new Definition()); } /** * Defines an instanceof-conditional to be applied to following service definitions. - * - * @param string $fqcn - * - * @return InstanceofConfigurator */ - final protected function setInstanceof($fqcn) + final public function instanceof(string $fqcn): InstanceofConfigurator { $this->instanceof[$fqcn] = $definition = new ChildDefinition(''); @@ -68,18 +64,27 @@ final protected function setInstanceof($fqcn) /** * Registers a service. * - * @param string $id - * @param string|null $class - * - * @return ServiceConfigurator + * @param string|null $id The service id, or null to create an anonymous service + * @param string|null $class The class of the service, or null when $id is also the class name */ - final public function set($id, $class = null) + final public function set(?string $id, string $class = null): ServiceConfigurator { $defaults = $this->defaults; $allowParent = !$defaults->getChanges() && empty($this->instanceof); $definition = new Definition(); - $definition->setPublic($defaults->isPublic()); + + if (null === $id) { + if (!$class) { + throw new \LogicException('Anonymous services must have a class name.'); + } + + $id = sprintf('%d_%s', ++$this->anonymousCount, preg_replace('/^.*\\\\/', '', $class).'~'.$this->anonymousHash); + $definition->setPublic(false); + } else { + $definition->setPublic($defaults->isPublic()); + } + $definition->setAutowired($defaults->isAutowired()); $definition->setAutoconfigured($defaults->isAutoconfigured()); $definition->setBindings($defaults->getBindings()); @@ -92,13 +97,8 @@ final public function set($id, $class = null) /** * Creates an alias. - * - * @param string $id - * @param string $referencedId - * - * @return AliasConfigurator */ - final public function alias($id, $referencedId) + final public function alias(string $id, string $referencedId): AliasConfigurator { $ref = static::processValue($referencedId, true); $alias = new Alias((string) $ref, $this->defaults->isPublic()); @@ -109,13 +109,8 @@ final public function alias($id, $referencedId) /** * Registers a PSR-4 namespace using a glob pattern. - * - * @param string $namespace - * @param string $resource - * - * @return PrototypeConfigurator */ - final public function load($namespace, $resource) + final public function load(string $namespace, string $resource): PrototypeConfigurator { $allowParent = !$this->defaults->getChanges() && empty($this->instanceof); @@ -125,13 +120,9 @@ final public function load($namespace, $resource) /** * Gets an already defined service definition. * - * @param string $id - * - * @return ServiceConfigurator - * * @throws ServiceNotFoundException if the service definition does not exist */ - final public function get($id) + final public function get(string $id): ServiceConfigurator { $allowParent = !$this->defaults->getChanges() && empty($this->instanceof); $definition = $this->container->getDefinition($id); @@ -141,13 +132,8 @@ final public function get($id) /** * Registers a service. - * - * @param string $id - * @param string|null $class - * - * @return ServiceConfigurator */ - final public function __invoke($id, $class = null) + final public function __invoke(string $id, string $class = null): ServiceConfigurator { return $this->set($id, $class); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AbstractTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AbstractTrait.php index f69a7a5be109d..57ddb480facf3 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AbstractTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AbstractTrait.php @@ -11,20 +11,15 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; -/** - * @method $this abstract(bool $abstract = true) - */ trait AbstractTrait { /** * Whether this definition is abstract, that means it merely serves as a * template for other definitions. * - * @param bool $abstract - * * @return $this */ - final protected function setAbstract($abstract = true) + final public function abstract(bool $abstract = true) { $this->definition->setAbstract($abstract); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutoconfigureTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutoconfigureTrait.php index 42a692353e9dc..a923b3fe59fd1 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutoconfigureTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutoconfigureTrait.php @@ -19,13 +19,11 @@ trait AutoconfigureTrait /** * Sets whether or not instanceof conditionals should be prepended with a global set. * - * @param bool $autoconfigured - * * @return $this * * @throws InvalidArgumentException when a parent is already set */ - final public function autoconfigure($autoconfigured = true) + final public function autoconfigure(bool $autoconfigured = true) { if ($autoconfigured && $this->definition instanceof ChildDefinition) { throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try disabling autoconfiguration for the service.', $this->id)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutowireTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutowireTrait.php index 3d4b2e854b4c7..4c90af849a055 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutowireTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/AutowireTrait.php @@ -16,11 +16,9 @@ trait AutowireTrait /** * Enables/disables autowiring. * - * @param bool $autowired - * * @return $this */ - final public function autowire($autowired = true) + final public function autowire(bool $autowired = true) { $this->definition->setAutowired($autowired); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ClassTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ClassTrait.php index ae5b1c0a3d0c5..ef44afe6c1da7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ClassTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ClassTrait.php @@ -11,19 +11,14 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; -/** - * @method $this class(string $class) - */ trait ClassTrait { /** * Sets the service class. * - * @param string $class The service class - * * @return $this */ - final protected function setClass($class) + final public function class($class) { $this->definition->setClass($class); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php index d7ea8b27f5ea3..2af867c19bb26 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php @@ -16,11 +16,9 @@ trait LazyTrait /** * Sets the lazy flag of this service. * - * @param bool $lazy - * * @return $this */ - final public function lazy($lazy = true) + final public function lazy(bool $lazy = true) { $this->definition->setLazy($lazy); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ParentTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ParentTrait.php index 43f1223e324a6..4eba03d7eebb8 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ParentTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ParentTrait.php @@ -14,21 +14,16 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -/** - * @method $this parent(string $parent) - */ trait ParentTrait { /** * Sets the Definition to inherit from. * - * @param string $parent - * * @return $this * * @throws InvalidArgumentException when parent cannot be set */ - final protected function setParent($parent) + final public function parent(string $parent) { if (!$this->allowParent) { throw new InvalidArgumentException(sprintf('A parent cannot be defined when either "_instanceof" or "_defaults" are also defined for service prototype "%s".', $this->id)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PropertyTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PropertyTrait.php index d5d938708e0a8..50c2ee863609d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PropertyTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PropertyTrait.php @@ -16,12 +16,9 @@ trait PropertyTrait /** * Sets a specific property. * - * @param string $name - * @param mixed $value - * * @return $this */ - final public function property($name, $value) + final public function property(string $name, $value) { $this->definition->setProperty($name, static::processValue($value, true)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PublicTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PublicTrait.php index 8f7f79f1cc218..9886523c95c95 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PublicTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/PublicTrait.php @@ -11,16 +11,12 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; -/** - * @method $this public() - * @method $this private() - */ trait PublicTrait { /** * @return $this */ - final protected function setPublic() + final public function public() { $this->definition->setPublic(true); @@ -30,7 +26,7 @@ final protected function setPublic() /** * @return $this */ - final protected function setPrivate() + final public function private() { $this->definition->setPublic(false); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/SyntheticTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/SyntheticTrait.php index 81eceff43d86d..bf124f0a40178 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/SyntheticTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/SyntheticTrait.php @@ -17,11 +17,9 @@ trait SyntheticTrait * Sets whether this definition is synthetic, that is not constructed by the * container, but dynamically injected. * - * @param bool $synthetic - * * @return $this */ - final public function synthetic($synthetic = true) + final public function synthetic(bool $synthetic = true) { $this->definition->setSynthetic($synthetic); diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index fa8c636edcd1f..83a3f4f87ca45 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -121,7 +121,7 @@ private function findClasses($namespace, $pattern, $excludePattern) $pattern = $parameterBag->unescapeValue($parameterBag->resolveValue($pattern)); $classes = array(); - $extRegexp = defined('HHVM_VERSION') ? '/\\.(?:php|hh)$/' : '/\\.php$/'; + $extRegexp = '/\\.php$/'; $prefixLen = null; foreach ($this->glob($pattern, true, $resource) as $path => $info) { if (null === $prefixLen) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 72774ddcd576d..71ddd2227b605 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -347,10 +347,6 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults) $definition->addTag($tag->getAttribute('name'), $parameters); } - foreach ($this->getChildren($service, 'autowiring-type') as $type) { - $definition->addAutowiringType($type->textContent); - } - $bindings = $this->getArgumentsAsPhp($service, 'bind', $file); if (isset($defaults['bind'])) { // deep clone, to avoid multiple process of the same instance in the passes @@ -416,7 +412,7 @@ private function processAnonymousServices(\DOMDocument $xml, $file, $defaults) $node->setAttribute('id', $id); $node->setAttribute('service', $id); - $definitions[$id] = array($services[0], $file, false); + $definitions[$id] = array($services[0], $file); $services[0]->setAttribute('id', $id); // anonymous services are always private @@ -429,27 +425,16 @@ private function processAnonymousServices(\DOMDocument $xml, $file, $defaults) // anonymous services "in the wild" if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { foreach ($nodes as $node) { - @trigger_error(sprintf('Top-level anonymous services are deprecated since Symfony 3.4, the "id" attribute will be required in version 4.0 in %s at line %d.', $file, $node->getLineNo()), E_USER_DEPRECATED); - - // give it a unique name - $id = sprintf('%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $node->getAttribute('class')).$suffix); - $node->setAttribute('id', $id); - $definitions[$id] = array($node, $file, true); + throw new InvalidArgumentException(sprintf('Top-level services must have "id" attribute, none found in %s at line %d.', $file, $node->getLineNo())); } } // resolve definitions uksort($definitions, 'strnatcmp'); - foreach (array_reverse($definitions) as $id => list($domElement, $file, $wild)) { - if (null !== $definition = $this->parseDefinition($domElement, $file, $wild ? $defaults : array())) { + foreach (array_reverse($definitions) as $id => list($domElement, $file)) { + if (null !== $definition = $this->parseDefinition($domElement, $file, array())) { $this->setDefinition($id, $definition); } - - if (true === $wild) { - $tmpDomElement = new \DOMElement('_services', null, self::NS); - $domElement->parentNode->replaceChild($tmpDomElement, $domElement); - $tmpDomElement->setAttribute('id', $id); - } } } @@ -499,9 +484,6 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = if (!$arg->getAttribute('id')) { throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service" has no or empty "id" attribute in "%s".', $name, $file)); } - if ($arg->hasAttribute('strict')) { - @trigger_error(sprintf('The "strict" attribute used when referencing the "%s" service is deprecated since Symfony 3.3 and will be removed in 4.0.', $arg->getAttribute('id')), E_USER_DEPRECATED); - } $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior); break; @@ -651,13 +633,13 @@ private function validateAlias(\DOMElement $alias, $file) { foreach ($alias->attributes as $name => $node) { if (!in_array($name, array('alias', 'id', 'public'))) { - @trigger_error(sprintf('Using the attribute "%s" is deprecated for the service "%s" which is defined as an alias in "%s". Allowed attributes for service aliases are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $name, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); + throw new InvalidArgumentException(sprintf('Invalid attribute "%s" defined for alias "%s" in "%s".', $name, $alias->getAttribute('id'), $file)); } } foreach ($alias->childNodes as $child) { if ($child instanceof \DOMElement && self::NS === $child->namespaceURI) { - @trigger_error(sprintf('Using the element "%s" is deprecated for the service "%s" which is defined as an alias in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.', $child->localName, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); + throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file)); } } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index fa27767b40388..81584f57ef306 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -57,7 +57,6 @@ class YamlFileLoader extends FileLoader 'decoration_inner_name' => 'decoration_inner_name', 'decoration_priority' => 'decoration_priority', 'autowire' => 'autowire', - 'autowiring_types' => 'autowiring_types', 'autoconfigure' => 'autoconfigure', 'bind' => 'bind', ); @@ -167,13 +166,7 @@ public function supports($resource, $type = null) return in_array($type, array('yaml', 'yml'), true); } - /** - * Parses all imports. - * - * @param array $content - * @param string $file - */ - private function parseImports(array $content, $file) + private function parseImports(array $content, string $file) { if (!isset($content['imports'])) { return; @@ -197,13 +190,7 @@ private function parseImports(array $content, $file) } } - /** - * Parses definitions. - * - * @param array $content - * @param string $file - */ - private function parseDefinitions(array $content, $file) + private function parseDefinitions(array $content, string $file) { if (!isset($content['services'])) { return; @@ -241,14 +228,9 @@ private function parseDefinitions(array $content, $file) } /** - * @param array $content - * @param string $file - * - * @return array - * * @throws InvalidArgumentException */ - private function parseDefaults(array &$content, $file) + private function parseDefaults(array &$content, string $file): array { if (!array_key_exists('_defaults', $content['services'])) { return array(); @@ -305,12 +287,7 @@ private function parseDefaults(array &$content, $file) return $defaults; } - /** - * @param array $service - * - * @return bool - */ - private function isUsingShortSyntax(array $service) + private function isUsingShortSyntax(array $service): bool { foreach ($service as $key => $value) { if (is_string($key) && ('' === $key || '$' !== $key[0])) { @@ -334,8 +311,9 @@ private function isUsingShortSyntax(array $service) private function parseDefinition($id, $service, $file, array $defaults) { if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) { - @trigger_error(sprintf('Service names that start with an underscore are deprecated since Symfony 3.3 and will be reserved in 4.0. Rename the "%s" service or define it in XML instead.', $id), E_USER_DEPRECATED); + throw new InvalidArgumentException(sprintf('Service names that start with an underscore are reserved. Rename the "%s" service or define it in XML instead.', $id)); } + if (is_string($service) && 0 === strpos($service, '@')) { $this->container->setAlias($id, $alias = new Alias(substr($service, 1))); if (isset($defaults['public'])) { @@ -369,7 +347,7 @@ private function parseDefinition($id, $service, $file, array $defaults) foreach ($service as $key => $value) { if (!in_array($key, array('alias', 'public'))) { - @trigger_error(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public". The YamlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $key, $id, $file), E_USER_DEPRECATED); + throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public".', $key, $id, $file)); } } @@ -530,24 +508,6 @@ private function parseDefinition($id, $service, $file, array $defaults) $definition->setAutowired($service['autowire']); } - if (isset($service['autowiring_types'])) { - if (is_string($service['autowiring_types'])) { - $definition->addAutowiringType($service['autowiring_types']); - } else { - if (!is_array($service['autowiring_types'])) { - throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); - } - - foreach ($service['autowiring_types'] as $autowiringType) { - if (!is_string($autowiringType)) { - throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); - } - - $definition->addAutowiringType($autowiringType); - } - } - } - if (isset($defaults['bind']) || isset($service['bind'])) { // deep clone, to avoid multiple process of the same instance in the passes $bindings = isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : array(); @@ -657,18 +617,10 @@ protected function loadFile($file) $this->yamlParser = new YamlParser(); } - $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) { - $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message; - - return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false; - }); - try { $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS); } catch (ParseException $e) { throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e); - } finally { - restore_error_handler(); } return $this->validate($configuration, $file); @@ -798,11 +750,6 @@ private function resolveServices($value, $file, $isParameter = false) $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; } - if ('=' === substr($value, -1)) { - @trigger_error(sprintf('The "=" suffix that used to disable strict references in Symfony 2.x is deprecated since Symfony 3.3 and will be unsupported in 4.0. Remove it in "%s".', $value), E_USER_DEPRECATED); - $value = substr($value, 0, -1); - } - if (null !== $invalidBehavior) { $value = new Reference($value, $invalidBehavior); } @@ -838,7 +785,7 @@ private function loadFromExtensions(array $content) */ private function checkDefinition($id, array $definition, $file) { - if ($throw = $this->isLoadingInstanceof) { + if ($this->isLoadingInstanceof) { $keywords = self::$instanceofKeywords; } elseif ($throw = (isset($definition['resource']) || isset($definition['namespace']))) { $keywords = self::$prototypeKeywords; @@ -848,11 +795,7 @@ private function checkDefinition($id, array $definition, $file) foreach ($definition as $key => $value) { if (!isset($keywords[$key])) { - if ($throw) { - throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords))); - } - - @trigger_error(sprintf('The configuration key "%s" is unsupported for service definition "%s" in "%s". Allowed configuration keys are "%s". The YamlFileLoader object will raise an exception instead in Symfony 4.0 when detecting an unsupported service configuration key.', $key, $id, $file, implode('", "', $keywords)), E_USER_DEPRECATED); + throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords))); } } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 3a55a7df676c7..99ac00567d915 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -117,7 +117,6 @@ - @@ -207,7 +206,6 @@ - @@ -233,7 +231,6 @@ - diff --git a/src/Symfony/Component/DependencyInjection/Parameter.php b/src/Symfony/Component/DependencyInjection/Parameter.php index cac6f6c4c2264..d484ac0f947eb 100644 --- a/src/Symfony/Component/DependencyInjection/Parameter.php +++ b/src/Symfony/Component/DependencyInjection/Parameter.php @@ -20,10 +20,7 @@ class Parameter { private $id; - /** - * @param string $id The parameter key - */ - public function __construct($id) + public function __construct(string $id) { $this->id = $id; } @@ -33,6 +30,6 @@ public function __construct($id) */ public function __toString() { - return (string) $this->id; + return $this->id; } } diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ContainerBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ContainerBag.php new file mode 100644 index 0000000000000..7671dfc6cabd9 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ContainerBag.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Container; + +/** + * @author Nicolas Grekas + */ +class ContainerBag extends FrozenParameterBag implements ContainerBagInterface +{ + private $container; + + public function __construct(Container $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->container->getParameterBag()->all(); + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + return $this->container->getParameter($name); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return $this->container->hasParameter($name); + } +} diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ContainerBagInterface.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ContainerBagInterface.php new file mode 100644 index 0000000000000..6b0bd2001117e --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ContainerBagInterface.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\ParameterBag; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; + +/** + * @author Nicolas Grekas + */ +interface ContainerBagInterface extends ContainerInterface +{ + /** + * Gets the service container parameters. + * + * @return array An array of parameters + */ + public function all(); + + /** + * Replaces parameter placeholders (%name%) by their values. + * + * @param mixed $value A value + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + */ + public function resolveValue($value); + + /** + * Escape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function escapeValue($value); + + /** + * Unescape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function unescapeValue($value); +} diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php index 257c4334300d5..da6a21b83734a 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php @@ -25,8 +25,6 @@ class ParameterBag implements ParameterBagInterface protected $parameters = array(); protected $resolved = false; - private $normalizedNames = array(); - /** * @param array $parameters An array of parameters */ @@ -68,7 +66,7 @@ public function all() */ public function get($name) { - $name = $this->normalizeName($name); + $name = (string) $name; if (!array_key_exists($name, $this->parameters)) { if (!$name) { @@ -113,7 +111,7 @@ public function get($name) */ public function set($name, $value) { - $this->parameters[$this->normalizeName($name)] = $value; + $this->parameters[(string) $name] = $value; } /** @@ -121,7 +119,7 @@ public function set($name, $value) */ public function has($name) { - return array_key_exists($this->normalizeName($name), $this->parameters); + return array_key_exists((string) $name, $this->parameters); } /** @@ -131,7 +129,7 @@ public function has($name) */ public function remove($name) { - unset($this->parameters[$this->normalizeName($name)]); + unset($this->parameters[(string) $name]); } /** @@ -208,13 +206,12 @@ public function resolveString($value, array $resolving = array()) // a non-string in a parameter value if (preg_match('/^%([^%\s]+)%$/', $value, $match)) { $key = $match[1]; - $lcKey = strtolower($key); // strtolower() to be removed in 4.0 - if (isset($resolving[$lcKey])) { + if (isset($resolving[$key])) { throw new ParameterCircularReferenceException(array_keys($resolving)); } - $resolving[$lcKey] = true; + $resolving[$key] = true; return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving); } @@ -226,8 +223,7 @@ public function resolveString($value, array $resolving = array()) } $key = $match[1]; - $lcKey = strtolower($key); // strtolower() to be removed in 4.0 - if (isset($resolving[$lcKey])) { + if (isset($resolving[$key])) { throw new ParameterCircularReferenceException(array_keys($resolving)); } @@ -238,7 +234,7 @@ public function resolveString($value, array $resolving = array()) } $resolved = (string) $resolved; - $resolving[$lcKey] = true; + $resolving[$key] = true; return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving); }, $value); @@ -290,18 +286,4 @@ public function unescapeValue($value) return $value; } - - private function normalizeName($name) - { - if (isset($this->normalizedNames[$normalizedName = strtolower($name)])) { - $normalizedName = $this->normalizedNames[$normalizedName]; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } } diff --git a/src/Symfony/Component/DependencyInjection/Reference.php b/src/Symfony/Component/DependencyInjection/Reference.php index 82906d2b7524c..c13cf6fe4cc86 100644 --- a/src/Symfony/Component/DependencyInjection/Reference.php +++ b/src/Symfony/Component/DependencyInjection/Reference.php @@ -21,15 +21,9 @@ class Reference private $id; private $invalidBehavior; - /** - * @param string $id The service identifier - * @param int $invalidBehavior The behavior when the service does not exist - * - * @see Container - */ - public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + public function __construct(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - $this->id = (string) $id; + $this->id = $id; $this->invalidBehavior = $invalidBehavior; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php index a941b960746be..7f359b94f5311 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ChildDefinition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; class ChildDefinitionTest extends TestCase { @@ -132,11 +131,6 @@ public function testGetArgumentShouldCheckBounds() $def->getArgument(1); } - public function testDefinitionDecoratorAliasExistsForBackwardsCompatibility() - { - $this->assertInstanceOf(ChildDefinition::class, new DefinitionDecorator('foo')); - } - /** * @expectedException \Symfony\Component\DependencyInjection\Exception\BadMethodCallException */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php deleted file mode 100644 index a9c3445cefdd8..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php +++ /dev/null @@ -1,145 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Compiler\AutowireExceptionPass; -use Symfony\Component\DependencyInjection\Compiler\AutowirePass; -use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; - -/** - * @group legacy - */ -class AutowireExceptionPassTest extends TestCase -{ - public function testThrowsException() - { - $autowirePass = $this->getMockBuilder(AutowirePass::class) - ->getMock(); - - $autowireException = new AutowiringFailedException('foo_service_id', 'An autowiring exception message'); - $autowirePass->expects($this->any()) - ->method('getAutowiringExceptions') - ->will($this->returnValue(array($autowireException))); - - $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) - ->getMock(); - $inlinePass->expects($this->any()) - ->method('getInlinedServiceIds') - ->will($this->returnValue(array())); - - $container = new ContainerBuilder(); - $container->register('foo_service_id'); - - $pass = new AutowireExceptionPass($autowirePass, $inlinePass); - - try { - $pass->process($container); - $this->fail('->process() should throw the exception if the service id exists'); - } catch (\Exception $e) { - $this->assertSame($autowireException, $e); - } - } - - public function testThrowExceptionIfServiceInlined() - { - $autowirePass = $this->getMockBuilder(AutowirePass::class) - ->getMock(); - - $autowireException = new AutowiringFailedException('a_service', 'An autowiring exception message'); - $autowirePass->expects($this->any()) - ->method('getAutowiringExceptions') - ->will($this->returnValue(array($autowireException))); - - $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) - ->getMock(); - $inlinePass->expects($this->any()) - ->method('getInlinedServiceIds') - ->will($this->returnValue(array( - // a_service inlined into b_service - 'a_service' => array('b_service'), - // b_service inlined into c_service - 'b_service' => array('c_service'), - ))); - - $container = new ContainerBuilder(); - // ONLY register c_service in the final container - $container->register('c_service', 'stdClass'); - - $pass = new AutowireExceptionPass($autowirePass, $inlinePass); - - try { - $pass->process($container); - $this->fail('->process() should throw the exception if the service id exists'); - } catch (\Exception $e) { - $this->assertSame($autowireException, $e); - } - } - - public function testDoNotThrowExceptionIfServiceInlinedButRemoved() - { - $autowirePass = $this->getMockBuilder(AutowirePass::class) - ->getMock(); - - $autowireException = new AutowiringFailedException('a_service', 'An autowiring exception message'); - $autowirePass->expects($this->any()) - ->method('getAutowiringExceptions') - ->will($this->returnValue(array($autowireException))); - - $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) - ->getMock(); - $inlinePass->expects($this->any()) - ->method('getInlinedServiceIds') - ->will($this->returnValue(array( - // a_service inlined into b_service - 'a_service' => array('b_service'), - // b_service inlined into c_service - 'b_service' => array('c_service'), - ))); - - // do NOT register c_service in the container - $container = new ContainerBuilder(); - - $pass = new AutowireExceptionPass($autowirePass, $inlinePass); - - $pass->process($container); - // mark the test as passed - $this->assertTrue(true); - } - - public function testNoExceptionIfServiceRemoved() - { - $autowirePass = $this->getMockBuilder(AutowirePass::class) - ->getMock(); - - $autowireException = new AutowiringFailedException('non_existent_service'); - $autowirePass->expects($this->any()) - ->method('getAutowiringExceptions') - ->will($this->returnValue(array($autowireException))); - - $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) - ->getMock(); - $inlinePass->expects($this->any()) - ->method('getInlinedServiceIds') - ->will($this->returnValue(array())); - - $container = new ContainerBuilder(); - - $pass = new AutowireExceptionPass($autowirePass, $inlinePass); - - $pass->process($container); - // mark the test as passed - $this->assertTrue(true); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 36382a00454e9..fb07f47932265 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -45,9 +45,6 @@ public function testProcess() $this->assertEquals(Foo::class, (string) $container->getDefinition('bar')->getArgument(0)); } - /** - * @requires PHP 5.6 - */ public function testProcessVariadic() { $container = new ContainerBuilder(); @@ -63,10 +60,8 @@ public function testProcessVariadic() } /** - * @group legacy - * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should alias the "Symfony\Component\DependencyInjection\Tests\Compiler\B" service to "Symfony\Component\DependencyInjection\Tests\Compiler\A" instead. - * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessageInSymfony4 Cannot autowire service "c": argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\B" service. + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot autowire service "c": argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\B" service. */ public function testProcessAutowireParent() { @@ -84,32 +79,8 @@ public function testProcessAutowireParent() } /** - * @group legacy - * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" to "Symfony\Component\DependencyInjection\Tests\Compiler\AInterface" instead. - * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessageInSymfony4 Cannot autowire service "c": argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\B" service. - */ - public function testProcessLegacyAutowireWithAvailableInterface() - { - $container = new ContainerBuilder(); - - $container->setAlias(AInterface::class, B::class); - $container->register(B::class); - $cDefinition = $container->register('c', __NAMESPACE__.'\C'); - $cDefinition->setAutowired(true); - - (new ResolveClassPass())->process($container); - (new AutowirePass())->process($container); - - $this->assertCount(1, $container->getDefinition('c')->getArguments()); - $this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0)); - } - - /** - * @group legacy - * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should alias the "Symfony\Component\DependencyInjection\Tests\Compiler\F" service to "Symfony\Component\DependencyInjection\Tests\Compiler\DInterface" instead. - * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessageInSymfony4 Cannot autowire service "g": argument "$d" of method "Symfony\Component\DependencyInjection\Tests\Compiler\G::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DInterface" but no such service exists. You should maybe alias this interface to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\F" service. + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot autowire service "g": argument "$d" of method "Symfony\Component\DependencyInjection\Tests\Compiler\G::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DInterface" but no such service exists. You should maybe alias this interface to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\F" service. */ public function testProcessAutowireInterface() { @@ -162,24 +133,6 @@ public function testCompleteExistingDefinitionWithNotDefinedArguments() $this->assertEquals(DInterface::class, (string) $container->getDefinition('h')->getArgument(1)); } - /** - * @group legacy - */ - public function testExceptionsAreStored() - { - $container = new ContainerBuilder(); - - $container->register('c1', __NAMESPACE__.'\CollisionA'); - $container->register('c2', __NAMESPACE__.'\CollisionB'); - $container->register('c3', __NAMESPACE__.'\CollisionB'); - $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(false); - $pass->process($container); - $this->assertCount(1, $pass->getAutowiringExceptions()); - } - /** * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException * @expectedExceptionMessage Invalid service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public. @@ -296,11 +249,10 @@ public function testWithTypeSet() } /** - * @group legacy - * @expectedDeprecation Relying on service auto-registration for type "Symfony\Component\DependencyInjection\Tests\Compiler\Lille" is deprecated since Symfony 3.4 and won't be supported in 4.0. Create a service named "Symfony\Component\DependencyInjection\Tests\Compiler\Lille" instead. - * @expectedDeprecation Relying on service auto-registration for type "Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas" is deprecated since Symfony 3.4 and won't be supported in 4.0. Create a service named "Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas" instead. + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "coop_tilleuls": argument "$j" of method "Symfony\Component\DependencyInjection\Tests\Compiler\LesTilleuls::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas" but no such service exists. */ - public function testCreateDefinition() + public function testServicesAreNotAutoCreated() { $container = new ContainerBuilder(); @@ -309,19 +261,6 @@ public function testCreateDefinition() $pass = new AutowirePass(); $pass->process($container); - - $this->assertCount(2, $container->getDefinition('coop_tilleuls')->getArguments()); - $this->assertEquals('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0)); - $this->assertEquals('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas', $container->getDefinition('coop_tilleuls')->getArgument(1)); - - $dunglasDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas'); - $this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass()); - $this->assertFalse($dunglasDefinition->isPublic()); - $this->assertCount(1, $dunglasDefinition->getArguments()); - $this->assertEquals('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille', $dunglasDefinition->getArgument(0)); - - $lilleDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille'); - $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass()); } public function testResolveParameter() @@ -405,10 +344,8 @@ public function testParentClassNotFoundThrowsException() } /** - * @group legacy - * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename (or alias) the "foo" service to "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" instead. - * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException - * @expectedExceptionMessageInSymfony4 Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but this service is abstract. You should maybe alias this class to the existing "foo" service. + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but this service is abstract. You should maybe alias this class to the existing "foo" service. */ public function testDontUseAbstractServices() { @@ -614,51 +551,6 @@ public function testExplicitMethodInjection() ); } - /** - * @group legacy - * @expectedDeprecation Relying on service auto-registration for type "Symfony\Component\DependencyInjection\Tests\Compiler\A" is deprecated since Symfony 3.4 and won't be supported in 4.0. Create a service named "Symfony\Component\DependencyInjection\Tests\Compiler\A" instead. - */ - public function testTypedReference() - { - $container = new ContainerBuilder(); - - $container - ->register('bar', Bar::class) - ->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class))) - ; - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertSame(A::class, $container->getDefinition('autowired.'.A::class)->getClass()); - } - - /** - * @dataProvider getCreateResourceTests - * @group legacy - */ - public function testCreateResourceForClass($className, $isEqual) - { - $startingResource = AutowirePass::createResourceForClass( - new \ReflectionClass(__NAMESPACE__.'\ClassForResource') - ); - $newResource = AutowirePass::createResourceForClass( - new \ReflectionClass(__NAMESPACE__.'\\'.$className) - ); - - // hack so the objects don't differ by the class name - $startingReflObject = new \ReflectionObject($startingResource); - $reflProp = $startingReflObject->getProperty('class'); - $reflProp->setAccessible(true); - $reflProp->setValue($startingResource, __NAMESPACE__.'\\'.$className); - - if ($isEqual) { - $this->assertEquals($startingResource, $newResource); - } else { - $this->assertNotEquals($startingResource, $newResource); - } - } - public function getCreateResourceTests() { return array( @@ -721,10 +613,8 @@ public function testInterfaceWithNoImplementationSuggestToWriteOne() } /** - * @group legacy - * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename (or alias) the "foo" service to "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" instead. - * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException - * @expectedExceptionMessageInSymfony4 Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but no such service exists. You should maybe alias this class to the existing "foo" service. + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but no such service exists. You should maybe alias this class to the existing "foo" service. */ public function testProcessDoesNotTriggerDeprecations() { @@ -813,38 +703,35 @@ public function provideNotWireableCalls() } /** - * @group legacy - * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead. - * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException - * @expectedExceptionMessageInSymfony4 Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead. + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "foo": argument "$sam" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireableBecauseOfATypo()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\lesTilleuls" but no such service exists. Did you mean "Symfony\Component\DependencyInjection\Tests\Compiler\LesTilleuls"? */ - public function testByIdAlternative() + public function testSuggestRegisteredServicesWithSimilarCase() { $container = new ContainerBuilder(); - $container->setAlias(IInterface::class, 'i'); - $container->register('i', I::class); - $container->register('j', J::class) - ->setAutowired(true); + $container->register(LesTilleuls::class, LesTilleuls::class); + $container->register('foo', NotWireable::class)->setAutowired(true) + ->addMethodCall('setNotAutowireableBecauseOfATypo', array()) + ; - $pass = new AutowirePass(); - $pass->process($container); + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); } /** - * @group legacy - * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for "Symfony\Component\DependencyInjection\Tests\Compiler\A" in "Symfony\Component\DependencyInjection\Tests\Compiler\Bar" to "Symfony\Component\DependencyInjection\Tests\Compiler\AInterface" instead. + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead. */ - public function testTypedReferenceDeprecationNotice() + public function testByIdAlternative() { $container = new ContainerBuilder(); - $container->register('aClass', A::class); - $container->setAlias(AInterface::class, 'aClass'); - $container - ->register('bar', Bar::class) - ->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class))) - ; + $container->setAlias(IInterface::class, 'i'); + $container->register('i', I::class); + $container->register('j', J::class) + ->setAutowired(true); $pass = new AutowirePass(); $pass->process($container); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php index 4430e83e983d2..8c51df86f6811 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -144,30 +144,6 @@ public function testProcessMovesTagsFromDecoratedDefinitionToDecoratingDefinitio $this->assertEquals(array('bar' => array('attr' => 'baz'), 'foobar' => array('attr' => 'bar')), $container->getDefinition('baz')->getTags()); } - /** - * @group legacy - */ - public function testProcessMergesAutowiringTypesInDecoratingDefinitionAndRemoveThemFromDecoratedDefinition() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addAutowiringType('Bar') - ; - - $container - ->register('child') - ->setDecoratedService('parent') - ->addAutowiringType('Foo') - ; - - $this->process($container); - - $this->assertEquals(array('Bar', 'Foo'), $container->getDefinition('child')->getAutowiringTypes()); - $this->assertEmpty($container->getDefinition('child.inner')->getAutowiringTypes()); - } - protected function process(ContainerBuilder $container) { $repeatedPass = new DecoratorServicePass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/FactoryReturnTypePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/FactoryReturnTypePassTest.php deleted file mode 100644 index f299463d5f44a..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/FactoryReturnTypePassTest.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Compiler\FactoryReturnTypePass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Tests\Fixtures\factoryFunction; -use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryDummy; -use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryParent; - -/** - * @author Guilhem N. - * - * @group legacy - */ -class FactoryReturnTypePassTest extends TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - - $factory = $container->register('factory'); - $factory->setFactory(array(FactoryDummy::class, 'createFactory')); - - $container->setAlias('alias_factory', 'factory'); - - $foo = $container->register('foo'); - $foo->setFactory(array(new Reference('alias_factory'), 'create')); - - $bar = $container->register('bar', __CLASS__); - $bar->setFactory(array(new Reference('factory'), 'create')); - - $pass = new FactoryReturnTypePass(); - $pass->process($container); - - if (method_exists(\ReflectionMethod::class, 'getReturnType')) { - $this->assertEquals(FactoryDummy::class, $factory->getClass()); - $this->assertEquals(\stdClass::class, $foo->getClass()); - } else { - $this->assertNull($factory->getClass()); - $this->assertNull($foo->getClass()); - } - $this->assertEquals(__CLASS__, $bar->getClass()); - } - - /** - * @dataProvider returnTypesProvider - */ - public function testReturnTypes($factory, $returnType, $hhvmSupport = true) - { - if (!$hhvmSupport && defined('HHVM_VERSION')) { - $this->markTestSkipped('Scalar typehints not supported by hhvm.'); - } - - $container = new ContainerBuilder(); - - $service = $container->register('service'); - $service->setFactory($factory); - - $pass = new FactoryReturnTypePass(); - $pass->process($container); - - if (method_exists(\ReflectionMethod::class, 'getReturnType')) { - $this->assertEquals($returnType, $service->getClass()); - } else { - $this->assertNull($service->getClass()); - } - } - - public function returnTypesProvider() - { - return array( - // must be loaded before the function as they are in the same file - array(array(FactoryDummy::class, 'createBuiltin'), null, false), - array(array(FactoryDummy::class, 'createParent'), FactoryParent::class), - array(array(FactoryDummy::class, 'createSelf'), FactoryDummy::class), - array(factoryFunction::class, FactoryDummy::class), - ); - } - - public function testCircularReference() - { - $container = new ContainerBuilder(); - - $factory = $container->register('factory'); - $factory->setFactory(array(new Reference('factory2'), 'createSelf')); - - $factory2 = $container->register('factory2'); - $factory2->setFactory(array(new Reference('factory'), 'create')); - - $pass = new FactoryReturnTypePass(); - $pass->process($container); - - $this->assertNull($factory->getClass()); - $this->assertNull($factory2->getClass()); - } - - /** - * @requires function ReflectionMethod::getReturnType - * @expectedDeprecation Relying on its factory's return-type to define the class of service "factory" is deprecated since Symfony 3.3 and won't work in 4.0. Set the "class" attribute to "Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryDummy" on the service definition instead. - */ - public function testCompile() - { - $container = new ContainerBuilder(); - - $factory = $container->register('factory'); - $factory->setFactory(array(FactoryDummy::class, 'createFactory')); - $container->compile(); - - $this->assertEquals(FactoryDummy::class, $container->getDefinition('factory')->getClass()); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index adaa4044f453c..bb60af4d19e64 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -325,33 +325,6 @@ public function testProcessDoesNotSetLazyArgumentValuesAfterInlining() $this->assertSame('inline', (string) $values[0]); } - /** - * @group legacy - */ - public function testGetInlinedServiceIdData() - { - $container = new ContainerBuilder(); - $container - ->register('inlinable.service') - ->setPublic(false) - ; - $container - ->register('non_inlinable.service') - ->setPublic(true) - ; - - $container - ->register('other_service') - ->setArguments(array(new Reference('inlinable.service'))) - ; - - $inlinePass = new InlineServiceDefinitionsPass(); - $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), $inlinePass)); - $repeatedPass->process($container); - - $this->assertEquals(array('inlinable.service' => array('other_service')), $inlinePass->getInlinedServiceIds()); - } - protected function process(ContainerBuilder $container) { $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass())); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 15c827d8270df..e7fdb3593aa38 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -40,6 +40,7 @@ public function testProcessRemovesAndInlinesRecursively() $a = $container ->register('a', '\stdClass') ->addArgument(new Reference('c')) + ->setPublic(true) ; $b = $container @@ -70,6 +71,7 @@ public function testProcessInlinesReferencesToAliases() $a = $container ->register('a', '\stdClass') ->addArgument(new Reference('b')) + ->setPublic(true) ; $container->setAlias('b', new Alias('c', false)); @@ -97,6 +99,7 @@ public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDe ->register('a', '\stdClass') ->addArgument(new Reference('b')) ->addMethodCall('setC', array(new Reference('c'))) + ->setPublic(true) ; $container diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php index cddb62dce9826..e330017bcd8e8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php @@ -33,6 +33,7 @@ public function testSimpleProcessor() 'base64' => array('string'), 'bool' => array('bool'), 'const' => array('bool', 'int', 'float', 'string', 'array'), + 'csv' => array('array'), 'file' => array('string'), 'float' => array('float'), 'int' => array('int'), diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php index 23a1915fd777c..d15be74ecd969 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; -use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; use Symfony\Component\DependencyInjection\ContainerBuilder; class ResolveChildDefinitionsPassTest extends TestCase @@ -325,32 +324,6 @@ public function testDecoratedServiceCanOverwriteDeprecatedParentStatus() $this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); } - /** - * @group legacy - */ - public function testProcessMergeAutowiringTypes() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addAutowiringType('Foo') - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ->addAutowiringType('Bar') - ; - - $this->process($container); - - $childDef = $container->getDefinition('child'); - $this->assertEquals(array('Foo', 'Bar'), $childDef->getAutowiringTypes()); - - $parentDef = $container->getDefinition('parent'); - $this->assertSame(array('Foo'), $parentDef->getAutowiringTypes()); - } - public function testProcessResolvesAliases() { $container = new ContainerBuilder(); @@ -397,14 +370,6 @@ public function testSetAutoconfiguredOnServiceIsParent() $this->assertFalse($container->getDefinition('child1')->isAutoconfigured()); } - /** - * @group legacy - */ - public function testAliasExistsForBackwardsCompatibility() - { - $this->assertInstanceOf(ResolveChildDefinitionsPass::class, new ResolveDefinitionTemplatesPass()); - } - protected function process(ContainerBuilder $container) { $pass = new ResolveChildDefinitionsPass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php deleted file mode 100644 index 39ae7f4f62297..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ /dev/null @@ -1,407 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\ChildDefinition; -use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * @group legacy - */ -class ResolveDefinitionTemplatesPassTest extends TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - $container->register('parent', 'foo')->setArguments(array('moo', 'b'))->setProperty('foo', 'moo'); - $container->setDefinition('child', new ChildDefinition('parent')) - ->replaceArgument(0, 'a') - ->setProperty('foo', 'bar') - ->setClass('bar') - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertNotInstanceOf(ChildDefinition::class, $def); - $this->assertEquals('bar', $def->getClass()); - $this->assertEquals(array('a', 'b'), $def->getArguments()); - $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); - } - - public function testProcessAppendsMethodCallsAlways() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addMethodCall('foo', array('bar')) - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ->addMethodCall('bar', array('foo')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertEquals(array( - array('foo', array('bar')), - array('bar', array('foo')), - ), $def->getMethodCalls()); - } - - public function testProcessDoesNotCopyAbstract() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->setAbstract(true) - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertFalse($def->isAbstract()); - } - - public function testProcessDoesNotCopyShared() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->setShared(false) - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertTrue($def->isShared()); - } - - public function testProcessDoesNotCopyTags() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addTag('foo') - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertEquals(array(), $def->getTags()); - } - - public function testProcessDoesNotCopyDecoratedService() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->setDecoratedService('foo') - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertNull($def->getDecoratedService()); - } - - public function testProcessDoesNotDropShared() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ->setShared(false) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertFalse($def->isShared()); - } - - public function testProcessHandlesMultipleInheritance() - { - $container = new ContainerBuilder(); - - $container - ->register('parent', 'foo') - ->setArguments(array('foo', 'bar', 'c')) - ; - - $container - ->setDefinition('child2', new ChildDefinition('child1')) - ->replaceArgument(1, 'b') - ; - - $container - ->setDefinition('child1', new ChildDefinition('parent')) - ->replaceArgument(0, 'a') - ; - - $this->process($container); - - $def = $container->getDefinition('child2'); - $this->assertEquals(array('a', 'b', 'c'), $def->getArguments()); - $this->assertEquals('foo', $def->getClass()); - } - - public function testSetLazyOnServiceHasParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass'); - - $container->setDefinition('child1', new ChildDefinition('parent')) - ->setLazy(true) - ; - - $this->process($container); - - $this->assertTrue($container->getDefinition('child1')->isLazy()); - } - - public function testSetLazyOnServiceIsParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass') - ->setLazy(true) - ; - - $container->setDefinition('child1', new ChildDefinition('parent')); - - $this->process($container); - - $this->assertTrue($container->getDefinition('child1')->isLazy()); - } - - public function testSetAutowiredOnServiceHasParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass') - ->setAutowired(true) - ; - - $container->setDefinition('child1', new ChildDefinition('parent')) - ->setAutowired(false) - ; - - $this->process($container); - - $this->assertFalse($container->getDefinition('child1')->isAutowired()); - } - - public function testSetAutowiredOnServiceIsParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass') - ->setAutowired(true) - ; - - $container->setDefinition('child1', new ChildDefinition('parent')); - - $this->process($container); - - $this->assertTrue($container->getDefinition('child1')->isAutowired()); - } - - public function testDeepDefinitionsResolving() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'parentClass'); - $container->register('sibling', 'siblingClass') - ->setConfigurator(new ChildDefinition('parent'), 'foo') - ->setFactory(array(new ChildDefinition('parent'), 'foo')) - ->addArgument(new ChildDefinition('parent')) - ->setProperty('prop', new ChildDefinition('parent')) - ->addMethodCall('meth', array(new ChildDefinition('parent'))) - ; - - $this->process($container); - - $configurator = $container->getDefinition('sibling')->getConfigurator(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $configurator); - $this->assertSame('parentClass', $configurator->getClass()); - - $factory = $container->getDefinition('sibling')->getFactory(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $factory[0]); - $this->assertSame('parentClass', $factory[0]->getClass()); - - $argument = $container->getDefinition('sibling')->getArgument(0); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $argument); - $this->assertSame('parentClass', $argument->getClass()); - - $properties = $container->getDefinition('sibling')->getProperties(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $properties['prop']); - $this->assertSame('parentClass', $properties['prop']->getClass()); - - $methodCalls = $container->getDefinition('sibling')->getMethodCalls(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $methodCalls[0][1][0]); - $this->assertSame('parentClass', $methodCalls[0][1][0]->getClass()); - } - - public function testSetDecoratedServiceOnServiceHasParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass'); - - $container->setDefinition('child1', new ChildDefinition('parent')) - ->setDecoratedService('foo', 'foo_inner', 5) - ; - - $this->process($container); - - $this->assertEquals(array('foo', 'foo_inner', 5), $container->getDefinition('child1')->getDecoratedService()); - } - - public function testDecoratedServiceCopiesDeprecatedStatusFromParent() - { - $container = new ContainerBuilder(); - $container->register('deprecated_parent') - ->setDeprecated(true) - ; - - $container->setDefinition('decorated_deprecated_parent', new ChildDefinition('deprecated_parent')); - - $this->process($container); - - $this->assertTrue($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); - } - - public function testDecoratedServiceCanOverwriteDeprecatedParentStatus() - { - $container = new ContainerBuilder(); - $container->register('deprecated_parent') - ->setDeprecated(true) - ; - - $container->setDefinition('decorated_deprecated_parent', new ChildDefinition('deprecated_parent')) - ->setDeprecated(false) - ; - - $this->process($container); - - $this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); - } - - /** - * @group legacy - */ - public function testProcessMergeAutowiringTypes() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addAutowiringType('Foo') - ; - - $container - ->setDefinition('child', new ChildDefinition('parent')) - ->addAutowiringType('Bar') - ; - - $this->process($container); - - $childDef = $container->getDefinition('child'); - $this->assertEquals(array('Foo', 'Bar'), $childDef->getAutowiringTypes()); - - $parentDef = $container->getDefinition('parent'); - $this->assertSame(array('Foo'), $parentDef->getAutowiringTypes()); - } - - public function testProcessResolvesAliases() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'ParentClass'); - $container->setAlias('parent_alias', 'parent'); - $container->setDefinition('child', new ChildDefinition('parent_alias')); - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertSame('ParentClass', $def->getClass()); - } - - public function testProcessSetsArguments() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'ParentClass')->setArguments(array(0)); - $container->setDefinition('child', (new ChildDefinition('parent'))->setArguments(array( - 1, - 'index_0' => 2, - 'foo' => 3, - ))); - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertSame(array(2, 1, 'foo' => 3), $def->getArguments()); - } - - public function testSetAutoconfiguredOnServiceIsParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass') - ->setAutoconfigured(true) - ; - - $container->setDefinition('child1', new ChildDefinition('parent')); - - $this->process($container); - - $this->assertFalse($container->getDefinition('child1')->isAutoconfigured()); - } - - protected function process(ContainerBuilder $container) - { - $pass = new ResolveDefinitionTemplatesPass(); - $pass->process($container); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php index fe681b41df788..4665ee96f5f3a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsVariadicsDummy; use Symfony\Component\DependencyInjection\Tests\Fixtures\SimilarArgumentsDummy; /** @@ -152,6 +153,36 @@ public function testResolvePrioritizeNamedOverType() $this->assertEquals(array(new Reference('bar'), 'qwerty', new Reference('foo')), $definition->getArguments()); } + + public function testVariadics() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NamedArgumentsVariadicsDummy::class, NamedArgumentsVariadicsDummy::class); + $definition->setArguments( + array( + '$class' => new \stdClass(), + '$variadics' => array( + new Reference('foo'), + new Reference('bar'), + new Reference('baz'), + ), + ) + ); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + + $this->assertEquals( + array( + 0 => new \stdClass(), + 1 => new Reference('foo'), + 2 => new Reference('bar'), + 3 => new Reference('baz'), + ), + $definition->getArguments() + ); + } } class NoConstructor diff --git a/src/Symfony/Component/DependencyInjection/Tests/Config/AutowireServiceResourceTest.php b/src/Symfony/Component/DependencyInjection/Tests/Config/AutowireServiceResourceTest.php deleted file mode 100644 index 64d99e6375b8f..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Config/AutowireServiceResourceTest.php +++ /dev/null @@ -1,124 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Config; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Compiler\AutowirePass; -use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; - -/** - * @group legacy - */ -class AutowireServiceResourceTest extends TestCase -{ - /** - * @var AutowireServiceResource - */ - private $resource; - private $file; - private $class; - private $time; - - protected function setUp() - { - $this->file = realpath(sys_get_temp_dir()).'/tmp.php'; - $this->time = time(); - touch($this->file, $this->time); - - $this->class = __NAMESPACE__.'\Foo'; - $this->resource = new AutowireServiceResource( - $this->class, - $this->file, - array() - ); - } - - public function testToString() - { - $this->assertSame('service.autowire.'.$this->class, (string) $this->resource); - } - - public function testSerializeUnserialize() - { - $unserialized = unserialize(serialize($this->resource)); - - $this->assertEquals($this->resource, $unserialized); - } - - public function testIsFresh() - { - $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); - $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); - $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); - } - - public function testIsFreshForDeletedResources() - { - unlink($this->file); - - $this->assertFalse($this->resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the resource does not exist'); - } - - public function testIsNotFreshChangedResource() - { - $oldResource = new AutowireServiceResource( - $this->class, - $this->file, - array('will_be_different') - ); - - // test with a stale file *and* a resource that *will* be different than the actual - $this->assertFalse($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed'); - } - - public function testIsFreshSameConstructorArgs() - { - $oldResource = AutowirePass::createResourceForClass( - new \ReflectionClass(__NAMESPACE__.'\Foo') - ); - - // test with a stale file *but* the resource will not be changed - $this->assertTrue($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed'); - } - - public function testNotFreshIfClassNotFound() - { - $resource = new AutowireServiceResource( - 'Some\Non\Existent\Class', - $this->file, - array() - ); - - $this->assertFalse($resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the class no longer exists'); - } - - protected function tearDown() - { - if (!file_exists($this->file)) { - return; - } - - unlink($this->file); - } - - private function getStaleFileTime() - { - return $this->time - 10; - } -} - -class Foo -{ - public function __construct($foo) - { - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 197bfae844532..1b719938a9d0f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -854,32 +854,6 @@ public function testAddObjectResource() $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); } - /** - * @group legacy - */ - public function testAddClassResource() - { - $container = new ContainerBuilder(); - - $container->setResourceTracking(false); - $container->addClassResource(new \ReflectionClass('BarClass')); - - $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); - - $container->setResourceTracking(true); - $container->addClassResource(new \ReflectionClass('BarClass')); - - $resources = $container->getResources(); - - $this->assertCount(2, $resources, '2 resources were registered'); - - /* @var $resource \Symfony\Component\Config\Resource\FileResource */ - $resource = end($resources); - - $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); - $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); - } - public function testGetReflectionClass() { $container = new ContainerBuilder(); @@ -1325,41 +1299,31 @@ public function testRegisterForAutoconfiguration() $this->assertSame($childDefA, $container->registerForAutoconfiguration('AInterface')); } - /** - * This test checks the trigger of a deprecation note and should not be removed in major releases. - * - * @group legacy - * @expectedDeprecation The "foo" service is deprecated. You should stop using it, as it will soon be removed. - */ - public function testPrivateServiceTriggersDeprecation() + public function testCaseSensitivity() { $container = new ContainerBuilder(); - $container->register('foo', 'stdClass') - ->setPublic(false) - ->setDeprecated(true); - $container->register('bar', 'stdClass') - ->setPublic(true) - ->setProperty('foo', new Reference('foo')); + $container->register('foo', 'stdClass')->setPublic(true); + $container->register('Foo', 'stdClass')->setProperty('foo', new Reference('foo'))->setPublic(false); + $container->register('fOO', 'stdClass')->setProperty('Foo', new Reference('Foo'))->setPublic(true); + + $this->assertSame(array('service_container', 'foo', 'Foo', 'fOO', 'Psr\Container\ContainerInterface', 'Symfony\Component\DependencyInjection\ContainerInterface'), $container->getServiceIds()); $container->compile(); - $container->get('bar'); + $this->assertNotSame($container->get('foo'), $container->get('fOO'), '->get() returns the service for the given id, case sensitively'); + $this->assertSame($container->get('fOO')->Foo->foo, $container->get('foo'), '->get() returns the service for the given id, case sensitively'); } - /** - * @group legacy - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "foo" is deprecated since Symfony 3.4. - */ public function testParameterWithMixedCase() { - $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar'))); + $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar', 'FOO' => 'BAR'))); $container->register('foo', 'stdClass') ->setPublic(true) ->setProperty('foo', '%FOO%'); $container->compile(); - $this->assertSame('bar', $container->get('foo')->foo); + $this->assertSame('BAR', $container->get('foo')->foo); } public function testArgumentsHaveHigherPriorityThanBindings() @@ -1385,6 +1349,24 @@ public function testArgumentsHaveHigherPriorityThanBindings() $this->assertSame('via-argument', $container->get('foo')->class1->identifier); $this->assertSame('via-bindings', $container->get('foo')->class2->identifier); } + + public function testIdCanBeAnObjectAsLongAsItCanBeCastToString() + { + $id = new Reference('another_service'); + $aliasId = new Reference('alias_id'); + + $container = new ContainerBuilder(); + $container->set($id, new \stdClass()); + $container->setAlias($aliasId, 'another_service'); + + $this->assertTrue($container->has('another_service')); + $this->assertTrue($container->has($id)); + $this->assertTrue($container->hasAlias('alias_id')); + $this->assertTrue($container->hasAlias($aliasId)); + + $container->removeAlias($aliasId); + $container->removeDefinition($id); + } } class FooClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php index 4307107072d4d..2ad4ac34fab04 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -83,19 +83,6 @@ public function testCompile() $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '->compile() copies the current parameters to the new parameter bag'); } - /** - * @group legacy - * @expectedDeprecation The Symfony\Component\DependencyInjection\Container::isFrozen() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead. - * @expectedDeprecation The Symfony\Component\DependencyInjection\Container::isFrozen() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead. - */ - public function testIsFrozen() - { - $sc = new Container(new ParameterBag(array('foo' => 'bar'))); - $this->assertFalse($sc->isFrozen(), '->isFrozen() returns false if the parameters are not frozen'); - $sc->compile(); - $this->assertTrue($sc->isFrozen(), '->isFrozen() returns true if the parameters are frozen'); - } - public function testIsCompiled() { $sc = new Container(new ParameterBag(array('foo' => 'bar'))); @@ -134,18 +121,13 @@ public function testGetSetParameter() } } - /** - * @group legacy - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "Foo" instead of "foo" is deprecated since Symfony 3.4. - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "foo" is deprecated since Symfony 3.4. - */ public function testGetSetParameterWithMixedCase() { $sc = new Container(new ParameterBag(array('foo' => 'bar'))); $sc->setParameter('Foo', 'baz1'); - $this->assertEquals('baz1', $sc->getParameter('foo'), '->setParameter() converts the key to lowercase'); - $this->assertEquals('baz1', $sc->getParameter('FOO'), '->getParameter() converts the key to lowercase'); + $this->assertEquals('bar', $sc->getParameter('foo')); + $this->assertEquals('baz1', $sc->getParameter('Foo')); } public function testGetServiceIds() @@ -157,19 +139,7 @@ public function testGetServiceIds() $sc = new ProjectServiceContainer(); $sc->set('foo', $obj = new \stdClass()); - $this->assertEquals(array('service_container', 'internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'internal_dependency', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by factory methods in the method map, followed by service ids defined by set()'); - } - - /** - * @group legacy - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - */ - public function testGetLegacyServiceIds() - { - $sc = new LegacyProjectServiceContainer(); - $sc->set('foo', $obj = new \stdClass()); - - $this->assertEquals(array('internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'service_container', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by getXXXService() methods, followed by service ids defined by set()'); + $this->assertEquals(array('service_container', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'internal_dependency', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by factory methods in the method map, followed by service ids defined by set()'); } public function testSet() @@ -182,6 +152,7 @@ public function testSet() public function testSetWithNullResetTheService() { $sc = new Container(); + $sc->set('foo', new \stdClass()); $sc->set('foo', null); $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); } @@ -195,8 +166,8 @@ public function testSetReplacesAlias() } /** - * @group legacy - * @expectedDeprecation The "bar" service is already initialized, unsetting it is deprecated since Symfony 3.3 and will fail in 4.0. + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "bar" service is already initialized, you cannot replace it. */ public function testSetWithNullOnInitializedPredefinedService() { @@ -242,58 +213,15 @@ public function testGet() $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty'); } - /** - * @group legacy - * @expectedDeprecation Service identifiers will be made case sensitive in Symfony 4.0. Using "Foo" instead of "foo" is deprecated since Symfony 3.3. - */ - public function testGetInsensitivity() - { - $sc = new ProjectServiceContainer(); - $sc->set('foo', $foo = new \stdClass()); - $this->assertSame($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase'); - } - - /** - * @group legacy - * @expectedDeprecation Service identifiers will be made case sensitive in Symfony 4.0. Using "foo" instead of "Foo" is deprecated since Symfony 3.3. - */ - public function testNormalizeIdKeepsCase() - { - $sc = new ProjectServiceContainer(); - $sc->normalizeId('Foo', true); - $this->assertSame('Foo', $sc->normalizeId('foo')); - } - - /** - * @group legacy - * @expectedDeprecation Service identifiers will be made case sensitive in Symfony 4.0. Using "Foo" instead of "foo" is deprecated since Symfony 3.3. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - */ - public function testLegacyGet() + public function testCaseSensitivity() { - $sc = new LegacyProjectServiceContainer(); - $sc->set('foo', $foo = new \stdClass()); - - $this->assertSame($foo, $sc->get('foo'), '->get() returns the service for the given id'); - $this->assertSame($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase'); - $this->assertSame($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id'); - $this->assertSame($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined'); - $this->assertSame($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined'); - $this->assertSame($sc->__foo_baz, $sc->get('foo\\baz'), '->get() returns the service if a get*Method() is defined'); - - $sc->set('bar', $bar = new \stdClass()); - $this->assertSame($bar, $sc->get('bar'), '->get() prefers to return a service defined with set() than one defined with a getXXXMethod()'); + $sc = new Container(); + $sc->set('foo', $foo1 = new \stdClass()); + $sc->set('Foo', $foo2 = new \stdClass()); - try { - $sc->get(''); - $this->fail('->get() throws a \InvalidArgumentException exception if the service is empty'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty'); - } - $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty'); + $this->assertSame(array('service_container', 'foo', 'Foo'), $sc->getServiceIds()); + $this->assertSame($foo1, $sc->get('foo'), '->get() returns the service for the given id, case sensitively'); + $this->assertSame($foo2, $sc->get('Foo'), '->get() returns the service for the given id, case sensitively'); } public function testGetThrowServiceNotFoundException() @@ -366,26 +294,6 @@ public function testHas() $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined'); } - /** - * @group legacy - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - */ - public function testLegacyHas() - { - $sc = new LegacyProjectServiceContainer(); - $sc->set('foo', new \stdClass()); - - $this->assertFalse($sc->has('foo1'), '->has() returns false if the service does not exist'); - $this->assertTrue($sc->has('foo'), '->has() returns true if the service exists'); - $this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo\\baz'), '->has() returns true if a get*Method() is defined'); - } - public function testInitialized() { $sc = new ProjectServiceContainer(); @@ -399,15 +307,11 @@ public function testInitialized() $this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized'); } - /** - * @group legacy - * @expectedDeprecation Checking for the initialization of the "internal" private service is deprecated since Symfony 3.4 and won't be supported anymore in Symfony 4.0. - */ public function testInitializedWithPrivateService() { $sc = new ProjectServiceContainer(); $sc->get('internal_dependency'); - $this->assertTrue($sc->initialized('internal')); + $this->assertFalse($sc->initialized('internal')); } public function testReset() @@ -483,71 +387,23 @@ public function testThatCloningIsNotSupported() $this->assertTrue($clone->isPrivate()); } - /** - * @group legacy - * @expectedDeprecation The "internal" service is private, unsetting it is deprecated since Symfony 3.2 and will fail in 4.0. - */ - public function testUnsetInternalPrivateServiceIsDeprecated() - { - $c = new ProjectServiceContainer(); - $c->set('internal', null); - } - - /** - * @group legacy - * @expectedDeprecation The "internal" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0. - */ - public function testChangeInternalPrivateServiceIsDeprecated() - { - $c = new ProjectServiceContainer(); - $c->set('internal', $internal = new \stdClass()); - $this->assertSame($c->get('internal'), $internal); - } - - /** - * @group legacy - * @expectedDeprecation The "internal" service is private, checking for its existence is deprecated since Symfony 3.2 and will fail in 4.0. - */ - public function testCheckExistenceOfAnInternalPrivateServiceIsDeprecated() + public function testCheckExistenceOfAnInternalPrivateService() { $c = new ProjectServiceContainer(); $c->get('internal_dependency'); - $this->assertTrue($c->has('internal')); + $this->assertFalse($c->has('internal')); } /** - * @group legacy - * @expectedDeprecation The "internal" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + * @expectedExceptionMessage You have requested a non-existent service "internal". */ - public function testRequestAnInternalSharedPrivateServiceIsDeprecated() + public function testRequestAnInternalSharedPrivateService() { $c = new ProjectServiceContainer(); $c->get('internal_dependency'); $c->get('internal'); } - - /** - * @group legacy - * @expectedDeprecation The "bar" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0. - */ - public function testReplacingAPreDefinedServiceIsDeprecated() - { - $c = new ProjectServiceContainer(); - $c->set('bar', new \stdClass()); - $c->set('bar', $bar = new \stdClass()); - - $this->assertSame($bar, $c->get('bar'), '->set() replaces a pre-defined service'); - } - - /** - * @group legacy - * @expectedDeprecation The "synthetic" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0. - */ - public function testSetWithPrivateSyntheticServiceThrowsDeprecation() - { - $c = new ProjectServiceContainer(); - $c->set('synthetic', new \stdClass()); - } } class ProjectServiceContainer extends Container @@ -558,7 +414,6 @@ class ProjectServiceContainer extends Container public $__internal; protected $privates; protected $methodMap = array( - 'internal' => 'getInternalService', 'bar' => 'getBarService', 'foo_bar' => 'getFooBarService', 'foo.baz' => 'getFoo_BazService', @@ -576,17 +431,13 @@ public function __construct() $this->__foo_bar = new \stdClass(); $this->__foo_baz = new \stdClass(); $this->__internal = new \stdClass(); - $this->privates = array( - 'internal' => true, - 'synthetic' => true, - ); + $this->privates = array(); $this->aliases = array('alias' => 'bar'); - $this->syntheticIds['synthetic'] = true; } protected function getInternalService() { - return $this->services['internal'] = $this->__internal; + return $this->privates['internal'] = $this->__internal; } protected function getBarService() @@ -625,65 +476,8 @@ protected function getInternalDependencyService() { $this->services['internal_dependency'] = $instance = new \stdClass(); - $instance->internal = isset($this->services['internal']) ? $this->services['internal'] : $this->getInternalService(); + $instance->internal = $this->privates['internal'] ?? $this->getInternalService(); return $instance; } } - -class LegacyProjectServiceContainer extends Container -{ - public $__bar; - public $__foo_bar; - public $__foo_baz; - public $__internal; - - public function __construct() - { - parent::__construct(); - - $this->__bar = new \stdClass(); - $this->__foo_bar = new \stdClass(); - $this->__foo_baz = new \stdClass(); - $this->__internal = new \stdClass(); - $this->privates = array('internal' => true); - $this->aliases = array('alias' => 'bar'); - } - - protected function getInternalService() - { - return $this->__internal; - } - - protected function getBarService() - { - return $this->__bar; - } - - protected function getFooBarService() - { - return $this->__foo_bar; - } - - protected function getFoo_BazService() - { - return $this->__foo_baz; - } - - protected function getCircularService() - { - return $this->get('circular'); - } - - protected function getThrowExceptionService() - { - throw new \Exception('Something went terribly wrong!'); - } - - protected function getThrowsExceptionOnServiceConfigurationService() - { - $this->services['throws_exception_on_service_configuration'] = $instance = new \stdClass(); - - throw new \Exception('Something was terribly wrong while trying to configure the service!'); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php index bcfe88acc0ce4..dbdbb79542316 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php @@ -55,12 +55,6 @@ public function testCrossCheck($fixture, $type) $this->assertEquals($container2->getAliases(), $container1->getAliases(), 'loading a dump from a previously loaded container returns the same container'); $this->assertEquals($container2->getDefinitions(), $container1->getDefinitions(), 'loading a dump from a previously loaded container returns the same container'); $this->assertEquals($container2->getParameterBag()->all(), $container1->getParameterBag()->all(), '->getParameterBag() returns the same value for both containers'); - - $r = new \ReflectionProperty(ContainerBuilder::class, 'normalizedIds'); - $r->setAccessible(true); - $r->setValue($container2, array()); - $r->setValue($container1, array()); - $this->assertEquals(serialize($container2), serialize($container1), 'loading a dump from a previously loaded container returns the same container'); $services1 = array(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionDecoratorTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionDecoratorTest.php deleted file mode 100644 index 92a212ec416a6..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionDecoratorTest.php +++ /dev/null @@ -1,132 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\DefinitionDecorator; - -/** - * @group legacy - */ -class DefinitionDecoratorTest extends TestCase -{ - public function testConstructor() - { - $def = new DefinitionDecorator('foo'); - - $this->assertEquals('foo', $def->getParent()); - $this->assertEquals(array(), $def->getChanges()); - } - - /** - * @dataProvider getPropertyTests - */ - public function testSetProperty($property, $changeKey) - { - $def = new DefinitionDecorator('foo'); - - $getter = 'get'.ucfirst($property); - $setter = 'set'.ucfirst($property); - - $this->assertNull($def->$getter()); - $this->assertSame($def, $def->$setter('foo')); - $this->assertEquals('foo', $def->$getter()); - $this->assertEquals(array($changeKey => true), $def->getChanges()); - } - - public function getPropertyTests() - { - return array( - array('class', 'class'), - array('factory', 'factory'), - array('configurator', 'configurator'), - array('file', 'file'), - ); - } - - public function testSetPublic() - { - $def = new DefinitionDecorator('foo'); - - $this->assertTrue($def->isPublic()); - $this->assertSame($def, $def->setPublic(false)); - $this->assertFalse($def->isPublic()); - $this->assertEquals(array('public' => true), $def->getChanges()); - } - - public function testSetLazy() - { - $def = new DefinitionDecorator('foo'); - - $this->assertFalse($def->isLazy()); - $this->assertSame($def, $def->setLazy(false)); - $this->assertFalse($def->isLazy()); - $this->assertEquals(array('lazy' => true), $def->getChanges()); - } - - public function testSetAutowired() - { - $def = new DefinitionDecorator('foo'); - - $this->assertFalse($def->isAutowired()); - $this->assertSame($def, $def->setAutowired(true)); - $this->assertTrue($def->isAutowired()); - $this->assertSame(array('autowired' => true), $def->getChanges()); - } - - public function testSetArgument() - { - $def = new DefinitionDecorator('foo'); - - $this->assertEquals(array(), $def->getArguments()); - $this->assertSame($def, $def->replaceArgument(0, 'foo')); - $this->assertEquals(array('index_0' => 'foo'), $def->getArguments()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testReplaceArgumentShouldRequireIntegerIndex() - { - $def = new DefinitionDecorator('foo'); - - $def->replaceArgument('0', 'foo'); - } - - public function testReplaceArgument() - { - $def = new DefinitionDecorator('foo'); - - $def->setArguments(array(0 => 'foo', 1 => 'bar')); - $this->assertEquals('foo', $def->getArgument(0)); - $this->assertEquals('bar', $def->getArgument(1)); - - $this->assertSame($def, $def->replaceArgument(1, 'baz')); - $this->assertEquals('foo', $def->getArgument(0)); - $this->assertEquals('baz', $def->getArgument(1)); - - $this->assertEquals(array(0 => 'foo', 1 => 'bar', 'index_1' => 'baz'), $def->getArguments()); - } - - /** - * @expectedException \OutOfBoundsException - */ - public function testGetArgumentShouldCheckBounds() - { - $def = new DefinitionDecorator('foo'); - - $def->setArguments(array(0 => 'foo')); - $def->replaceArgument(0, 'foo'); - - $def->getArgument(1); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index 5fe4236ad9384..0ed135915e491 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -364,22 +364,6 @@ public function testGetChangesWithChanges() $this->assertSame(array(), $def->getChanges()); } - /** - * @group legacy - */ - public function testTypes() - { - $def = new Definition('stdClass'); - - $this->assertEquals(array(), $def->getAutowiringTypes()); - $this->assertSame($def, $def->setAutowiringTypes(array('Foo'))); - $this->assertEquals(array('Foo'), $def->getAutowiringTypes()); - $this->assertSame($def, $def->addAutowiringType('Bar')); - $this->assertTrue($def->hasAutowiringType('Bar')); - $this->assertSame($def, $def->removeAutowiringType('Foo')); - $this->assertEquals(array('Bar'), $def->getAutowiringTypes()); - } - public function testShouldAutoconfigure() { $def = new Definition('stdClass'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index f12fc608c9608..cdeaac72cf787 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -18,7 +18,6 @@ use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; -use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; @@ -178,14 +177,13 @@ public function testAddParameters() } /** - * @group legacy - * @expectedDeprecation Dumping an uncompiled ContainerBuilder is deprecated since Symfony 3.3 and will not be supported anymore in 4.0. Compile the container beforehand. + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage Cannot dump an uncompiled container. */ public function testAddServiceWithoutCompilation() { $container = include self::$fixturesPath.'/containers/container9.php'; - $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services9.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), '%path%', $dumper->dump()), '->dump() dumps services'); + new PhpDumper($container); } public function testAddService() @@ -212,6 +210,10 @@ public function testDumpAsFiles() { $container = include self::$fixturesPath.'/containers/container9.php'; $container->getDefinition('bar')->addTag('hot'); + $container->register('non_shared_foo', \Bar\FooClass::class) + ->setFile(realpath(self::$fixturesPath.'/includes/foo.php')) + ->setShared(false) + ->setPublic(true); $container->compile(); $dumper = new PhpDumper($container); $dump = print_r($dumper->dump(array('as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot')), true); @@ -329,8 +331,8 @@ public function testFrozenContainerWithoutAliases() } /** - * @group legacy - * @expectedDeprecation The "decorator_service" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0. + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "decorator_service" service is already initialized, you cannot replace it. */ public function testOverrideServiceWhenUsingADumpedContainer() { @@ -413,6 +415,23 @@ public function testDumpedBase64EnvParameters() $this->assertSame('world', $container->getParameter('hello')); } + public function testDumpedCsvEnvParameters() + { + $container = new ContainerBuilder(); + $container->setParameter('env(foo)', 'foo,bar'); + $container->setParameter('hello', '%env(csv:foo)%'); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->dump(); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_csv_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_CsvParameters'))); + + require self::$fixturesPath.'/php/services_csv_env.php'; + $container = new \Symfony_DI_PhpDumper_Test_CsvParameters(); + $this->assertSame(array('foo', 'bar'), $container->getParameter('hello')); + } + public function testCustomEnvParameters() { $container = new ContainerBuilder(); @@ -888,55 +907,6 @@ public function testDumpHandlesObjectClassNames() $this->assertInstanceOf('stdClass', $container->get('bar')); } - /** - * @group legacy - * @expectedDeprecation The "private" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - * @expectedDeprecation The "private_alias" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - * @expectedDeprecation The "decorated_private" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - * @expectedDeprecation The "decorated_private_alias" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - * @expectedDeprecation The "private_not_inlined" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - * @expectedDeprecation The "private_not_removed" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - * @expectedDeprecation The "private_child" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - * @expectedDeprecation The "private_parent" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. - */ - public function testLegacyPrivateServices() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services_legacy_privates.yml'); - - $container->setDefinition('private_child', new ChildDefinition('foo')); - $container->setDefinition('private_parent', new ChildDefinition('private')); - - $container->getDefinition('private')->setPrivate(true); - $container->getDefinition('private_not_inlined')->setPrivate(true); - $container->getDefinition('private_not_removed')->setPrivate(true); - $container->getDefinition('decorated_private')->setPrivate(true); - $container->getDefinition('private_child')->setPrivate(true); - $container->getAlias('decorated_private_alias')->setPrivate(true); - $container->getAlias('private_alias')->setPrivate(true); - - $container->compile(); - $dumper = new PhpDumper($container); - - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_legacy_privates.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Legacy_Privates', 'file' => self::$fixturesPath.'/php/services_legacy_privates.php'))); - - require self::$fixturesPath.'/php/services_legacy_privates.php'; - - $container = new \Symfony_DI_PhpDumper_Test_Legacy_Privates(); - - $container->get('private'); - $container->get('private_alias'); - $container->get('alias_to_private'); - $container->get('decorated_private'); - $container->get('decorated_private_alias'); - $container->get('private_not_inlined'); - $container->get('private_not_removed'); - $container->get('private_child'); - $container->get('private_parent'); - $container->get('public_child'); - } - /** * This test checks the trigger of a deprecation note and should not be removed in major releases. * @@ -963,12 +933,6 @@ public function testPrivateServiceTriggersDeprecation() $container->get('bar'); } - /** - * @group legacy - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "foo" instead of "Foo" is deprecated since Symfony 3.4. - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "Foo" is deprecated since Symfony 3.4. - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "bar" instead of "BAR" is deprecated since Symfony 3.4. - */ public function testParameterWithMixedCase() { $container = new ContainerBuilder(new ParameterBag(array('Foo' => 'bar', 'BAR' => 'foo'))); @@ -979,28 +943,9 @@ public function testParameterWithMixedCase() $container = new \Symfony_DI_PhpDumper_Test_Parameter_With_Mixed_Case(); - $this->assertSame('bar', $container->getParameter('foo')); - $this->assertSame('bar', $container->getParameter('FOO')); - $this->assertSame('foo', $container->getParameter('bar')); + $this->assertSame('bar', $container->getParameter('Foo')); $this->assertSame('foo', $container->getParameter('BAR')); } - - /** - * @group legacy - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "foo" is deprecated since Symfony 3.4. - */ - public function testParameterWithLowerCase() - { - $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar'))); - $container->compile(); - - $dumper = new PhpDumper($container); - eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Parameter_With_Lower_Case'))); - - $container = new \Symfony_DI_PhpDumper_Test_Parameter_With_Lower_Case(); - - $this->assertSame('bar', $container->getParameter('FOO')); - } } class Rot13EnvVarProcessor implements EnvVarProcessorInterface diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FactoryDummy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FactoryDummy.php index da984b562a39d..b7ceac7d8a4eb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FactoryDummy.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FactoryDummy.php @@ -21,7 +21,6 @@ public function create(): \stdClass { } - // Not supported by hhvm public function createBuiltin(): int { } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedArgumentsVariadicsDummy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedArgumentsVariadicsDummy.php new file mode 100644 index 0000000000000..39a8a8e6b4307 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedArgumentsVariadicsDummy.php @@ -0,0 +1,10 @@ +foo = $foo; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.expected.yml new file mode 100644 index 0000000000000..3f26138991021 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.expected.yml @@ -0,0 +1,19 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + listener_aggregator: + class: Bar\FooClass + public: true + arguments: [!tagged listener] + 2_stdClass~%s: + class: stdClass + public: false + tags: + - { name: listener } + decorated: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\StdClassDecorator + public: true + arguments: [!service { class: stdClass, public: false }] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php new file mode 100644 index 0000000000000..c8164d0741c7d --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php @@ -0,0 +1,21 @@ +services(); + + $s->set('decorated', stdClass::class); + + $s->set(null, StdClassDecorator::class) + ->decorate('decorated', 'decorator42') + ->args(array(ref('decorator42'))); + + $s->set('listener_aggregator', FooClass::class)->public()->args(array(tagged('listener'))); + + $s->set(null, stdClass::class)->tag('listener'); +}; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php index b013e5126d3b6..4bf3b89d8e3e0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php @@ -14,7 +14,8 @@ $p->set('foo_class', FooClass::class) ->set('foo', 'bar'); - $s = $c->services(); + $s = $c->services()->defaults() + ->public(); $s->set('foo') ->args(array('foo', ref('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, ref('service_container'))) ->class(FooClass::class) @@ -115,6 +116,10 @@ $s->set('lazy_context_ignore_invalid_ref', 'LazyContext') ->args(array(iterator(array(ref('foo.baz'), ref('invalid')->ignoreOnInvalid())), iterator(array()))); + $s->set('BAR', 'stdClass')->property('bar', ref('bar')); + $s->set('bar2', 'stdClass'); + $s->set('BAR2', 'stdClass'); + $s->set('tagged_iterator_foo', 'Bar') ->private() ->tag('foo'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index 21d35611c33ae..06789bd350fe1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -160,6 +160,13 @@ ->setArguments(array(new IteratorArgument(array(new Reference('foo.baz'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))), new IteratorArgument(array()))) ->setPublic(true) ; +$container + ->register('BAR', 'stdClass') + ->setProperty('bar', new Reference('bar')) + ->setPublic(true) +; +$container->register('bar2', 'stdClass')->setPublic(true); +$container->register('BAR2', 'stdClass')->setPublic(true); $container ->register('tagged_iterator_foo', 'Bar') ->addTag('foo') diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot index 6a34c5735c777..2c116979e4b6f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot @@ -29,6 +29,9 @@ digraph sc { node_factory_service_simple [label="factory_service_simple\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_lazy_context [label="lazy_context\nLazyContext\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_lazy_context_ignore_invalid_ref [label="lazy_context_ignore_invalid_ref\nLazyContext\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_BAR [label="BAR\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar2 [label="bar2\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_BAR2 [label="BAR2\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_tagged_iterator_foo [label="tagged_iterator_foo\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_tagged_iterator [label="tagged_iterator\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"]; @@ -53,4 +56,5 @@ digraph sc { node_lazy_context -> node_service_container [label="" style="filled" color="#9999ff"]; node_lazy_context_ignore_invalid_ref -> node_foo_baz [label="" style="filled" color="#9999ff"]; node_lazy_context_ignore_invalid_ref -> node_invalid [label="" style="filled" color="#9999ff"]; + node_BAR -> node_bar [label="" style="dashed"]; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php index ba07d7c44cbd6..a50ce7763bd8f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php @@ -17,10 +17,10 @@ public function load(array $configs, ContainerBuilder $configuration) $config = array(); } - $configuration->setDefinition('project.service.bar', new Definition('FooClass')); + $configuration->register('project.service.bar', 'FooClass')->setPublic(true); $configuration->setParameter('project.parameter.bar', isset($config['foo']) ? $config['foo'] : 'foobar'); - $configuration->setDefinition('project.service.foo', new Definition('FooClass')); + $configuration->register('project.service.foo', 'FooClass')->setPublic(true); $configuration->setParameter('project.parameter.foo', isset($config['foo']) ? $config['foo'] : 'foobar'); return $configuration; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index 990a873f34985..04c17ee188fa7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -306,6 +306,10 @@ public function setNotAutowireable(NotARealClass $n) { } + public function setNotAutowireableBecauseOfATypo(lesTilleuls $sam) + { + } + public function setBar() { } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php index 3020602b89804..01caae6489535 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php @@ -21,21 +21,24 @@ class ProjectServiceContainer extends \Symfony\Component\DependencyInjection\Tes private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { parent::__construct(); - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -48,10 +51,11 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php index dd9ed9cbb3141..e513fd7219147 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php @@ -21,19 +21,22 @@ class ProjectServiceContainer extends \Symfony\Component\DependencyInjection\Tes private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -46,10 +49,11 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php index d65ac7dfbcac9..1c20741bf7dc4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php @@ -21,21 +21,24 @@ class ProjectServiceContainer extends \Symfony\Component\DependencyInjection\Tes private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { parent::__construct(); - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -48,10 +51,11 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_without_constructor.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_without_constructor.php index 4cf1ae40b44df..0a4975b7da395 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_without_constructor.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_without_constructor.php @@ -21,19 +21,22 @@ class ProjectServiceContainer extends \Symfony\Component\DependencyInjection\Tes private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -46,10 +49,11 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php index bdd0d101ab8d6..a04d80affcec1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -21,19 +21,22 @@ class Container extends \Symfony\Component\DependencyInjection\Dump\AbstractCont private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -46,10 +49,11 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php index 85830af78c1db..f25c59b81596f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php @@ -19,19 +19,22 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -44,10 +47,11 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php index a7b43ceefbde1..31c4475ec7dab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php @@ -19,11 +19,16 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'test' => 'getTestService', ); @@ -31,12 +36,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -49,11 +52,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** @@ -69,12 +73,9 @@ protected function getTestService() public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -86,7 +87,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -126,22 +126,6 @@ private function getDynamicParameter($name) throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php index 9947d36ac5484..d4f872f944923 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -19,6 +19,11 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $dir = __DIR__; @@ -27,7 +32,7 @@ public function __construct() } $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'test' => 'getTestService', ); @@ -35,12 +40,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -53,11 +56,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** @@ -73,12 +77,9 @@ protected function getTestService() public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -90,7 +91,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -140,22 +140,6 @@ private function getDynamicParameter($name) return $this->dynamicParameters[$name] = $value; } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php index 04705a29e4692..8c90280d272a2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php @@ -19,9 +19,14 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar' => 'getBarService', ); @@ -29,13 +34,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'foo' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -48,11 +50,13 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'foo' => true, + ); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index 667dd852a0fe0..673c9d54bbeca 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -19,9 +19,14 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService', 'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService', @@ -30,12 +35,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -48,11 +51,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php index 1be6319eef079..090a77dd3c2c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php @@ -19,9 +19,14 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'foo' => 'getFooService', ); @@ -29,12 +34,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -47,11 +50,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php index 16b99007aeca3..942eb0eb7296f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php @@ -19,6 +19,11 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $dir = __DIR__; @@ -27,7 +32,7 @@ public function __construct() } $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar' => 'getBarService', 'test' => 'getTestService', @@ -36,12 +41,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -54,11 +57,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** @@ -86,12 +90,9 @@ protected function getTestService() public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -103,7 +104,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -159,25 +159,6 @@ private function getDynamicParameter($name) return $this->dynamicParameters[$name] = $value; } - private $normalizedParameterNames = array( - 'env(foo)' => 'env(FOO)', - 'env(db)' => 'env(DB)', - ); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php index 3e308c1d4d367..1c70b0ee8d06b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php @@ -19,13 +19,14 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); - $this->normalizedIds = array( - 'bar\\foo' => 'Bar\\Foo', - 'foo\\foo' => 'Foo\\Foo', - ); + $this->services = $this->privates = array(); $this->methodMap = array( 'Bar\\Foo' => 'getFooService', 'Foo\\Foo' => 'getFoo2Service', @@ -34,12 +35,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -52,11 +51,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index 56e2a98cc5e0f..dc7da1c274e5c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -19,21 +19,24 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -46,22 +49,20 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -73,7 +74,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -113,22 +113,6 @@ private function getDynamicParameter($name) throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php deleted file mode 100644 index ad316b23c6ee9..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ /dev/null @@ -1,443 +0,0 @@ -getDefaultParameters())); - $this->normalizedIds = array( - 'psr\\container\\containerinterface' => 'Psr\\Container\\ContainerInterface', - 'symfony\\component\\dependencyinjection\\containerinterface' => 'Symfony\\Component\\DependencyInjection\\ContainerInterface', - ); - $this->syntheticIds = array( - 'request' => true, - ); - $this->methodMap = array( - 'bar' => 'getBarService', - 'baz' => 'getBazService', - 'configurator_service' => 'getConfiguratorServiceService', - 'configurator_service_simple' => 'getConfiguratorServiceSimpleService', - 'configured_service' => 'getConfiguredServiceService', - 'configured_service_simple' => 'getConfiguredServiceSimpleService', - 'decorated' => 'getDecoratedService', - 'decorator_service' => 'getDecoratorServiceService', - 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', - 'deprecated_service' => 'getDeprecatedServiceService', - 'factory_service' => 'getFactoryServiceService', - 'factory_service_simple' => 'getFactoryServiceSimpleService', - 'factory_simple' => 'getFactorySimpleService', - 'foo' => 'getFooService', - 'foo.baz' => 'getFoo_BazService', - 'foo_bar' => 'getFooBarService', - 'foo_with_inline' => 'getFooWithInlineService', - 'inlined' => 'getInlinedService', - 'lazy_context' => 'getLazyContextService', - 'lazy_context_ignore_invalid_ref' => 'getLazyContextIgnoreInvalidRefService', - 'method_call1' => 'getMethodCall1Service', - 'new_factory' => 'getNewFactoryService', - 'new_factory_service' => 'getNewFactoryServiceService', - 'service_from_static_method' => 'getServiceFromStaticMethodService', - 'tagged_iterator' => 'getTaggedIteratorService', - 'tagged_iterator_foo' => 'getTaggedIteratorFooService', - ); - $this->privates = array( - 'configurator_service' => true, - 'configurator_service_simple' => true, - 'factory_simple' => true, - 'inlined' => true, - 'new_factory' => true, - 'tagged_iterator_foo' => true, - ); - $this->aliases = array( - 'Psr\\Container\\ContainerInterface' => 'service_container', - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => 'service_container', - 'alias_for_alias' => 'foo', - 'alias_for_foo' => 'foo', - ); - } - - /** - * Gets the public 'bar' shared service. - * - * @return \Bar\FooClass - */ - protected function getBarService() - { - $a = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; - - $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); - - $a->configure($instance); - - return $instance; - } - - /** - * Gets the public 'baz' shared service. - * - * @return \Baz - */ - protected function getBazService() - { - $this->services['baz'] = $instance = new \Baz(); - - $instance->setFoo(${($_ = isset($this->services['foo_with_inline']) ? $this->services['foo_with_inline'] : $this->getFooWithInlineService()) && false ?: '_'}); - - return $instance; - } - - /** - * Gets the public 'configured_service' shared service. - * - * @return \stdClass - */ - protected function getConfiguredServiceService() - { - $this->services['configured_service'] = $instance = new \stdClass(); - - ${($_ = isset($this->services['configurator_service']) ? $this->services['configurator_service'] : $this->getConfiguratorServiceService()) && false ?: '_'}->configureStdClass($instance); - - return $instance; - } - - /** - * Gets the public 'configured_service_simple' shared service. - * - * @return \stdClass - */ - protected function getConfiguredServiceSimpleService() - { - $this->services['configured_service_simple'] = $instance = new \stdClass(); - - ${($_ = isset($this->services['configurator_service_simple']) ? $this->services['configurator_service_simple'] : $this->services['configurator_service_simple'] = new \ConfClass('bar')) && false ?: '_'}->configureStdClass($instance); - - return $instance; - } - - /** - * Gets the public 'decorated' shared service. - * - * @return \stdClass - */ - protected function getDecoratedService() - { - return $this->services['decorated'] = new \stdClass(); - } - - /** - * Gets the public 'decorator_service' shared service. - * - * @return \stdClass - */ - protected function getDecoratorServiceService() - { - return $this->services['decorator_service'] = new \stdClass(); - } - - /** - * Gets the public 'decorator_service_with_name' shared service. - * - * @return \stdClass - */ - protected function getDecoratorServiceWithNameService() - { - return $this->services['decorator_service_with_name'] = new \stdClass(); - } - - /** - * Gets the public 'deprecated_service' shared service. - * - * @return \stdClass - * - * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. - */ - protected function getDeprecatedServiceService() - { - @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); - - return $this->services['deprecated_service'] = new \stdClass(); - } - - /** - * Gets the public 'factory_service' shared service. - * - * @return \Bar - */ - protected function getFactoryServiceService() - { - return $this->services['factory_service'] = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}->getInstance(); - } - - /** - * Gets the public 'factory_service_simple' shared service. - * - * @return \Bar - */ - protected function getFactoryServiceSimpleService() - { - return $this->services['factory_service_simple'] = ${($_ = isset($this->services['factory_simple']) ? $this->services['factory_simple'] : $this->getFactorySimpleService()) && false ?: '_'}->getInstance(); - } - - /** - * Gets the public 'foo' shared service. - * - * @return \Bar\FooClass - */ - protected function getFooService() - { - $a = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; - - $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')), true, $this); - - $instance->foo = 'bar'; - $instance->moo = $a; - $instance->qux = array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')); - $instance->setBar(${($_ = isset($this->services['bar']) ? $this->services['bar'] : $this->getBarService()) && false ?: '_'}); - $instance->initialize(); - sc_configure($instance); - - return $instance; - } - - /** - * Gets the public 'foo.baz' shared service. - * - * @return object A %baz_class% instance - */ - protected function getFoo_BazService() - { - $this->services['foo.baz'] = $instance = \call_user_func(array($this->getParameter('baz_class'), 'getInstance')); - - \call_user_func(array($this->getParameter('baz_class'), 'configureStatic1'), $instance); - - return $instance; - } - - /** - * Gets the public 'foo_bar' service. - * - * @return object A %foo_class% instance - */ - protected function getFooBarService() - { - $class = $this->getParameter('foo_class'); - - return new $class(${($_ = isset($this->services['deprecated_service']) ? $this->services['deprecated_service'] : $this->getDeprecatedServiceService()) && false ?: '_'}); - } - - /** - * Gets the public 'foo_with_inline' shared service. - * - * @return \Foo - */ - protected function getFooWithInlineService() - { - $this->services['foo_with_inline'] = $instance = new \Foo(); - - $instance->setBar(${($_ = isset($this->services['inlined']) ? $this->services['inlined'] : $this->getInlinedService()) && false ?: '_'}); - - return $instance; - } - - /** - * Gets the public 'lazy_context' shared service. - * - * @return \LazyContext - */ - protected function getLazyContextService() - { - return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () { - yield 'k1' => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; - yield 'k2' => $this; - }, 2), new RewindableGenerator(function () { - return new \EmptyIterator(); - }, 0)); - } - - /** - * Gets the public 'lazy_context_ignore_invalid_ref' shared service. - * - * @return \LazyContext - */ - protected function getLazyContextIgnoreInvalidRefService() - { - return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () { - yield 0 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; - if ($this->has('invalid')) { - yield 1 => ${($_ = isset($this->services['invalid']) ? $this->services['invalid'] : $this->get('invalid', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'}; - } - }, function () { - return 1 + (int) ($this->has('invalid')); - }), new RewindableGenerator(function () { - return new \EmptyIterator(); - }, 0)); - } - - /** - * Gets the public 'method_call1' shared service. - * - * @return \Bar\FooClass - */ - protected function getMethodCall1Service() - { - include_once '%path%foo.php'; - - $this->services['method_call1'] = $instance = new \Bar\FooClass(); - - $instance->setBar(${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}); - $instance->setBar(${($_ = isset($this->services['foo2']) ? $this->services['foo2'] : $this->get('foo2', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'}); - if ($this->has('foo3')) { - $instance->setBar(${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : $this->get('foo3', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'}); - } - if ($this->has('foobaz')) { - $instance->setBar(${($_ = isset($this->services['foobaz']) ? $this->services['foobaz'] : $this->get('foobaz', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'}); - } - $instance->setBar((${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); - - return $instance; - } - - /** - * Gets the public 'new_factory_service' shared service. - * - * @return \FooBarBaz - */ - protected function getNewFactoryServiceService() - { - $this->services['new_factory_service'] = $instance = ${($_ = isset($this->services['new_factory']) ? $this->services['new_factory'] : $this->getNewFactoryService()) && false ?: '_'}->getInstance(); - - $instance->foo = 'bar'; - - return $instance; - } - - /** - * Gets the public 'service_from_static_method' shared service. - * - * @return \Bar\FooClass - */ - protected function getServiceFromStaticMethodService() - { - return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); - } - - /** - * Gets the public 'tagged_iterator' shared service. - * - * @return \Bar - */ - protected function getTaggedIteratorService() - { - return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { - return new \EmptyIterator(); - }, 0)); - } - - /** - * Gets the private 'configurator_service' shared service. - * - * @return \ConfClass - */ - protected function getConfiguratorServiceService() - { - $this->services['configurator_service'] = $instance = new \ConfClass(); - - $instance->setFoo(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->getBazService()) && false ?: '_'}); - - return $instance; - } - - /** - * Gets the private 'configurator_service_simple' shared service. - * - * @return \ConfClass - */ - protected function getConfiguratorServiceSimpleService() - { - return $this->services['configurator_service_simple'] = new \ConfClass('bar'); - } - - /** - * Gets the private 'factory_simple' shared service. - * - * @return \SimpleFactoryClass - * - * @deprecated The "factory_simple" service is deprecated. You should stop using it, as it will soon be removed. - */ - protected function getFactorySimpleService() - { - @trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); - - return $this->services['factory_simple'] = new \SimpleFactoryClass('foo'); - } - - /** - * Gets the private 'inlined' shared service. - * - * @return \Bar - */ - protected function getInlinedService() - { - $this->services['inlined'] = $instance = new \Bar(); - - $instance->pub = 'pub'; - $instance->setBaz(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->getBazService()) && false ?: '_'}); - - return $instance; - } - - /** - * Gets the private 'new_factory' shared service. - * - * @return \FactoryClass - */ - protected function getNewFactoryService() - { - $this->services['new_factory'] = $instance = new \FactoryClass(); - - $instance->foo = 'bar'; - - return $instance; - } - - /** - * Gets the private 'tagged_iterator_foo' shared service. - * - * @return \Bar - */ - protected function getTaggedIteratorFooService() - { - return $this->services['tagged_iterator_foo'] = new \Bar(); - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\\FooClass', - 'foo' => 'bar', - ); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 19e538b1846e5..6cea40ce0624e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -15,6 +15,37 @@ return array( 'tagged_iterator_foo' => true, ); + [Container%s/getBAR2Service.php] => services['BAR'] = $instance = new \stdClass(); + +$instance->bar = ($this->services['bar'] ?? $this->getBarService()); + +return $instance; + + [Container%s/getBAR22Service.php] => services['BAR2'] = new \stdClass(); + + [Container%s/getBar23Service.php] => services['bar2'] = new \stdClass(); + [Container%s/getBazService.php] => services['baz'] = $instance = new \Baz(); -$instance->setFoo(${($_ = isset($this->services['foo_with_inline']) ? $this->services['foo_with_inline'] : $this->load(__DIR__.'/getFooWithInlineService.php')) && false ?: '_'}); +$instance->setFoo(($this->services['foo_with_inline'] ?? $this->load(__DIR__.'/getFooWithInlineService.php'))); return $instance; @@ -38,7 +69,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; $this->services['configured_service'] = $instance = new \stdClass(); $a = new \ConfClass(); -$a->setFoo(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->load(__DIR__.'/getBazService.php')) && false ?: '_'}); +$a->setFoo(($this->services['baz'] ?? $this->load(__DIR__.'/getBazService.php'))); $a->configureStdClass($instance); @@ -93,7 +124,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // Returns the public 'factory_service' shared service. -return $this->services['factory_service'] = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->load(__DIR__.'/getFoo_BazService.php')) && false ?: '_'}->getInstance(); +return $this->services['factory_service'] = ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php'))->getInstance(); [Container%s/getFactoryServiceSimpleService.php] => services['factory_service_simple'] = ${($_ = isset($this->services['factory_simple']) ? $this->services['factory_simple'] : $this->load(__DIR__.'/getFactorySimpleService.php')) && false ?: '_'}->getInstance(); +return $this->services['factory_service_simple'] = ($this->privates['factory_simple'] ?? $this->load(__DIR__.'/getFactorySimpleService.php'))->getInstance(); [Container%s/getFactorySimpleService.php] => services['factory_simple'] = new \SimpleFactoryClass('foo'); +return $this->privates['factory_simple'] = new \SimpleFactoryClass('foo'); [Container%s/getFooService.php] => services['foo.baz']) ? $this->services['foo.baz'] : $this->load(__DIR__.'/getFoo_BazService.php')) && false ?: '_'}; +$a = ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php')); $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this); $instance->foo = 'bar'; $instance->moo = $a; $instance->qux = array('bar' => 'foo is bar', 'foobar' => 'bar'); -$instance->setBar(${($_ = isset($this->services['bar']) ? $this->services['bar'] : $this->getBarService()) && false ?: '_'}); +$instance->setBar(($this->services['bar'] ?? $this->getBarService())); $instance->initialize(); sc_configure($instance); @@ -148,6 +179,20 @@ $this->services['foo.baz'] = $instance = \BazClass::getInstance(); return $instance; + [Container%s/getFooBarService.php] => factories['foo_bar'] = function () { + // Returns the public 'foo_bar' service. + + return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->load(__DIR__.'/getDeprecatedServiceService.php'))); +}; + +return $this->factories['foo_bar'](); + [Container%s/getFooWithInlineService.php] => services['foo_with_inline'] = $instance = new \Foo(); $a = new \Bar(); $a->pub = 'pub'; -$a->setBaz(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->load(__DIR__.'/getBazService.php')) && false ?: '_'}); +$a->setBaz(($this->services['baz'] ?? $this->load(__DIR__.'/getBazService.php'))); $instance->setBar($a); @@ -174,7 +219,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; // Returns the public 'lazy_context' shared service. return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () { - yield 'k1' => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->load(__DIR__.'/getFoo_BazService.php')) && false ?: '_'}; + yield 'k1' => ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php')); yield 'k2' => $this; }, 2), new RewindableGenerator(function () { return new \EmptyIterator(); @@ -188,7 +233,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; // Returns the public 'lazy_context_ignore_invalid_ref' shared service. return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () { - yield 0 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->load(__DIR__.'/getFoo_BazService.php')) && false ?: '_'}; + yield 0 => ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php')); }, 1), new RewindableGenerator(function () { return new \EmptyIterator(); }, 0)); @@ -204,9 +249,9 @@ include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php'); $this->services['method_call1'] = $instance = new \Bar\FooClass(); -$instance->setBar(${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->load(__DIR__.'/getFooService.php')) && false ?: '_'}); +$instance->setBar(($this->services['foo'] ?? $this->load(__DIR__.'/getFooService.php'))); $instance->setBar(NULL); -$instance->setBar((${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->load(__DIR__.'/getFooService.php')) && false ?: '_'}->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); +$instance->setBar((($this->services['foo'] ?? $this->load(__DIR__.'/getFooService.php'))->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); return $instance; @@ -226,6 +271,21 @@ $instance->foo = 'bar'; return $instance; + [Container%s/getNonSharedFooService.php] => targetDirs[0].'/Fixtures/includes/foo.php'); + +$this->factories['non_shared_foo'] = function () { + return new \Bar\FooClass(); +}; + +return $this->factories['non_shared_foo'](); + [Container%s/getServiceFromStaticMethodService.php] => services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { - yield 0 => ${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->load(__DIR__.'/getFooService.php')) && false ?: '_'}; - yield 1 => ${($_ = isset($this->services['tagged_iterator_foo']) ? $this->services['tagged_iterator_foo'] : $this->services['tagged_iterator_foo'] = new \Bar()) && false ?: '_'}; + yield 0 => ($this->services['foo'] ?? $this->load(__DIR__.'/getFooService.php')); + yield 1 => ($this->privates['tagged_iterator_foo'] ?? $this->privates['tagged_iterator_foo'] = new \Bar()); }, 2)); [Container%s/getTaggedIteratorFooService.php] => services['tagged_iterator_foo'] = new \Bar(); +return $this->privates['tagged_iterator_foo'] = new \Bar(); [Container%s/ProjectServiceContainer.php] => targetDirs[0] = \dirname(__DIR__); @@ -289,15 +354,17 @@ class ProjectServiceContainer extends Container $this->buildParameters = $buildParameters; $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->syntheticIds = array( 'request' => true, ); $this->methodMap = array( 'bar' => 'getBarService', - 'foo_bar' => 'getFooBarService', ); $this->fileMap = array( + 'BAR' => __DIR__.'/getBAR2Service.php', + 'BAR2' => __DIR__.'/getBAR22Service.php', + 'bar2' => __DIR__.'/getBar23Service.php', 'baz' => __DIR__.'/getBazService.php', 'configured_service' => __DIR__.'/getConfiguredServiceService.php', 'configured_service_simple' => __DIR__.'/getConfiguredServiceSimpleService.php', @@ -306,21 +373,17 @@ class ProjectServiceContainer extends Container 'deprecated_service' => __DIR__.'/getDeprecatedServiceService.php', 'factory_service' => __DIR__.'/getFactoryServiceService.php', 'factory_service_simple' => __DIR__.'/getFactoryServiceSimpleService.php', - 'factory_simple' => __DIR__.'/getFactorySimpleService.php', 'foo' => __DIR__.'/getFooService.php', 'foo.baz' => __DIR__.'/getFoo_BazService.php', + 'foo_bar' => __DIR__.'/getFooBarService.php', 'foo_with_inline' => __DIR__.'/getFooWithInlineService.php', 'lazy_context' => __DIR__.'/getLazyContextService.php', 'lazy_context_ignore_invalid_ref' => __DIR__.'/getLazyContextIgnoreInvalidRefService.php', 'method_call1' => __DIR__.'/getMethodCall1Service.php', 'new_factory_service' => __DIR__.'/getNewFactoryServiceService.php', + 'non_shared_foo' => __DIR__.'/getNonSharedFooService.php', 'service_from_static_method' => __DIR__.'/getServiceFromStaticMethodService.php', 'tagged_iterator' => __DIR__.'/getTaggedIteratorService.php', - 'tagged_iterator_foo' => __DIR__.'/getTaggedIteratorFooService.php', - ); - $this->privates = array( - 'factory_simple' => true, - 'tagged_iterator_foo' => true, ); $this->aliases = array( 'alias_for_alias' => 'foo', @@ -329,9 +392,10 @@ class ProjectServiceContainer extends Container ); } - public function getRemovedIds() + public function reset() { - return require __DIR__.'/removed-ids.php'; + $this->privates = array(); + parent::reset(); } public function compile() @@ -344,11 +408,9 @@ class ProjectServiceContainer extends Container return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return require __DIR__.'/removed-ids.php'; } protected function load($file, $lazyLoad = true) @@ -363,7 +425,7 @@ class ProjectServiceContainer extends Container */ protected function getBarService() { - $a = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->load(__DIR__.'/getFoo_BazService.php')) && false ?: '_'}; + $a = ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php')); $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); @@ -372,28 +434,15 @@ class ProjectServiceContainer extends Container return $instance; } - /** - * Gets the public 'foo_bar' service. - * - * @return \Bar\FooClass - */ - protected function getFooBarService() - { - return new \Bar\FooClass(${($_ = isset($this->services['deprecated_service']) ? $this->services['deprecated_service'] : $this->load(__DIR__.'/getDeprecatedServiceService.php')) && false ?: '_'}); - } - public function getParameter($name) { $name = (string) $name; if (isset($this->buildParameters[$name])) { return $this->buildParameters[$name]; } - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -408,7 +457,6 @@ class ProjectServiceContainer extends Container if (isset($this->buildParameters[$name])) { return true; } - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -451,22 +499,6 @@ class ProjectServiceContainer extends Container throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 1af126fa3c961..a45456163f796 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -19,16 +19,24 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->syntheticIds = array( 'request' => true, ); $this->methodMap = array( - 'bar' => 'getBarService', + 'BAR' => 'getBARService', + 'BAR2' => 'getBAR2Service', + 'bar' => 'getBar3Service', + 'bar2' => 'getBar22Service', 'baz' => 'getBazService', 'configured_service' => 'getConfiguredServiceService', 'configured_service_simple' => 'getConfiguredServiceSimpleService', @@ -37,7 +45,6 @@ public function __construct() 'deprecated_service' => 'getDeprecatedServiceService', 'factory_service' => 'getFactoryServiceService', 'factory_service_simple' => 'getFactoryServiceSimpleService', - 'factory_simple' => 'getFactorySimpleService', 'foo' => 'getFooService', 'foo.baz' => 'getFoo_BazService', 'foo_bar' => 'getFooBarService', @@ -48,11 +55,6 @@ public function __construct() 'new_factory_service' => 'getNewFactoryServiceService', 'service_from_static_method' => 'getServiceFromStaticMethodService', 'tagged_iterator' => 'getTaggedIteratorService', - 'tagged_iterator_foo' => 'getTaggedIteratorFooService', - ); - $this->privates = array( - 'factory_simple' => true, - 'tagged_iterator_foo' => true, ); $this->aliases = array( 'alias_for_alias' => 'foo', @@ -61,6 +63,22 @@ public function __construct() ); } + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + public function getRemovedIds() { return array( @@ -77,21 +95,28 @@ public function getRemovedIds() ); } - public function compile() + /** + * Gets the public 'BAR' shared service. + * + * @return \stdClass + */ + protected function getBARService() { - throw new LogicException('You cannot compile a dumped container that was already compiled.'); - } + $this->services['BAR'] = $instance = new \stdClass(); - public function isCompiled() - { - return true; + $instance->bar = ($this->services['bar'] ?? $this->getBar3Service()); + + return $instance; } - public function isFrozen() + /** + * Gets the public 'BAR2' shared service. + * + * @return \stdClass + */ + protected function getBAR2Service() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return $this->services['BAR2'] = new \stdClass(); } /** @@ -99,9 +124,9 @@ public function isFrozen() * * @return \Bar\FooClass */ - protected function getBarService() + protected function getBar3Service() { - $a = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; + $a = ($this->services['foo.baz'] ?? $this->getFoo_BazService()); $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); @@ -110,6 +135,16 @@ protected function getBarService() return $instance; } + /** + * Gets the public 'bar2' shared service. + * + * @return \stdClass + */ + protected function getBar22Service() + { + return $this->services['bar2'] = new \stdClass(); + } + /** * Gets the public 'baz' shared service. * @@ -119,7 +154,7 @@ protected function getBazService() { $this->services['baz'] = $instance = new \Baz(); - $instance->setFoo(${($_ = isset($this->services['foo_with_inline']) ? $this->services['foo_with_inline'] : $this->getFooWithInlineService()) && false ?: '_'}); + $instance->setFoo(($this->services['foo_with_inline'] ?? $this->getFooWithInlineService())); return $instance; } @@ -134,7 +169,7 @@ protected function getConfiguredServiceService() $this->services['configured_service'] = $instance = new \stdClass(); $a = new \ConfClass(); - $a->setFoo(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->getBazService()) && false ?: '_'}); + $a->setFoo(($this->services['baz'] ?? $this->getBazService())); $a->configureStdClass($instance); @@ -196,7 +231,7 @@ protected function getDeprecatedServiceService() */ protected function getFactoryServiceService() { - return $this->services['factory_service'] = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}->getInstance(); + return $this->services['factory_service'] = ($this->services['foo.baz'] ?? $this->getFoo_BazService())->getInstance(); } /** @@ -206,7 +241,7 @@ protected function getFactoryServiceService() */ protected function getFactoryServiceSimpleService() { - return $this->services['factory_service_simple'] = ${($_ = isset($this->services['factory_simple']) ? $this->services['factory_simple'] : $this->getFactorySimpleService()) && false ?: '_'}->getInstance(); + return $this->services['factory_service_simple'] = ($this->privates['factory_simple'] ?? $this->getFactorySimpleService())->getInstance(); } /** @@ -216,14 +251,14 @@ protected function getFactoryServiceSimpleService() */ protected function getFooService() { - $a = ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; + $a = ($this->services['foo.baz'] ?? $this->getFoo_BazService()); $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this); $instance->foo = 'bar'; $instance->moo = $a; $instance->qux = array('bar' => 'foo is bar', 'foobar' => 'bar'); - $instance->setBar(${($_ = isset($this->services['bar']) ? $this->services['bar'] : $this->getBarService()) && false ?: '_'}); + $instance->setBar(($this->services['bar'] ?? $this->getBar3Service())); $instance->initialize(); sc_configure($instance); @@ -251,7 +286,7 @@ protected function getFoo_BazService() */ protected function getFooBarService() { - return new \Bar\FooClass(${($_ = isset($this->services['deprecated_service']) ? $this->services['deprecated_service'] : $this->getDeprecatedServiceService()) && false ?: '_'}); + return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService())); } /** @@ -266,7 +301,7 @@ protected function getFooWithInlineService() $a = new \Bar(); $a->pub = 'pub'; - $a->setBaz(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->getBazService()) && false ?: '_'}); + $a->setBaz(($this->services['baz'] ?? $this->getBazService())); $instance->setBar($a); @@ -281,7 +316,7 @@ protected function getFooWithInlineService() protected function getLazyContextService() { return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () { - yield 'k1' => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; + yield 'k1' => ($this->services['foo.baz'] ?? $this->getFoo_BazService()); yield 'k2' => $this; }, 2), new RewindableGenerator(function () { return new \EmptyIterator(); @@ -296,7 +331,7 @@ protected function getLazyContextService() protected function getLazyContextIgnoreInvalidRefService() { return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () { - yield 0 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'}; + yield 0 => ($this->services['foo.baz'] ?? $this->getFoo_BazService()); }, 1), new RewindableGenerator(function () { return new \EmptyIterator(); }, 0)); @@ -313,9 +348,9 @@ protected function getMethodCall1Service() $this->services['method_call1'] = $instance = new \Bar\FooClass(); - $instance->setBar(${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}); + $instance->setBar(($this->services['foo'] ?? $this->getFooService())); $instance->setBar(NULL); - $instance->setBar((${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); + $instance->setBar((($this->services['foo'] ?? $this->getFooService())->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); return $instance; } @@ -355,8 +390,8 @@ protected function getServiceFromStaticMethodService() protected function getTaggedIteratorService() { return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { - yield 0 => ${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}; - yield 1 => ${($_ = isset($this->services['tagged_iterator_foo']) ? $this->services['tagged_iterator_foo'] : $this->services['tagged_iterator_foo'] = new \Bar()) && false ?: '_'}; + yield 0 => ($this->services['foo'] ?? $this->getFooService()); + yield 1 => ($this->privates['tagged_iterator_foo'] ?? $this->privates['tagged_iterator_foo'] = new \Bar()); }, 2)); } @@ -371,28 +406,15 @@ protected function getFactorySimpleService() { @trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); - return $this->services['factory_simple'] = new \SimpleFactoryClass('foo'); - } - - /** - * Gets the private 'tagged_iterator_foo' shared service. - * - * @return \Bar - */ - protected function getTaggedIteratorFooService() - { - return $this->services['tagged_iterator_foo'] = new \Bar(); + return $this->privates['factory_simple'] = new \SimpleFactoryClass('foo'); } public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -404,7 +426,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -444,22 +465,6 @@ private function getDynamicParameter($name) throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 45fb2432c33c9..4de6bfc233193 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -19,9 +19,14 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar2' => 'getBar2Service', 'bar3' => 'getBar3Service', @@ -34,18 +39,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'bar' => true, - 'bar5' => true, - 'foo4' => true, - 'foobar' => true, - 'foobar2' => true, - 'foobar3' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -58,11 +55,18 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar' => true, + 'bar5' => true, + 'foo4' => true, + 'foobar' => true, + 'foobar2' => true, + 'foobar3' => true, + ); } /** @@ -74,7 +78,7 @@ protected function getBar2Service() { $this->services['bar2'] = $instance = new \BarCircular(); - $instance->addFoobar(new \FoobarCircular(${($_ = isset($this->services['foo2']) ? $this->services['foo2'] : $this->getFoo2Service()) && false ?: '_'})); + $instance->addFoobar(new \FoobarCircular(($this->services['foo2'] ?? $this->getFoo2Service()))); return $instance; } @@ -118,7 +122,7 @@ protected function getFooService() */ protected function getFoo2Service() { - $a = ${($_ = isset($this->services['bar2']) ? $this->services['bar2'] : $this->getBar2Service()) && false ?: '_'}; + $a = ($this->services['bar2'] ?? $this->getBar2Service()); if (isset($this->services['foo2'])) { return $this->services['foo2']; @@ -136,7 +140,7 @@ protected function getFoo5Service() { $this->services['foo5'] = $instance = new \stdClass(); - $a = new \stdClass(${($_ = isset($this->services['foo5']) ? $this->services['foo5'] : $this->getFoo5Service()) && false ?: '_'}); + $a = new \stdClass(($this->services['foo5'] ?? $this->getFoo5Service())); $a->foo = $instance; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index be18eb183abd0..79a0c11cc15c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -19,9 +19,14 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar' => 'getBarService', 'bar3' => 'getBar3Service', @@ -39,13 +44,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'bar2' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -58,11 +60,13 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar2' => true, + ); } /** @@ -74,7 +78,7 @@ protected function getBarService() { $this->services['bar'] = $instance = new \BarCircular(); - $instance->addFoobar(${($_ = isset($this->services['foobar']) ? $this->services['foobar'] : $this->getFoobarService()) && false ?: '_'}); + $instance->addFoobar(($this->services['foobar'] ?? $this->getFoobarService())); return $instance; } @@ -88,7 +92,7 @@ protected function getBar3Service() { $this->services['bar3'] = $instance = new \BarCircular(); - $a = ${($_ = isset($this->services['foobar3']) ? $this->services['foobar3'] : $this->services['foobar3'] = new \FoobarCircular()) && false ?: '_'}; + $a = ($this->services['foobar3'] ?? $this->services['foobar3'] = new \FoobarCircular()); $instance->addFoobar($a, $a); @@ -102,7 +106,7 @@ protected function getBar3Service() */ protected function getBar5Service() { - $a = ${($_ = isset($this->services['foo5']) ? $this->services['foo5'] : $this->getFoo5Service()) && false ?: '_'}; + $a = ($this->services['foo5'] ?? $this->getFoo5Service()); if (isset($this->services['bar5'])) { return $this->services['bar5']; @@ -122,7 +126,7 @@ protected function getBar5Service() */ protected function getFooService() { - $a = ${($_ = isset($this->services['bar']) ? $this->services['bar'] : $this->getBarService()) && false ?: '_'}; + $a = ($this->services['bar'] ?? $this->getBarService()); if (isset($this->services['foo'])) { return $this->services['foo']; @@ -142,7 +146,7 @@ protected function getFoo2Service() $this->services['foo2'] = $instance = new \FooCircular($a); - $a->addFoobar(${($_ = isset($this->services['foobar2']) ? $this->services['foobar2'] : $this->getFoobar2Service()) && false ?: '_'}); + $a->addFoobar(($this->services['foobar2'] ?? $this->getFoobar2Service())); return $instance; } @@ -156,7 +160,7 @@ protected function getFoo4Service() { $instance = new \stdClass(); - $instance->foobar = ${($_ = isset($this->services['foobar4']) ? $this->services['foobar4'] : $this->getFoobar4Service()) && false ?: '_'}; + $instance->foobar = ($this->services['foobar4'] ?? $this->getFoobar4Service()); return $instance; } @@ -170,7 +174,7 @@ protected function getFoo5Service() { $this->services['foo5'] = $instance = new \stdClass(); - $instance->bar = ${($_ = isset($this->services['bar5']) ? $this->services['bar5'] : $this->getBar5Service()) && false ?: '_'}; + $instance->bar = ($this->services['bar5'] ?? $this->getBar5Service()); return $instance; } @@ -182,7 +186,7 @@ protected function getFoo5Service() */ protected function getFoobarService() { - $a = ${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}; + $a = ($this->services['foo'] ?? $this->getFooService()); if (isset($this->services['foobar'])) { return $this->services['foobar']; @@ -198,7 +202,7 @@ protected function getFoobarService() */ protected function getFoobar2Service() { - $a = ${($_ = isset($this->services['foo2']) ? $this->services['foo2'] : $this->getFoo2Service()) && false ?: '_'}; + $a = ($this->services['foo2'] ?? $this->getFoo2Service()); if (isset($this->services['foobar2'])) { return $this->services['foobar2']; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php index 4776d98c31081..d72e6fd00077a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php @@ -19,6 +19,11 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $dir = __DIR__; @@ -27,7 +32,7 @@ public function __construct() } $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar' => 'getBarService', ); @@ -35,12 +40,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -53,11 +56,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** @@ -77,12 +81,9 @@ protected function getBarService() public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -94,7 +95,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -144,22 +144,6 @@ private function getDynamicParameter($name) return $this->dynamicParameters[$name] = $value; } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php index f1b92397ddbc3..8af802f70dab3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php @@ -19,21 +19,24 @@ class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $this->parameters = $this->getDefaultParameters(); - $this->services = array(); + $this->services = $this->privates = array(); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -46,22 +49,20 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -73,7 +74,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -121,22 +121,6 @@ private function getDynamicParameter($name) return $this->dynamicParameters[$name] = $value; } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_csv_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_csv_env.php new file mode 100644 index 0000000000000..99215f5fd685b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_csv_env.php @@ -0,0 +1,135 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'hello' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'hello': $value = $this->getEnv('csv:foo'); break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'env(foo)' => 'foo,bar', + ); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php index 6bc714a204c97..4100dcdd2b914 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php @@ -19,34 +19,28 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $this->parameters = $this->getDefaultParameters(); - $this->services = array(); - $this->normalizedIds = array( - 'bar_%env(bar)%' => 'bar_%env(BAR)%', - ); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar' => 'getBarService', - 'bar_%env(BAR)%' => 'getBarenvBARService', 'foo' => 'getFooService', ); - $this->privates = array( - 'bar_%env(BAR)%' => true, - ); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'bar_%env(BAR)%' => true, - 'baz_%env(BAR)%' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -59,11 +53,14 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar_%env(BAR)%' => true, + 'baz_%env(BAR)%' => true, + ); } /** @@ -73,7 +70,7 @@ public function isFrozen() */ protected function getBarService() { - return $this->services['bar'] = new \stdClass(${($_ = isset($this->services['bar_%env(BAR)%']) ? $this->services['bar_%env(BAR)%'] : $this->services['bar_%env(BAR)%'] = new \stdClass()) && false ?: '_'}); + return $this->services['bar'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? $this->privates['bar_%env(BAR)%'] = new \stdClass())); } /** @@ -83,28 +80,15 @@ protected function getBarService() */ protected function getFooService() { - return $this->services['foo'] = new \stdClass(${($_ = isset($this->services['bar_%env(BAR)%']) ? $this->services['bar_%env(BAR)%'] : $this->services['bar_%env(BAR)%'] = new \stdClass()) && false ?: '_'}, array('baz_'.$this->getEnv('string:BAR') => new \stdClass())); - } - - /** - * Gets the private 'bar_%env(BAR)%' shared service. - * - * @return \stdClass - */ - protected function getBarenvBARService() - { - return $this->services['bar_%env(BAR)%'] = new \stdClass(); + return $this->services['foo'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? $this->privates['bar_%env(BAR)%'] = new \stdClass()), array('baz_'.$this->getEnv('string:BAR') => new \stdClass())); } public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -116,7 +100,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -156,24 +139,6 @@ private function getDynamicParameter($name) throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); } - private $normalizedParameterNames = array( - 'env(bar)' => 'env(BAR)', - ); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php index 2db58bddaab4f..7a882f4461f9a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php @@ -19,6 +19,11 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $dir = __DIR__; @@ -27,21 +32,11 @@ public function __construct() } $this->parameters = $this->getDefaultParameters(); - $this->services = array(); - $this->normalizedIds = array( - 'symfony\\component\\dependencyinjection\\tests\\fixtures\\includes\\hotpath\\c1' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C1', - 'symfony\\component\\dependencyinjection\\tests\\fixtures\\includes\\hotpath\\c2' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C2', - 'symfony\\component\\dependencyinjection\\tests\\fixtures\\includes\\hotpath\\c3' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3', - 'symfony\\component\\dependencyinjection\\tests\\fixtures\\parentnotexists' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\ParentNotExists', - ); + $this->services = $this->privates = array(); $this->methodMap = array( 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\ParentNotExists' => 'getParentNotExistsService', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C1' => 'getC1Service', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C2' => 'getC2Service', - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3' => 'getC3Service', - ); - $this->privates = array( - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3' => true, ); $this->aliases = array(); @@ -54,13 +49,10 @@ public function __construct() }; } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -73,11 +65,13 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3' => true, + ); } /** @@ -106,34 +100,19 @@ protected function getC1Service() * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2 */ protected function getC2Service() - { - include_once $this->targetDirs[1].'/includes/HotPath/C2.php'; - include_once $this->targetDirs[1].'/includes/HotPath/C3.php'; - - return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3()) && false ?: '_'}); - } - - /** - * Gets the private 'Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3' shared service. - * - * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3 - */ - protected function getC3Service() { include_once $this->targetDirs[1].'/includes/HotPath/C3.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C2.php'; - return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3(); + return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3()); } public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -145,7 +124,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -185,22 +163,6 @@ private function getDynamicParameter($name) throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_legacy_privates.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_legacy_privates.php deleted file mode 100644 index 7aa1bff87069a..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_legacy_privates.php +++ /dev/null @@ -1,193 +0,0 @@ -targetDirs[$i] = $dir = \dirname($dir); - } - $this->services = array(); - $this->methodMap = array( - 'bar' => 'getBarService', - 'private' => 'getPrivateService', - 'private_alias' => 'getPrivateAliasService', - 'private_alias_decorator' => 'getPrivateAliasDecoratorService', - 'private_child' => 'getPrivateChildService', - 'private_decorator' => 'getPrivateDecoratorService', - 'private_not_inlined' => 'getPrivateNotInlinedService', - 'private_not_removed' => 'getPrivateNotRemovedService', - 'private_parent' => 'getPrivateParentService', - 'public_child' => 'getPublicChildService', - ); - $this->privates = array( - 'decorated_private' => true, - 'decorated_private_alias' => true, - 'private' => true, - 'private_alias' => true, - 'private_child' => true, - 'private_not_inlined' => true, - 'private_not_removed' => true, - 'private_parent' => true, - ); - $this->aliases = array( - 'alias_to_private' => 'private', - 'decorated_private' => 'private_decorator', - 'decorated_private_alias' => 'private_alias_decorator', - ); - } - - public function getRemovedIds() - { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'decorated_private' => true, - 'decorated_private_alias' => true, - 'foo' => true, - 'private' => true, - 'private_alias' => true, - 'private_alias_decorator.inner' => true, - 'private_child' => true, - 'private_decorator.inner' => true, - 'private_not_inlined' => true, - 'private_not_removed' => true, - 'private_parent' => true, - ); - } - - public function compile() - { - throw new LogicException('You cannot compile a dumped container that was already compiled.'); - } - - public function isCompiled() - { - return true; - } - - public function isFrozen() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; - } - - /** - * Gets the public 'bar' shared service. - * - * @return \stdClass - */ - protected function getBarService() - { - return $this->services['bar'] = new \stdClass(${($_ = isset($this->services['private_not_inlined']) ? $this->services['private_not_inlined'] : $this->services['private_not_inlined'] = new \stdClass()) && false ?: '_'}); - } - - /** - * Gets the public 'private_alias_decorator' shared service. - * - * @return \stdClass - */ - protected function getPrivateAliasDecoratorService() - { - return $this->services['private_alias_decorator'] = new \stdClass(); - } - - /** - * Gets the public 'private_decorator' shared service. - * - * @return \stdClass - */ - protected function getPrivateDecoratorService() - { - return $this->services['private_decorator'] = new \stdClass(); - } - - /** - * Gets the public 'public_child' shared service. - * - * @return \stdClass - */ - protected function getPublicChildService() - { - return $this->services['public_child'] = new \stdClass(); - } - - /** - * Gets the private 'private' shared service. - * - * @return \stdClass - */ - protected function getPrivateService() - { - return $this->services['private'] = new \stdClass(); - } - - /** - * Gets the private 'private_alias' shared service. - * - * @return \stdClass - */ - protected function getPrivateAliasService() - { - return $this->services['private_alias'] = new \stdClass(); - } - - /** - * Gets the private 'private_child' shared service. - * - * @return \stdClass - */ - protected function getPrivateChildService() - { - return $this->services['private_child'] = new \stdClass(); - } - - /** - * Gets the private 'private_not_inlined' shared service. - * - * @return \stdClass - */ - protected function getPrivateNotInlinedService() - { - return $this->services['private_not_inlined'] = new \stdClass(); - } - - /** - * Gets the private 'private_not_removed' shared service. - * - * @return \stdClass - */ - protected function getPrivateNotRemovedService() - { - return $this->services['private_not_removed'] = new \stdClass(); - } - - /** - * Gets the private 'private_parent' shared service. - * - * @return \stdClass - */ - protected function getPrivateParentService() - { - return $this->services['private_parent'] = new \stdClass(); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php index 496f6aa77d306..59bbce3a995c4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php @@ -19,12 +19,16 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar_service' => 'getBarServiceService', - 'baz_service' => 'getBazServiceService', 'foo_service' => 'getFooServiceService', 'translator.loader_1' => 'getTranslator_Loader1Service', 'translator.loader_2' => 'getTranslator_Loader2Service', @@ -33,23 +37,14 @@ public function __construct() 'translator_2' => 'getTranslator2Service', 'translator_3' => 'getTranslator3Service', ); - $this->privates = array( - 'baz_service' => true, - ); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'baz_service' => true, - 'translator.loader_1_locator' => true, - 'translator.loader_2_locator' => true, - 'translator.loader_3_locator' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -62,11 +57,16 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'baz_service' => true, + 'translator.loader_1_locator' => true, + 'translator.loader_2_locator' => true, + 'translator.loader_3_locator' => true, + ); } /** @@ -76,7 +76,7 @@ public function isFrozen() */ protected function getBarServiceService() { - return $this->services['bar_service'] = new \stdClass(${($_ = isset($this->services['baz_service']) ? $this->services['baz_service'] : $this->services['baz_service'] = new \stdClass()) && false ?: '_'}); + return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); } /** @@ -87,9 +87,9 @@ protected function getBarServiceService() protected function getFooServiceService() { return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('bar' => function () { - return ${($_ = isset($this->services['bar_service']) ? $this->services['bar_service'] : $this->getBarServiceService()) && false ?: '_'}; - }, 'baz' => function () { - $f = function (\stdClass $v) { return $v; }; return $f(${($_ = isset($this->services['baz_service']) ? $this->services['baz_service'] : $this->services['baz_service'] = new \stdClass()) && false ?: '_'}); + return ($this->services['bar_service'] ?? $this->getBarServiceService()); + }, 'baz' => function (): \stdClass { + return ($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass()); }, 'nil' => function () { return NULL; })); @@ -133,7 +133,7 @@ protected function getTranslator_Loader3Service() protected function getTranslator1Service() { return $this->services['translator_1'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_1' => function () { - return ${($_ = isset($this->services['translator.loader_1']) ? $this->services['translator.loader_1'] : $this->services['translator.loader_1'] = new \stdClass()) && false ?: '_'}; + return ($this->services['translator.loader_1'] ?? $this->services['translator.loader_1'] = new \stdClass()); }))); } @@ -145,10 +145,10 @@ protected function getTranslator1Service() protected function getTranslator2Service() { $this->services['translator_2'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_2' => function () { - return ${($_ = isset($this->services['translator.loader_2']) ? $this->services['translator.loader_2'] : $this->services['translator.loader_2'] = new \stdClass()) && false ?: '_'}; + return ($this->services['translator.loader_2'] ?? $this->services['translator.loader_2'] = new \stdClass()); }))); - $instance->addResource('db', ${($_ = isset($this->services['translator.loader_2']) ? $this->services['translator.loader_2'] : $this->services['translator.loader_2'] = new \stdClass()) && false ?: '_'}, 'nl'); + $instance->addResource('db', ($this->services['translator.loader_2'] ?? $this->services['translator.loader_2'] = new \stdClass()), 'nl'); return $instance; } @@ -161,24 +161,14 @@ protected function getTranslator2Service() protected function getTranslator3Service() { $this->services['translator_3'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_3' => function () { - return ${($_ = isset($this->services['translator.loader_3']) ? $this->services['translator.loader_3'] : $this->services['translator.loader_3'] = new \stdClass()) && false ?: '_'}; + return ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass()); }))); - $a = ${($_ = isset($this->services['translator.loader_3']) ? $this->services['translator.loader_3'] : $this->services['translator.loader_3'] = new \stdClass()) && false ?: '_'}; + $a = ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass()); $instance->addResource('db', $a, 'nl'); $instance->addResource('db', $a, 'en'); return $instance; } - - /** - * Gets the private 'baz_service' shared service. - * - * @return \stdClass - */ - protected function getBazServiceService() - { - return $this->services['baz_service'] = new \stdClass(); - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php index 1275e9f2642a3..86315e2ebaffc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php @@ -19,28 +19,26 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar_service' => 'getBarServiceService', - 'baz_service' => 'getBazServiceService', 'foo_service' => 'getFooServiceService', ); - $this->privates = array( - 'baz_service' => true, - ); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'baz_service' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -53,11 +51,13 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'baz_service' => true, + ); } /** @@ -67,7 +67,7 @@ public function isFrozen() */ protected function getBarServiceService() { - return $this->services['bar_service'] = new \stdClass(${($_ = isset($this->services['baz_service']) ? $this->services['baz_service'] : $this->services['baz_service'] = new \stdClass()) && false ?: '_'}); + return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); } /** @@ -77,16 +77,6 @@ protected function getBarServiceService() */ protected function getFooServiceService() { - return $this->services['foo_service'] = new \stdClass(${($_ = isset($this->services['baz_service']) ? $this->services['baz_service'] : $this->services['baz_service'] = new \stdClass()) && false ?: '_'}); - } - - /** - * Gets the private 'baz_service' shared service. - * - * @return \stdClass - */ - protected function getBazServiceService() - { - return $this->services['baz_service'] = new \stdClass(); + return $this->services['foo_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php index fe84f49753c52..5caf9104dd34d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php @@ -19,28 +19,25 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( - 'private_foo' => 'getPrivateFooService', 'public_foo' => 'getPublicFooService', ); - $this->privates = array( - 'private_foo' => true, - ); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'private_bar' => true, - 'private_foo' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -53,11 +50,14 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'private_bar' => true, + 'private_foo' => true, + ); } /** @@ -67,16 +67,6 @@ public function isFrozen() */ protected function getPublicFooService() { - return $this->services['public_foo'] = new \stdClass(${($_ = isset($this->services['private_foo']) ? $this->services['private_foo'] : $this->services['private_foo'] = new \stdClass()) && false ?: '_'}); - } - - /** - * Gets the private 'private_foo' shared service. - * - * @return \stdClass - */ - protected function getPrivateFooService() - { - return $this->services['private_foo'] = new \stdClass(); + return $this->services['public_foo'] = new \stdClass(($this->privates['private_foo'] ?? $this->privates['private_foo'] = new \stdClass())); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php index aab87ec7af9f0..012a36023b0f8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php @@ -19,14 +19,16 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { $this->parameters = $this->getDefaultParameters(); - $this->services = array(); - $this->normalizedIds = array( - 'symfony\\component\\dependencyinjection\\tests\\dumper\\rot13envvarprocessor' => 'Symfony\\Component\\DependencyInjection\\Tests\\Dumper\\Rot13EnvVarProcessor', - ); + $this->services = $this->privates = array(); $this->methodMap = array( 'Symfony\\Component\\DependencyInjection\\Tests\\Dumper\\Rot13EnvVarProcessor' => 'getRot13EnvVarProcessorService', 'container.env_var_processors_locator' => 'getContainer_EnvVarProcessorsLocatorService', @@ -35,12 +37,10 @@ public function __construct() $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -53,11 +53,12 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); } /** @@ -78,19 +79,16 @@ protected function getRot13EnvVarProcessorService() protected function getContainer_EnvVarProcessorsLocatorService() { return $this->services['container.env_var_processors_locator'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('rot13' => function () { - return ${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] : $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor()) && false ?: '_'}; + return ($this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor()); })); } public function getParameter($name) { $name = (string) $name; - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - $name = $this->normalizeParameterName($name); - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); @@ -102,7 +100,6 @@ public function getParameter($name) public function hasParameter($name) { $name = (string) $name; - $name = $this->normalizeParameterName($name); return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } @@ -150,22 +147,6 @@ private function getDynamicParameter($name) return $this->dynamicParameters[$name] = $value; } - private $normalizedParameterNames = array(); - - private function normalizeParameterName($name) - { - if (isset($this->normalizedParameterNames[$normalizedName = strtolower($name)]) || isset($this->parameters[$normalizedName]) || array_key_exists($normalizedName, $this->parameters)) { - $normalizedName = isset($this->normalizedParameterNames[$normalizedName]) ? $this->normalizedParameterNames[$normalizedName] : $normalizedName; - if ((string) $name !== $normalizedName) { - @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED); - } - } else { - $normalizedName = $this->normalizedParameterNames[$normalizedName] = (string) $name; - } - - return $normalizedName; - } - /** * Gets the default parameters. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php index 9475c923068f2..4f41330edb390 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -19,34 +19,26 @@ class ProjectServiceContainer extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); - $this->normalizedIds = array( - 'symfony\\component\\dependencyinjection\\tests\\fixtures\\customdefinition' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', - 'symfony\\component\\dependencyinjection\\tests\\fixtures\\testservicesubscriber' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', - ); + $this->services = $this->privates = array(); $this->methodMap = array( - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => 'getCustomDefinitionService', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'getTestServiceSubscriberService', 'foo_service' => 'getFooServiceService', ); - $this->privates = array( - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true, - ); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true, - 'service_locator.jmktfsv' => true, - 'service_locator.jmktfsv.foo_service' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -59,11 +51,15 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true, + 'service_locator.MtGsMEd' => true, + 'service_locator.MtGsMEd.foo_service' => true, + ); } /** @@ -83,24 +79,14 @@ protected function getTestServiceSubscriberService() */ protected function getFooServiceService() { - return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber((new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function () { - $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v = null) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()) && false ?: '_'}); - }, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => function () { - $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber $v) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()) && false ?: '_'}); - }, 'bar' => function () { - $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()) && false ?: '_'}); - }, 'baz' => function () { - $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v = null) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()) && false ?: '_'}); + return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber((new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function (): ?\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { + return ($this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()); + }, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber { + return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()); + }, 'bar' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { + return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()); + }, 'baz' => function (): ?\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { + return ($this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()); })))->withContext('foo_service', $this)); } - - /** - * Gets the private 'Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition' shared service. - * - * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition - */ - protected function getCustomDefinitionService() - { - return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition(); - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php index 4d0c00b289138..0f5090c80bebe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php @@ -19,30 +19,27 @@ class Symfony_DI_PhpDumper_Test_Uninitialized_Reference extends Container private $parameters; private $targetDirs = array(); + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); + public function __construct() { - $this->services = array(); + $this->services = $this->privates = array(); $this->methodMap = array( 'bar' => 'getBarService', 'baz' => 'getBazService', 'foo1' => 'getFoo1Service', - 'foo3' => 'getFoo3Service', - ); - $this->privates = array( - 'foo3' => true, ); $this->aliases = array(); } - public function getRemovedIds() + public function reset() { - return array( - 'Psr\\Container\\ContainerInterface' => true, - 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, - 'foo2' => true, - 'foo3' => true, - ); + $this->privates = array(); + parent::reset(); } public function compile() @@ -55,11 +52,14 @@ public function isCompiled() return true; } - public function isFrozen() + public function getRemovedIds() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); - - return true; + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'foo2' => true, + 'foo3' => true, + ); } /** @@ -71,28 +71,28 @@ protected function getBarService() { $this->services['bar'] = $instance = new \stdClass(); - $instance->foo1 = ${($_ = isset($this->services['foo1']) ? $this->services['foo1'] : null) && false ?: '_'}; + $instance->foo1 = ($this->services['foo1'] ?? null); $instance->foo2 = null; - $instance->foo3 = ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : null) && false ?: '_'}; + $instance->foo3 = ($this->privates['foo3'] ?? null); $instance->closures = array(0 => function () { - return ${($_ = isset($this->services['foo1']) ? $this->services['foo1'] : null) && false ?: '_'}; + return ($this->services['foo1'] ?? null); }, 1 => function () { return null; }, 2 => function () { - return ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : null) && false ?: '_'}; + return ($this->privates['foo3'] ?? null); }); $instance->iter = new RewindableGenerator(function () { if (isset($this->services['foo1'])) { - yield 'foo1' => ${($_ = isset($this->services['foo1']) ? $this->services['foo1'] : null) && false ?: '_'}; + yield 'foo1' => ($this->services['foo1'] ?? null); } if (false) { yield 'foo2' => null; } - if (isset($this->services['foo3'])) { - yield 'foo3' => ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : null) && false ?: '_'}; + if (isset($this->privates['foo3'])) { + yield 'foo3' => ($this->privates['foo3'] ?? null); } }, function () { - return 0 + (int) (isset($this->services['foo1'])) + (int) (false) + (int) (isset($this->services['foo3'])); + return 0 + (int) (isset($this->services['foo1'])) + (int) (false) + (int) (isset($this->privates['foo3'])); }); return $instance; @@ -107,7 +107,7 @@ protected function getBazService() { $this->services['baz'] = $instance = new \stdClass(); - $instance->foo3 = ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : $this->services['foo3'] = new \stdClass()) && false ?: '_'}; + $instance->foo3 = ($this->privates['foo3'] ?? $this->privates['foo3'] = new \stdClass()); return $instance; } @@ -121,14 +121,4 @@ protected function getFoo1Service() { return $this->services['foo1'] = new \stdClass(); } - - /** - * Gets the private 'foo3' shared service. - * - * @return \stdClass - */ - protected function getFoo3Service() - { - return $this->services['foo3'] = new \stdClass(); - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/class_from_id.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/class_from_id.xml index 45415cce472f0..e302ce301df07 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/class_from_id.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/class_from_id.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services1.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services1.xml index 792fa07051748..b1cc3904ab0de 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services1.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services1.xml @@ -8,7 +8,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services2.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services2.xml index 67d462be98fbb..2cdcfb48effde 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services2.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services2.xml @@ -11,7 +11,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services3.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services3.xml index c23f02a087750..ed5df6e49a2c0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services3.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services3.xml @@ -11,7 +11,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy_invalid_alias_definition.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/invalid_alias_definition.xml similarity index 62% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy_invalid_alias_definition.xml rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/invalid_alias_definition.xml index 52386e5bf52df..8e99561dca608 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy_invalid_alias_definition.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/invalid_alias_definition.xml @@ -1,11 +1,6 @@ - - - - - - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml deleted file mode 100644 index fa79d389489fb..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - Bar - Baz - - - diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml index 721f028287750..7610cb425f8bc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -18,8 +18,5 @@ - - - diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index d12c00319aca1..3848a83dbd463 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -133,6 +133,11 @@
+ + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml index 103045d38fcb3..187247078de4b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml @@ -1,7 +1,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_bindings.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_bindings.xml index 408bca5f75346..dd3c41a64ea52 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_bindings.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_bindings.xml @@ -1,7 +1,7 @@ - + null quz factory diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_case.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_case.xml new file mode 100644 index 0000000000000..95558244a19cc --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_case.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml index 875ed6d51f996..2bbef6242be98 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml @@ -3,7 +3,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml index 67ce6917249bd..949e73f04d438 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml @@ -3,7 +3,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_named_args.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_named_args.xml index 95dabde9c4b6b..89681861e84bf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_named_args.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_named_args.xml @@ -1,6 +1,7 @@ + ABCD null diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_alias.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_alias.yml new file mode 100644 index 0000000000000..78975e5092866 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_alias.yml @@ -0,0 +1,11 @@ +services: + foo: + class: stdClass + public: false + + bar: + alias: foo + public: true + # keys other than "alias" and "public" are invalid when defining an alias. + calls: + - [doSomething] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy_invalid_definition.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_keyword.yml similarity index 100% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy_invalid_definition.yml rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_keyword.yml diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml deleted file mode 100644 index 891e01497cadf..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - foo_service: - class: FooClass - # types is not an array - autowiring_types: 1 diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml deleted file mode 100644 index fb1d53e151014..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - foo_service: - class: FooClass - # autowiring_types is not a string - autowiring_types: [ 1 ] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/class_from_id.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/class_from_id.yml index 33f0b2ea6821f..5a6532fc637f4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/class_from_id.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/class_from_id.yml @@ -1,3 +1,4 @@ services: Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass: autowire: true + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml index ca08caad673f7..157b58459afb1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml @@ -8,3 +8,4 @@ services: # set on the parent, not the child #calls: # - [enableSummer, [true]] + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml index 02533bf0f5739..5a522cc0ac790 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml @@ -5,3 +5,4 @@ services: parent_service: autoconfigure: true abstract: true + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml index c1dca0763cc90..3c1c89c1d8d3f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml @@ -3,3 +3,4 @@ services: class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub # autoconfigure is set on the parent, but not on the child autoconfigure: false + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml index ab9877d16b9e7..4f70ba7fdf52e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml @@ -5,3 +5,4 @@ services: parent_service: class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub autoconfigure: true + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml index 02cf0037e215d..4e32bea451fc5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml @@ -4,3 +4,4 @@ services: # from an autoconfigured "instanceof" applied to parent class # but NOT inherited down to child # tags: [from_autoconfigure] + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml index ab9877d16b9e7..4f70ba7fdf52e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml @@ -5,3 +5,4 @@ services: parent_service: class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub autoconfigure: true + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml index cb36636e00213..5f8df808bd959 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml @@ -6,3 +6,4 @@ services: # set explicitly on child autowire: true tags: [from_defaults] + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml index b5dc66ff0eb04..621eee9cb253c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml @@ -10,6 +10,7 @@ services: autoconfigure: true # parent definitions are not applied to children tags: [from_parent] + public: true child_service: parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml index 012672ff8b8fb..272c375131918 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml @@ -4,3 +4,4 @@ services: # _defaults is applied to the parent, but autoconfigure: true # does not cascade to the child autoconfigure: false + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml index 8b90b4e985892..d8cd43cf419d9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml @@ -7,3 +7,4 @@ services: parent_service: class: stdClass + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml index 074ed01d03c4c..79ba81db630ff 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml @@ -5,3 +5,4 @@ services: autowire: true # from _instanceof, applies to parent, but does NOT inherit to here # tags: [from_instanceof] + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml index 44cf9b0045d40..7569dd9c93e1f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml @@ -9,3 +9,4 @@ services: parent_service: class: stdClass + public: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services10.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services10.yml index c66084cdbe8e6..993fc7c1bc586 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services10.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services10.yml @@ -4,6 +4,7 @@ parameters: services: project.service.foo: class: BAR + public: true project: test: '%project.parameter.foo%' diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml deleted file mode 100644 index 55d015baea0fb..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - foo_service: - class: FooClass - autowiring_types: [ Foo, Bar ] - - baz_service: - class: Baz - autowiring_types: Foo diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 2501ddcc1b5c9..dbe59ed9e56a2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -139,6 +139,16 @@ services: class: LazyContext arguments: [!iterator ['@foo.baz', '@?invalid'], !iterator []] public: true + BAR: + class: stdClass + properties: { bar: '@bar' } + public: true + bar2: + class: stdClass + public: true + BAR2: + class: stdClass + public: true tagged_iterator_foo: class: Bar tags: diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml index c6e3080192a7e..a6b973b0b9f85 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml @@ -1,6 +1,7 @@ services: parent_service: class: stdClass + public: true child_service: class: stdClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_bindings.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_bindings.yml index 48ad040406219..0eba120b586e2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_bindings.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_bindings.yml @@ -4,6 +4,7 @@ services: NonExistent: ~ $quz: quz $factory: factory + public: true bar: class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_case.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_case.yml new file mode 100644 index 0000000000000..38e477b375113 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_case.yml @@ -0,0 +1,11 @@ +services: + _defaults: {public: true} + bar: + class: stdClass + Bar: + class: stdClass + properties: { bar: '@bar' } + BAR: + class: Bar\FooClass + arguments: ['@Bar'] + calls: [[setBar, ['@bar']]] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml index 28dec4ce91791..fb07a5c7f08eb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml @@ -4,6 +4,7 @@ services: parent_service: class: stdClass + public: true child_service: class: stdClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_dump_load.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_dump_load.yml index 8f3e153afcea4..9c25cbcbc5835 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_dump_load.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_dump_load.yml @@ -2,6 +2,7 @@ services: service_container: class: Symfony\Component\DependencyInjection\ContainerInterface + public: true synthetic: true foo: autoconfigure: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml index fb21cdf2fb674..dfdb6cdd53220 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml @@ -5,6 +5,7 @@ services: parent_service: class: stdClass + public: true child_service: class: stdClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_legacy_privates.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_legacy_privates.yml deleted file mode 100644 index 0fd20446e7964..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_legacy_privates.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - _defaults: {public: true} - - foo: {class: stdClass, public: false} - - bar: - class: stdClass - arguments: [ '@private_not_inlined' ] - - private: {class: stdClass, public: false} - decorated_private: {class: stdClass} - decorated_private_alias: '@foo' - alias_to_private: '@private' - private_alias: {alias: foo, public: false} - - private_decorator: - class: stdClass - decorates: 'decorated_private' - - private_alias_decorator: - class: stdClass - decorates: 'decorated_private_alias' - - private_not_inlined: {class: stdClass, public: false} - private_not_removed: {class: stdClass, public: false} - - public_child: {parent: private, public: true} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_named_args.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_named_args.yml index 97e310148df04..84242e3d79145 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_named_args.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_named_args.yml @@ -1,4 +1,6 @@ services: + _defaults: {public: true} + Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy: { 0: ~, $apiKey: ABCD } another_one: diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php index d3c2dfc76fc5e..84c934c08614f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php @@ -45,15 +45,10 @@ public function testTypeConversions($key, $value, $supported) /** * @dataProvider getTypeConversions - * @requires PHP 5.6.1 * This test illustrates where our conversions differs from INI_SCANNER_TYPED introduced in PHP 5.6.1 */ public function testTypeConversionsWithNativePhp($key, $value, $supported) { - if (defined('HHVM_VERSION_ID')) { - $this->markTestSkipped(); - } - if (!$supported) { $this->markTestSkipped(sprintf('Converting the value "%s" to "%s" is not supported by the IniFileLoader.', $key, $value)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php index 8c84dc7e8041c..f4c8d36e6249e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php @@ -61,7 +61,7 @@ public function testConfig($file) $container->compile(); $dumper = new YamlDumper($container); - $this->assertStringEqualsFile($fixtures.'/config/'.$file.'.expected.yml', $dumper->dump()); + $this->assertStringMatchesFormatFile($fixtures.'/config/'.$file.'.expected.yml', $dumper->dump()); } public function provideConfig() @@ -71,10 +71,8 @@ public function provideConfig() yield array('instanceof'); yield array('prototype'); yield array('child'); - - if (\PHP_VERSION_ID >= 70000) { - yield array('php7'); - } + yield array('php7'); + yield array('anonymous'); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 1c21045f42571..fb80cd49360fe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -185,7 +185,7 @@ public function testLoadAnonymousServices() $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); $loader->load('services5.xml'); $services = $container->getDefinitions(); - $this->assertCount(7, $services, '->load() attributes unique ids to anonymous services'); + $this->assertCount(6, $services, '->load() attributes unique ids to anonymous services'); // anonymous service as an argument $args = $services['foo']->getArguments(); @@ -214,16 +214,6 @@ public function testLoadAnonymousServices() $this->assertEquals('BuzClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); $this->assertFalse($inner->isPublic()); - // "wild" service - $service = $container->findTaggedServiceIds('biz_tag'); - $this->assertCount(1, $service); - - foreach ($service as $id => $tag) { - $service = $container->getDefinition($id); - } - $this->assertEquals('BizClass', $service->getClass(), '->load() uses the same configuration as for the anonymous ones'); - $this->assertTrue($service->isPublic()); - // anonymous services are shared when using decoration definitions $container->compile(); $services = $container->getDefinitions(); @@ -233,8 +223,8 @@ public function testLoadAnonymousServices() } /** - * @group legacy - * @expectedDeprecation Top-level anonymous services are deprecated since Symfony 3.4, the "id" attribute will be required in version 4.0 in %sservices_without_id.xml at line 5. + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Top-level services must have "id" attribute, none found in */ public function testLoadAnonymousServicesWithoutId() { @@ -458,9 +448,6 @@ public function testExtensionInPhar() if (extension_loaded('suhosin') && false === strpos(ini_get('suhosin.executor.include.whitelist'), 'phar')) { $this->markTestSkipped('To run this test, add "phar" to the "suhosin.executor.include.whitelist" settings in your php.ini file.'); } - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM makes this test conflict with those run in separate processes.'); - } require_once self::$fixturesPath.'/includes/ProjectWithXsdExtensionInPhar.phar'; @@ -590,18 +577,6 @@ public function testLoadInlinedServices() $this->assertSame('configureBar', $barConfigurator[1]); } - /** - * @group legacy - */ - public function testType() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services22.xml'); - - $this->assertEquals(array('Bar', 'Baz'), $container->getDefinition('foo')->getAutowiringTypes()); - } - public function testAutowire() { $container = new ContainerBuilder(); @@ -642,17 +617,15 @@ public function testPrototype() } /** - * @group legacy - * @expectedDeprecation Using the attribute "class" is deprecated for the service "bar" which is defined as an alias %s. - * @expectedDeprecation Using the element "tag" is deprecated for the service "bar" which is defined as an alias %s. - * @expectedDeprecation Using the element "factory" is deprecated for the service "bar" which is defined as an alias %s. + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid attribute "class" defined for alias "bar" in */ public function testAliasDefinitionContainsUnsupportedElements() { $container = new ContainerBuilder(); $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('legacy_invalid_alias_definition.xml'); + $loader->load('invalid_alias_definition.xml'); $this->assertTrue($container->has('bar')); } @@ -774,6 +747,21 @@ public function testAutoConfigureInstanceof() $this->assertFalse($container->getDefinition('override_defaults_settings_to_false')->isAutoconfigured()); } + public function testCaseSensitivity() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_case.xml'); + $container->compile(); + + $this->assertTrue($container->has('bar')); + $this->assertTrue($container->has('BAR')); + $this->assertFalse($container->has('baR')); + $this->assertNotSame($container->get('BAR'), $container->get('bar')); + $this->assertSame($container->get('BAR')->arguments->bar, $container->get('bar')); + $this->assertSame($container->get('BAR')->bar, $container->get('bar')); + } + public function testBindings() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 2812033bdf068..df625bb0131b1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -320,37 +320,6 @@ public function testTagWithNonStringNameThrowsException() $loader->load('tag_name_no_string.yml'); } - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testTypesNotArray() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('bad_types1.yml'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testTypeNotString() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('bad_types2.yml'); - } - - /** - * @group legacy - */ - public function testTypes() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services22.yml'); - - $this->assertEquals(array('Foo', 'Bar'), $container->getDefinition('foo_service')->getAutowiringTypes()); - $this->assertEquals(array('Foo'), $container->getDefinition('baz_service')->getAutowiringTypes()); - } - public function testParsesIteratorArgument() { $container = new ContainerBuilder(); @@ -562,8 +531,8 @@ public function testInvalidTagsWithDefaults() } /** - * @group legacy - * @expectedDeprecation Service names that start with an underscore are deprecated since Symfony 3.3 and will be reserved in 4.0. Rename the "_foo" service or define it in XML instead. + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Service names that start with an underscore are reserved. Rename the "_foo" service or define it in XML instead. */ public function testUnderscoreServiceId() { @@ -699,6 +668,43 @@ public function testEmptyInstanceofThrowsClearException() $loader->load('bad_empty_instanceof.yml'); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /^The configuration key "private" is unsupported for definition "bar"/ + */ + public function testUnsupportedKeywordThrowsException() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_keyword.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /^The configuration key "calls" is unsupported for the service "bar" which is defined as an alias/ + */ + public function testUnsupportedKeywordInServiceAliasThrowsException() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_alias.yml'); + } + + public function testCaseSensitivity() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_case.yml'); + $container->compile(); + + $this->assertTrue($container->has('bar')); + $this->assertTrue($container->has('BAR')); + $this->assertFalse($container->has('baR')); + $this->assertNotSame($container->get('BAR'), $container->get('bar')); + $this->assertSame($container->get('BAR')->arguments->bar, $container->get('bar')); + $this->assertSame($container->get('BAR')->bar, $container->get('bar')); + } + public function testBindings() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ContainerBagTest.php b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ContainerBagTest.php new file mode 100644 index 0000000000000..a5e358dd1f213 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ContainerBagTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class ContainerBagTest extends TestCase +{ + /** @var ParameterBag */ + private $parameterBag; + /** @var ContainerBag */ + private $containerBag; + + public function setUp() + { + $this->parameterBag = new ParameterBag(array('foo' => 'value')); + $this->containerBag = new ContainerBag(new Container($this->parameterBag)); + } + + public function testGetAllParameters() + { + $this->assertSame(array('foo' => 'value'), $this->containerBag->all()); + } + + public function testHasAParameter() + { + $this->assertTrue($this->containerBag->has('foo')); + $this->assertFalse($this->containerBag->has('bar')); + } + + public function testGetParameter() + { + $this->assertSame('value', $this->containerBag->get('foo')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testGetParameterNotFound() + { + $this->containerBag->get('bar'); + } + + public function testInstanceOf() + { + $this->assertInstanceOf(FrozenParameterBag::class, $this->containerBag); + $this->assertInstanceOf(ContainerBagInterface::class, $this->containerBag); + $this->assertInstanceOf(ContainerInterface::class, $this->containerBag); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php index c139f0a134473..c53decf8f017d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php @@ -106,28 +106,20 @@ public function testHas() $this->assertFalse($bag->has('bar'), '->has() returns false if a parameter is not defined'); } - /** - * @group legacy - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "BAR" instead of "bar" is deprecated since Symfony 3.4. - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "Foo" instead of "foo" is deprecated since Symfony 3.4. - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "foo" is deprecated since Symfony 3.4. - * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "Foo" instead of "foo" is deprecated since Symfony 3.4. - */ public function testMixedCase() { $bag = new ParameterBag(array( 'foo' => 'foo', 'bar' => 'bar', + 'BAR' => 'baz', )); $bag->remove('BAR'); - $this->assertEquals(array('foo' => 'foo'), $bag->all(), '->remove() converts key to lowercase before removing'); + $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar'), $bag->all()); $bag->set('Foo', 'baz1'); - $this->assertEquals('baz1', $bag->get('foo'), '->set() converts the key to lowercase'); - $this->assertEquals('baz1', $bag->get('FOO'), '->get() converts the key to lowercase'); - - $this->assertTrue($bag->has('Foo'), '->has() converts the key to lowercase'); + $this->assertEquals('foo', $bag->get('foo')); + $this->assertEquals('baz1', $bag->get('Foo')); } public function testResolveValue() diff --git a/src/Symfony/Component/DependencyInjection/TypedReference.php b/src/Symfony/Component/DependencyInjection/TypedReference.php index aad78e806b6b6..dc869ec9b101e 100644 --- a/src/Symfony/Component/DependencyInjection/TypedReference.php +++ b/src/Symfony/Component/DependencyInjection/TypedReference.php @@ -27,7 +27,7 @@ class TypedReference extends Reference * @param string $requiringClass The class of the service that requires the referenced type * @param int $invalidBehavior The behavior when the service does not exist */ - public function __construct($id, $type, $requiringClass = '', $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + public function __construct(string $id, string $type, string $requiringClass = '', int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { parent::__construct($id, $invalidBehavior); $this->type = $type; diff --git a/src/Symfony/Component/DependencyInjection/Variable.php b/src/Symfony/Component/DependencyInjection/Variable.php index 9654ee4ddc6d8..95e28d60ed302 100644 --- a/src/Symfony/Component/DependencyInjection/Variable.php +++ b/src/Symfony/Component/DependencyInjection/Variable.php @@ -28,10 +28,7 @@ class Variable { private $name; - /** - * @param string $name - */ - public function __construct($name) + public function __construct(string $name) { $this->name = $name; } diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index 6eaa3fd089cff..d4937fb485599 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -16,13 +16,13 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "psr/container": "^1.0" }, "require-dev": { "symfony/yaml": "~3.4|~4.0", - "symfony/config": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0" + "symfony/config": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0" }, "suggest": { "symfony/yaml": "", @@ -32,8 +32,8 @@ "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" }, "conflict": { - "symfony/config": "<3.3.7", - "symfony/finder": "<3.3", + "symfony/config": "<3.4", + "symfony/finder": "<3.4", "symfony/proxy-manager-bridge": "<3.4", "symfony/yaml": "<3.4" }, @@ -49,7 +49,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/DomCrawler/AbstractUriElement.php b/src/Symfony/Component/DomCrawler/AbstractUriElement.php index d602d6f3316bf..b477c7142269f 100644 --- a/src/Symfony/Component/DomCrawler/AbstractUriElement.php +++ b/src/Symfony/Component/DomCrawler/AbstractUriElement.php @@ -36,11 +36,11 @@ abstract class AbstractUriElement /** * @param \DOMElement $node A \DOMElement instance * @param string $currentUri The URI of the page where the link is embedded (or the base href) - * @param string $method The method to use for the link (get by default) + * @param string $method The method to use for the link (GET by default) * * @throws \InvalidArgumentException if the node is not a link */ - public function __construct(\DOMElement $node, $currentUri, $method = 'GET') + public function __construct(\DOMElement $node, string $currentUri, ?string $method = 'GET') { if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) { throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri)); @@ -168,24 +168,16 @@ abstract protected function setNode(\DOMElement $node); /** * Removes the query string and the anchor from the given uri. - * - * @param string $uri The uri to clean - * - * @return string */ - private function cleanupUri($uri) + private function cleanupUri(string $uri): string { return $this->cleanupQuery($this->cleanupAnchor($uri)); } /** * Remove the query string from the uri. - * - * @param string $uri - * - * @return string */ - private function cleanupQuery($uri) + private function cleanupQuery(string $uri): string { if (false !== $pos = strpos($uri, '?')) { return substr($uri, 0, $pos); @@ -196,12 +188,8 @@ private function cleanupQuery($uri) /** * Remove the anchor from the uri. - * - * @param string $uri - * - * @return string */ - private function cleanupAnchor($uri) + private function cleanupAnchor(string $uri): string { if (false !== $pos = strpos($uri, '#')) { return substr($uri, 0, $pos); diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index d96b4bb1d5adf..c71af316c02e1 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -59,7 +59,7 @@ class Crawler implements \Countable, \IteratorAggregate * @param string $uri The current URI * @param string $baseHref The base href value */ - public function __construct($node = null, $uri = null, $baseHref = null) + public function __construct($node = null, string $uri = null, string $baseHref = null) { $this->uri = $uri; $this->baseHref = $baseHref ?: $uri; @@ -958,12 +958,8 @@ private function filterRelativeXPath($xpath) * * The returned XPath will match elements matching the XPath inside the current crawler * when running in the context of a node of the crawler. - * - * @param string $xpath - * - * @return string */ - private function relativize($xpath) + private function relativize(string $xpath): string { $expressions = array(); @@ -1095,14 +1091,9 @@ protected function sibling($node, $siblingDir = 'nextSibling') } /** - * @param \DOMDocument $document - * @param array $prefixes - * - * @return \DOMXPath - * * @throws \InvalidArgumentException */ - private function createDOMXPath(\DOMDocument $document, array $prefixes = array()) + private function createDOMXPath(\DOMDocument $document, array $prefixes = array()): \DOMXPath { $domxpath = new \DOMXPath($document); @@ -1117,14 +1108,9 @@ private function createDOMXPath(\DOMDocument $document, array $prefixes = array( } /** - * @param \DOMXPath $domxpath - * @param string $prefix - * - * @return string - * * @throws \InvalidArgumentException */ - private function discoverNamespace(\DOMXPath $domxpath, $prefix) + private function discoverNamespace(\DOMXPath $domxpath, string $prefix): ?string { if (isset($this->namespaces[$prefix])) { return $this->namespaces[$prefix]; @@ -1136,14 +1122,11 @@ private function discoverNamespace(\DOMXPath $domxpath, $prefix) if ($node = $namespaces->item(0)) { return $node->nodeValue; } + + return null; } - /** - * @param string $xpath - * - * @return array - */ - private function findNamespacePrefixes($xpath) + private function findNamespacePrefixes(string $xpath): array { if (preg_match_all('/(?P[a-z_][a-z_0-9\-\.]*+):[^"\/:]/i', $xpath, $matches)) { return array_unique($matches['prefix']); diff --git a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php index c479daa75ee78..6d321e0282f6c 100644 --- a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php +++ b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -254,12 +254,8 @@ protected function initialize() /** * Returns option value with associated disabled flag. - * - * @param \DOMElement $node - * - * @return array */ - private function buildOptionValue(\DOMElement $node) + private function buildOptionValue(\DOMElement $node): array { $option = array(); diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php index 8eaae3e3891e5..473e3919d6a29 100644 --- a/src/Symfony/Component/DomCrawler/Form.php +++ b/src/Symfony/Component/DomCrawler/Form.php @@ -44,7 +44,7 @@ class Form extends Link implements \ArrayAccess * * @throws \LogicException if the node is not a button inside a form tag */ - public function __construct(\DOMElement $node, $currentUri, $method = null, $baseHref = null) + public function __construct(\DOMElement $node, string $currentUri, string $method = null, string $baseHref = null) { parent::__construct($node, $currentUri, $method); $this->baseHref = $baseHref; diff --git a/src/Symfony/Component/DomCrawler/Image.php b/src/Symfony/Component/DomCrawler/Image.php index 4d6403258057c..fb83eaac1989d 100644 --- a/src/Symfony/Component/DomCrawler/Image.php +++ b/src/Symfony/Component/DomCrawler/Image.php @@ -16,7 +16,7 @@ */ class Image extends AbstractUriElement { - public function __construct(\DOMElement $node, $currentUri) + public function __construct(\DOMElement $node, string $currentUri) { parent::__construct($node, $currentUri, 'GET'); } diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index 63a5d4adb54c0..b16af12d51f6d 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -16,11 +16,11 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~2.8|~3.0|~4.0" + "symfony/css-selector": "~3.4|~4.0" }, "suggest": { "symfony/css-selector": "" @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index 3ae735c7db78b..29e3daab04a8e 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -45,10 +45,11 @@ final class Dotenv * @throws FormatException when a file has a syntax error * @throws PathException when a file does not exist or is not readable */ - public function load($path/*, ...$paths*/) + public function load(string $path, string ...$paths): void { - // func_get_args() to be replaced by a variadic argument for Symfony 4.0 - foreach (func_get_args() as $path) { + array_unshift($paths, $path); + + foreach ($paths as $path) { if (!is_readable($path) || is_dir($path)) { throw new PathException($path); } @@ -64,7 +65,7 @@ public function load($path/*, ...$paths*/) * * @param array $values An array of env variables */ - public function populate($values) + public function populate(array $values): void { $loadedVars = array_flip(explode(',', getenv('SYMFONY_DOTENV_VARS'))); unset($loadedVars['']); @@ -103,7 +104,7 @@ public function populate($values) * * @throws FormatException when a file has a syntax error */ - public function parse($data, $path = '.env') + public function parse(string $data, string $path = '.env'): array { $this->path = $path; $this->data = str_replace(array("\r\n", "\r"), "\n", $data); diff --git a/src/Symfony/Component/Dotenv/Exception/FormatException.php b/src/Symfony/Component/Dotenv/Exception/FormatException.php index 26f1442b695a5..f845d7bde1e88 100644 --- a/src/Symfony/Component/Dotenv/Exception/FormatException.php +++ b/src/Symfony/Component/Dotenv/Exception/FormatException.php @@ -20,7 +20,7 @@ final class FormatException extends \LogicException implements ExceptionInterfac { private $context; - public function __construct($message, FormatExceptionContext $context, $code = 0, \Exception $previous = null) + public function __construct(string $message, FormatExceptionContext $context, int $code = 0, \Exception $previous = null) { $this->context = $context; diff --git a/src/Symfony/Component/Dotenv/Exception/FormatExceptionContext.php b/src/Symfony/Component/Dotenv/Exception/FormatExceptionContext.php index 70d2ef11df3fb..995477cf898be 100644 --- a/src/Symfony/Component/Dotenv/Exception/FormatExceptionContext.php +++ b/src/Symfony/Component/Dotenv/Exception/FormatExceptionContext.php @@ -21,7 +21,7 @@ final class FormatExceptionContext private $lineno; private $cursor; - public function __construct($data, $path, $lineno, $cursor) + public function __construct(string $data, string $path, int $lineno, int $cursor) { $this->data = $data; $this->path = $path; diff --git a/src/Symfony/Component/Dotenv/Exception/PathException.php b/src/Symfony/Component/Dotenv/Exception/PathException.php index ac4d540ff4831..6a82e94100ec1 100644 --- a/src/Symfony/Component/Dotenv/Exception/PathException.php +++ b/src/Symfony/Component/Dotenv/Exception/PathException.php @@ -18,7 +18,7 @@ */ final class PathException extends \RuntimeException implements ExceptionInterface { - public function __construct($path, $code = 0, \Exception $previous = null) + public function __construct(string $path, int $code = 0, \Exception $previous = null) { parent::__construct(sprintf('Unable to read the "%s" environment file.', $path), $code, $previous); } diff --git a/src/Symfony/Component/Dotenv/composer.json b/src/Symfony/Component/Dotenv/composer.json index 3bcfd89c8d350..90ffb8f5f5ca7 100644 --- a/src/Symfony/Component/Dotenv/composer.json +++ b/src/Symfony/Component/Dotenv/composer.json @@ -16,10 +16,10 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "require-dev": { - "symfony/process": "~3.2|~4.0" + "symfony/process": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Dotenv\\": "" }, @@ -30,7 +30,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/EventDispatcher/CHANGELOG.md b/src/Symfony/Component/EventDispatcher/CHANGELOG.md index c6aa5389ac724..055d9d096ceb0 100644 --- a/src/Symfony/Component/EventDispatcher/CHANGELOG.md +++ b/src/Symfony/Component/EventDispatcher/CHANGELOG.md @@ -1,6 +1,19 @@ CHANGELOG ========= +4.1.0 +----- + + * added support for invokable event listeners tagged with `kernel.event_listener` by default + * The `TraceableEventDispatcher::getOrphanedEvents()` method has been added. + * The `TraceableEventDispatcherInterface` has been deprecated and will be removed in 5.0. + +4.0.0 +----- + + * removed the `ContainerAwareEventDispatcher` class + * added the `reset()` method to the `TraceableEventDispatcherInterface` + 3.4.0 ----- diff --git a/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php deleted file mode 100644 index 81a1ff7caf7b2..0000000000000 --- a/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php +++ /dev/null @@ -1,197 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -use Symfony\Component\DependencyInjection\ContainerInterface; - -/** - * Lazily loads listeners and subscribers from the dependency injection - * container. - * - * @author Fabien Potencier - * @author Bernhard Schussek - * @author Jordan Alliot - * - * @deprecated since 3.3, to be removed in 4.0. Use EventDispatcher with closure factories instead. - */ -class ContainerAwareEventDispatcher extends EventDispatcher -{ - private $container; - - /** - * The service IDs of the event listeners and subscribers. - */ - private $listenerIds = array(); - - /** - * The services registered as listeners. - */ - private $listeners = array(); - - public function __construct(ContainerInterface $container) - { - $this->container = $container; - - $class = get_class($this); - if ($this instanceof \PHPUnit_Framework_MockObject_MockObject || $this instanceof \Prophecy\Doubler\DoubleInterface) { - $class = get_parent_class($class); - } - if (__CLASS__ !== $class) { - @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED); - } - } - - /** - * Adds a service as event listener. - * - * @param string $eventName Event for which the listener is added - * @param array $callback The service ID of the listener service & the method - * name that has to be called - * @param int $priority The higher this value, the earlier an event listener - * will be triggered in the chain. - * Defaults to 0. - * - * @throws \InvalidArgumentException - */ - public function addListenerService($eventName, $callback, $priority = 0) - { - @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED); - - if (!is_array($callback) || 2 !== count($callback)) { - throw new \InvalidArgumentException('Expected an array("service", "method") argument'); - } - - $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); - } - - public function removeListener($eventName, $listener) - { - $this->lazyLoad($eventName); - - if (isset($this->listenerIds[$eventName])) { - foreach ($this->listenerIds[$eventName] as $i => list($serviceId, $method)) { - $key = $serviceId.'.'.$method; - if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) { - unset($this->listeners[$eventName][$key]); - if (empty($this->listeners[$eventName])) { - unset($this->listeners[$eventName]); - } - unset($this->listenerIds[$eventName][$i]); - if (empty($this->listenerIds[$eventName])) { - unset($this->listenerIds[$eventName]); - } - } - } - } - - parent::removeListener($eventName, $listener); - } - - /** - * {@inheritdoc} - */ - public function hasListeners($eventName = null) - { - if (null === $eventName) { - return $this->listenerIds || $this->listeners || parent::hasListeners(); - } - - if (isset($this->listenerIds[$eventName])) { - return true; - } - - return parent::hasListeners($eventName); - } - - /** - * {@inheritdoc} - */ - public function getListeners($eventName = null) - { - if (null === $eventName) { - foreach ($this->listenerIds as $serviceEventName => $args) { - $this->lazyLoad($serviceEventName); - } - } else { - $this->lazyLoad($eventName); - } - - return parent::getListeners($eventName); - } - - /** - * {@inheritdoc} - */ - public function getListenerPriority($eventName, $listener) - { - $this->lazyLoad($eventName); - - return parent::getListenerPriority($eventName, $listener); - } - - /** - * Adds a service as event subscriber. - * - * @param string $serviceId The service ID of the subscriber service - * @param string $class The service's class name (which must implement EventSubscriberInterface) - */ - public function addSubscriberService($serviceId, $class) - { - @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED); - - foreach ($class::getSubscribedEvents() as $eventName => $params) { - if (is_string($params)) { - $this->listenerIds[$eventName][] = array($serviceId, $params, 0); - } elseif (is_string($params[0])) { - $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); - } else { - foreach ($params as $listener) { - $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); - } - } - } - } - - public function getContainer() - { - @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 as its class will be removed in 4.0. Inject the container or the services you need in your listeners/subscribers instead.', E_USER_DEPRECATED); - - return $this->container; - } - - /** - * Lazily loads listeners for this event from the dependency injection - * container. - * - * @param string $eventName The name of the event to dispatch. The name of - * the event is the name of the method that is - * invoked on listeners. - */ - protected function lazyLoad($eventName) - { - if (isset($this->listenerIds[$eventName])) { - foreach ($this->listenerIds[$eventName] as list($serviceId, $method, $priority)) { - $listener = $this->container->get($serviceId); - - $key = $serviceId.'.'.$method; - if (!isset($this->listeners[$eventName][$key])) { - $this->addListener($eventName, array($listener, $method), $priority); - } elseif ($this->listeners[$eventName][$key] !== $listener) { - parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); - $this->addListener($eventName, array($listener, $method), $priority); - } - - $this->listeners[$eventName][$key] = $listener; - } - } - } -} diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 9b5c689ad7137..7860e3b6d950b 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -32,6 +32,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface private $called; private $dispatcher; private $wrappedListeners; + private $orphanedEvents; public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) { @@ -40,6 +41,7 @@ public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $sto $this->logger = $logger; $this->called = array(); $this->wrappedListeners = array(); + $this->orphanedEvents = array(); } /** @@ -207,6 +209,16 @@ public function getNotCalledListeners() return $notCalled; } + /** + * Gets the orphaned events. + * + * @return array An array of orphaned events + */ + public function getOrphanedEvents() + { + return $this->orphanedEvents; + } + public function reset() { $this->called = array(); @@ -247,6 +259,12 @@ protected function postDispatch($eventName, Event $event) private function preProcess($eventName) { + if (!$this->dispatcher->hasListeners($eventName)) { + $this->orphanedEvents[] = $eventName; + + return; + } + foreach ($this->dispatcher->getListeners($eventName) as $listener) { $priority = $this->getListenerPriority($eventName, $listener); $wrappedListener = new WrappedListener($listener, null, $this->stopwatch, $this); diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php index f0212753be591..c76918d0cdb7c 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php @@ -14,9 +14,9 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** - * @author Fabien Potencier + * @deprecated since version 4.1, will be removed in 5.0. * - * @method reset() Resets the trace. + * @author Fabien Potencier */ interface TraceableEventDispatcherInterface extends EventDispatcherInterface { @@ -33,4 +33,9 @@ public function getCalledListeners(); * @return array An array of not called listeners */ public function getNotCalledListeners(); + + /** + * Resets the trace. + */ + public function reset(); } diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php index ff636d6180bfe..17a89e7f5782f 100644 --- a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php +++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -31,12 +31,7 @@ class RegisterListenersPass implements CompilerPassInterface private $hotPathEvents = array(); private $hotPathTagName; - /** - * @param string $dispatcherService Service name of the event dispatcher in processed container - * @param string $listenerTag Tag name used for listener - * @param string $subscriberTag Tag name used for subscribers - */ - public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + public function __construct(string $dispatcherService = 'event_dispatcher', string $listenerTag = 'kernel.event_listener', string $subscriberTag = 'kernel.event_subscriber') { $this->dispatcherService = $dispatcherService; $this->listenerTag = $listenerTag; @@ -73,6 +68,10 @@ public function process(ContainerBuilder $container) '/[^a-z0-9]/i', ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + + if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) { + $event['method'] = '__invoke'; + } } $definition->addMethodCall('addListener', array($event['event'], array(new ServiceClosureArgument(new Reference($id)), $event['method']), $priority)); diff --git a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php deleted file mode 100644 index 9d5eecc547bd4..0000000000000 --- a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php +++ /dev/null @@ -1,210 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * @group legacy - */ -class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest -{ - protected function createEventDispatcher() - { - $container = new Container(); - - return new ContainerAwareEventDispatcher($container); - } - - public function testAddAListenerService() - { - $event = new Event(); - - $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->dispatch('onEvent', $event); - } - - public function testAddASubscriberService() - { - $event = new Event(); - - $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\SubscriberService')->getMock(); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $service - ->expects($this->once()) - ->method('onEventWithPriority') - ->with($event) - ; - - $service - ->expects($this->once()) - ->method('onEventNested') - ->with($event) - ; - - $container = new Container(); - $container->set('service.subscriber', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); - - $dispatcher->dispatch('onEvent', $event); - $dispatcher->dispatch('onEventWithPriority', $event); - $dispatcher->dispatch('onEventNested', $event); - } - - public function testPreventDuplicateListenerService() - { - $event = new Event(); - - $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); - - $dispatcher->dispatch('onEvent', $event); - } - - public function testHasListenersOnLazyLoad() - { - $event = new Event(); - - $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $this->assertTrue($dispatcher->hasListeners()); - - if ($dispatcher->hasListeners('onEvent')) { - $dispatcher->dispatch('onEvent'); - } - } - - public function testGetListenersOnLazyLoad() - { - $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $listeners = $dispatcher->getListeners(); - - $this->assertArrayHasKey('onEvent', $listeners); - - $this->assertCount(1, $dispatcher->getListeners('onEvent')); - } - - public function testRemoveAfterDispatch() - { - $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->dispatch('onEvent', new Event()); - $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); - $this->assertFalse($dispatcher->hasListeners('onEvent')); - } - - public function testRemoveBeforeDispatch() - { - $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); - $this->assertFalse($dispatcher->hasListeners('onEvent')); - } -} - -class Service -{ - public function onEvent(Event $e) - { - } -} - -class SubscriberService implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array( - 'onEvent' => 'onEvent', - 'onEventWithPriority' => array('onEventWithPriority', 10), - 'onEventNested' => array(array('onEventNested')), - ); - } - - public function onEvent(Event $e) - { - } - - public function onEventWithPriority(Event $e) - { - } - - public function onEventNested(Event $e) - { - } -} diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php index 53a3421afaf6a..2521f741ea1fc 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -153,6 +153,31 @@ public function testGetCalledListenersNested() $this->assertCount(2, $dispatcher->getCalledListeners()); } + public function testItReturnsNoOrphanedEventsWhenCreated() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertEmpty($events); + } + + public function testItReturnsOrphanedEventsAfterDispatch() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->dispatch('foo'); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertCount(1, $events); + $this->assertEquals(array('foo'), $events); + } + + public function testItDoesNotReturnHandledEvents() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->addListener('foo', function () {}); + $tdispatcher->dispatch('foo'); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertEmpty($events); + } + public function testLogger() { $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); diff --git a/src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php index dbb1aa5c57b57..19e19f83dbf24 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php @@ -166,6 +166,47 @@ public function testEventSubscriberUnresolvableClassName() $registerListenersPass = new RegisterListenersPass(); $registerListenersPass->process($container); } + + public function testInvokableEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', \stdClass::class)->addTag('kernel.event_listener', array('event' => 'foo.bar')); + $container->register('bar', InvokableListenerService::class)->addTag('kernel.event_listener', array('event' => 'foo.bar')); + $container->register('baz', InvokableListenerService::class)->addTag('kernel.event_listener', array('event' => 'event')); + $container->register('event_dispatcher', \stdClass::class); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + + $definition = $container->getDefinition('event_dispatcher'); + $expectedCalls = array( + array( + 'addListener', + array( + 'foo.bar', + array(new ServiceClosureArgument(new Reference('foo')), 'onFooBar'), + 0, + ), + ), + array( + 'addListener', + array( + 'foo.bar', + array(new ServiceClosureArgument(new Reference('bar')), '__invoke'), + 0, + ), + ), + array( + 'addListener', + array( + 'event', + array(new ServiceClosureArgument(new Reference('baz')), 'onEvent'), + 0, + ), + ), + ); + $this->assertEquals($expectedCalls, $definition->getMethodCalls()); + } } class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface @@ -177,3 +218,14 @@ public static function getSubscribedEvents() ); } } + +class InvokableListenerService +{ + public function __invoke() + { + } + + public function onEvent() + { + } +} diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index 75b881b917cd9..12ee53270edb9 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -16,17 +16,17 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "require-dev": { - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", "psr/log": "~1.0" }, "conflict": { - "symfony/dependency-injection": "<3.3" + "symfony/dependency-injection": "<3.4" }, "suggest": { "symfony/dependency-injection": "", @@ -41,7 +41,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md b/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md index d00d17c776dc2..6c50b2ea424df 100644 --- a/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md +++ b/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +4.0.0 +----- + + * the first argument of the `ExpressionLanguage` constructor must be an instance + of `CacheItemPoolInterface` + * removed the `ArrayParserCache` and `ParserCacheAdapter` classes + * removed the `ParserCacheInterface` + 2.6.0 ----- diff --git a/src/Symfony/Component/ExpressionLanguage/Expression.php b/src/Symfony/Component/ExpressionLanguage/Expression.php index ac656cce033bf..59d0e2a6a524d 100644 --- a/src/Symfony/Component/ExpressionLanguage/Expression.php +++ b/src/Symfony/Component/ExpressionLanguage/Expression.php @@ -20,12 +20,9 @@ class Expression { protected $expression; - /** - * @param string $expression An expression - */ - public function __construct($expression) + public function __construct(string $expression) { - $this->expression = (string) $expression; + $this->expression = $expression; } /** diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php index ad775dbd9472c..448cd466f095e 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php @@ -39,7 +39,7 @@ class ExpressionFunction * @param callable $compiler A callable able to compile the function * @param callable $evaluator A callable able to evaluate the function */ - public function __construct($name, callable $compiler, callable $evaluator) + public function __construct(string $name, callable $compiler, callable $evaluator) { $this->name = $name; $this->compiler = $compiler; diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php index a9cfc4caf7688..703d393715598 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php @@ -13,8 +13,6 @@ use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; -use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheAdapter; -use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; /** * Allows to compile and evaluate expressions written in your own DSL. @@ -34,17 +32,8 @@ class ExpressionLanguage * @param CacheItemPoolInterface $cache * @param ExpressionFunctionProviderInterface[] $providers */ - public function __construct($cache = null, array $providers = array()) + public function __construct(CacheItemPoolInterface $cache = null, array $providers = array()) { - if (null !== $cache) { - if ($cache instanceof ParserCacheInterface) { - @trigger_error(sprintf('Passing an instance of %s as constructor argument for %s is deprecated as of 3.2 and will be removed in 4.0. Pass an instance of %s instead.', ParserCacheInterface::class, self::class, CacheItemPoolInterface::class), E_USER_DEPRECATED); - $cache = new ParserCacheAdapter($cache); - } elseif (!$cache instanceof CacheItemPoolInterface) { - throw new \InvalidArgumentException(sprintf('Cache argument has to implement %s.', CacheItemPoolInterface::class)); - } - } - $this->cache = $cache ?: new ArrayAdapter(); $this->registerFunctions(); foreach ($providers as $provider) { @@ -149,7 +138,7 @@ protected function registerFunctions() $this->addFunction(ExpressionFunction::fromPhp('constant')); } - private function getLexer() + private function getLexer(): Lexer { if (null === $this->lexer) { $this->lexer = new Lexer(); @@ -158,7 +147,7 @@ private function getLexer() return $this->lexer; } - private function getParser() + private function getParser(): Parser { if (null === $this->parser) { $this->parser = new Parser($this->functions); @@ -167,7 +156,7 @@ private function getParser() return $this->parser; } - private function getCompiler() + private function getCompiler(): Compiler { if (null === $this->compiler) { $this->compiler = new Compiler($this->functions); diff --git a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php index 33b4c8f089ade..26b456145c1c3 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php @@ -33,7 +33,7 @@ class BinaryNode extends Node 'not in' => '!in_array', ); - public function __construct($operator, Node $left, Node $right) + public function __construct(string $operator, Node $left, Node $right) { parent::__construct( array('left' => $left, 'right' => $right), diff --git a/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php b/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php index 733d481b28183..d1bd7c69e8d4d 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php @@ -22,7 +22,7 @@ class ConstantNode extends Node { private $isIdentifier; - public function __construct($value, $isIdentifier = false) + public function __construct($value, bool $isIdentifier = false) { $this->isIdentifier = $isIdentifier; parent::__construct( diff --git a/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php b/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php index 13928c8d4f830..e3424b50d4ba2 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php @@ -20,7 +20,7 @@ */ class FunctionNode extends Node { - public function __construct($name, Node $arguments) + public function __construct(string $name, Node $arguments) { parent::__construct( array('arguments' => $arguments), diff --git a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php index 2ecba97f7f6ad..bafd227b48eda 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php @@ -24,7 +24,7 @@ class GetAttrNode extends Node const METHOD_CALL = 2; const ARRAY_CALL = 3; - public function __construct(Node $node, Node $attribute, ArrayNode $arguments, $type) + public function __construct(Node $node, Node $attribute, ArrayNode $arguments, int $type) { parent::__construct( array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), diff --git a/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php b/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php index 9e1462f2c6798..c084bd4793c50 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php @@ -20,7 +20,7 @@ */ class NameNode extends Node { - public function __construct($name) + public function __construct(string $name) { parent::__construct( array(), diff --git a/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php b/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php index 583103217a626..4a18e83a5d0b9 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php @@ -27,7 +27,7 @@ class UnaryNode extends Node '-' => '-', ); - public function __construct($operator, Node $node) + public function __construct(string $operator, Node $node) { parent::__construct( array('node' => $node), @@ -59,7 +59,7 @@ public function evaluate($functions, $values) return $value; } - public function toArray() + public function toArray(): array { return array('(', $this->attributes['operator'].' ', $this->nodes['node'], ')'); } diff --git a/src/Symfony/Component/ExpressionLanguage/ParsedExpression.php b/src/Symfony/Component/ExpressionLanguage/ParsedExpression.php index a5603fc3ca96d..2e2bf374e31a2 100644 --- a/src/Symfony/Component/ExpressionLanguage/ParsedExpression.php +++ b/src/Symfony/Component/ExpressionLanguage/ParsedExpression.php @@ -26,7 +26,7 @@ class ParsedExpression extends Expression * @param string $expression An expression * @param Node $nodes A Node representing the expression */ - public function __construct($expression, Node $nodes) + public function __construct(string $expression, Node $nodes) { parent::__construct($expression); diff --git a/src/Symfony/Component/ExpressionLanguage/ParserCache/ArrayParserCache.php b/src/Symfony/Component/ExpressionLanguage/ParserCache/ArrayParserCache.php deleted file mode 100644 index 124962505dfb8..0000000000000 --- a/src/Symfony/Component/ExpressionLanguage/ParserCache/ArrayParserCache.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ExpressionLanguage\ParserCache; - -@trigger_error('The '.__NAMESPACE__.'\ArrayParserCache class is deprecated since Symfony 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\ArrayAdapter class instead.', E_USER_DEPRECATED); - -use Symfony\Component\ExpressionLanguage\ParsedExpression; - -/** - * @author Adrien Brault - * - * @deprecated ArrayParserCache class is deprecated since version 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\ArrayAdapter class instead. - */ -class ArrayParserCache implements ParserCacheInterface -{ - private $cache = array(); - - /** - * {@inheritdoc} - */ - public function fetch($key) - { - return isset($this->cache[$key]) ? $this->cache[$key] : null; - } - - /** - * {@inheritdoc} - */ - public function save($key, ParsedExpression $expression) - { - $this->cache[$key] = $expression; - } -} diff --git a/src/Symfony/Component/ExpressionLanguage/ParserCache/ParserCacheAdapter.php b/src/Symfony/Component/ExpressionLanguage/ParserCache/ParserCacheAdapter.php deleted file mode 100644 index a3e227d0a5ccd..0000000000000 --- a/src/Symfony/Component/ExpressionLanguage/ParserCache/ParserCacheAdapter.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ExpressionLanguage\ParserCache; - -use Psr\Cache\CacheItemInterface; -use Psr\Cache\CacheItemPoolInterface; -use Symfony\Component\Cache\CacheItem; - -/** - * @author Alexandre GESLIN - * - * @internal and will be removed in Symfony 4.0. - */ -class ParserCacheAdapter implements CacheItemPoolInterface -{ - private $pool; - private $createCacheItem; - - public function __construct(ParserCacheInterface $pool) - { - $this->pool = $pool; - - $this->createCacheItem = \Closure::bind( - function ($key, $value, $isHit) { - $item = new CacheItem(); - $item->key = $key; - $item->value = $value; - $item->isHit = $isHit; - - return $item; - }, - null, - CacheItem::class - ); - } - - /** - * {@inheritdoc} - */ - public function getItem($key) - { - $value = $this->pool->fetch($key); - $f = $this->createCacheItem; - - return $f($key, $value, null !== $value); - } - - /** - * {@inheritdoc} - */ - public function save(CacheItemInterface $item) - { - $this->pool->save($item->getKey(), $item->get()); - } - - /** - * {@inheritdoc} - */ - public function getItems(array $keys = array()) - { - throw new \BadMethodCallException('Not implemented'); - } - - /** - * {@inheritdoc} - */ - public function hasItem($key) - { - throw new \BadMethodCallException('Not implemented'); - } - - /** - * {@inheritdoc} - */ - public function clear() - { - throw new \BadMethodCallException('Not implemented'); - } - - /** - * {@inheritdoc} - */ - public function deleteItem($key) - { - throw new \BadMethodCallException('Not implemented'); - } - - /** - * {@inheritdoc} - */ - public function deleteItems(array $keys) - { - throw new \BadMethodCallException('Not implemented'); - } - - /** - * {@inheritdoc} - */ - public function saveDeferred(CacheItemInterface $item) - { - throw new \BadMethodCallException('Not implemented'); - } - - /** - * {@inheritdoc} - */ - public function commit() - { - throw new \BadMethodCallException('Not implemented'); - } -} diff --git a/src/Symfony/Component/ExpressionLanguage/ParserCache/ParserCacheInterface.php b/src/Symfony/Component/ExpressionLanguage/ParserCache/ParserCacheInterface.php deleted file mode 100644 index ed66b21bf2f40..0000000000000 --- a/src/Symfony/Component/ExpressionLanguage/ParserCache/ParserCacheInterface.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ExpressionLanguage\ParserCache; - -@trigger_error('The '.__NAMESPACE__.'\ParserCacheInterface interface is deprecated since Symfony 3.2 and will be removed in 4.0. Use Psr\Cache\CacheItemPoolInterface instead.', E_USER_DEPRECATED); - -use Symfony\Component\ExpressionLanguage\ParsedExpression; - -/** - * @author Adrien Brault - * - * @deprecated since version 3.2, to be removed in 4.0. Use Psr\Cache\CacheItemPoolInterface instead. - */ -interface ParserCacheInterface -{ - /** - * Saves an expression in the cache. - * - * @param string $key The cache key - * @param ParsedExpression $expression A ParsedExpression instance to store in the cache - */ - public function save($key, ParsedExpression $expression); - - /** - * Fetches an expression from the cache. - * - * @param string $key The cache key - * - * @return ParsedExpression|null - */ - public function fetch($key); -} diff --git a/src/Symfony/Component/ExpressionLanguage/SerializedParsedExpression.php b/src/Symfony/Component/ExpressionLanguage/SerializedParsedExpression.php index dd763f75b524d..a1f838c030232 100644 --- a/src/Symfony/Component/ExpressionLanguage/SerializedParsedExpression.php +++ b/src/Symfony/Component/ExpressionLanguage/SerializedParsedExpression.php @@ -24,9 +24,9 @@ class SerializedParsedExpression extends ParsedExpression * @param string $expression An expression * @param string $nodes The serialized nodes for the expression */ - public function __construct($expression, $nodes) + public function __construct(string $expression, string $nodes) { - $this->expression = (string) $expression; + $this->expression = $expression; $this->nodes = $nodes; } diff --git a/src/Symfony/Component/ExpressionLanguage/SyntaxError.php b/src/Symfony/Component/ExpressionLanguage/SyntaxError.php index 12348e6830836..d3313b83219fd 100644 --- a/src/Symfony/Component/ExpressionLanguage/SyntaxError.php +++ b/src/Symfony/Component/ExpressionLanguage/SyntaxError.php @@ -13,7 +13,7 @@ class SyntaxError extends \LogicException { - public function __construct($message, $cursor = 0, $expression = '', $subject = null, array $proposals = null) + public function __construct(string $message, int $cursor = 0, string $expression = '', string $subject = null, array $proposals = null) { $message = sprintf('%s around position %d', $message, $cursor); if ($expression) { diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index 70328c8e59c22..c5aed437209d0 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -63,47 +63,6 @@ public function testCachedParse() $this->assertSame($savedParsedExpression, $parsedExpression); } - /** - * @group legacy - */ - public function testCachedParseWithDeprecatedParserCacheInterface() - { - $cacheMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - - $cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock(); - $savedParsedExpression = null; - $expressionLanguage = new ExpressionLanguage($cacheMock); - - $cacheMock - ->expects($this->exactly(1)) - ->method('fetch') - ->with('1%20%2B%201%2F%2F') - ->willReturn($savedParsedExpression) - ; - - $cacheMock - ->expects($this->exactly(1)) - ->method('save') - ->with('1%20%2B%201%2F%2F', $this->isInstanceOf(ParsedExpression::class)) - ->will($this->returnCallback(function ($key, $expression) use (&$savedParsedExpression) { - $savedParsedExpression = $expression; - })) - ; - - $parsedExpression = $expressionLanguage->parse('1 + 1', array()); - $this->assertSame($savedParsedExpression, $parsedExpression); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Cache argument has to implement Psr\Cache\CacheItemPoolInterface. - */ - public function testWrongCacheImplementation() - { - $cacheMock = $this->getMockBuilder('Psr\Cache\CacheItemSpoolInterface')->getMock(); - $expressionLanguage = new ExpressionLanguage($cacheMock); - } - public function testConstantFunction() { $expressionLanguage = new ExpressionLanguage(); diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ParserCache/ParserCacheAdapterTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ParserCache/ParserCacheAdapterTest.php deleted file mode 100644 index 447316111b840..0000000000000 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ParserCache/ParserCacheAdapterTest.php +++ /dev/null @@ -1,140 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ExpressionLanguage\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\ExpressionLanguage\ParsedExpression; -use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheAdapter; -use Symfony\Component\ExpressionLanguage\Node\Node; - -/** - * @group legacy - */ -class ParserCacheAdapterTest extends TestCase -{ - public function testGetItem() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - - $key = 'key'; - $value = 'value'; - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - - $poolMock - ->expects($this->once()) - ->method('fetch') - ->with($key) - ->willReturn($value) - ; - - $cacheItem = $parserCacheAdapter->getItem($key); - - $this->assertEquals($cacheItem->get(), $value); - $this->assertEquals($cacheItem->isHit(), true); - } - - public function testSave() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock(); - $key = 'key'; - $value = new ParsedExpression('1 + 1', new Node(array(), array())); - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - - $poolMock - ->expects($this->once()) - ->method('save') - ->with($key, $value) - ; - - $cacheItemMock - ->expects($this->once()) - ->method('getKey') - ->willReturn($key) - ; - - $cacheItemMock - ->expects($this->once()) - ->method('get') - ->willReturn($value) - ; - - $cacheItem = $parserCacheAdapter->save($cacheItemMock); - } - - public function testGetItems() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(\BadMethodCallException::class); - - $parserCacheAdapter->getItems(); - } - - public function testHasItem() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $key = 'key'; - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(\BadMethodCallException::class); - - $parserCacheAdapter->hasItem($key); - } - - public function testClear() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(\BadMethodCallException::class); - - $parserCacheAdapter->clear(); - } - - public function testDeleteItem() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $key = 'key'; - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(\BadMethodCallException::class); - - $parserCacheAdapter->deleteItem($key); - } - - public function testDeleteItems() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $keys = array('key'); - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(\BadMethodCallException::class); - - $parserCacheAdapter->deleteItems($keys); - } - - public function testSaveDeferred() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - $cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock(); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(\BadMethodCallException::class); - - $parserCacheAdapter->saveDeferred($cacheItemMock); - } - - public function testCommit() - { - $poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock(); - $parserCacheAdapter = new ParserCacheAdapter($poolMock); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(\BadMethodCallException::class); - - $parserCacheAdapter->commit(); - } -} diff --git a/src/Symfony/Component/ExpressionLanguage/Token.php b/src/Symfony/Component/ExpressionLanguage/Token.php index 4517335bbc739..40bb75587ec96 100644 --- a/src/Symfony/Component/ExpressionLanguage/Token.php +++ b/src/Symfony/Component/ExpressionLanguage/Token.php @@ -34,7 +34,7 @@ class Token * @param string|int|float|null $value The token value * @param int $cursor The cursor position in the source */ - public function __construct($type, $value, $cursor) + public function __construct(string $type, $value, ?int $cursor) { $this->type = $type; $this->value = $value; diff --git a/src/Symfony/Component/ExpressionLanguage/TokenStream.php b/src/Symfony/Component/ExpressionLanguage/TokenStream.php index 9096b183ff378..919add6b90953 100644 --- a/src/Symfony/Component/ExpressionLanguage/TokenStream.php +++ b/src/Symfony/Component/ExpressionLanguage/TokenStream.php @@ -24,11 +24,7 @@ class TokenStream private $position = 0; private $expression; - /** - * @param array $tokens An array of tokens - * @param string $expression - */ - public function __construct(array $tokens, $expression = '') + public function __construct(array $tokens, string $expression = '') { $this->tokens = $tokens; $this->current = $tokens[0]; diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index 5f662ffd38b23..3c453b8f49f12 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -16,8 +16,8 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/cache": "~3.1|~4.0" + "php": "^7.1.3", + "symfony/cache": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" }, @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Filesystem/CHANGELOG.md b/src/Symfony/Component/Filesystem/CHANGELOG.md index d01f5f45e1a7d..9f1f817e753dd 100644 --- a/src/Symfony/Component/Filesystem/CHANGELOG.md +++ b/src/Symfony/Component/Filesystem/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.0.0 +----- + + * removed `LockHandler` + * Support for passing relative paths to `Filesystem::makePathRelative()` has been removed. + 3.4.0 ----- diff --git a/src/Symfony/Component/Filesystem/Exception/FileNotFoundException.php b/src/Symfony/Component/Filesystem/Exception/FileNotFoundException.php index bcc8fe81fcef4..f941b536e7a22 100644 --- a/src/Symfony/Component/Filesystem/Exception/FileNotFoundException.php +++ b/src/Symfony/Component/Filesystem/Exception/FileNotFoundException.php @@ -19,7 +19,7 @@ */ class FileNotFoundException extends IOException { - public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null) + public function __construct(string $message = null, int $code = 0, \Exception $previous = null, string $path = null) { if (null === $message) { if (null === $path) { diff --git a/src/Symfony/Component/Filesystem/Exception/IOException.php b/src/Symfony/Component/Filesystem/Exception/IOException.php index 144e0e602bdfe..f02cbd885cfca 100644 --- a/src/Symfony/Component/Filesystem/Exception/IOException.php +++ b/src/Symfony/Component/Filesystem/Exception/IOException.php @@ -22,7 +22,7 @@ class IOException extends \RuntimeException implements IOExceptionInterface { private $path; - public function __construct($message, $code = 0, \Exception $previous = null, $path = null) + public function __construct(string $message, int $code = 0, \Exception $previous = null, string $path = null) { $this->path = $path; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php b/src/Symfony/Component/Filesystem/Exception/InvalidArgumentException.php similarity index 54% rename from src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php rename to src/Symfony/Component/Filesystem/Exception/InvalidArgumentException.php index 1208003bcc2c4..abadc20029763 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php +++ b/src/Symfony/Component/Filesystem/Exception/InvalidArgumentException.php @@ -9,13 +9,11 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle; - -use Symfony\Component\HttpKernel\Bundle\Bundle; +namespace Symfony\Component\Filesystem\Exception; /** - * @author Kévin Dunglas + * @author Christian Flothmann */ -class AclBundle extends Bundle +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 554bf22694e55..50c8a2b30e575 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Filesystem; +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\FileNotFoundException; @@ -252,7 +253,7 @@ public function chgrp($files, $group, $recursive = false) $this->chgrp(new \FilesystemIterator($file), $group, true); } if (is_link($file) && function_exists('lchgrp')) { - if (true !== @lchgrp($file, $group) || (defined('HHVM_VERSION') && !posix_getgrnam($group))) { + if (true !== @lchgrp($file, $group)) { throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); } } else { @@ -450,8 +451,12 @@ public function readlink($path, $canonicalize = false) */ public function makePathRelative($endPath, $startPath) { - if (!$this->isAbsolutePath($endPath) || !$this->isAbsolutePath($startPath)) { - @trigger_error(sprintf('Support for passing relative paths to %s() is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); + if (!$this->isAbsolutePath($startPath)) { + throw new InvalidArgumentException(sprintf('The start path "%s" is not absolute.', $startPath)); + } + + if (!$this->isAbsolutePath($endPath)) { + throw new InvalidArgumentException(sprintf('The end path "%s" is not absolute.', $endPath)); } // Normalize separators on Windows @@ -475,11 +480,11 @@ public function makePathRelative($endPath, $startPath) $startPathArr = explode('/', trim($startPath, '/')); $endPathArr = explode('/', trim($endPath, '/')); - $normalizePathArray = function ($pathSegments, $absolute) { + $normalizePathArray = function ($pathSegments) { $result = array(); foreach ($pathSegments as $segment) { - if ('..' === $segment && ($absolute || count($result))) { + if ('..' === $segment) { array_pop($result); } elseif ('.' !== $segment) { $result[] = $segment; @@ -489,8 +494,8 @@ public function makePathRelative($endPath, $startPath) return $result; }; - $startPathArr = $normalizePathArray($startPathArr, static::isAbsolutePath($startPath)); - $endPathArr = $normalizePathArray($endPathArr, static::isAbsolutePath($endPath)); + $startPathArr = $normalizePathArray($startPathArr); + $endPathArr = $normalizePathArray($endPathArr); // Find for which directory the common path stops $index = 0; @@ -719,24 +724,15 @@ public function appendToFile($filename, $content) } } - /** - * @param mixed $files - * - * @return array|\Traversable - */ - private function toIterable($files) + private function toIterable($files): iterable { return is_array($files) || $files instanceof \Traversable ? $files : array($files); } /** * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> array(file, tmp)). - * - * @param string $filename The filename to be parsed - * - * @return array The filename scheme and hierarchical part */ - private function getSchemeAndHierarchy($filename) + private function getSchemeAndHierarchy(string $filename): array { $components = explode('://', $filename, 2); diff --git a/src/Symfony/Component/Filesystem/LockHandler.php b/src/Symfony/Component/Filesystem/LockHandler.php deleted file mode 100644 index a2e7ad5c336a5..0000000000000 --- a/src/Symfony/Component/Filesystem/LockHandler.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem; - -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Lock\Store\FlockStore; -use Symfony\Component\Lock\Store\SemaphoreStore; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use %s or %s instead.', LockHandler::class, SemaphoreStore::class, FlockStore::class), E_USER_DEPRECATED); - -/** - * LockHandler class provides a simple abstraction to lock anything by means of - * a file lock. - * - * A locked file is created based on the lock name when calling lock(). Other - * lock handlers will not be able to lock the same name until it is released - * (explicitly by calling release() or implicitly when the instance holding the - * lock is destroyed). - * - * @author Grégoire Pineau - * @author Romain Neutron - * @author Nicolas Grekas - * - * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\Lock\Store\SemaphoreStore or Symfony\Component\Lock\Store\FlockStore instead. - */ -class LockHandler -{ - private $file; - private $handle; - - /** - * @param string $name The lock name - * @param string|null $lockPath The directory to store the lock. Default values will use temporary directory - * - * @throws IOException If the lock directory could not be created or is not writable - */ - public function __construct($name, $lockPath = null) - { - $lockPath = $lockPath ?: sys_get_temp_dir(); - - if (!is_dir($lockPath)) { - $fs = new Filesystem(); - $fs->mkdir($lockPath); - } - - if (!is_writable($lockPath)) { - throw new IOException(sprintf('The directory "%s" is not writable.', $lockPath), 0, null, $lockPath); - } - - $this->file = sprintf('%s/sf.%s.%s.lock', $lockPath, preg_replace('/[^a-z0-9\._-]+/i', '-', $name), hash('sha256', $name)); - } - - /** - * Lock the resource. - * - * @param bool $blocking Wait until the lock is released - * - * @return bool Returns true if the lock was acquired, false otherwise - * - * @throws IOException If the lock file could not be created or opened - */ - public function lock($blocking = false) - { - if ($this->handle) { - return true; - } - - $error = null; - - // Silence error reporting - set_error_handler(function ($errno, $msg) use (&$error) { - $error = $msg; - }); - - if (!$this->handle = fopen($this->file, 'r')) { - if ($this->handle = fopen($this->file, 'x')) { - chmod($this->file, 0444); - } elseif (!$this->handle = fopen($this->file, 'r')) { - usleep(100); // Give some time for chmod() to complete - $this->handle = fopen($this->file, 'r'); - } - } - restore_error_handler(); - - if (!$this->handle) { - throw new IOException($error, 0, null, $this->file); - } - - // On Windows, even if PHP doc says the contrary, LOCK_NB works, see - // https://bugs.php.net/54129 - if (!flock($this->handle, LOCK_EX | ($blocking ? 0 : LOCK_NB))) { - fclose($this->handle); - $this->handle = null; - - return false; - } - - return true; - } - - /** - * Release the resource. - */ - public function release() - { - if ($this->handle) { - flock($this->handle, LOCK_UN | LOCK_NB); - fclose($this->handle); - $this->handle = null; - } - } -} diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 5898fc257b09f..def1e5fc09953 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -449,10 +449,6 @@ public function testChmodWithWrongModLeavesPreviousPermissionsUntouched() { $this->markAsSkippedIfChmodIsMissing(); - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('chmod() changes permissions even when passing invalid modes on HHVM'); - } - $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; touch($dir); @@ -1142,33 +1138,21 @@ public function providePathsForMakePathRelative() } /** - * @group legacy - * @dataProvider provideLegacyPathsForMakePathRelativeWithRelativePaths - * @expectedDeprecation Support for passing relative paths to Symfony\Component\Filesystem\Filesystem::makePathRelative() is deprecated since Symfony 3.4 and will be removed in 4.0. + * @expectedException \Symfony\Component\Filesystem\Exception\InvalidArgumentException + * @expectedExceptionMessage The start path "var/lib/symfony/src/Symfony/Component" is not absolute. */ - public function testMakePathRelativeWithRelativePaths($endPath, $startPath, $expectedPath) + public function testMakePathRelativeWithRelativeStartPath() { - $path = $this->filesystem->makePathRelative($endPath, $startPath); - - $this->assertEquals($expectedPath, $path); + $this->assertSame('../../../', $this->filesystem->makePathRelative('/var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component')); } - public function provideLegacyPathsForMakePathRelativeWithRelativePaths() + /** + * @expectedException \Symfony\Component\Filesystem\Exception\InvalidArgumentException + * @expectedExceptionMessage The end path "var/lib/symfony/" is not absolute. + */ + public function testMakePathRelativeWithRelativeEndPath() { - return array( - array('usr/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), - array('aa/bb', 'aa/cc', '../bb/'), - array('aa/cc', 'bb/cc', '../../aa/cc/'), - array('aa/bb', 'aa/./cc', '../bb/'), - array('aa/./bb', 'aa/cc', '../bb/'), - array('aa/./bb', 'aa/./cc', '../bb/'), - array('../../', '../../', './'), - array('../aa/bb/', 'aa/bb/', '../../../aa/bb/'), - array('../../../', '../../', '../'), - array('', '', './'), - array('', 'aa/', '../'), - array('aa/', '', 'aa/'), - ); + $this->assertSame('../../../', $this->filesystem->makePathRelative('var/lib/symfony/', '/var/lib/symfony/src/Symfony/Component')); } public function testMirrorCopiesFilesAndDirectoriesRecursively() @@ -1496,10 +1480,6 @@ public function testDumpFileOverwritesAnExistingFile() public function testDumpFileWithFileScheme() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM does not handle the file:// scheme correctly'); - } - $scheme = 'file://'; $filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; @@ -1546,10 +1526,6 @@ public function testAppendToFile() public function testAppendToFileWithScheme() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM does not handle the file:// scheme correctly'); - } - $scheme = 'file://'; $filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; $this->filesystem->dumpFile($filename, 'foo'); @@ -1626,12 +1602,8 @@ public function testCopyShouldKeepExecutionPermission() /** * Normalize the given path (transform each blackslash into a real directory separator). - * - * @param string $path - * - * @return string */ - private function normalize($path) + private function normalize(string $path): string { return str_replace('/', DIRECTORY_SEPARATOR, $path); } diff --git a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php b/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php deleted file mode 100644 index e0b2281e0552a..0000000000000 --- a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php +++ /dev/null @@ -1,144 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Filesystem\LockHandler; - -/** - * @group legacy - */ -class LockHandlerTest extends TestCase -{ - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - * @expectedExceptionMessage Failed to create "/a/b/c/d/e": mkdir(): Permission denied. - */ - public function testConstructWhenRepositoryDoesNotExist() - { - if (!getenv('USER') || 'root' === getenv('USER')) { - $this->markTestSkipped('This test will fail if run under superuser'); - } - new LockHandler('lock', '/a/b/c/d/e'); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - * @expectedExceptionMessage The directory "/" is not writable. - */ - public function testConstructWhenRepositoryIsNotWriteable() - { - if (!getenv('USER') || 'root' === getenv('USER')) { - $this->markTestSkipped('This test will fail if run under superuser'); - } - new LockHandler('lock', '/'); - } - - public function testErrorHandlingInLockIfLockPathBecomesUnwritable() - { - // skip test on Windows; PHP can't easily set file as unreadable on Windows - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test cannot run on Windows.'); - } - - $lockPath = sys_get_temp_dir().'/'.uniqid('', true); - $e = null; - $wrongMessage = null; - - try { - mkdir($lockPath); - - $lockHandler = new LockHandler('lock', $lockPath); - - chmod($lockPath, 0444); - - $lockHandler->lock(); - } catch (IOException $e) { - if (false === strpos($e->getMessage(), 'Permission denied')) { - $wrongMessage = $e->getMessage(); - } else { - $this->addToAssertionCount(1); - } - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - - if (is_dir($lockPath)) { - $fs = new Filesystem(); - $fs->remove($lockPath); - } - - $this->assertInstanceOf('Symfony\Component\Filesystem\Exception\IOException', $e, sprintf('Expected IOException to be thrown, got %s instead.', get_class($e))); - $this->assertNull($wrongMessage, sprintf('Expected exception message to contain "Permission denied", got "%s" instead.', $wrongMessage)); - } - - public function testConstructSanitizeName() - { - $lock = new LockHandler(''); - - $file = sprintf('%s/sf.-php-echo-hello-word-.4b3d9d0d27ddef3a78a64685dda3a963e478659a9e5240feaf7b4173a8f28d5f.lock', sys_get_temp_dir()); - // ensure the file does not exist before the lock - @unlink($file); - - $lock->lock(); - - $this->assertFileExists($file); - - $lock->release(); - } - - public function testLockRelease() - { - $name = 'symfony-test-filesystem.lock'; - - $l1 = new LockHandler($name); - $l2 = new LockHandler($name); - - $this->assertTrue($l1->lock()); - $this->assertFalse($l2->lock()); - - $l1->release(); - - $this->assertTrue($l2->lock()); - $l2->release(); - } - - public function testLockTwice() - { - $name = 'symfony-test-filesystem.lock'; - - $lockHandler = new LockHandler($name); - - $this->assertTrue($lockHandler->lock()); - $this->assertTrue($lockHandler->lock()); - - $lockHandler->release(); - } - - public function testLockIsReleased() - { - $name = 'symfony-test-filesystem.lock'; - - $l1 = new LockHandler($name); - $l2 = new LockHandler($name); - - $this->assertTrue($l1->lock()); - $this->assertFalse($l2->lock()); - - $l1 = null; - - $this->assertTrue($l2->lock()); - $l2->release(); - } -} diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json index 878a3c80ea00b..512486e41c56a 100644 --- a/src/Symfony/Component/Filesystem/composer.json +++ b/src/Symfony/Component/Filesystem/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Finder/CHANGELOG.md b/src/Symfony/Component/Finder/CHANGELOG.md index 53c34be860c56..c795e54cf5a28 100644 --- a/src/Symfony/Component/Finder/CHANGELOG.md +++ b/src/Symfony/Component/Finder/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.0.0 +----- + + * removed `ExceptionInterface` + * removed `Symfony\Component\Finder\Iterator\FilterIterator` + 3.4.0 ----- diff --git a/src/Symfony/Component/Finder/Comparator/DateComparator.php b/src/Symfony/Component/Finder/Comparator/DateComparator.php index 3de43ef4b8ddb..d17c77a9d3fd0 100644 --- a/src/Symfony/Component/Finder/Comparator/DateComparator.php +++ b/src/Symfony/Component/Finder/Comparator/DateComparator.php @@ -23,7 +23,7 @@ class DateComparator extends Comparator * * @throws \InvalidArgumentException If the test is not understood */ - public function __construct($test) + public function __construct(string $test) { if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); diff --git a/src/Symfony/Component/Finder/Comparator/NumberComparator.php b/src/Symfony/Component/Finder/Comparator/NumberComparator.php index f62c0e5740f69..80667c9dddd51 100644 --- a/src/Symfony/Component/Finder/Comparator/NumberComparator.php +++ b/src/Symfony/Component/Finder/Comparator/NumberComparator.php @@ -39,7 +39,7 @@ class NumberComparator extends Comparator * * @throws \InvalidArgumentException If the test is not understood */ - public function __construct($test) + public function __construct(?string $test) { if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) { throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); diff --git a/src/Symfony/Component/Finder/Exception/ExceptionInterface.php b/src/Symfony/Component/Finder/Exception/ExceptionInterface.php deleted file mode 100644 index 161e9686d2b70..0000000000000 --- a/src/Symfony/Component/Finder/Exception/ExceptionInterface.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Exception; - -/** - * @author Jean-François Simon - * - * @deprecated since 3.3, to be removed in 4.0. - */ -interface ExceptionInterface -{ - /** - * @return \Symfony\Component\Finder\Adapter\AdapterInterface - */ - public function getAdapter(); -} diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index 8e33d4118bd4d..e7157b1b0a599 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -634,12 +634,7 @@ public function count() return iterator_count($this->getIterator()); } - /** - * @param $dir - * - * @return \Iterator - */ - private function searchInDirectory($dir) + private function searchInDirectory(string $dir): \Iterator { if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) { $this->exclude = array_merge($this->exclude, self::$vcsPatterns); diff --git a/src/Symfony/Component/Finder/Iterator/CustomFilterIterator.php b/src/Symfony/Component/Finder/Iterator/CustomFilterIterator.php index 399b6a9719c01..031bc15710f27 100644 --- a/src/Symfony/Component/Finder/Iterator/CustomFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/CustomFilterIterator.php @@ -19,7 +19,7 @@ * * @author Fabien Potencier */ -class CustomFilterIterator extends FilterIterator +class CustomFilterIterator extends \FilterIterator { private $filters = array(); diff --git a/src/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php b/src/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php index b01e5e3f5d885..c08e0a163f2ee 100644 --- a/src/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php @@ -18,7 +18,7 @@ * * @author Fabien Potencier */ -class DateRangeFilterIterator extends FilterIterator +class DateRangeFilterIterator extends \FilterIterator { private $comparators = array(); diff --git a/src/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php b/src/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php index ce9d3aa73a13e..436a66d84b99b 100644 --- a/src/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php @@ -16,7 +16,7 @@ * * @author Fabien Potencier */ -class DepthRangeFilterIterator extends FilterIterator +class DepthRangeFilterIterator extends \FilterIterator { private $minDepth = 0; @@ -25,7 +25,7 @@ class DepthRangeFilterIterator extends FilterIterator * @param int $minDepth The min depth * @param int $maxDepth The max depth */ - public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX) + public function __construct(\RecursiveIteratorIterator $iterator, int $minDepth = 0, int $maxDepth = PHP_INT_MAX) { $this->minDepth = $minDepth; $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth); diff --git a/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php b/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php index c57128c278492..d2d41f4a3d670 100644 --- a/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php @@ -16,7 +16,7 @@ * * @author Fabien Potencier */ -class ExcludeDirectoryFilterIterator extends FilterIterator implements \RecursiveIterator +class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator { private $iterator; private $isRecursive; diff --git a/src/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php b/src/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php index e9811d4a03ee6..a4c4eec72e90d 100644 --- a/src/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php @@ -16,7 +16,7 @@ * * @author Fabien Potencier */ -class FileTypeFilterIterator extends FilterIterator +class FileTypeFilterIterator extends \FilterIterator { const ONLY_FILES = 1; const ONLY_DIRECTORIES = 2; @@ -27,7 +27,7 @@ class FileTypeFilterIterator extends FilterIterator * @param \Iterator $iterator The Iterator to filter * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) */ - public function __construct(\Iterator $iterator, $mode) + public function __construct(\Iterator $iterator, int $mode) { $this->mode = $mode; diff --git a/src/Symfony/Component/Finder/Iterator/FilterIterator.php b/src/Symfony/Component/Finder/Iterator/FilterIterator.php deleted file mode 100644 index c16dd8fa9e4a0..0000000000000 --- a/src/Symfony/Component/Finder/Iterator/FilterIterator.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * This iterator just overrides the rewind method in order to correct a PHP bug, - * which existed before version 5.5.23/5.6.7. - * - * @see https://bugs.php.net/68557 - * - * @author Alex Bogomazov - * - * @deprecated since 3.4, to be removed in 4.0. - */ -abstract class FilterIterator extends \FilterIterator -{ - /** - * This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after - * rewind in some cases. - * - * @see FilterIterator::rewind() - */ - public function rewind() - { - if (\PHP_VERSION_ID > 50607 || (\PHP_VERSION_ID > 50523 && \PHP_VERSION_ID < 50600)) { - parent::rewind(); - - return; - } - - $iterator = $this; - while ($iterator instanceof \OuterIterator) { - $innerIterator = $iterator->getInnerIterator(); - - if ($innerIterator instanceof RecursiveDirectoryIterator) { - // this condition is necessary for iterators to work properly with non-local filesystems like ftp - if ($innerIterator->isRewindable()) { - $innerIterator->next(); - $innerIterator->rewind(); - } - } elseif ($innerIterator instanceof \FilesystemIterator) { - $innerIterator->next(); - $innerIterator->rewind(); - } - - $iterator = $innerIterator; - } - - parent::rewind(); - } -} diff --git a/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php b/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php index fc8854047e342..b94e1d439b389 100644 --- a/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php @@ -16,7 +16,7 @@ * * @author Fabien Potencier */ -abstract class MultiplePcreFilterIterator extends FilterIterator +abstract class MultiplePcreFilterIterator extends \FilterIterator { protected $matchRegexps = array(); protected $noMatchRegexps = array(); diff --git a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php index c1c0e2f18cfbf..acd5a37634abe 100644 --- a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php +++ b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php @@ -37,13 +37,9 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator private $directorySeparator = '/'; /** - * @param string $path - * @param int $flags - * @param bool $ignoreUnreadableDirs - * * @throws \RuntimeException */ - public function __construct($path, $flags, $ignoreUnreadableDirs = false) + public function __construct(string $path, int $flags, bool $ignoreUnreadableDirs = false) { if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) { throw new \RuntimeException('This iterator only support returning current as fileinfo.'); @@ -116,11 +112,6 @@ public function rewind() return; } - // @see https://bugs.php.net/68557 - if (\PHP_VERSION_ID < 50523 || \PHP_VERSION_ID >= 50600 && \PHP_VERSION_ID < 50607) { - parent::next(); - } - parent::rewind(); } @@ -135,11 +126,6 @@ public function isRewindable() return $this->rewindable; } - // workaround for an HHVM bug, should be removed when https://github.com/facebook/hhvm/issues/7281 is fixed - if ('' === $this->getPath()) { - return $this->rewindable = false; - } - if (false !== $stream = @opendir($this->getPath())) { $infos = stream_get_meta_data($stream); closedir($stream); diff --git a/src/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php b/src/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php index bd1a7fb700481..a68666b41dab2 100644 --- a/src/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php @@ -18,7 +18,7 @@ * * @author Fabien Potencier */ -class SizeRangeFilterIterator extends FilterIterator +class SizeRangeFilterIterator extends \FilterIterator { private $comparators = array(); diff --git a/src/Symfony/Component/Finder/SplFileInfo.php b/src/Symfony/Component/Finder/SplFileInfo.php index 19f95e26be69a..6516113a3ece4 100644 --- a/src/Symfony/Component/Finder/SplFileInfo.php +++ b/src/Symfony/Component/Finder/SplFileInfo.php @@ -26,7 +26,7 @@ class SplFileInfo extends \SplFileInfo * @param string $relativePath The relative path * @param string $relativePathname The relative path name */ - public function __construct($file, $relativePath, $relativePathname) + public function __construct(string $file, string $relativePath, string $relativePathname) { parent::__construct($file); $this->relativePath = $relativePath; diff --git a/src/Symfony/Component/Finder/Tests/Iterator/FilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/FilterIteratorTest.php deleted file mode 100644 index 8b1a4482bae2e..0000000000000 --- a/src/Symfony/Component/Finder/Tests/Iterator/FilterIteratorTest.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Tests\Iterator; - -/** - * @author Alex Bogomazov - * - * @group legacy - */ -class FilterIteratorTest extends RealIteratorTestCase -{ - public function testFilterFilesystemIterators() - { - $i = new \FilesystemIterator($this->toAbsolute()); - - // it is expected that there are test.py test.php in the tmpDir - $i = $this->getMockForAbstractClass('Symfony\Component\Finder\Iterator\FilterIterator', array($i)); - $i->expects($this->any()) - ->method('accept') - ->will($this->returnCallback(function () use ($i) { - return (bool) preg_match('/\.php/', (string) $i->current()); - }) - ); - - $c = 0; - foreach ($i as $item) { - ++$c; - } - - $this->assertEquals(1, $c); - - $i->rewind(); - - $c = 0; - foreach ($i as $item) { - ++$c; - } - - // This would fail in php older than 5.5.23/5.6.7 with \FilterIterator - // but works with Symfony\Component\Finder\Iterator\FilterIterator - // see https://bugs.php.net/68557 - $this->assertEquals(1, $c); - } -} diff --git a/src/Symfony/Component/Finder/composer.json b/src/Symfony/Component/Finder/composer.json index de19826f73b31..dd37f2e0ee1f5 100644 --- a/src/Symfony/Component/Finder/composer.json +++ b/src/Symfony/Component/Finder/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" }, @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Form/AbstractRendererEngine.php b/src/Symfony/Component/Form/AbstractRendererEngine.php index fb0e68a9cfa4d..c6f14c4b9a5f8 100644 --- a/src/Symfony/Component/Form/AbstractRendererEngine.php +++ b/src/Symfony/Component/Form/AbstractRendererEngine.php @@ -44,15 +44,13 @@ public function __construct(array $defaultThemes = array()) /** * {@inheritdoc} */ - public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */) + public function setTheme(FormView $view, $themes, $useDefaultThemes = true) { $cacheKey = $view->vars[self::CACHE_KEY_VAR]; // Do not cast, as casting turns objects into arrays of properties $this->themes[$cacheKey] = is_array($themes) ? $themes : array($themes); - - $args = func_get_args(); - $this->useDefaultThemes[$cacheKey] = isset($args[2]) ? (bool) $args[2] : true; + $this->useDefaultThemes[$cacheKey] = (bool) $useDefaultThemes; // Unset instead of resetting to an empty array, in order to allow // implementations (like TwigRendererEngine) to check whether $cacheKey diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php index 7864116d23dcb..c49a49f8ada9c 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -53,17 +53,11 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface private $options; /** - * Creates a new button builder. - * - * @param string $name The name of the button - * @param array $options The button's options - * * @throws InvalidArgumentException if the name is empty */ - public function __construct($name, array $options = array()) + public function __construct(?string $name, array $options = array()) { - $name = (string) $name; - if ('' === $name) { + if ('' === $name || null === $name) { throw new InvalidArgumentException('Buttons cannot have empty names.'); } diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 123ec4fbe8781..537ba84286885 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -1,6 +1,31 @@ CHANGELOG ========= +4.1.0 +----- + + * added `input=datetime_immutable` to DateType, TimeType, DateTimeType + +4.0.0 +----- + + * using the `choices` option in `CountryType`, `CurrencyType`, `LanguageType`, + `LocaleType`, and `TimezoneType` when the `choice_loader` option is not `null` + is not supported anymore and the configured choices will be ignored + * callable strings that are passed to the options of the `ChoiceType` are + treated as property paths + * the `choices_as_values` option of the `ChoiceType` has been removed + * removed the support for caching loaded choice lists in `LazyChoiceList`, + cache the choice list in the used `ChoiceLoaderInterface` implementation + instead + * removed the support for objects implementing both `\Traversable` and `\ArrayAccess` in `ResizeFormListener::preSubmit()` + * removed the ability to use `FormDataCollector` without the `symfony/var-dumper` component + * removed passing a `ValueExporter` instance to the `FormDataExtractor::__construct()` method + * removed passing guesser services ids as the fourth argument of `DependencyInjectionExtension::__construct()` + * removed the ability to validate an unsubmitted form. + * removed `ChoiceLoaderInterface` implementation in `TimezoneType` + * added the `false_values` option to the `CheckboxType` which allows to configure custom values which will be treated as `false` during submission + 3.4.0 ----- diff --git a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php index 1953980c29716..5ae457b2452a7 100644 --- a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php @@ -62,7 +62,7 @@ class ArrayChoiceList implements ChoiceListInterface * incrementing integers are used as * values */ - public function __construct($choices, callable $value = null) + public function __construct(iterable $choices, callable $value = null) { if ($choices instanceof \Traversable) { $choices = iterator_to_array($choices); diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php index d8a007c90b839..8584c911707c8 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php @@ -71,10 +71,8 @@ public function getDecoratedFactory() */ public function createListFromChoices($choices, $value = null) { - if (is_string($value) && !is_callable($value)) { + if (is_string($value)) { $value = new PropertyPath($value); - } elseif (is_string($value) && is_callable($value)) { - @trigger_error('Passing callable strings is deprecated since Symfony 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } if ($value instanceof PropertyPath) { @@ -104,10 +102,8 @@ public function createListFromChoices($choices, $value = null) */ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null) { - if (is_string($value) && !is_callable($value)) { + if (is_string($value)) { $value = new PropertyPath($value); - } elseif (is_string($value) && is_callable($value)) { - @trigger_error('Passing callable strings is deprecated since Symfony 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } if ($value instanceof PropertyPath) { @@ -142,10 +138,8 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, { $accessor = $this->propertyAccessor; - if (is_string($label) && !is_callable($label)) { + if (is_string($label)) { $label = new PropertyPath($label); - } elseif (is_string($label) && is_callable($label)) { - @trigger_error('Passing callable strings is deprecated since Symfony 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } if ($label instanceof PropertyPath) { @@ -154,10 +148,8 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, }; } - if (is_string($preferredChoices) && !is_callable($preferredChoices)) { + if (is_string($preferredChoices)) { $preferredChoices = new PropertyPath($preferredChoices); - } elseif (is_string($preferredChoices) && is_callable($preferredChoices)) { - @trigger_error('Passing callable strings is deprecated since Symfony 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } if ($preferredChoices instanceof PropertyPath) { @@ -171,10 +163,8 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, }; } - if (is_string($index) && !is_callable($index)) { + if (is_string($index)) { $index = new PropertyPath($index); - } elseif (is_string($index) && is_callable($index)) { - @trigger_error('Passing callable strings is deprecated since Symfony 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } if ($index instanceof PropertyPath) { @@ -183,10 +173,8 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, }; } - if (is_string($groupBy) && !is_callable($groupBy)) { + if (is_string($groupBy)) { $groupBy = new PropertyPath($groupBy); - } elseif (is_string($groupBy) && is_callable($groupBy)) { - @trigger_error('Passing callable strings is deprecated since Symfony 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } if ($groupBy instanceof PropertyPath) { @@ -199,10 +187,8 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, }; } - if (is_string($attr) && !is_callable($attr)) { + if (is_string($attr)) { $attr = new PropertyPath($attr); - } elseif (is_string($attr) && is_callable($attr)) { - @trigger_error('Passing callable strings is deprecated since Symfony 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } if ($attr instanceof PropertyPath) { diff --git a/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php b/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php index 486dce58ec7c1..2dae74cc64646 100644 --- a/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php @@ -38,20 +38,6 @@ class LazyChoiceList implements ChoiceListInterface */ private $value; - /** - * @var ChoiceListInterface|null - * - * @deprecated Since 3.1, to be removed in 4.0. Cache the choice list in the {@link ChoiceLoaderInterface} instead. - */ - private $loadedList; - - /** - * @var bool - * - * @deprecated Flag used for BC layer since 3.1. To be removed in 4.0. - */ - private $loaded = false; - /** * Creates a lazily-loaded list using the given loader. * @@ -73,23 +59,7 @@ public function __construct(ChoiceLoaderInterface $loader, callable $value = nul */ public function getChoices() { - if ($this->loaded) { - // We can safely invoke the {@link ChoiceLoaderInterface} assuming it has the list - // in cache when the lazy list is already loaded - if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since Symfony 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); - } - - return $this->loadedList->getChoices(); - } - - // BC - $this->loadedList = $this->loader->loadChoiceList($this->value); - $this->loaded = true; - - return $this->loadedList->getChoices(); - // In 4.0 keep the following line only: - // return $this->loader->loadChoiceList($this->value)->getChoices() + return $this->loader->loadChoiceList($this->value)->getChoices(); } /** @@ -97,22 +67,7 @@ public function getChoices() */ public function getValues() { - if ($this->loaded) { - // Check whether the loader has the same cache - if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since Symfony 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); - } - - return $this->loadedList->getValues(); - } - - // BC - $this->loadedList = $this->loader->loadChoiceList($this->value); - $this->loaded = true; - - return $this->loadedList->getValues(); - // In 4.0 keep the following line only: - // return $this->loader->loadChoiceList($this->value)->getValues() + return $this->loader->loadChoiceList($this->value)->getValues(); } /** @@ -120,22 +75,7 @@ public function getValues() */ public function getStructuredValues() { - if ($this->loaded) { - // Check whether the loader has the same cache - if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since Symfony 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); - } - - return $this->loadedList->getStructuredValues(); - } - - // BC - $this->loadedList = $this->loader->loadChoiceList($this->value); - $this->loaded = true; - - return $this->loadedList->getStructuredValues(); - // In 4.0 keep the following line only: - // return $this->loader->loadChoiceList($this->value)->getStructuredValues(); + return $this->loader->loadChoiceList($this->value)->getStructuredValues(); } /** @@ -143,22 +83,7 @@ public function getStructuredValues() */ public function getOriginalKeys() { - if ($this->loaded) { - // Check whether the loader has the same cache - if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since Symfony 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); - } - - return $this->loadedList->getOriginalKeys(); - } - - // BC - $this->loadedList = $this->loader->loadChoiceList($this->value); - $this->loaded = true; - - return $this->loadedList->getOriginalKeys(); - // In 4.0 keep the following line only: - // return $this->loader->loadChoiceList($this->value)->getOriginalKeys(); + return $this->loader->loadChoiceList($this->value)->getOriginalKeys(); } /** @@ -166,15 +91,6 @@ public function getOriginalKeys() */ public function getChoicesForValues(array $values) { - if ($this->loaded) { - // Check whether the loader has the same cache - if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since Symfony 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); - } - - return $this->loadedList->getChoicesForValues($values); - } - return $this->loader->loadChoicesForValues($values, $this->value); } @@ -183,15 +99,6 @@ public function getChoicesForValues(array $values) */ public function getValuesForChoices(array $choices) { - if ($this->loaded) { - // Check whether the loader has the same cache - if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since Symfony 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); - } - - return $this->loadedList->getValuesForChoices($choices); - } - return $this->loader->loadValuesForChoices($choices, $this->value); } } diff --git a/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php b/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php index 8feed2106e27b..eb27f0891dd33 100644 --- a/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php +++ b/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php @@ -35,7 +35,7 @@ class ChoiceView * @param string $label The label displayed to humans * @param array $attr Additional attributes for the HTML tag */ - public function __construct($data, $value, $label, array $attr = array()) + public function __construct($data, string $value, $label, array $attr = array()) { $this->data = $data; $this->value = $value; diff --git a/src/Symfony/Component/Form/DependencyInjection/FormPass.php b/src/Symfony/Component/Form/DependencyInjection/FormPass.php index df297da277f93..4926f549c22c8 100644 --- a/src/Symfony/Component/Form/DependencyInjection/FormPass.php +++ b/src/Symfony/Component/Form/DependencyInjection/FormPass.php @@ -35,7 +35,7 @@ class FormPass implements CompilerPassInterface private $formTypeGuesserTag; private $formDebugCommandService; - public function __construct($formExtensionService = 'form.extension', $formTypeTag = 'form.type', $formTypeExtensionTag = 'form.type_extension', $formTypeGuesserTag = 'form.type_guesser', $formDebugCommandService = 'console.command.form_debug') + public function __construct(string $formExtensionService = 'form.extension', string $formTypeTag = 'form.type', string $formTypeExtensionTag = 'form.type_extension', string $formTypeGuesserTag = 'form.type_guesser', string $formDebugCommandService = 'console.command.form_debug') { $this->formExtensionService = $formExtensionService; $this->formTypeTag = $formTypeTag; diff --git a/src/Symfony/Component/Form/Exception/UnexpectedTypeException.php b/src/Symfony/Component/Form/Exception/UnexpectedTypeException.php index 474e244bd84b8..77a5c79d97f25 100644 --- a/src/Symfony/Component/Form/Exception/UnexpectedTypeException.php +++ b/src/Symfony/Component/Form/Exception/UnexpectedTypeException.php @@ -13,7 +13,7 @@ class UnexpectedTypeException extends InvalidArgumentException { - public function __construct($value, $expectedType) + public function __construct($value, string $expectedType) { parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php index 44fa3d8119878..7652621b65d5d 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php @@ -13,7 +13,6 @@ use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\InvalidArgumentException; -use Symfony\Component\Form\Exception\UnexpectedTypeException; abstract class BaseDateTimeTransformer implements DataTransformerInterface { @@ -33,19 +32,10 @@ abstract class BaseDateTimeTransformer implements DataTransformerInterface * @param string $inputTimezone The name of the input timezone * @param string $outputTimezone The name of the output timezone * - * @throws UnexpectedTypeException if a timezone is not a string * @throws InvalidArgumentException if a timezone is not valid */ - public function __construct($inputTimezone = null, $outputTimezone = null) + public function __construct(string $inputTimezone = null, string $outputTimezone = null) { - if (null !== $inputTimezone && !is_string($inputTimezone)) { - throw new UnexpectedTypeException($inputTimezone, 'string'); - } - - if (null !== $outputTimezone && !is_string($outputTimezone)) { - throw new UnexpectedTypeException($outputTimezone, 'string'); - } - $this->inputTimezone = $inputTimezone ?: date_default_timezone_get(); $this->outputTimezone = $outputTimezone ?: date_default_timezone_get(); diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php index 592d90862f06d..f72cace1191c8 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Extension\Core\DataTransformer; use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\InvalidArgumentException; use Symfony\Component\Form\Exception\TransformationFailedException; /** @@ -24,12 +25,19 @@ class BooleanToStringTransformer implements DataTransformerInterface { private $trueValue; + private $falseValues; + /** - * @param string $trueValue The value emitted upon transform if the input is true + * @param string $trueValue The value emitted upon transform if the input is true + * @param array $falseValues */ - public function __construct($trueValue) + public function __construct(string $trueValue, array $falseValues = array(null)) { $this->trueValue = $trueValue; + $this->falseValues = $falseValues; + if (in_array($this->trueValue, $this->falseValues, true)) { + throw new InvalidArgumentException('The specified "true" value is contained in the false-values'); + } } /** @@ -65,7 +73,7 @@ public function transform($value) */ public function reverseTransform($value) { - if (null === $value) { + if (in_array($value, $this->falseValues, true)) { return false; } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php index f5004241f3a99..e0a4226cab165 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php @@ -46,13 +46,13 @@ class DateIntervalToArrayTransformer implements DataTransformerInterface * @param string[] $fields The date fields * @param bool $pad Whether to use padding */ - public function __construct(array $fields = null, $pad = false) + public function __construct(array $fields = null, bool $pad = false) { if (null === $fields) { $fields = array('years', 'months', 'days', 'hours', 'minutes', 'seconds', 'invert'); } $this->fields = $fields; - $this->pad = (bool) $pad; + $this->pad = $pad; } /** diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php index 4624e585e7e0d..4aeede8232588 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php @@ -31,7 +31,7 @@ class DateIntervalToStringTransformer implements DataTransformerInterface * * @param string $format The date format */ - public function __construct($format = 'P%yY%mM%dDT%hH%iM%sS') + public function __construct(string $format = 'P%yY%mM%dDT%hH%iM%sS') { $this->format = $format; } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php new file mode 100644 index 0000000000000..b0737393e4e3f --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a DateTimeImmutable object and a DateTime object. + * + * @author Valentin Udaltsov + */ +final class DateTimeImmutableToDateTimeTransformer implements DataTransformerInterface +{ + /** + * Transforms a DateTimeImmutable into a DateTime object. + * + * @param \DateTimeImmutable|null $value A DateTimeImmutable object + * + * @return \DateTime|null A \DateTime object + * + * @throws TransformationFailedException If the given value is not a \DateTimeImmutable + */ + public function transform($value) + { + if (null === $value) { + return null; + } + + if (!$value instanceof \DateTimeImmutable) { + throw new TransformationFailedException('Expected a \DateTimeImmutable.'); + } + + return \DateTime::createFromFormat(\DateTime::RFC3339, $value->format(\DateTime::RFC3339)); + } + + /** + * Transforms a DateTime object into a DateTimeImmutable object. + * + * @param \DateTime|null $value A DateTime object + * + * @return \DateTimeImmutable|null A DateTimeImmutable object + * + * @throws TransformationFailedException If the given value is not a \DateTime + */ + public function reverseTransform($value) + { + if (null === $value) { + return null; + } + + if (!$value instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + return \DateTimeImmutable::createFromMutable($value); + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php index 3ddd4675fa7a4..8edc501fca277 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Form\Extension\Core\DataTransformer; use Symfony\Component\Form\Exception\TransformationFailedException; -use Symfony\Component\Form\Exception\UnexpectedTypeException; /** * Transforms between a normalized time and a localized time string/array. @@ -31,10 +30,8 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer * @param string $outputTimezone The output timezone * @param array $fields The date fields * @param bool $pad Whether to use padding - * - * @throws UnexpectedTypeException if a timezone is not a string */ - public function __construct($inputTimezone = null, $outputTimezone = null, array $fields = null, $pad = false) + public function __construct(string $inputTimezone = null, string $outputTimezone = null, array $fields = null, bool $pad = false) { parent::__construct($inputTimezone, $outputTimezone); @@ -43,7 +40,7 @@ public function __construct($inputTimezone = null, $outputTimezone = null, array } $this->fields = $fields; - $this->pad = (bool) $pad; + $this->pad = $pad; } /** diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index 69bcd2412fac6..16b141985ea94 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -39,7 +39,7 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer * * @throws UnexpectedTypeException If a format is not supported or if a timezone is not a string */ - public function __construct($inputTimezone = null, $outputTimezone = null, $dateFormat = null, $timeFormat = null, $calendar = \IntlDateFormatter::GREGORIAN, $pattern = null) + public function __construct(string $inputTimezone = null, string $outputTimezone = null, int $dateFormat = null, int $timeFormat = null, int $calendar = \IntlDateFormatter::GREGORIAN, string $pattern = null) { parent::__construct($inputTimezone, $outputTimezone); diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php index 82f912041265d..c93d2c995d965 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Form\Extension\Core\DataTransformer; use Symfony\Component\Form\Exception\TransformationFailedException; -use Symfony\Component\Form\Exception\UnexpectedTypeException; /** * Transforms between a date string and a DateTime object. @@ -48,10 +47,8 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer * @param string $inputTimezone The name of the input timezone * @param string $outputTimezone The name of the output timezone * @param string $format The date format - * - * @throws UnexpectedTypeException if a timezone is not a string */ - public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s') + public function __construct(string $inputTimezone = null, string $outputTimezone = null, string $format = 'Y-m-d H:i:s') { parent::__construct($inputTimezone, $outputTimezone); diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeZoneToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeZoneToStringTransformer.php index 7e133c2e9b3b9..978a646907985 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeZoneToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeZoneToStringTransformer.php @@ -23,7 +23,7 @@ class DateTimeZoneToStringTransformer implements DataTransformerInterface { private $multiple; - public function __construct($multiple = false) + public function __construct(bool $multiple = false) { $this->multiple = $multiple; } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php index 999e47391b576..760e8aa664bbc 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php @@ -26,7 +26,7 @@ class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransfo * @param bool $grouping Whether thousands should be grouped * @param int $roundingMode One of the ROUND_ constants in this class */ - public function __construct($scale = 0, $grouping = false, $roundingMode = self::ROUND_DOWN) + public function __construct(?int $scale = 0, ?bool $grouping = false, int $roundingMode = self::ROUND_DOWN) { if (null === $roundingMode) { $roundingMode = self::ROUND_DOWN; diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php index 7449fedfc69cc..787805e2abcf2 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php @@ -23,7 +23,7 @@ class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransform { private $divisor; - public function __construct($scale = 2, $grouping = true, $roundingMode = self::ROUND_HALF_UP, $divisor = 1) + public function __construct(?int $scale = 2, ?bool $grouping = true, ?int $roundingMode = self::ROUND_HALF_UP, ?int $divisor = 1) { if (null === $grouping) { $grouping = true; diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 9a22169d76d27..d513caffc779a 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -78,7 +78,7 @@ class NumberToLocalizedStringTransformer implements DataTransformerInterface private $scale; - public function __construct($scale = null, $grouping = false, $roundingMode = self::ROUND_HALF_UP) + public function __construct(int $scale = null, ?bool $grouping = false, ?int $roundingMode = self::ROUND_HALF_UP) { if (null === $grouping) { $grouping = false; diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php index b843aa1823bda..0ac32d5e2f09c 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php @@ -42,7 +42,7 @@ class PercentToLocalizedStringTransformer implements DataTransformerInterface * * @throws UnexpectedTypeException if the given value of type is unknown */ - public function __construct($scale = null, $type = null) + public function __construct(int $scale = null, string $type = null) { if (null === $scale) { $scale = 0; diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php index cfaee9f06dc69..aba9da676ae24 100644 --- a/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php @@ -27,7 +27,7 @@ class FixUrlProtocolListener implements EventSubscriberInterface /** * @param string|null $defaultProtocol The URL scheme to add when there is none or null to not modify the data */ - public function __construct($defaultProtocol = 'http') + public function __construct(?string $defaultProtocol = 'http') { $this->defaultProtocol = $defaultProtocol; } diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php index 698a95de2d48a..2a99581e7517f 100644 --- a/src/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php @@ -28,7 +28,7 @@ class MergeCollectionListener implements EventSubscriberInterface * @param bool $allowAdd Whether values might be added to the collection * @param bool $allowDelete Whether values might be removed from the collection */ - public function __construct($allowAdd = false, $allowDelete = false) + public function __construct(bool $allowAdd = false, bool $allowDelete = false) { $this->allowAdd = $allowAdd; $this->allowDelete = $allowDelete; diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php index 33b1bdb85236e..d4b36b921e1bf 100644 --- a/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php @@ -38,7 +38,7 @@ class ResizeFormListener implements EventSubscriberInterface * @param bool $allowDelete Whether children could be removed from the group * @param bool|callable $deleteEmpty */ - public function __construct($type, array $options = array(), $allowAdd = false, $allowDelete = false, $deleteEmpty = false) + public function __construct(string $type, array $options = array(), bool $allowAdd = false, bool $allowDelete = false, $deleteEmpty = false) { $this->type = $type; $this->allowAdd = $allowAdd; @@ -88,11 +88,7 @@ public function preSubmit(FormEvent $event) $form = $event->getForm(); $data = $event->getData(); - if ($data instanceof \Traversable && $data instanceof \ArrayAccess) { - @trigger_error('Support for objects implementing both \Traversable and \ArrayAccess is deprecated since Symfony 3.1 and will be removed in 4.0. Use an array instead.', E_USER_DEPRECATED); - } - - if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + if (!is_array($data)) { $data = array(); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php index 90646e8712a23..9f0491c151e00 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php @@ -32,7 +32,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) // We cannot solve this case via overriding the "data" option, because // doing so also calls setDataLocked(true). $builder->setData(isset($options['data']) ? $options['data'] : false); - $builder->addViewTransformer(new BooleanToStringTransformer($options['value'])); + $builder->addViewTransformer(new BooleanToStringTransformer($options['value'], $options['false_values'])); } /** @@ -59,7 +59,10 @@ public function configureOptions(OptionsResolver $resolver) 'value' => '1', 'empty_data' => $emptyData, 'compound' => false, + 'false_values' => array(null), )); + + $resolver->setAllowedTypes('false_values', 'array'); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index e94b79d67f7d3..e536efac37ce3 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -268,22 +268,6 @@ public function configureOptions(OptionsResolver $resolver) return $options['required'] ? null : ''; }; - $choicesAsValuesNormalizer = function (Options $options, $choicesAsValues) { - // Not set by the user - if (null === $choicesAsValues) { - return true; - } - - // Set by the user - if (true !== $choicesAsValues) { - throw new \RuntimeException(sprintf('The "choices_as_values" option of the %s should not be used. Remove it and flip the contents of the "choices" option instead.', get_class($this))); - } - - @trigger_error('The "choices_as_values" option is deprecated since Symfony 3.1 and will be removed in 4.0. You should not use it anymore.', E_USER_DEPRECATED); - - return true; - }; - $placeholderNormalizer = function (Options $options, $placeholder) { if ($options['multiple']) { // never use an empty value for this case @@ -319,7 +303,6 @@ public function configureOptions(OptionsResolver $resolver) 'multiple' => false, 'expanded' => false, 'choices' => array(), - 'choices_as_values' => null, // deprecated since 3.1 'choice_loader' => null, 'choice_label' => null, 'choice_name' => null, @@ -340,7 +323,6 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); - $resolver->setNormalizer('choices_as_values', $choicesAsValuesNormalizer); $resolver->setAllowedTypes('choices', array('null', 'array', '\Traversable')); $resolver->setAllowedTypes('choice_translation_domain', array('null', 'bool', 'string')); @@ -385,7 +367,7 @@ private function addSubForms(FormBuilderInterface $builder, array $choiceViews, /** * @return mixed */ - private function addSubForm(FormBuilderInterface $builder, $name, ChoiceView $choiceView, array $options) + private function addSubForm(FormBuilderInterface $builder, string $name, ChoiceView $choiceView, array $options) { $choiceOpts = array( 'value' => $choiceView->value, diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php index 5032ef4b761d3..a96a42d3d6b67 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php @@ -15,7 +15,6 @@ use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; use Symfony\Component\Intl\Intl; -use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class CountryType extends AbstractType implements ChoiceLoaderInterface @@ -37,15 +36,7 @@ class CountryType extends AbstractType implements ChoiceLoaderInterface public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choice_loader' => function (Options $options) { - if ($options['choices']) { - @trigger_error(sprintf('Using the "choices" option in %s has been deprecated since Symfony 3.3 and will be ignored in 4.0. Override the "choice_loader" option instead or set it to null.', __CLASS__), E_USER_DEPRECATED); - - return null; - } - - return $this; - }, + 'choice_loader' => $this, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php index e4fd0622a4ef1..9970d03ad7195 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php @@ -15,7 +15,6 @@ use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; use Symfony\Component\Intl\Intl; -use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class CurrencyType extends AbstractType implements ChoiceLoaderInterface @@ -37,15 +36,7 @@ class CurrencyType extends AbstractType implements ChoiceLoaderInterface public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choice_loader' => function (Options $options) { - if ($options['choices']) { - @trigger_error(sprintf('Using the "choices" option in %s has been deprecated since Symfony 3.3 and will be ignored in 4.0. Override the "choice_loader" option instead or set it to null.', __CLASS__), E_USER_DEPRECATED); - - return null; - } - - return $this; - }, + 'choice_loader' => $this, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index d34d1c210044c..06d085d30c1d7 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -18,6 +18,7 @@ use Symfony\Component\Form\FormView; use Symfony\Component\Form\ReversedTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; @@ -165,7 +166,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) ; } - if ('string' === $options['input']) { + if ('datetime_immutable' === $options['input']) { + $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); + } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone']) )); @@ -254,6 +257,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedValues('input', array( 'datetime', + 'datetime_immutable', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index bf51b15b6a4dc..91aa8e383926d 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -15,6 +15,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; @@ -123,7 +124,9 @@ class_exists('IntlTimeZone', false) ? \IntlTimeZone::createDefault() : null, ; } - if ('string' === $options['input']) { + if ('datetime_immutable' === $options['input']) { + $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); + } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d') )); @@ -258,6 +261,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedValues('input', array( 'datetime', + 'datetime_immutable', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php index 04f25a62f206f..279402a3e28e3 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php @@ -15,7 +15,6 @@ use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; use Symfony\Component\Intl\Intl; -use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class LanguageType extends AbstractType implements ChoiceLoaderInterface @@ -37,15 +36,7 @@ class LanguageType extends AbstractType implements ChoiceLoaderInterface public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choice_loader' => function (Options $options) { - if ($options['choices']) { - @trigger_error(sprintf('Using the "choices" option in %s has been deprecated since Symfony 3.3 and will be ignored in 4.0. Override the "choice_loader" option instead or set it to null.', __CLASS__), E_USER_DEPRECATED); - - return null; - } - - return $this; - }, + 'choice_loader' => $this, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php index 2622e3222410b..de795956b77a1 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php @@ -15,7 +15,6 @@ use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; use Symfony\Component\Intl\Intl; -use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class LocaleType extends AbstractType implements ChoiceLoaderInterface @@ -37,15 +36,7 @@ class LocaleType extends AbstractType implements ChoiceLoaderInterface public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choice_loader' => function (Options $options) { - if ($options['choices']) { - @trigger_error(sprintf('Using the "choices" option in %s has been deprecated since Symfony 3.3 and will be ignored in 4.0. Override the "choice_loader" option instead or set it to null.', __CLASS__), E_USER_DEPRECATED); - - return null; - } - - return $this; - }, + 'choice_loader' => $this, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index d1c04f73d9278..df8973d932e2c 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -18,6 +18,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\ReversedTransformer; use Symfony\Component\Form\Exception\InvalidConfigurationException; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; @@ -133,7 +134,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'])); } - if ('string' === $options['input']) { + if ('datetime_immutable' === $options['input']) { + $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); + } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'H:i:s') )); @@ -252,6 +255,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedValues('input', array( 'datetime', + 'datetime_immutable', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php index a1c214c8bcd29..3adf18e84fc9c 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php @@ -12,27 +12,14 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; -use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; -class TimezoneType extends AbstractType implements ChoiceLoaderInterface +class TimezoneType extends AbstractType { - /** - * Timezone loaded choice list. - * - * The choices are generated from the ICU function \DateTimeZone::listIdentifiers(). - * - * @var ArrayChoiceList - * - * @deprecated since version 3.4, to be removed in 4.0 - */ - private $choiceList; - /** * {@inheritdoc} */ @@ -50,12 +37,6 @@ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'choice_loader' => function (Options $options) { - if ($options['choices']) { - @trigger_error(sprintf('Using the "choices" option in %s has been deprecated since Symfony 3.3 and will be ignored in 4.0. Override the "choice_loader" option instead or set it to null.', __CLASS__), E_USER_DEPRECATED); - - return null; - } - $regions = $options['regions']; return new CallbackChoiceLoader(function () use ($regions) { @@ -88,76 +69,10 @@ public function getBlockPrefix() return 'timezone'; } - /** - * {@inheritdoc} - * - * @deprecated since version 3.4, to be removed in 4.0 - */ - public function loadChoiceList($value = null) - { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - if (null !== $this->choiceList) { - return $this->choiceList; - } - - return $this->choiceList = new ArrayChoiceList(self::getTimezones(\DateTimeZone::ALL), $value); - } - - /** - * {@inheritdoc} - * - * @deprecated since version 3.4, to be removed in 4.0 - */ - public function loadChoicesForValues(array $values, $value = null) - { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - // Optimize - $values = array_filter($values); - if (empty($values)) { - return array(); - } - - // If no callable is set, values are the same as choices - if (null === $value) { - return $values; - } - - return $this->loadChoiceList($value)->getChoicesForValues($values); - } - - /** - * {@inheritdoc} - * - * @deprecated since version 3.4, to be removed in 4.0 - */ - public function loadValuesForChoices(array $choices, $value = null) - { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - // Optimize - $choices = array_filter($choices); - if (empty($choices)) { - return array(); - } - - // If no callable is set, choices are the same as values - if (null === $value) { - return $choices; - } - - return $this->loadChoiceList($value)->getValuesForChoices($choices); - } - /** * Returns a normalized array of timezone choices. - * - * @param int $regions - * - * @return array The timezone choices */ - private static function getTimezones($regions) + private static function getTimezones(int $regions): array { $timezones = array(); diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php index 3331b6b3baad0..865f756e5521a 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php @@ -31,7 +31,7 @@ class CsrfExtension extends AbstractExtension * @param TranslatorInterface $translator The translator for translating error messages * @param null|string $translationDomain The translation domain for translating */ - public function __construct(CsrfTokenManagerInterface $tokenManager, TranslatorInterface $translator = null, $translationDomain = null) + public function __construct(CsrfTokenManagerInterface $tokenManager, TranslatorInterface $translator = null, string $translationDomain = null) { $this->tokenManager = $tokenManager; $this->translator = $translator; diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php index 9eab35ed26b90..9774304a152a9 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php @@ -40,7 +40,7 @@ public static function getSubscribedEvents() ); } - public function __construct($fieldName, CsrfTokenManagerInterface $tokenManager, $tokenId, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null, ServerParams $serverParams = null) + public function __construct(string $fieldName, CsrfTokenManagerInterface $tokenManager, string $tokenId, string $errorMessage, TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null) { $this->fieldName = $fieldName; $this->tokenManager = $tokenManager; diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index f7f9bc0d9152d..5a24d6574ace4 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -33,15 +33,7 @@ class FormTypeCsrfExtension extends AbstractTypeExtension private $translationDomain; private $serverParams; - /** - * @param CsrfTokenManagerInterface $defaultTokenManager - * @param bool $defaultEnabled - * @param string $defaultFieldName - * @param TranslatorInterface $translator - * @param null|string $translationDomain - * @param ServerParams $serverParams - */ - public function __construct(CsrfTokenManagerInterface $defaultTokenManager, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null, ServerParams $serverParams = null) + public function __construct(CsrfTokenManagerInterface $defaultTokenManager, bool $defaultEnabled = true, string $defaultFieldName = '_token', TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null) { $this->defaultTokenManager = $defaultTokenManager; $this->defaultEnabled = $defaultEnabled; diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 448f7cb815dca..3fac07591db98 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -64,12 +64,13 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf */ private $formsByView; - private $hasVarDumper; - public function __construct(FormDataExtractorInterface $dataExtractor) { + if (!class_exists(ClassStub::class)) { + throw new \LogicException(sprintf('The VarDumper component is needed for using the %s class. Install symfony/var-dumper version 3.4 or above.', __CLASS__)); + } + $this->dataExtractor = $dataExtractor; - $this->hasVarDumper = class_exists(ClassStub::class); $this->reset(); } @@ -229,11 +230,9 @@ public function getData() public function serialize() { - if ($this->hasVarDumper) { - foreach ($this->data['forms_by_hash'] as &$form) { - if (isset($form['type_class']) && !$form['type_class'] instanceof ClassStub) { - $form['type_class'] = new ClassStub($form['type_class']); - } + foreach ($this->data['forms_by_hash'] as &$form) { + if (isset($form['type_class']) && !$form['type_class'] instanceof ClassStub) { + $form['type_class'] = new ClassStub($form['type_class']); } } diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php index 490a4ad711b96..9e7a9b579ea30 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php @@ -13,7 +13,6 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; -use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; use Symfony\Component\Validator\ConstraintViolationInterface; /** @@ -23,16 +22,6 @@ */ class FormDataExtractor implements FormDataExtractorInterface { - /** - * Constructs a new data extractor. - */ - public function __construct(ValueExporter $valueExporter = null, $triggerDeprecationNotice = true) - { - if (null !== $valueExporter && $triggerDeprecationNotice) { - @trigger_error('Passing a ValueExporter instance to '.__METHOD__.'() is deprecated in version 3.2 and will be removed in 4.0.', E_USER_DEPRECATED); - } - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php index 9af6eaf8c25bc..e261fa4f7b830 100644 --- a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php +++ b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php @@ -24,25 +24,13 @@ class DependencyInjectionExtension implements FormExtensionInterface private $typeExtensionServices; private $guesserServices; - // @deprecated to be removed in Symfony 4.0 - private $typeServiceIds; - private $guesserServiceIds; - /** * @param ContainerInterface $typeContainer * @param iterable[] $typeExtensionServices * @param iterable $guesserServices */ - public function __construct(ContainerInterface $typeContainer, array $typeExtensionServices, $guesserServices, array $guesserServiceIds = null) + public function __construct(ContainerInterface $typeContainer, array $typeExtensionServices, iterable $guesserServices) { - if (null !== $guesserServiceIds) { - @trigger_error(sprintf('Passing four arguments to the %s::__construct() method is deprecated since Symfony 3.3 and will be disallowed in Symfony 4.0. The new constructor only accepts three arguments.', __CLASS__), E_USER_DEPRECATED); - $this->guesserServiceIds = $guesserServiceIds; - $this->typeServiceIds = $typeExtensionServices; - $typeExtensionServices = $guesserServices; - $guesserServices = $guesserServiceIds; - } - $this->typeContainer = $typeContainer; $this->typeExtensionServices = $typeExtensionServices; $this->guesserServices = $guesserServices; @@ -50,14 +38,6 @@ public function __construct(ContainerInterface $typeContainer, array $typeExtens public function getType($name) { - if (null !== $this->guesserServiceIds) { - if (!isset($this->typeServiceIds[$name])) { - throw new InvalidArgumentException(sprintf('The field type "%s" is not registered in the service container.', $name)); - } - - return $this->typeContainer->get($this->typeServiceIds[$name]); - } - if (!$this->typeContainer->has($name)) { throw new InvalidArgumentException(sprintf('The field type "%s" is not registered in the service container.', $name)); } @@ -67,10 +47,6 @@ public function getType($name) public function hasType($name) { - if (null !== $this->guesserServiceIds) { - return isset($this->typeServiceIds[$name]); - } - return $this->typeContainer->has($name); } @@ -80,10 +56,6 @@ public function getTypeExtensions($name) if (isset($this->typeExtensionServices[$name])) { foreach ($this->typeExtensionServices[$name] as $serviceId => $extension) { - if (null !== $this->guesserServiceIds) { - $extension = $this->typeContainer->get($serviceId = $extension); - } - $extensions[] = $extension; // validate result of getExtendedType() to ensure it is consistent with the service definition @@ -114,10 +86,6 @@ public function getTypeGuesser() $guessers = array(); foreach ($this->guesserServices as $serviceId => $service) { - if (null !== $this->guesserServiceIds) { - $service = $this->typeContainer->get($serviceId = $service); - } - $guessers[] = $service; } diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php index 6f1f632118c81..25bc229fa3e25 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php @@ -25,11 +25,7 @@ class UploadValidatorExtension extends AbstractTypeExtension private $translator; private $translationDomain; - /** - * @param TranslatorInterface $translator The translator for translating error messages - * @param null|string $translationDomain The translation domain for translating - */ - public function __construct(TranslatorInterface $translator, $translationDomain = null) + public function __construct(TranslatorInterface $translator, string $translationDomain = null) { $this->translator = $translator; $this->translationDomain = $translationDomain; diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php index 1fac736e8c9eb..4f31227408ff4 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php +++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php @@ -23,12 +23,7 @@ class MappingRule private $propertyPath; private $targetPath; - /** - * @param FormInterface $origin - * @param string $propertyPath - * @param string $targetPath - */ - public function __construct(FormInterface $origin, $propertyPath, $targetPath) + public function __construct(FormInterface $origin, string $propertyPath, string $targetPath) { $this->origin = $origin; $this->propertyPath = $propertyPath; diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php index 658bad5a48f50..0efd168e3dc99 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php +++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php @@ -21,11 +21,7 @@ class RelativePath extends PropertyPath { private $root; - /** - * @param FormInterface $root - * @param string $propertyPath - */ - public function __construct(FormInterface $root, $propertyPath) + public function __construct(FormInterface $root, string $propertyPath) { parent::__construct($propertyPath); diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php index 007968ab4c24b..72c4cacfec7fe 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php +++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php @@ -50,7 +50,7 @@ class ViolationPath implements \IteratorAggregate, PropertyPathInterface * * @param string $violationPath The property path of a {@link \Symfony\Component\Validator\ConstraintViolation} object */ - public function __construct($violationPath) + public function __construct(string $violationPath) { $path = new PropertyPath($violationPath); $elements = $path->getElements(); diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 7f70567b30a1b..41170ea87aee7 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -734,9 +734,7 @@ public function isEmpty() public function isValid() { if (!$this->submitted) { - @trigger_error('Call Form::isValid() with an unsubmitted form is deprecated since Symfony 3.2 and will throw an exception in 4.0. Use Form::isSubmitted() before Form::isValid() instead.', E_USER_DEPRECATED); - - return false; + throw new LogicException('Cannot check if an unsubmitted form is valid. Call Form::isSubmitted() before Form::isValid().'); } if ($this->isDisabled()) { diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index e8648ac41b171..ce0bbaa9c21a6 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -37,16 +37,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB */ private $unresolvedChildren = array(); - /** - * Creates a new form builder. - * - * @param string $name - * @param string $dataClass - * @param EventDispatcherInterface $dispatcher - * @param FormFactoryInterface $factory - * @param array $options - */ - public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = array()) + public function __construct(?string $name, ?string $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = array()) { parent::__construct($name, $dataClass, $dispatcher, $options); @@ -245,12 +236,8 @@ public function getIterator() /** * Converts an unresolved child into a {@link FormBuilder} instance. - * - * @param string $name The name of the unresolved child - * - * @return self The created instance */ - private function resolveChild($name) + private function resolveChild(string $name): self { $info = $this->unresolvedChildren[$name]; $child = $this->create($name, $info['type'], $info['options']); diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php index f6cdff5310494..7ac98b1cf2e0b 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -188,7 +188,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface * @throws InvalidArgumentException if the data class is not a valid class or if * the name contains invalid characters */ - public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, array $options = array()) + public function __construct($name, ?string $dataClass, EventDispatcherInterface $dispatcher, array $options = array()) { self::validateName($name); diff --git a/src/Symfony/Component/Form/FormError.php b/src/Symfony/Component/Form/FormError.php index 7384cb2805de8..e8921ce10b022 100644 --- a/src/Symfony/Component/Form/FormError.php +++ b/src/Symfony/Component/Form/FormError.php @@ -47,7 +47,7 @@ class FormError implements \Serializable * * @see \Symfony\Component\Translation\Translator */ - public function __construct($message, $messageTemplate = null, array $messageParameters = array(), $messagePluralization = null, $cause = null) + public function __construct(?string $message, string $messageTemplate = null, array $messageParameters = array(), int $messagePluralization = null, $cause = null) { $this->message = $message; $this->messageTemplate = $messageTemplate ?: $message; @@ -157,10 +157,6 @@ public function serialize() */ public function unserialize($serialized) { - if (\PHP_VERSION_ID >= 70000) { - list($this->message, $this->messageTemplate, $this->messageParameters, $this->messagePluralization, $this->cause) = unserialize($serialized, array('allowed_classes' => false)); - } else { - list($this->message, $this->messageTemplate, $this->messageParameters, $this->messagePluralization, $this->cause) = unserialize($serialized); - } + list($this->message, $this->messageTemplate, $this->messageParameters, $this->messagePluralization, $this->cause) = unserialize($serialized, array('allowed_classes' => false)); } } diff --git a/src/Symfony/Component/Form/FormEvents.php b/src/Symfony/Component/Form/FormEvents.php index b795f95dcfafc..c9b01736d495f 100644 --- a/src/Symfony/Component/Form/FormEvents.php +++ b/src/Symfony/Component/Form/FormEvents.php @@ -31,7 +31,7 @@ final class FormEvents * * @Event("Symfony\Component\Form\FormEvent") */ - const PRE_SUBMIT = 'form.pre_bind'; + const PRE_SUBMIT = 'form.pre_submit'; /** * The SUBMIT event is dispatched just before the Form::submit() method @@ -41,7 +41,7 @@ final class FormEvents * * @Event("Symfony\Component\Form\FormEvent") */ - const SUBMIT = 'form.bind'; + const SUBMIT = 'form.submit'; /** * The FormEvents::POST_SUBMIT event is dispatched after the Form::submit() @@ -51,7 +51,7 @@ final class FormEvents * * @Event("Symfony\Component\Form\FormEvent") */ - const POST_SUBMIT = 'form.post_bind'; + const POST_SUBMIT = 'form.post_submit'; /** * The FormEvents::PRE_SET_DATA event is dispatched at the beginning of the Form::setData() method. diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 253f28dd7a1ef..921ae2829c5ff 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -187,7 +187,7 @@ public function addError(FormError $error); /** * Returns whether the form and all children are valid. * - * If the form is not submitted, this method always returns false (but will throw an exception in 4.0). + * @throws Exception\LogicException if the form is not submitted * * @return bool */ diff --git a/src/Symfony/Component/Form/FormRenderer.php b/src/Symfony/Component/Form/FormRenderer.php index 4f4107c5f7f53..40cdea3695a32 100644 --- a/src/Symfony/Component/Form/FormRenderer.php +++ b/src/Symfony/Component/Form/FormRenderer.php @@ -47,10 +47,9 @@ public function getEngine() /** * {@inheritdoc} */ - public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */) + public function setTheme(FormView $view, $themes, $useDefaultThemes = true) { - $args = func_get_args(); - $this->engine->setTheme($view, $themes, isset($args[2]) ? (bool) $args[2] : true); + $this->engine->setTheme($view, $themes, $useDefaultThemes); } /** diff --git a/src/Symfony/Component/Form/FormRendererEngineInterface.php b/src/Symfony/Component/Form/FormRendererEngineInterface.php index 6f5a262d68831..6a35d46bf43c7 100644 --- a/src/Symfony/Component/Form/FormRendererEngineInterface.php +++ b/src/Symfony/Component/Form/FormRendererEngineInterface.php @@ -25,9 +25,9 @@ interface FormRendererEngineInterface * @param mixed $themes The theme(s). The type of these themes * is open to the implementation. * @param bool $useDefaultThemes If true, will use default themes specified - * in the engine, will be added to the interface in 4.0 + * in the engine */ - public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */); + public function setTheme(FormView $view, $themes, $useDefaultThemes = true); /** * Returns the resource for a block name. diff --git a/src/Symfony/Component/Form/FormRendererInterface.php b/src/Symfony/Component/Form/FormRendererInterface.php index 33530aeb82c47..432892dbbc2ad 100644 --- a/src/Symfony/Component/Form/FormRendererInterface.php +++ b/src/Symfony/Component/Form/FormRendererInterface.php @@ -32,9 +32,9 @@ public function getEngine(); * @param mixed $themes The theme(s). The type of these themes * is open to the implementation. * @param bool $useDefaultThemes If true, will use default themes specified - * in the renderer, will be added to the interface in 4.0 + * in the renderer */ - public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */); + public function setTheme(FormView $view, $themes, $useDefaultThemes = true); /** * Renders a named block of the form theme. diff --git a/src/Symfony/Component/Form/FormTypeGuesserChain.php b/src/Symfony/Component/Form/FormTypeGuesserChain.php index 104403fa02390..ea6ba66149e73 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserChain.php +++ b/src/Symfony/Component/Form/FormTypeGuesserChain.php @@ -23,12 +23,8 @@ class FormTypeGuesserChain implements FormTypeGuesserInterface * * @throws UnexpectedTypeException if any guesser does not implement FormTypeGuesserInterface */ - public function __construct($guessers) + public function __construct(iterable $guessers) { - if (!is_array($guessers) && !$guessers instanceof \Traversable) { - throw new UnexpectedTypeException($guessers, 'array or Traversable'); - } - foreach ($guessers as $guesser) { if (!$guesser instanceof FormTypeGuesserInterface) { throw new UnexpectedTypeException($guesser, 'Symfony\Component\Form\FormTypeGuesserInterface'); diff --git a/src/Symfony/Component/Form/Forms.php b/src/Symfony/Component/Form/Forms.php index e84fcd0ab0048..061e98e170589 100644 --- a/src/Symfony/Component/Form/Forms.php +++ b/src/Symfony/Component/Form/Forms.php @@ -105,7 +105,7 @@ final class Forms * * @return FormFactoryInterface The form factory */ - public static function createFormFactory() + public static function createFormFactory(): FormFactoryInterface { return self::createFormFactoryBuilder()->getFormFactory(); } @@ -115,7 +115,7 @@ public static function createFormFactory() * * @return FormFactoryBuilderInterface The form factory builder */ - public static function createFormFactoryBuilder() + public static function createFormFactoryBuilder(): FormFactoryBuilderInterface { $builder = new FormFactoryBuilder(); $builder->addExtension(new CoreExtension()); diff --git a/src/Symfony/Component/Form/Guess/Guess.php b/src/Symfony/Component/Form/Guess/Guess.php index b216af5d55dc6..5007528631e8f 100644 --- a/src/Symfony/Component/Form/Guess/Guess.php +++ b/src/Symfony/Component/Form/Guess/Guess.php @@ -84,7 +84,7 @@ public static function getBestGuess(array $guesses) * * @throws InvalidArgumentException if the given value of confidence is unknown */ - public function __construct($confidence) + public function __construct(int $confidence) { if (self::VERY_HIGH_CONFIDENCE !== $confidence && self::HIGH_CONFIDENCE !== $confidence && self::MEDIUM_CONFIDENCE !== $confidence && self::LOW_CONFIDENCE !== $confidence) { diff --git a/src/Symfony/Component/Form/Guess/TypeGuess.php b/src/Symfony/Component/Form/Guess/TypeGuess.php index 1431b5e400569..ff0c6a7498215 100644 --- a/src/Symfony/Component/Form/Guess/TypeGuess.php +++ b/src/Symfony/Component/Form/Guess/TypeGuess.php @@ -29,7 +29,7 @@ class TypeGuess extends Guess * @param int $confidence The confidence that the guessed class name * is correct */ - public function __construct($type, array $options, $confidence) + public function __construct(string $type, array $options, int $confidence) { parent::__construct($confidence); diff --git a/src/Symfony/Component/Form/Guess/ValueGuess.php b/src/Symfony/Component/Form/Guess/ValueGuess.php index 251e9cd428375..fe19dfeb04e4a 100644 --- a/src/Symfony/Component/Form/Guess/ValueGuess.php +++ b/src/Symfony/Component/Form/Guess/ValueGuess.php @@ -25,7 +25,7 @@ class ValueGuess extends Guess * @param int $confidence The confidence that the guessed class name * is correct */ - public function __construct($value, $confidence) + public function __construct($value, int $confidence) { parent::__construct($confidence); diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php index d100dcff4a5fd..43c91c363df88 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php @@ -64,21 +64,6 @@ public function testCreateFromChoicesPropertyPathInstance() $this->assertSame(array('value'), $this->factory->createListFromChoices($choices, new PropertyPath('property'))); } - /** - * @group legacy - */ - public function testCreateFromChoicesPropertyPathWithCallableString() - { - $choices = array('foo' => 'bar'); - - $this->decoratedFactory->expects($this->once()) - ->method('createListFromChoices') - ->with($choices, 'end') - ->willReturn('RESULT'); - - $this->assertSame('RESULT', $this->factory->createListFromChoices($choices, 'end')); - } - public function testCreateFromLoaderPropertyPath() { $loader = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface')->getMock(); @@ -93,21 +78,6 @@ public function testCreateFromLoaderPropertyPath() $this->assertSame('value', $this->factory->createListFromLoader($loader, 'property')); } - /** - * @group legacy - */ - public function testCreateFromLoaderPropertyPathWithCallableString() - { - $loader = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface')->getMock(); - - $this->decoratedFactory->expects($this->once()) - ->method('createListFromLoader') - ->with($loader, 'end') - ->willReturn('RESULT'); - - $this->assertSame('RESULT', $this->factory->createListFromLoader($loader, 'end')); - } - // https://github.com/symfony/symfony/issues/5494 public function testCreateFromChoicesAssumeNullIfValuePropertyPathUnreadable() { @@ -169,24 +139,6 @@ public function testCreateViewPreferredChoicesAsPropertyPath() )); } - /** - * @group legacy - */ - public function testCreateViewPreferredChoicesAsPropertyPathWithCallableString() - { - $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); - - $this->decoratedFactory->expects($this->once()) - ->method('createView') - ->with($list, 'end') - ->willReturn('RESULT'); - - $this->assertSame('RESULT', $this->factory->createView( - $list, - 'end' - )); - } - public function testCreateViewPreferredChoicesAsPropertyPathInstance() { $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); @@ -240,25 +192,6 @@ public function testCreateViewLabelsAsPropertyPath() )); } - /** - * @group legacy - */ - public function testCreateViewLabelsAsPropertyPathWithCallableString() - { - $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); - - $this->decoratedFactory->expects($this->once()) - ->method('createView') - ->with($list, null, 'end') - ->willReturn('RESULT'); - - $this->assertSame('RESULT', $this->factory->createView( - $list, - null, // preferred choices - 'end' - )); - } - public function testCreateViewLabelsAsPropertyPathInstance() { $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); @@ -296,26 +229,6 @@ public function testCreateViewIndicesAsPropertyPath() )); } - /** - * @group legacy - */ - public function testCreateViewIndicesAsPropertyPathWithCallableString() - { - $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); - - $this->decoratedFactory->expects($this->once()) - ->method('createView') - ->with($list, null, null, 'end') - ->willReturn('RESULT'); - - $this->assertSame('RESULT', $this->factory->createView( - $list, - null, // preferred choices - null, // label - 'end' - )); - } - public function testCreateViewIndicesAsPropertyPathInstance() { $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); @@ -355,27 +268,6 @@ public function testCreateViewGroupsAsPropertyPath() )); } - /** - * @group legacy - */ - public function testCreateViewGroupsAsPropertyPathWithCallableString() - { - $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); - - $this->decoratedFactory->expects($this->once()) - ->method('createView') - ->with($list, null, null, null, 'end') - ->willReturn('RESULT'); - - $this->assertSame('RESULT', $this->factory->createView( - $list, - null, // preferred choices - null, // label - null, // index - 'end' - )); - } - public function testCreateViewGroupsAsPropertyPathInstance() { $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); @@ -438,28 +330,6 @@ public function testCreateViewAttrAsPropertyPath() )); } - /** - * @group legacy - */ - public function testCreateViewAttrAsPropertyPathWithCallableString() - { - $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); - - $this->decoratedFactory->expects($this->once()) - ->method('createView') - ->with($list, null, null, null, null, 'end') - ->willReturn('RESULT'); - - $this->assertSame('RESULT', $this->factory->createView( - $list, - null, // preferred choices - null, // label - null, // inde - null, // groups - 'end' - )); - } - public function testCreateViewAttrAsPropertyPathInstance() { $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/LazyChoiceListTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/LazyChoiceListTest.php index 61c8a5aa0b0e3..57b125925435f 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/LazyChoiceListTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/LazyChoiceListTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Form\Tests\ChoiceList; use PHPUnit\Framework\TestCase; -use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\LazyChoiceList; /** @@ -61,30 +60,6 @@ public function testGetChoiceLoadersLoadsLoadedListOnFirstCall() $this->assertSame('RESULT', $this->list->getChoices()); } - /** - * @group legacy - */ - public function testGetChoicesUsesLoadedListWhenLoaderDoesNotCacheChoiceListOnFirstCall() - { - $this->loader->expects($this->at(0)) - ->method('loadChoiceList') - ->with($this->value) - ->willReturn($this->loadedList); - - $this->loader->expects($this->at(1)) - ->method('loadChoiceList') - ->with($this->value) - ->willReturn(new ArrayChoiceList(array('a', 'b'))); - - // The same list is returned by the lazy choice list - $this->loadedList->expects($this->exactly(2)) - ->method('getChoices') - ->will($this->returnValue('RESULT')); - - $this->assertSame('RESULT', $this->list->getChoices()); - $this->assertSame('RESULT', $this->list->getChoices()); - } - public function testGetValuesLoadsLoadedListOnFirstCall() { $this->loader->expects($this->exactly(2)) @@ -146,18 +121,13 @@ public function testGetChoicesForValuesForwardsCallIfListNotLoaded() public function testGetChoicesForValuesUsesLoadedList() { - $this->loader->expects($this->exactly(3)) + $this->loader->expects($this->exactly(1)) ->method('loadChoiceList') ->with($this->value) - // For BC, the same choice loaded list is returned 3 times - // It should only twice in 4.0 ->will($this->returnValue($this->loadedList)); - $this->loader->expects($this->never()) - ->method('loadChoicesForValues'); - - $this->loadedList->expects($this->exactly(2)) - ->method('getChoicesForValues') + $this->loader->expects($this->exactly(2)) + ->method('loadChoicesForValues') ->with(array('a', 'b')) ->will($this->returnValue('RESULT')); @@ -168,34 +138,15 @@ public function testGetChoicesForValuesUsesLoadedList() $this->assertSame('RESULT', $this->list->getChoicesForValues(array('a', 'b'))); } - /** - * @group legacy - */ - public function testGetValuesForChoicesForwardsCallIfListNotLoaded() - { - $this->loader->expects($this->exactly(2)) - ->method('loadValuesForChoices') - ->with(array('a', 'b')) - ->will($this->returnValue('RESULT')); - - $this->assertSame('RESULT', $this->list->getValuesForChoices(array('a', 'b'))); - $this->assertSame('RESULT', $this->list->getValuesForChoices(array('a', 'b'))); - } - public function testGetValuesForChoicesUsesLoadedList() { - $this->loader->expects($this->exactly(3)) + $this->loader->expects($this->exactly(1)) ->method('loadChoiceList') ->with($this->value) - // For BC, the same choice loaded list is returned 3 times - // It should only twice in 4.0 ->will($this->returnValue($this->loadedList)); - $this->loader->expects($this->never()) - ->method('loadValuesForChoices'); - - $this->loadedList->expects($this->exactly(2)) - ->method('getValuesForChoices') + $this->loader->expects($this->exactly(2)) + ->method('loadValuesForChoices') ->with(array('a', 'b')) ->will($this->returnValue('RESULT')); diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index 85813a1ffbc36..73863a4dead71 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -597,7 +597,7 @@ public function testSubmitPostOrPutRequest($method) { $path = tempnam(sys_get_temp_dir(), 'sf2'); touch($path); - + file_put_contents($path, 'zaza'); $values = array( 'author' => array( 'name' => 'Bernhard', @@ -609,7 +609,7 @@ public function testSubmitPostOrPutRequest($method) 'author' => array( 'error' => array('image' => UPLOAD_ERR_OK), 'name' => array('image' => 'upload.png'), - 'size' => array('image' => 123), + 'size' => array('image' => null), 'tmp_name' => array('image' => $path), 'type' => array('image' => 'image/png'), ), @@ -630,7 +630,7 @@ public function testSubmitPostOrPutRequest($method) $form->handleRequest($request); - $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + $file = new UploadedFile($path, 'upload.png', 'image/png', UPLOAD_ERR_OK); $this->assertEquals('Bernhard', $form['name']->getData()); $this->assertEquals($file, $form['image']->getData()); @@ -645,6 +645,7 @@ public function testSubmitPostOrPutRequestWithEmptyRootFormName($method) { $path = tempnam(sys_get_temp_dir(), 'sf2'); touch($path); + file_put_contents($path, 'zaza'); $values = array( 'name' => 'Bernhard', @@ -655,7 +656,7 @@ public function testSubmitPostOrPutRequestWithEmptyRootFormName($method) 'image' => array( 'error' => UPLOAD_ERR_OK, 'name' => 'upload.png', - 'size' => 123, + 'size' => null, 'tmp_name' => $path, 'type' => 'image/png', ), @@ -676,7 +677,7 @@ public function testSubmitPostOrPutRequestWithEmptyRootFormName($method) $form->handleRequest($request); - $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + $file = new UploadedFile($path, 'upload.png', 'image/png', UPLOAD_ERR_OK); $this->assertEquals('Bernhard', $form['name']->getData()); $this->assertEquals($file, $form['image']->getData()); @@ -692,12 +693,13 @@ public function testSubmitPostOrPutRequestWithSingleChildForm($method) { $path = tempnam(sys_get_temp_dir(), 'sf2'); touch($path); + file_put_contents($path, 'zaza'); $files = array( 'image' => array( 'error' => UPLOAD_ERR_OK, 'name' => 'upload.png', - 'size' => 123, + 'size' => null, 'tmp_name' => $path, 'type' => 'image/png', ), @@ -714,7 +716,7 @@ public function testSubmitPostOrPutRequestWithSingleChildForm($method) $form->handleRequest($request); - $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + $file = new UploadedFile($path, 'upload.png', 'image/png', UPLOAD_ERR_OK); $this->assertEquals($file, $form->getData()); @@ -728,6 +730,7 @@ public function testSubmitPostOrPutRequestWithSingleChildFormUploadedFile($metho { $path = tempnam(sys_get_temp_dir(), 'sf2'); touch($path); + file_put_contents($path, 'zaza'); $values = array( 'name' => 'Bernhard', diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php index 43112a4960eb0..e99b7e2715b95 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php @@ -52,13 +52,9 @@ private function getPropertyPath($path) } /** - * @param FormConfigInterface $config - * @param bool $synchronized - * @param bool $submitted - * * @return \PHPUnit_Framework_MockObject_MockObject */ - private function getForm(FormConfigInterface $config, $synchronized = true, $submitted = true) + private function getForm(FormConfigInterface $config, bool $synchronized = true, bool $submitted = true) { $form = $this->getMockBuilder('Symfony\Component\Form\Form') ->setConstructorArgs(array($config)) diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php index 5195092e18b88..bbb9d0792abe5 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php @@ -68,4 +68,26 @@ public function testReverseTransform() $this->assertTrue($this->transformer->reverseTransform('')); $this->assertFalse($this->transformer->reverseTransform(null)); } + + public function testCustomFalseValues() + { + $customFalseTransformer = new BooleanToStringTransformer(self::TRUE_VALUE, array('0', 'myFalse', true)); + $this->assertFalse($customFalseTransformer->reverseTransform('myFalse')); + $this->assertFalse($customFalseTransformer->reverseTransform('0')); + $this->assertFalse($customFalseTransformer->reverseTransform(true)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testTrueValueContainedInFalseValues() + { + new BooleanToStringTransformer('0', array(null, '0')); + } + + public function testBeStrictOnTrueInFalseValueCheck() + { + $transformer = new BooleanToStringTransformer('0', array(null, false)); + $this->assertInstanceOf(BooleanToStringTransformer::class, $transformer); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php new file mode 100644 index 0000000000000..244ef0f790fa9 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; + +class DateTimeImmutableToDateTimeTransformerTest extends TestCase +{ + public function testTransform() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $input = new \DateTimeImmutable('2010-02-03 04:05:06 UTC'); + $expectedOutput = new \DateTime('2010-02-03 04:05:06 UTC'); + $actualOutput = $transformer->transform($input); + + $this->assertInstanceOf(\DateTime::class, $actualOutput); + $this->assertEquals($expectedOutput, $actualOutput); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $this->assertNull($transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage Expected a \DateTimeImmutable. + */ + public function testTransformFail() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + $transformer->transform(new \DateTime()); + } + + public function testReverseTransform() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + $expectedOutput = new \DateTimeImmutable('2010-02-03 04:05:06 UTC'); + $actualOutput = $transformer->reverseTransform($input); + + $this->assertInstanceOf(\DateTimeImmutable::class, $actualOutput); + $this->assertEquals($expectedOutput, $actualOutput); + } + + public function testReverseTransformEmpty() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $this->assertNull($transformer->reverseTransform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage Expected a \DateTime. + */ + public function testReverseTransformFail() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + $transformer->reverseTransform(new \DateTimeImmutable()); + } +} diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index 8e7ad393ebe7c..5653efabd8ee9 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -307,22 +307,6 @@ public function testReverseTransformWrapsIntlErrors() $transformer->reverseTransform('12345'); } - /** - * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException - */ - public function testValidateDateFormatOption() - { - new DateTimeToLocalizedStringTransformer(null, null, 'foobar'); - } - - /** - * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException - */ - public function testValidateTimeFormatOption() - { - new DateTimeToLocalizedStringTransformer(null, null, null, 'foobar'); - } - /** * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException */ diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php index a9aeb9c270ec7..5ef2f66ca2898 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php @@ -173,6 +173,38 @@ public function provideCustomModelTransformerData() ); } + /** + * @dataProvider provideCustomFalseValues + */ + public function testCustomFalseValues($falseValue) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'false_values' => array($falseValue), + )); + $form->submit($falseValue); + $this->assertFalse($form->getData()); + } + + public function provideCustomFalseValues() + { + return array( + array(''), + array('false'), + array('0'), + ); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testDontAllowNonArrayFalseValues() + { + $this->expectExceptionMessageRegExp('/"false_values" with value "invalid" is expected to be of type "array"/'); + $this->factory->create(static::TESTED_TYPE, null, array( + 'false_values' => 'invalid', + )); + } + public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull(false, false, null); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php index a22da49ac5137..0602ca5e13f3d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -52,6 +52,34 @@ public function testSubmitDateTime() $this->assertEquals($dateTime, $form->getData()); } + public function testSubmitDateTimeImmutable() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'years' => array(2010), + 'time_widget' => 'choice', + 'input' => 'datetime_immutable', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $dateTime = new \DateTimeImmutable('2010-06-02 03:04:00 UTC'); + + $this->assertEquals($dateTime, $form->getData()); + } + public function testSubmitString() { $form = $this->factory->create(static::TESTED_TYPE, null, array( @@ -219,6 +247,26 @@ public function testSubmitDifferentTimezonesDateTime() $this->assertEquals('2010-06-02T03:04:00-10:00', $form->getViewData()); } + public function testSubmitDifferentTimezonesDateTimeImmutable() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'widget' => 'single_text', + 'input' => 'datetime_immutable', + )); + + $outputTime = new \DateTimeImmutable('2010-06-02 03:04:00 Pacific/Tahiti'); + + $form->submit('2010-06-02T03:04:00-10:00'); + + $outputTime = $outputTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertInstanceOf(\DateTimeImmutable::class, $form->getData()); + $this->assertEquals($outputTime, $form->getData()); + $this->assertEquals('2010-06-02T03:04:00-10:00', $form->getViewData()); + } + public function testSubmitStringSingleText() { $form = $this->factory->create(static::TESTED_TYPE, null, array( diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index 3a0289a4f34e8..c8d1b4614d7cf 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -105,6 +105,28 @@ public function testSubmitFromSingleTextDateTime() $this->assertEquals('02.06.2010', $form->getViewData()); } + public function testSubmitFromSingleTextDateTimeImmutable() + { + // we test against "de_DE", so we need the full implementation + IntlTestHelper::requireFullIntl($this, false); + + \Locale::setDefault('de_DE'); + + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'datetime_immutable', + )); + + $form->submit('2.6.2010'); + + $this->assertInstanceOf(\DateTimeImmutable::class, $form->getData()); + $this->assertEquals(new \DateTimeImmutable('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + public function testSubmitFromSingleTextString() { // we test against "de_DE", so we need the full implementation diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php index 959ae488a1ee7..0475254d970a2 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php @@ -18,26 +18,6 @@ class ExtendedChoiceTypeTest extends TestCase { - /** - * @group legacy - * @dataProvider provideTestedTypes - */ - public function testLegacyChoicesAreOverridden($type) - { - $factory = Forms::createFormFactoryBuilder() - ->addTypeExtension(new ChoiceTypeExtension($type)) - ->getFormFactory() - ; - - $choices = $factory->create($type)->createView()->vars['choices']; - - $this->assertCount(2, $choices); - $this->assertSame('A', $choices[0]->label); - $this->assertSame('a', $choices[0]->value); - $this->assertSame('B', $choices[1]->label); - $this->assertSame('b', $choices[1]->value); - } - /** * @dataProvider provideTestedTypes */ diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php index 5ba1dc5a5101d..a7edef01442b3 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -188,7 +188,7 @@ public function requestHandlerProvider() private function createUploadedFileMock(RequestHandlerInterface $requestHandler, $path, $originalName) { if ($requestHandler instanceof HttpFoundationRequestHandler) { - return new UploadedFile($path, $originalName, null, 10, null, true); + return new UploadedFile($path, $originalName, null, null, true); } return array( @@ -196,7 +196,7 @@ private function createUploadedFileMock(RequestHandlerInterface $requestHandler, 'error' => 0, 'type' => 'text/plain', 'tmp_name' => $path, - 'size' => 10, + 'size' => null, ); } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 56b1d14774cd2..c58cb44fa8c2d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -39,6 +39,28 @@ public function testSubmitDateTime() $this->assertEquals($input, $form->getViewData()); } + public function testSubmitDateTimeImmutable() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime_immutable', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $dateTime = new \DateTimeImmutable('1970-01-01 03:04:00 UTC'); + + $this->assertInstanceOf(\DateTimeImmutable::class, $form->getData()); + $this->assertEquals($dateTime, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + public function testSubmitString() { $form = $this->factory->create(static::TESTED_TYPE, null, array( diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php index 50fdfd8aa985b..0693b28c135ac 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -410,13 +410,7 @@ public function testExtractViewVariables() ), $this->dataExtractor->extractViewVariables($view)); } - /** - * @param string $name - * @param array $options - * - * @return FormBuilder - */ - private function createBuilder($name, array $options = array()) + private function createBuilder(string $name, array $options = array()): FormBuilder { return new FormBuilder($name, null, $this->dispatcher, $this->factory, $options); } diff --git a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php index 66a7bf07191d2..a94d57ec475a4 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Form\Tests\Extension\DependencyInjection; use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension; use Symfony\Component\Form\FormTypeGuesserChain; use Symfony\Component\Form\FormTypeGuesserInterface; @@ -59,61 +58,6 @@ public function testThrowExceptionForInvalidExtendedType() $extension->getTypeExtensions('test'); } - /** - * @group legacy - * @expectedDeprecation Passing four arguments to the Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension::__construct() method is deprecated since Symfony 3.3 and will be disallowed in Symfony 4.0. The new constructor only accepts three arguments. - */ - public function testLegacyGetTypeExtensions() - { - $container = $this->createContainerMock(); - - $services = array( - 'extension1' => $typeExtension1 = $this->createFormTypeExtensionMock('test'), - 'extension2' => $typeExtension2 = $this->createFormTypeExtensionMock('test'), - 'extension3' => $typeExtension3 = $this->createFormTypeExtensionMock('other'), - ); - - $container->expects($this->any()) - ->method('get') - ->willReturnCallback(function ($id) use ($services) { - if (isset($services[$id])) { - return $services[$id]; - } - - throw new ServiceNotFoundException($id); - }); - - $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension1', 'extension2'), 'other' => array('extension3')), array()); - - $this->assertTrue($extension->hasTypeExtensions('test')); - $this->assertFalse($extension->hasTypeExtensions('unknown')); - $this->assertSame(array($typeExtension1, $typeExtension2), $extension->getTypeExtensions('test')); - } - - /** - * @group legacy - * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException - * @expectedDeprecation Passing four arguments to the Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension::__construct() method is deprecated since Symfony 3.3 and will be disallowed in Symfony 4.0. The new constructor only accepts three arguments. - */ - public function testLegacyThrowExceptionForInvalidExtendedType() - { - $formTypeExtension = $this->createFormTypeExtensionMock('unmatched'); - - $container = $this->createContainerMock(); - - $container->expects($this->any()) - ->method('get') - ->with('extension') - ->willReturn($formTypeExtension); - - $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension')), array()); - - $extensions = $extension->getTypeExtensions('test'); - - $this->assertCount(1, $extensions); - $this->assertSame($formTypeExtension, $extensions[0]); - } - public function testGetTypeGuesser() { $container = $this->createContainerMock(); @@ -130,33 +74,6 @@ public function testGetTypeGuesserReturnsNullWhenNoTypeGuessersHaveBeenConfigure $this->assertNull($extension->getTypeGuesser()); } - /** - * @group legacy - */ - public function testLegacyGetTypeGuesser() - { - $container = $this->createContainerMock(); - $container - ->expects($this->once()) - ->method('get') - ->with('foo') - ->willReturn($this->getMockBuilder(FormTypeGuesserInterface::class)->getMock()); - $extension = new DependencyInjectionExtension($container, array(), array(), array('foo')); - - $this->assertInstanceOf(FormTypeGuesserChain::class, $extension->getTypeGuesser()); - } - - /** - * @group legacy - */ - public function testLegacyGetTypeGuesserReturnsNullWhenNoTypeGuessersHaveBeenConfigured() - { - $container = $this->createContainerMock(); - $extension = new DependencyInjectionExtension($container, array(), array(), array()); - - $this->assertNull($extension->getTypeGuesser()); - } - private function createContainerMock() { return $this->getMockBuilder('Psr\Container\ContainerInterface') diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index 56bcf9fc4bd8e..6301d2bf8af46 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -646,14 +646,7 @@ private function getMockExecutionContext() return $context; } - /** - * @param string $name - * @param string $dataClass - * @param array $options - * - * @return FormBuilder - */ - private function getBuilder($name = 'name', $dataClass = null, array $options = array()) + private function getBuilder(string $name = 'name', string $dataClass = null, array $options = array()): FormBuilder { $options = array_replace(array( 'constraints' => array(), diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json index b0c083b5b71dc..acbb82aa8fe49 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json @@ -10,7 +10,6 @@ "choice_translation_domain", "choice_value", "choices", - "choices_as_values", "expanded", "group_by", "multiple", diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt index 2b34ad960d672..6d698a6171f15 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt @@ -12,12 +12,12 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice") choice_translation_domain empty_data auto_initialize csrf_protection choice_value error_bubbling block_name csrf_token_id choices by_reference csrf_token_manager - choices_as_values data - expanded disabled - group_by inherit_data - multiple label - placeholder label_attr - preferred_choices label_format + expanded data + group_by disabled + multiple inherit_data + placeholder label + preferred_choices label_attr + label_format mapped method post_max_size_message diff --git a/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php index a938a176ccd92..36caf2d15b9f8 100644 --- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php +++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -313,15 +313,6 @@ public function testValidIfSubmittedAndDisabled() $this->assertTrue($form->isValid()); } - /** - * @group legacy - * @expectedDeprecation Call Form::isValid() with an unsubmitted form %s. - */ - public function testNotValidIfNotSubmitted() - { - $this->assertFalse($this->form->isValid()); - } - public function testNotValidIfErrors() { $form = $this->getBuilder()->getForm(); diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index f9836b8ac066e..b8bfd9e9cd6ad 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -16,31 +16,31 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/intl": "^2.8.18|^3.2.5|~4.0", + "php": "^7.1.3", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/intl": "~3.4|~4.0", "symfony/options-resolver": "~3.4|~4.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "~2.8|~3.0|~4.0" + "symfony/property-access": "~3.4|~4.0" }, "require-dev": { "doctrine/collections": "~1.0", - "symfony/validator": "^3.2.5|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/config": "~2.7|~3.0|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "^3.3.5|~4.0", - "symfony/security-csrf": "~2.8|~3.0|~4.0", - "symfony/translation": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~3.3.11|~3.4|~4.0", - "symfony/console": "~3.4|~4.0" + "symfony/validator": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/security-csrf": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/dependency-injection": "<3.3", - "symfony/doctrine-bridge": "<2.7", + "symfony/dependency-injection": "<3.4", + "symfony/doctrine-bridge": "<3.4", "symfony/framework-bundle": "<3.4", - "symfony/http-kernel": "<3.3.5", + "symfony/http-kernel": "<3.4", "symfony/twig-bridge": "<3.4" }, "suggest": { @@ -58,7 +58,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeader.php b/src/Symfony/Component/HttpFoundation/AcceptHeader.php index d1740266b7a80..12ca50651f74d 100644 --- a/src/Symfony/Component/HttpFoundation/AcceptHeader.php +++ b/src/Symfony/Component/HttpFoundation/AcceptHeader.php @@ -91,7 +91,7 @@ public function has($value) */ public function get($value) { - return isset($this->items[$value]) ? $this->items[$value] : null; + return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null; } /** diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php b/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php index c69dbbba35663..367531496e23f 100644 --- a/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php +++ b/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php @@ -23,11 +23,7 @@ class AcceptHeaderItem private $index = 0; private $attributes = array(); - /** - * @param string $value - * @param array $attributes - */ - public function __construct($value, array $attributes = array()) + public function __construct(string $value, array $attributes = array()) { $this->value = $value; foreach ($attributes as $name => $value) { diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index 1010223042ef5..d5a08ea8fa89a 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -44,7 +44,7 @@ class BinaryFileResponse extends Response * @param bool $autoEtag Whether the ETag header should be automatically set * @param bool $autoLastModified Whether the Last-Modified header should be automatically set */ - public function __construct($file, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + public function __construct($file, int $status = 200, array $headers = array(), bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) { parent::__construct(null, $status, $headers); diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index ee5b6cecf2e85..0c4b3ab4e10b1 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -1,6 +1,40 @@ CHANGELOG ========= +4.1.0 +----- + + * Passing the file size to the constructor of the `UploadedFile` class is deprecated and won't be + supported anymore in 5.0. + + * The `getClientSize()` method of the `UploadedFile` class is deprecated. Use `getSize()` instead. + * added `RedisSessionHandler` to use Redis as a session storage + +* The `get()` method of the `AcceptHeader` class now takes into account the + `*` and `*/*` default values (if they are present in the Accept HTTP header) + when looking for items. + +4.0.0 +----- + + * the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` + methods have been removed + * the `Request::HEADER_CLIENT_IP` constant has been removed, use + `Request::HEADER_X_FORWARDED_FOR` instead + * the `Request::HEADER_CLIENT_HOST` constant has been removed, use + `Request::HEADER_X_FORWARDED_HOST` instead + * the `Request::HEADER_CLIENT_PROTO` constant has been removed, use + `Request::HEADER_X_FORWARDED_PROTO` instead + * the `Request::HEADER_CLIENT_PORT` constant has been removed, use + `Request::HEADER_X_FORWARDED_PORT` instead + * 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 `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` + 3.4.0 ----- diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php index 4519a6adaeda5..78b67bb3d5d44 100644 --- a/src/Symfony/Component/HttpFoundation/Cookie.php +++ b/src/Symfony/Component/HttpFoundation/Cookie.php @@ -93,7 +93,7 @@ public static function fromString($cookie, $decode = false) * * @throws \InvalidArgumentException */ - public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null) + public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null) { // from PHP source code if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { @@ -120,9 +120,9 @@ public function __construct($name, $value = null, $expire = 0, $path = '/', $dom $this->domain = $domain; $this->expire = 0 < $expire ? (int) $expire : 0; $this->path = empty($path) ? '/' : $path; - $this->secure = (bool) $secure; - $this->httpOnly = (bool) $httpOnly; - $this->raw = (bool) $raw; + $this->secure = $secure; + $this->httpOnly = $httpOnly; + $this->raw = $raw; if (null !== $sameSite) { $sameSite = strtolower($sameSite); diff --git a/src/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php b/src/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php index 3b8e41d4a2cf9..c25c3629bb617 100644 --- a/src/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php +++ b/src/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php @@ -21,7 +21,7 @@ class AccessDeniedException extends FileException /** * @param string $path The path to the accessed file */ - public function __construct($path) + public function __construct(string $path) { parent::__construct(sprintf('The file %s could not be accessed', $path)); } diff --git a/src/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php b/src/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php index bfcc37ec66ea0..0f1f3f951d806 100644 --- a/src/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php +++ b/src/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php @@ -21,7 +21,7 @@ class FileNotFoundException extends FileException /** * @param string $path The path to the file that was not found */ - public function __construct($path) + public function __construct(string $path) { parent::__construct(sprintf('The file "%s" does not exist', $path)); } diff --git a/src/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php b/src/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php index 0444b8778218f..82e6691fff899 100644 --- a/src/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php +++ b/src/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php @@ -13,7 +13,7 @@ class UnexpectedTypeException extends FileException { - public function __construct($value, $expectedType) + public function __construct($value, string $expectedType) { parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); } diff --git a/src/Symfony/Component/HttpFoundation/File/File.php b/src/Symfony/Component/HttpFoundation/File/File.php index e2a67684fcda6..4e6887a52ad48 100644 --- a/src/Symfony/Component/HttpFoundation/File/File.php +++ b/src/Symfony/Component/HttpFoundation/File/File.php @@ -31,7 +31,7 @@ class File extends \SplFileInfo * * @throws FileNotFoundException If the given path is not a file */ - public function __construct($path, $checkPath = true) + public function __construct(string $path, bool $checkPath = true) { if ($checkPath && !is_file($path)) { throw new FileNotFoundException($path); diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php index c2ac6768c3013..b00f58c4e8a27 100644 --- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php +++ b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -31,7 +31,7 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface * * @param string $cmd The command to run to get the mime type of a file */ - public function __construct($cmd = 'file -b --mime %s 2>/dev/null') + public function __construct(string $cmd = 'file -b --mime %s 2>/dev/null') { $this->cmd = $cmd; } diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php index 9b42835e43044..d3ba8b31b5eba 100644 --- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php +++ b/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php @@ -28,7 +28,7 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface * * @see http://www.php.net/manual/en/function.finfo-open.php */ - public function __construct($magicFile = null) + public function __construct(string $magicFile = null) { $this->magicFile = $magicFile; } diff --git a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php index 082d8d534e17a..b311bf6b811e9 100644 --- a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php +++ b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -27,7 +27,6 @@ class UploadedFile extends File private $test = false; private $originalName; private $mimeType; - private $size; private $error; /** @@ -47,7 +46,6 @@ class UploadedFile extends File * @param string $path The full temporary path to the file * @param string $originalName The original file name of the uploaded file * @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream - * @param int|null $size The file size provided by the uploader * @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK * @param bool $test Whether the test mode is active * Local files are used in test mode hence the code should not enforce HTTP uploads @@ -55,13 +53,19 @@ class UploadedFile extends File * @throws FileException If file_uploads is disabled * @throws FileNotFoundException If the file does not exist */ - public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false) + public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false) { $this->originalName = $this->getName($originalName); $this->mimeType = $mimeType ?: 'application/octet-stream'; - $this->size = $size; + + if (4 < func_num_args() ? !is_bool($test) : null !== $error && @filesize($path) === $error) { + @trigger_error(sprintf('Passing a size as 4th argument to the constructor of "%s" is deprecated since Symfony 4.1 and will be unsupported in 5.0.', __CLASS__), E_USER_DEPRECATED); + $error = $test; + $test = 5 < func_num_args() ? func_get_arg(5) : false; + } + $this->error = $error ?: UPLOAD_ERR_OK; - $this->test = (bool) $test; + $this->test = $test; parent::__construct($path, UPLOAD_ERR_OK === $this->error); } @@ -141,11 +145,15 @@ public function guessClientExtension() * It is extracted from the request from which the file has been uploaded. * Then it should not be considered as a safe value. * - * @return int|null The file size + * @deprecated since 4.1 will be removed in 5.0 use getSize() instead. + * + * @return int|null The file sizes */ public function getClientSize() { - return $this->size; + @trigger_error(sprintf('"%s" is deprecated since Symfony 4.1 and will be removed in 5.0. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->getSize(); } /** diff --git a/src/Symfony/Component/HttpFoundation/FileBag.php b/src/Symfony/Component/HttpFoundation/FileBag.php index 5edd0e6210c52..f0d97caca2f20 100644 --- a/src/Symfony/Component/HttpFoundation/FileBag.php +++ b/src/Symfony/Component/HttpFoundation/FileBag.php @@ -84,7 +84,7 @@ protected function convertFileInformation($file) if (UPLOAD_ERR_NO_FILE == $file['error']) { $file = null; } else { - $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); + $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error']); } } else { $file = array_map(array($this, 'convertFileInformation'), $file); diff --git a/src/Symfony/Component/HttpFoundation/HeaderBag.php b/src/Symfony/Component/HttpFoundation/HeaderBag.php index 7aaa52ae56c11..27fa1c7cca990 100644 --- a/src/Symfony/Component/HttpFoundation/HeaderBag.php +++ b/src/Symfony/Component/HttpFoundation/HeaderBag.php @@ -46,7 +46,7 @@ public function __toString() $max = max(array_map('strlen', array_keys($headers))) + 1; $content = ''; foreach ($headers as $name => $values) { - $name = implode('-', array_map('ucfirst', explode('-', $name))); + $name = ucwords($name, '-'); foreach ($values as $value) { $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value); } diff --git a/src/Symfony/Component/HttpFoundation/JsonResponse.php b/src/Symfony/Component/HttpFoundation/JsonResponse.php index 137ac33c46ed0..729264029097e 100644 --- a/src/Symfony/Component/HttpFoundation/JsonResponse.php +++ b/src/Symfony/Component/HttpFoundation/JsonResponse.php @@ -39,7 +39,7 @@ class JsonResponse extends Response * @param array $headers An array of response headers * @param bool $json If the data is already a JSON string */ - public function __construct($data = null, $status = 200, $headers = array(), $json = false) + public function __construct($data = null, int $status = 200, array $headers = array(), bool $json = false) { parent::__construct('', $status, $headers); @@ -139,29 +139,13 @@ public function setJson($json) */ public function setData($data = array()) { - if (defined('HHVM_VERSION')) { - // HHVM does not trigger any warnings and let exceptions - // thrown from a JsonSerializable object pass through. - // If only PHP did the same... + try { $data = json_encode($data, $this->encodingOptions); - } else { - if (!interface_exists('JsonSerializable', false)) { - set_error_handler(function () { return false; }); - try { - $data = @json_encode($data, $this->encodingOptions); - } finally { - restore_error_handler(); - } - } else { - try { - $data = json_encode($data, $this->encodingOptions); - } catch (\Exception $e) { - if ('Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) { - throw $e->getPrevious() ?: $e; - } - throw $e; - } + } catch (\Exception $e) { + if ('Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) { + throw $e->getPrevious() ?: $e; } + throw $e; } if (JSON_ERROR_NONE !== json_last_error()) { diff --git a/src/Symfony/Component/HttpFoundation/RedirectResponse.php b/src/Symfony/Component/HttpFoundation/RedirectResponse.php index 01681dcdf787a..11f71a03426c6 100644 --- a/src/Symfony/Component/HttpFoundation/RedirectResponse.php +++ b/src/Symfony/Component/HttpFoundation/RedirectResponse.php @@ -32,7 +32,7 @@ class RedirectResponse extends Response * * @see http://tools.ietf.org/html/rfc2616#section-10.3 */ - public function __construct($url, $status = 302, $headers = array()) + public function __construct(?string $url, int $status = 302, array $headers = array()) { parent::__construct('', $status, $headers); diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 42c1a6556cea5..8fd2ecc2ed977 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -38,15 +38,6 @@ class Request const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR; - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_HOST = self::HEADER_X_FORWARDED_HOST; - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_PROTO = self::HEADER_X_FORWARDED_PROTO; - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_PORT = self::HEADER_X_FORWARDED_PORT; - const METHOD_HEAD = 'HEAD'; const METHOD_GET = 'GET'; const METHOD_POST = 'POST'; @@ -73,25 +64,6 @@ class Request */ protected static $trustedHosts = array(); - /** - * Names for headers that can be trusted when - * using trusted proxies. - * - * The FORWARDED header is the standard as of rfc7239. - * - * The other headers are non-standard, but widely used - * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). - * - * @deprecated since version 3.3, to be removed in 4.0 - */ - protected static $trustedHeaders = array( - self::HEADER_FORWARDED => 'FORWARDED', - self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', - self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', - self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', - self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', - ); - protected static $httpMethodParameterOverride = false; /** @@ -225,15 +197,6 @@ class Request private static $trustedHeaderSet = -1; - /** @deprecated since version 3.3, to be removed in 4.0 */ - private static $trustedHeaderNames = array( - self::HEADER_FORWARDED => 'FORWARDED', - self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', - self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', - self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', - self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', - ); - private static $forwardedParams = array( self::HEADER_X_FORWARDED_FOR => 'for', self::HEADER_X_FORWARDED_HOST => 'host', @@ -241,6 +204,23 @@ class Request self::HEADER_X_FORWARDED_PORT => 'host', ); + /** + * Names for headers that can be trusted when + * using trusted proxies. + * + * The FORWARDED header is the standard as of rfc7239. + * + * The other headers are non-standard, but widely used + * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). + */ + private static $trustedHeaders = array( + self::HEADER_FORWARDED => 'FORWARDED', + self::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR', + self::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST', + self::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO', + self::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT', + ); + /** * @param array $query The GET parameters * @param array $request The POST parameters @@ -298,20 +278,7 @@ public function initialize(array $query = array(), array $request = array(), arr */ public static function createFromGlobals() { - // With the php's bug #66606, the php's built-in web server - // stores the Content-Type and Content-Length header values in - // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields. - $server = $_SERVER; - if ('cli-server' === PHP_SAPI) { - if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) { - $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH']; - } - if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) { - $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE']; - } - } - - $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server); + $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) @@ -345,7 +312,7 @@ public static function create($uri, $method = 'GET', $parameters = array(), $coo 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => 80, 'HTTP_HOST' => 'localhost', - 'HTTP_USER_AGENT' => 'Symfony/3.X', + 'HTTP_USER_AGENT' => 'Symfony', 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', @@ -594,20 +561,9 @@ public function overrideGlobals() * * @throws \InvalidArgumentException When $trustedHeaderSet is invalid */ - public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/) + public static function setTrustedProxies(array $proxies, int $trustedHeaderSet) { self::$trustedProxies = $proxies; - - if (2 > func_num_args()) { - @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED); - - return; - } - $trustedHeaderSet = (int) func_get_arg(1); - - foreach (self::$trustedHeaderNames as $header => $name) { - self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null; - } self::$trustedHeaderSet = $trustedHeaderSet; } @@ -657,78 +613,6 @@ public static function getTrustedHosts() return self::$trustedHostPatterns; } - /** - * Sets the name for trusted headers. - * - * The following header keys are supported: - * - * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp()) - * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getHost()) - * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getPort()) - * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure()) - * * Request::HEADER_FORWARDED: defaults to Forwarded (see RFC 7239) - * - * Setting an empty value allows to disable the trusted header for the given key. - * - * @param string $key The header key - * @param string $value The header name - * - * @throws \InvalidArgumentException - * - * @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. - */ - public static function setTrustedHeaderName($key, $value) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED); - - if ('forwarded' === $key) { - $key = self::HEADER_FORWARDED; - } elseif ('client_ip' === $key) { - $key = self::HEADER_CLIENT_IP; - } elseif ('client_host' === $key) { - $key = self::HEADER_CLIENT_HOST; - } elseif ('client_proto' === $key) { - $key = self::HEADER_CLIENT_PROTO; - } elseif ('client_port' === $key) { - $key = self::HEADER_CLIENT_PORT; - } elseif (!array_key_exists($key, self::$trustedHeaders)) { - throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); - } - - self::$trustedHeaders[$key] = $value; - - if (null !== $value) { - self::$trustedHeaderNames[$key] = $value; - self::$trustedHeaderSet |= $key; - } else { - self::$trustedHeaderSet &= ~$key; - } - } - - /** - * Gets the trusted proxy header name. - * - * @param string $key The header key - * - * @return string The header name - * - * @throws \InvalidArgumentException - * - * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead. - */ - public static function getTrustedHeaderName($key) - { - if (2 > func_num_args() || func_get_arg(1)) { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED); - } - - if (!array_key_exists($key, self::$trustedHeaders)) { - throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key)); - } - - return self::$trustedHeaders[$key]; - } - /** * Normalizes a query string. * @@ -836,7 +720,12 @@ public function get($key, $default = null) */ public function getSession() { - return $this->session; + $session = $this->session; + if (!$session instanceof SessionInterface && null !== $session) { + $this->setSession($session = $session()); + } + + return $session; } /** @@ -848,7 +737,7 @@ public function getSession() public function hasPreviousSession() { // the check for $this->session avoids malicious users trying to fake a session cookie with proper name - return $this->hasSession() && $this->cookies->has($this->session->getName()); + return $this->hasSession() && $this->cookies->has($this->getSession()->getName()); } /** @@ -875,6 +764,14 @@ public function setSession(SessionInterface $session) $this->session = $session; } + /** + * @internal + */ + public function setSessionFactory(callable $factory) + { + $this->session = $factory; + } + /** * Returns the client IP addresses. * @@ -896,7 +793,7 @@ public function getClientIps() return array($ip); } - return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip); + return $this->getTrustedValues(self::HEADER_X_FORWARDED_FOR, $ip) ?: array($ip); } /** @@ -908,10 +805,6 @@ public function getClientIps() * being the original client, and each successive proxy that passed the request * adding the IP address where it received the request from. * - * If your reverse proxy uses a different header name than "X-Forwarded-For", - * ("Client-Ip" for instance), configure it via the $trustedHeaderSet - * argument of the Request::setTrustedProxies() method instead. - * * @return string|null The client IP address * * @see getClientIps() @@ -1015,17 +908,13 @@ public function getScheme() * * The "X-Forwarded-Port" header must contain the client port. * - * If your reverse proxy uses a different header name than "X-Forwarded-Port", - * configure it via via the $trustedHeaderSet argument of the - * Request::setTrustedProxies() method instead. - * * @return int|string can be a string if fetched from the server bag */ public function getPort() { - if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_PORT)) { + if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_PORT)) { $host = $host[0]; - } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) { + } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) { $host = $host[0]; } elseif (!$host = $this->headers->get('HOST')) { return $this->server->get('SERVER_PORT'); @@ -1233,15 +1122,11 @@ public function getQueryString() * * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". * - * If your reverse proxy uses a different header name than "X-Forwarded-Proto" - * ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet - * argument of the Request::setTrustedProxies() method instead. - * * @return bool */ public function isSecure() { - if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) { + if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_X_FORWARDED_PROTO)) { return in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true); } @@ -1258,17 +1143,13 @@ public function isSecure() * * The "X-Forwarded-Host" header must contain the client host name. * - * If your reverse proxy uses a different header name than "X-Forwarded-Host", - * configure it via the $trustedHeaderSet argument of the - * Request::setTrustedProxies() method instead. - * * @return string * * @throws SuspiciousOperationException when the host name is invalid or not trusted */ public function getHost() { - if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) { + if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) { $host = $host[0]; } elseif (!$host = $this->headers->get('HOST')) { if (!$host = $this->server->get('SERVER_NAME')) { @@ -1558,11 +1439,8 @@ public function isMethod($method) public function isMethodSafe(/* $andCacheable = true */) { if (!func_num_args() || func_get_arg(0)) { - // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature) - // then setting $andCacheable to false should be deprecated in 4.1 - @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED); - - return in_array($this->getMethod(), array('GET', 'HEAD')); + // setting $andCacheable to false should be deprecated in 4.1 + throw new \BadMethodCallException('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is not supported.'); } return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); @@ -1626,9 +1504,6 @@ public function getProtocolVersion() public function getContent($asResource = false) { $currentContentIsResource = is_resource($this->content); - if (\PHP_VERSION_ID < 50600 && false === $this->content) { - throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.'); - } if (true === $asResource) { if ($currentContentIsResource) { @@ -2011,12 +1886,7 @@ protected static function initializeFormats() ); } - /** - * Sets the default PHP locale. - * - * @param string $locale - */ - private function setPhpDefaultLocale($locale) + private function setPhpDefaultLocale(string $locale) { // if either the class Locale doesn't exist, or an exception is thrown when // setting the default locale, the intl module is not installed, and @@ -2033,12 +1903,9 @@ private function setPhpDefaultLocale($locale) * Returns the prefix as encoded in the string when the string starts with * the given prefix, false otherwise. * - * @param string $string The urlencoded string - * @param string $prefix The prefix not encoded - * * @return string|false The prefix as it is encoded in $string, or false */ - private function getUrlencodedPrefix($string, $prefix) + private function getUrlencodedPrefix(string $string, string $prefix) { if (0 !== strpos(rawurldecode($string), $prefix)) { return false; @@ -2086,13 +1953,13 @@ private function getTrustedValues($type, $ip = null) $clientValues = array(); $forwardedValues = array(); - if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) { + if ((self::$trustedHeaderSet & $type) && $this->headers->has(self::$trustedHeaders[$type])) { foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) { - $clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v); + $clientValues[] = (self::HEADER_X_FORWARDED_PORT === $type ? '0.0.0.0:' : '').trim($v); } } - if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { + if ((self::$trustedHeaderSet & self::HEADER_FORWARDED) && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); $forwardedValues = preg_match_all(sprintf('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array(); } diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcher.php b/src/Symfony/Component/HttpFoundation/RequestMatcher.php index 076d077c7d072..cc13f71af28ac 100644 --- a/src/Symfony/Component/HttpFoundation/RequestMatcher.php +++ b/src/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -56,7 +56,7 @@ class RequestMatcher implements RequestMatcherInterface * @param array $attributes * @param string|string[]|null $schemes */ - public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null) + public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null) { $this->matchPath($path); $this->matchHost($host); diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index cf1325afe0627..9449dc10af6f5 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -188,13 +188,9 @@ class Response ); /** - * @param mixed $content The response content, see setContent() - * @param int $status The response status code - * @param array $headers An array of response headers - * * @throws \InvalidArgumentException When the HTTP status code is not valid */ - public function __construct($content = '', $status = 200, $headers = array()) + public function __construct($content = '', int $status = 200, array $headers = array()) { $this->headers = new ResponseHeaderBag($headers); $this->setContent($content); @@ -414,13 +410,11 @@ public function getContent() /** * Sets the HTTP protocol version (1.0 or 1.1). * - * @param string $version The HTTP protocol version - * * @return $this * * @final since version 3.2 */ - public function setProtocolVersion($version) + public function setProtocolVersion(string $version) { $this->version = $version; @@ -430,11 +424,9 @@ public function setProtocolVersion($version) /** * Gets the HTTP protocol version. * - * @return string The HTTP protocol version - * * @final since version 3.2 */ - public function getProtocolVersion() + public function getProtocolVersion(): string { return $this->version; } @@ -445,18 +437,15 @@ public function getProtocolVersion() * If the status text is null it will be automatically populated for the known * status codes and left empty otherwise. * - * @param int $code HTTP status code - * @param mixed $text HTTP status text - * * @return $this * * @throws \InvalidArgumentException When the HTTP status code is not valid * * @final since version 3.2 */ - public function setStatusCode($code, $text = null) + public function setStatusCode(int $code, $text = null) { - $this->statusCode = $code = (int) $code; + $this->statusCode = $code; if ($this->isInvalid()) { throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); } @@ -481,11 +470,9 @@ public function setStatusCode($code, $text = null) /** * Retrieves the status code for the current web response. * - * @return int Status code - * * @final since version 3.2 */ - public function getStatusCode() + public function getStatusCode(): int { return $this->statusCode; } @@ -493,13 +480,11 @@ public function getStatusCode() /** * Sets the response charset. * - * @param string $charset Character set - * * @return $this * * @final since version 3.2 */ - public function setCharset($charset) + public function setCharset(string $charset) { $this->charset = $charset; @@ -509,11 +494,9 @@ public function setCharset($charset) /** * Retrieves the response charset. * - * @return string Character set - * * @final since version 3.2 */ - public function getCharset() + public function getCharset(): ?string { return $this->charset; } @@ -527,11 +510,9 @@ public function getCharset() * Responses with neither a freshness lifetime (Expires, max-age) nor cache * validator (Last-Modified, ETag) are considered uncacheable. * - * @return bool true if the response is worth caching, false otherwise - * * @final since version 3.3 */ - public function isCacheable() + public function isCacheable(): bool { if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { return false; @@ -551,11 +532,9 @@ public function isCacheable() * origin. A response is considered fresh when it includes a Cache-Control/max-age * indicator or Expires header and the calculated age is less than the freshness lifetime. * - * @return bool true if the response is fresh, false otherwise - * * @final since version 3.3 */ - public function isFresh() + public function isFresh(): bool { return $this->getTtl() > 0; } @@ -564,11 +543,9 @@ public function isFresh() * Returns true if the response includes headers that can be used to validate * the response with the origin server using a conditional GET request. * - * @return bool true if the response is validateable, false otherwise - * * @final since version 3.3 */ - public function isValidateable() + public function isValidateable(): bool { return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); } @@ -610,13 +587,11 @@ public function setPublic() /** * Marks the response as "immutable". * - * @param bool $immutable enables or disables the immutable directive - * * @return $this * * @final */ - public function setImmutable($immutable = true) + public function setImmutable(bool $immutable = true) { if ($immutable) { $this->headers->addCacheControlDirective('immutable'); @@ -630,11 +605,9 @@ public function setImmutable($immutable = true) /** * Returns true if the response is marked as "immutable". * - * @return bool returns true if the response is marked as "immutable"; otherwise false - * * @final */ - public function isImmutable() + public function isImmutable(): bool { return $this->headers->hasCacheControlDirective('immutable'); } @@ -647,11 +620,9 @@ public function isImmutable() * When present, the TTL of the response should not be overridden to be * greater than the value provided by the origin. * - * @return bool true if the response must be revalidated by a cache, false otherwise - * * @final since version 3.3 */ - public function mustRevalidate() + public function mustRevalidate(): bool { return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate'); } @@ -659,13 +630,11 @@ public function mustRevalidate() /** * Returns the Date header as a DateTime instance. * - * @return \DateTime A \DateTime instance - * * @throws \RuntimeException When the header is not parseable * * @final since version 3.2 */ - public function getDate() + public function getDate(): ?\DateTimeInterface { return $this->headers->getDate('Date'); } @@ -677,22 +646,24 @@ public function getDate() * * @final since version 3.2 */ - public function setDate(\DateTime $date) + public function setDate(\DateTimeInterface $date) { - $date->setTimezone(new \DateTimeZone('UTC')); + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); + } + + $date = $date->setTimezone(new \DateTimeZone('UTC')); $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT'); return $this; } /** - * Returns the age of the response. - * - * @return int The age of the response in seconds + * Returns the age of the response in seconds. * * @final since version 3.2 */ - public function getAge() + public function getAge(): int { if (null !== $age = $this->headers->get('Age')) { return (int) $age; @@ -718,17 +689,15 @@ public function expire() /** * Returns the value of the Expires header as a DateTime instance. * - * @return \DateTime|null A DateTime instance or null if the header does not exist - * * @final since version 3.2 */ - public function getExpires() + public function getExpires(): ?\DateTimeInterface { try { return $this->headers->getDate('Expires'); } catch (\RuntimeException $e) { // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past - return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000'); + return \DateTime::createFromFormat('U', time() - 172800); } } @@ -737,22 +706,25 @@ public function getExpires() * * Passing null as value will remove the header. * - * @param \DateTime|null $date A \DateTime instance or null to remove the header - * * @return $this * * @final since version 3.2 */ - public function setExpires(\DateTime $date = null) + public function setExpires(\DateTimeInterface $date = null) { if (null === $date) { $this->headers->remove('Expires'); - } else { - $date = clone $date; - $date->setTimezone(new \DateTimeZone('UTC')); - $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); } + $date = $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + return $this; } @@ -763,11 +735,9 @@ public function setExpires(\DateTime $date = null) * First, it checks for a s-maxage directive, then a max-age directive, and then it falls * back on an expires header. It returns null when no maximum age can be established. * - * @return int|null Number of seconds - * * @final since version 3.2 */ - public function getMaxAge() + public function getMaxAge(): ?int { if ($this->headers->hasCacheControlDirective('s-maxage')) { return (int) $this->headers->getCacheControlDirective('s-maxage'); @@ -778,8 +748,10 @@ public function getMaxAge() } if (null !== $this->getExpires()) { - return $this->getExpires()->format('U') - $this->getDate()->format('U'); + return (int) ($this->getExpires()->format('U') - $this->getDate()->format('U')); } + + return null; } /** @@ -787,13 +759,11 @@ public function getMaxAge() * * This methods sets the Cache-Control max-age directive. * - * @param int $value Number of seconds - * * @return $this * * @final since version 3.2 */ - public function setMaxAge($value) + public function setMaxAge(int $value) { $this->headers->addCacheControlDirective('max-age', $value); @@ -805,13 +775,11 @@ public function setMaxAge($value) * * This methods sets the Cache-Control s-maxage directive. * - * @param int $value Number of seconds - * * @return $this * * @final since version 3.2 */ - public function setSharedMaxAge($value) + public function setSharedMaxAge(int $value) { $this->setPublic(); $this->headers->addCacheControlDirective('s-maxage', $value); @@ -827,29 +795,25 @@ public function setSharedMaxAge($value) * When the responses TTL is <= 0, the response may not be served from cache without first * revalidating with the origin. * - * @return int|null The TTL in seconds - * * @final since version 3.2 */ - public function getTtl() + public function getTtl(): ?int { - if (null !== $maxAge = $this->getMaxAge()) { - return $maxAge - $this->getAge(); - } + $maxAge = $this->getMaxAge(); + + return null !== $maxAge ? $maxAge - $this->getAge() : null; } /** - * Sets the response's time-to-live for shared caches. + * Sets the response's time-to-live for shared caches in seconds. * * This method adjusts the Cache-Control/s-maxage directive. * - * @param int $seconds Number of seconds - * * @return $this * * @final since version 3.2 */ - public function setTtl($seconds) + public function setTtl(int $seconds) { $this->setSharedMaxAge($this->getAge() + $seconds); @@ -857,17 +821,15 @@ public function setTtl($seconds) } /** - * Sets the response's time-to-live for private/client caches. + * Sets the response's time-to-live for private/client caches in seconds. * * This method adjusts the Cache-Control/max-age directive. * - * @param int $seconds Number of seconds - * * @return $this * * @final since version 3.2 */ - public function setClientTtl($seconds) + public function setClientTtl(int $seconds) { $this->setMaxAge($this->getAge() + $seconds); @@ -877,13 +839,11 @@ public function setClientTtl($seconds) /** * Returns the Last-Modified HTTP header as a DateTime instance. * - * @return \DateTime|null A DateTime instance or null if the header does not exist - * * @throws \RuntimeException When the HTTP header is not parseable * * @final since version 3.2 */ - public function getLastModified() + public function getLastModified(): ?\DateTimeInterface { return $this->headers->getDate('Last-Modified'); } @@ -893,33 +853,34 @@ public function getLastModified() * * Passing null as value will remove the header. * - * @param \DateTime|null $date A \DateTime instance or null to remove the header - * * @return $this * * @final since version 3.2 */ - public function setLastModified(\DateTime $date = null) + public function setLastModified(\DateTimeInterface $date = null) { if (null === $date) { $this->headers->remove('Last-Modified'); - } else { - $date = clone $date; - $date->setTimezone(new \DateTimeZone('UTC')); - $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); } + $date = $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + return $this; } /** * Returns the literal value of the ETag HTTP header. * - * @return string|null The ETag HTTP header or null if it does not exist - * * @final since version 3.2 */ - public function getEtag() + public function getEtag(): ?string { return $this->headers->get('ETag'); } @@ -934,7 +895,7 @@ public function getEtag() * * @final since version 3.2 */ - public function setEtag($etag = null, $weak = false) + public function setEtag(string $etag = null, bool $weak = false) { if (null === $etag) { $this->headers->remove('Etag'); @@ -954,8 +915,6 @@ public function setEtag($etag = null, $weak = false) * * Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable. * - * @param array $options An array of cache options - * * @return $this * * @throws \InvalidArgumentException @@ -1035,11 +994,9 @@ public function setNotModified() /** * Returns true if the response includes a Vary header. * - * @return bool true if the response includes a Vary header, false otherwise - * * @final since version 3.2 */ - public function hasVary() + public function hasVary(): bool { return null !== $this->headers->get('Vary'); } @@ -1047,11 +1004,9 @@ public function hasVary() /** * Returns an array of header names given in the Vary header. * - * @return array An array of Vary names - * * @final since version 3.2 */ - public function getVary() + public function getVary(): array { if (!$vary = $this->headers->get('Vary', null, false)) { return array(); @@ -1075,7 +1030,7 @@ public function getVary() * * @final since version 3.2 */ - public function setVary($headers, $replace = true) + public function setVary($headers, bool $replace = true) { $this->headers->set('Vary', $headers, $replace); @@ -1093,7 +1048,7 @@ public function setVary($headers, $replace = true) * * @final since version 3.3 */ - public function isNotModified(Request $request) + public function isNotModified(Request $request): bool { if (!$request->isMethodCacheable()) { return false; @@ -1121,13 +1076,11 @@ public function isNotModified(Request $request) /** * Is response invalid? * - * @return bool - * * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html * * @final since version 3.2 */ - public function isInvalid() + public function isInvalid(): bool { return $this->statusCode < 100 || $this->statusCode >= 600; } @@ -1135,11 +1088,9 @@ public function isInvalid() /** * Is response informative? * - * @return bool - * * @final since version 3.3 */ - public function isInformational() + public function isInformational(): bool { return $this->statusCode >= 100 && $this->statusCode < 200; } @@ -1147,11 +1098,9 @@ public function isInformational() /** * Is response successful? * - * @return bool - * * @final since version 3.2 */ - public function isSuccessful() + public function isSuccessful(): bool { return $this->statusCode >= 200 && $this->statusCode < 300; } @@ -1159,11 +1108,9 @@ public function isSuccessful() /** * Is the response a redirect? * - * @return bool - * * @final since version 3.2 */ - public function isRedirection() + public function isRedirection(): bool { return $this->statusCode >= 300 && $this->statusCode < 400; } @@ -1171,11 +1118,9 @@ public function isRedirection() /** * Is there a client error? * - * @return bool - * * @final since version 3.2 */ - public function isClientError() + public function isClientError(): bool { return $this->statusCode >= 400 && $this->statusCode < 500; } @@ -1183,11 +1128,9 @@ public function isClientError() /** * Was there a server side error? * - * @return bool - * * @final since version 3.3 */ - public function isServerError() + public function isServerError(): bool { return $this->statusCode >= 500 && $this->statusCode < 600; } @@ -1195,11 +1138,9 @@ public function isServerError() /** * Is the response OK? * - * @return bool - * * @final since version 3.2 */ - public function isOk() + public function isOk(): bool { return 200 === $this->statusCode; } @@ -1207,11 +1148,9 @@ public function isOk() /** * Is the response forbidden? * - * @return bool - * * @final since version 3.2 */ - public function isForbidden() + public function isForbidden(): bool { return 403 === $this->statusCode; } @@ -1219,11 +1158,9 @@ public function isForbidden() /** * Is the response a not found error? * - * @return bool - * * @final since version 3.2 */ - public function isNotFound() + public function isNotFound(): bool { return 404 === $this->statusCode; } @@ -1231,13 +1168,9 @@ public function isNotFound() /** * Is the response a redirect of some form? * - * @param string $location - * - * @return bool - * * @final since version 3.2 */ - public function isRedirect($location = null) + public function isRedirect(string $location = null): bool { return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location')); } @@ -1245,11 +1178,9 @@ public function isRedirect($location = null) /** * Is the response empty? * - * @return bool - * * @final since version 3.2 */ - public function isEmpty() + public function isEmpty(): bool { return in_array($this->statusCode, array(204, 304)); } @@ -1259,17 +1190,13 @@ public function isEmpty() * * Resulting level can be greater than target level if a non-removable buffer has been encountered. * - * @param int $targetLevel The target output buffering level - * @param bool $flush Whether to flush or clean the buffers - * * @final since version 3.3 */ - public static function closeOutputBuffers($targetLevel, $flush) + public static function closeOutputBuffers(int $targetLevel, bool $flush) { $status = ob_get_status(true); $level = count($status); - // PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3 - $flags = defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1; + $flags = PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE); while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) { if ($flush) { diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php index ea1fda290fdfe..286534b8f2eda 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php @@ -24,7 +24,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta /** * @param string $storageKey The key used to store attributes in the session */ - public function __construct($storageKey = '_sf2_attributes') + public function __construct(string $storageKey = '_sf2_attributes') { $this->storageKey = $storageKey; } diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php index abbf37ee7c33c..19b57762a69fd 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php @@ -25,7 +25,7 @@ class NamespacedAttributeBag extends AttributeBag * @param string $storageKey Session storage key * @param string $namespaceCharacter Namespace character to use in keys */ - public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/') + public function __construct(string $storageKey = '_sf2_attributes', string $namespaceCharacter = '/') { $this->namespaceCharacter = $namespaceCharacter; parent::__construct($storageKey); diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php index 77521c24789c6..ef23457b410f4 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php @@ -25,7 +25,7 @@ class AutoExpireFlashBag implements FlashBagInterface /** * @param string $storageKey The key used to store flashes in the session */ - public function __construct($storageKey = '_symfony_flashes') + public function __construct(string $storageKey = '_symfony_flashes') { $this->storageKey = $storageKey; } diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php index 12fb740c5220c..44ddb96330d55 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php @@ -25,7 +25,7 @@ class FlashBag implements FlashBagInterface /** * @param string $storageKey The key used to store flashes in the session */ - public function __construct($storageKey = '_symfony_flashes') + public function __construct(string $storageKey = '_symfony_flashes') { $this->storageKey = $storageKey; } diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php index 6ae1355816fdb..1d3500a995902 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php @@ -91,9 +91,6 @@ public function read($sessionId) $data = $this->doRead($sessionId); $this->newSessionId = '' === $data ? $sessionId : null; - if (\PHP_VERSION_ID < 70000) { - $this->prefetchData = $data; - } return $data; } @@ -103,14 +100,6 @@ public function read($sessionId) */ 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()) : ''; @@ -128,9 +117,6 @@ public function write($sessionId, $data) */ 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))); diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php deleted file mode 100644 index 90726beb04efe..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * 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; - -@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.', MemcacheSessionHandler::class), E_USER_DEPRECATED); - -/** - * @author Drak - * - * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead. - */ -class MemcacheSessionHandler implements \SessionHandlerInterface -{ - private $memcache; - - /** - * @var int Time to live in seconds - */ - private $ttl; - - /** - * @var string Key prefix for shared environments - */ - private $prefix; - - /** - * Constructor. - * - * List of available options: - * * prefix: The prefix to use for the memcache keys in order to avoid collision - * * expiretime: The time to live in seconds - * - * @param \Memcache $memcache A \Memcache instance - * @param array $options An associative array of Memcache options - * - * @throws \InvalidArgumentException When unsupported options are passed - */ - public function __construct(\Memcache $memcache, array $options = array()) - { - if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) { - throw new \InvalidArgumentException(sprintf( - 'The following options are not supported "%s"', implode(', ', $diff) - )); - } - - $this->memcache = $memcache; - $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400; - $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s'; - } - - /** - * {@inheritdoc} - */ - public function open($savePath, $sessionName) - { - return true; - } - - /** - * {@inheritdoc} - */ - public function close() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function read($sessionId) - { - return $this->memcache->get($this->prefix.$sessionId) ?: ''; - } - - /** - * {@inheritdoc} - */ - public function write($sessionId, $data) - { - return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl); - } - - /** - * {@inheritdoc} - */ - public function destroy($sessionId) - { - $this->memcache->delete($this->prefix.$sessionId); - - return true; - } - - /** - * {@inheritdoc} - */ - public function gc($maxlifetime) - { - // not required here because memcache will auto expire the records anyhow. - return true; - } - - /** - * Return a Memcache instance. - * - * @return \Memcache - */ - protected function getMemcache() - { - return $this->memcache; - } -} diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php index 7d3fa218a269d..edc0149f995a4 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -64,19 +64,10 @@ class MongoDbSessionHandler extends AbstractSessionHandler * @param \MongoDB\Client $mongo A MongoDB\Client instance * @param array $options An associative array of field options * - * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided * @throws \InvalidArgumentException When "database" or "collection" not provided */ - public function __construct($mongo, array $options) + public function __construct(\MongoDB\Client $mongo, array $options) { - if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) { - @trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED); - } - - if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { - throw new \InvalidArgumentException('MongoClient or Mongo instance required'); - } - if (!isset($options['database']) || !isset($options['collection'])) { throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler'); } @@ -104,9 +95,7 @@ public function close() */ protected function doDestroy($sessionId) { - $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove'; - - $this->getCollection()->$methodName(array( + $this->getCollection()->deleteOne(array( $this->options['id_field'] => $sessionId, )); @@ -118,10 +107,8 @@ protected function doDestroy($sessionId) */ public function gc($maxlifetime) { - $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove'; - - $this->getCollection()->$methodName(array( - $this->options['expiry_field'] => array('$lt' => $this->createDateTime()), + $this->getCollection()->deleteMany(array( + $this->options['expiry_field'] => array('$lt' => new \MongoDB\BSON\UTCDateTime()), )); return true; @@ -132,28 +119,18 @@ public function gc($maxlifetime) */ protected function doWrite($sessionId, $data) { - $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime')); + $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000); $fields = array( - $this->options['time_field'] => $this->createDateTime(), + $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(), $this->options['expiry_field'] => $expiry, + $this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY), ); - $options = array('upsert' => true); - - if ($this->mongo instanceof \MongoDB\Client) { - $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY); - } else { - $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY); - $options['multiple'] = false; - } - - $methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update'; - - $this->getCollection()->$methodName( + $this->getCollection()->updateOne( array($this->options['id_field'] => $sessionId), array('$set' => $fields), - $options + array('upsert' => true) ); return true; @@ -193,23 +170,17 @@ protected function doRead($sessionId) { $dbData = $this->getCollection()->findOne(array( $this->options['id_field'] => $sessionId, - $this->options['expiry_field'] => array('$gte' => $this->createDateTime()), + $this->options['expiry_field'] => array('$gte' => new \MongoDB\BSON\UTCDateTime()), )); if (null === $dbData) { return ''; } - if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) { - return $dbData[$this->options['data_field']]->getData(); - } - - return $dbData[$this->options['data_field']]->bin; + return $dbData[$this->options['data_field']]->getData(); } /** - * Return a "MongoCollection" instance. - * * @return \MongoCollection */ private function getCollection() @@ -222,34 +193,10 @@ private function getCollection() } /** - * Return a Mongo instance. - * - * @return \Mongo|\MongoClient|\MongoDB\Client + * @return \MongoDB\Client */ protected function getMongo() { return $this->mongo; } - - /** - * Create a date object using the class appropriate for the current mongo connection. - * - * Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime - * - * @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now. - * - * @return \MongoDate|\MongoDB\BSON\UTCDateTime - */ - private function createDateTime($seconds = null) - { - if (null === $seconds) { - $seconds = time(); - } - - if ($this->mongo instanceof \MongoDB\Client) { - return new \MongoDB\BSON\UTCDateTime($seconds * 1000); - } - - return new \MongoDate($seconds); - } } diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php index 4e9704bd5858f..f962965a82a6d 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php @@ -16,7 +16,7 @@ * * @author Drak */ -class NativeFileSessionHandler extends NativeSessionHandler +class NativeFileSessionHandler extends \SessionHandler { /** * @param string $savePath Path of directory to save session files @@ -28,7 +28,7 @@ class NativeFileSessionHandler extends NativeSessionHandler * @throws \InvalidArgumentException On invalid $savePath * @throws \RuntimeException When failing to create the save directory */ - public function __construct($savePath = null) + public function __construct(string $savePath = null) { if (null === $savePath) { $savePath = ini_get('session.save_path'); diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php deleted file mode 100644 index 9be4528aeb436..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * 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; - -/** - * @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead. - * @see http://php.net/sessionhandler - */ -class NativeSessionHandler extends \SessionHandler -{ - public function __construct() - { - @trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED); - } -} diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 2e1692b6f0c99..6748079686221 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -564,8 +564,6 @@ protected function doRead($sessionId) /** * Executes an application-level lock on the database. * - * @param string $sessionId Session ID - * * @return \PDOStatement The statement that needs to be executed later to release the lock * * @throws \DomainException When an unsupported PDO driver is used @@ -574,7 +572,7 @@ protected function doRead($sessionId) * - for oci using DBMS_LOCK.REQUEST * - for sqlsrv using sp_getapplock with LockOwner = Session */ - private function doAdvisoryLock($sessionId) + private function doAdvisoryLock(string $sessionId) { switch ($this->driver) { case 'mysql': @@ -627,12 +625,8 @@ private function doAdvisoryLock($sessionId) * Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer. * * Keep in mind, PHP integers are signed. - * - * @param string $string - * - * @return int */ - private function convertStringToInt($string) + private function convertStringToInt(string $string): int { if (4 === \PHP_INT_SIZE) { return (ord($string[3]) << 24) + (ord($string[2]) << 16) + (ord($string[1]) << 8) + ord($string[0]); @@ -647,11 +641,9 @@ private function convertStringToInt($string) /** * Return a locking or nonlocking SQL query to read session information. * - * @return string The SQL string - * * @throws \DomainException When an unsupported PDO driver is used */ - private function getSelectSql() + private function getSelectSql(): string { if (self::LOCK_TRANSACTIONAL === $this->lockMode) { $this->beginTransaction(); @@ -742,14 +734,8 @@ private function getUpdateStatement($sessionId, $sessionData, $maxlifetime) /** * Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data. - * - * @param string $sessionId Session ID - * @param string $data Encoded session data - * @param int $maxlifetime session.gc_maxlifetime - * - * @return \PDOStatement|null The merge statement or null when not supported */ - private function getMergeStatement($sessionId, $data, $maxlifetime) + private function getMergeStatement(string $sessionId, string $data, int$maxlifetime): ?\PDOStatement { switch (true) { case 'mysql' === $this->driver: diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php new file mode 100644 index 0000000000000..974d930e90868 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php @@ -0,0 +1,105 @@ + + * + * 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; + +use Predis\Response\ErrorInterface; + +/** + * Redis based session storage handler based on the Redis class + * provided by the PHP redis extension. + * + * @author Dalibor Karlović + */ +class RedisSessionHandler extends AbstractSessionHandler +{ + private $redis; + + /** + * @var string Key prefix for shared environments + */ + private $prefix; + + /** + * List of available options: + * * prefix: The prefix to use for the keys in order to avoid collision on the Redis server. + * + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redis + * @param array $options An associative array of options + * + * @throws \InvalidArgumentException When unsupported client or options are passed + */ + public function __construct($redis, array $options = array()) + { + if (!$redis instanceof \Redis && !$redis instanceof \RedisArray && !$redis instanceof \Predis\Client && !$redis instanceof RedisProxy) { + throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, is_object($redis) ? get_class($redis) : gettype($redis))); + } + + if ($diff = array_diff(array_keys($options), array('prefix'))) { + throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff))); + } + + $this->redis = $redis; + $this->prefix = $options['prefix'] ?? 'sf_s'; + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId): string + { + return $this->redis->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data): bool + { + $result = $this->redis->setEx($this->prefix.$sessionId, (int) ini_get('session.gc_maxlifetime'), $data); + + return $result && !$result instanceof ErrorInterface; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId): bool + { + $this->redis->del($this->prefix.$sessionId); + + return true; + } + + /** + * {@inheritdoc} + */ + public function close(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + return $this->redis->expire($this->prefix.$sessionId, (int) ini_get('session.gc_maxlifetime')); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/WriteCheckSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/WriteCheckSessionHandler.php deleted file mode 100644 index 1541ec4e0aa49..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/WriteCheckSessionHandler.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * 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; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', WriteCheckSessionHandler::class), E_USER_DEPRECATED); - -/** - * Wraps another SessionHandlerInterface to only write the session when it has been modified. - * - * @author Adrien Brault - * - * @deprecated since version 3.4, to be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead. - */ -class WriteCheckSessionHandler implements \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); - } -} diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php index 6f59af486981e..ea0d5ecb51826 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php @@ -57,7 +57,7 @@ class MetadataBag implements SessionBagInterface * @param string $storageKey The key used to store bag in the session * @param int $updateThreshold The time to wait between two UPDATED updates */ - public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0) + public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0) { $this->storageKey = $storageKey; $this->updateThreshold = $updateThreshold; diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php index 027f4efffce51..47cac39854a5d 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php @@ -62,11 +62,7 @@ class MockArraySessionStorage implements SessionStorageInterface */ protected $bags = array(); - /** - * @param string $name Session name - * @param MetadataBag $metaBag MetadataBag instance - */ - public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null) + public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null) { $this->name = $name; $this->setMetadataBag($metaBag); diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php index 14f427007ba82..732f92abf9e4d 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php @@ -31,7 +31,7 @@ class MockFileSessionStorage extends MockArraySessionStorage * @param string $name Session name * @param MetadataBag $metaBag MetadataBag instance */ - public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null) + public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null) { if (null === $savePath) { $savePath = sys_get_temp_dir(); diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php index 0dfad9acb3532..6a1dd634ad7c4 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -67,13 +67,9 @@ class NativeSessionStorage implements SessionStorageInterface * cookie_lifetime, "0" * cookie_path, "/" * cookie_secure, "" - * entropy_file, "" - * entropy_length, "0" * gc_divisor, "100" * gc_maxlifetime, "1440" * gc_probability, "1" - * hash_bits_per_character, "4" - * hash_function, "0" * lazy_write, "1" * name, "PHPSESSID" * referer_check, "" @@ -105,6 +101,7 @@ public function __construct(array $options = array(), $handler = null, MetadataB 'cache_expire' => 0, 'use_cookies' => 1, 'lazy_write' => 1, + 'use_strict_mode' => 1, ); session_register_shutdown(); @@ -351,9 +348,8 @@ public function setOptions(array $options) $validOptions = array_flip(array( 'cache_limiter', 'cache_expire', 'cookie_domain', 'cookie_httponly', 'cookie_lifetime', 'cookie_path', 'cookie_secure', - 'entropy_file', 'entropy_length', 'gc_divisor', - 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', - 'hash_function', 'lazy_write', 'name', 'referer_check', + 'gc_divisor', 'gc_maxlifetime', 'gc_probability', + 'lazy_write', 'name', 'referer_check', 'serialize_handler', 'use_strict_mode', 'use_cookies', 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php deleted file mode 100644 index 082eed143eb43..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * 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; - -@trigger_error('The '.__NAMESPACE__.'\NativeProxy class is deprecated since Symfony 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED); - -/** - * This proxy is built-in session handlers in PHP 5.3.x. - * - * @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly. - * - * @author Drak - */ -class NativeProxy extends AbstractProxy -{ - public function __construct() - { - // this makes an educated guess as to what the handler is since it should already be set. - $this->saveHandlerName = ini_get('session.save_handler'); - } - - /** - * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. - * - * @return bool False - */ - public function isWrapper() - { - return false; - } -} diff --git a/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/src/Symfony/Component/HttpFoundation/StreamedResponse.php index 92868d33e4814..a7c2150252eb6 100644 --- a/src/Symfony/Component/HttpFoundation/StreamedResponse.php +++ b/src/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -35,7 +35,7 @@ class StreamedResponse extends Response * @param int $status The response status code * @param array $headers An array of response headers */ - public function __construct(callable $callback = null, $status = 200, $headers = array()) + public function __construct(callable $callback = null, int $status = 200, array $headers = array()) { parent::__construct(null, $status, $headers); diff --git a/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php b/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php index 9929eac28ef01..1ac6103e0da70 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php @@ -100,4 +100,31 @@ public function provideSortingData() 'order matters when q is equal2' => array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('utf-8', 'ISO-8859-1', '*')), ); } + + /** + * @dataProvider provideDefaultValueData + */ + public function testDefaultValue($acceptHeader, $value, $expectedQuality) + { + $header = AcceptHeader::fromString($acceptHeader); + $this->assertSame($expectedQuality, $header->get($value)->getQuality()); + } + + public function provideDefaultValueData() + { + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, *;q=0.3', 'text/xml', 0.3); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/xml', 0.3); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/html', 1.0); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/plain', 0.5); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', '*', 0.3); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', '*', 1.0); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/xml', 1.0); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/*', 1.0); + yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/*', 0.8); + yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/html', 1.0); + yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/x-dvi', 0.8); + yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', '*', 0.3); + yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'utf-8', 0.7); + yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'SHIFT_JIS', 0.3); + } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php index 070b7dd4290e6..264fafa097596 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php @@ -104,9 +104,6 @@ public function testConstructorWithDateTime() $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date'); } - /** - * @requires PHP 5.5 - */ public function testConstructorWithDateTimeImmutable() { $expire = new \DateTimeImmutable(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php b/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php index 36f122fe79223..2c4f29c07a2b5 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php @@ -40,7 +40,6 @@ public function testFileUploadsWithNoMimeType() __DIR__.'/Fixtures/test.gif', 'original.gif', null, - filesize(__DIR__.'/Fixtures/test.gif'), UPLOAD_ERR_OK ); @@ -57,7 +56,6 @@ public function testFileUploadsWithUnknownMimeType() __DIR__.'/Fixtures/.unknownextension', 'original.gif', null, - filesize(__DIR__.'/Fixtures/.unknownextension'), UPLOAD_ERR_OK ); @@ -70,7 +68,6 @@ public function testGuessClientExtension() __DIR__.'/Fixtures/test.gif', 'original.gif', 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), null ); @@ -83,7 +80,6 @@ public function testGuessClientExtensionWithIncorrectMimeType() __DIR__.'/Fixtures/test.gif', 'original.gif', 'image/jpeg', - filesize(__DIR__.'/Fixtures/test.gif'), null ); @@ -96,7 +92,6 @@ public function testErrorIsOkByDefault() __DIR__.'/Fixtures/test.gif', 'original.gif', 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), null ); @@ -109,7 +104,6 @@ public function testGetClientOriginalName() __DIR__.'/Fixtures/test.gif', 'original.gif', 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), null ); @@ -122,7 +116,6 @@ public function testGetClientOriginalExtension() __DIR__.'/Fixtures/test.gif', 'original.gif', 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), null ); @@ -138,7 +131,6 @@ public function testMoveLocalFileIsNotAllowed() __DIR__.'/Fixtures/test.gif', 'original.gif', 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), UPLOAD_ERR_OK ); @@ -158,7 +150,6 @@ public function testMoveLocalFileIsAllowedInTestMode() $path, 'original.gif', 'image/gif', - filesize($path), UPLOAD_ERR_OK, true ); @@ -177,9 +168,7 @@ public function testGetClientOriginalNameSanitizeFilename() $file = new UploadedFile( __DIR__.'/Fixtures/test.gif', '../../original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null + 'image/gif' ); $this->assertEquals('original.gif', $file->getClientOriginalName()); @@ -190,9 +179,7 @@ public function testGetSize() $file = new UploadedFile( __DIR__.'/Fixtures/test.gif', 'original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null + 'image/gif' ); $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize()); @@ -206,12 +193,45 @@ public function testGetSize() $this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize()); } - public function testGetExtension() + /** + * @group legacy + * @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1 and will be unsupported in 5.0. + */ + public function testConstructDeprecatedSize() { $file = new UploadedFile( __DIR__.'/Fixtures/test.gif', 'original.gif', - null + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + UPLOAD_ERR_OK, + false + ); + + $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize()); + } + + /** + * @group legacy + * @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1 and will be unsupported in 5.0. + */ + public function testConstructDeprecatedSizeWhenPassingOnlyThe4Needed() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif') + ); + + $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize()); + } + + public function testGetExtension() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif' ); $this->assertEquals('gif', $file->getExtension()); @@ -223,7 +243,6 @@ public function testIsValid() __DIR__.'/Fixtures/test.gif', 'original.gif', null, - filesize(__DIR__.'/Fixtures/test.gif'), UPLOAD_ERR_OK, true ); @@ -240,7 +259,6 @@ public function testIsInvalidOnUploadError($error) __DIR__.'/Fixtures/test.gif', 'original.gif', null, - filesize(__DIR__.'/Fixtures/test.gif'), $error ); @@ -264,7 +282,6 @@ public function testIsInvalidIfNotHttpUpload() __DIR__.'/Fixtures/test.gif', 'original.gif', null, - filesize(__DIR__.'/Fixtures/test.gif'), UPLOAD_ERR_OK ); diff --git a/src/Symfony/Component/HttpFoundation/Tests/FileBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/FileBagTest.php index b1bbba0d3f57c..06136e2097cda 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/FileBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/FileBagTest.php @@ -34,14 +34,14 @@ public function testFileMustBeAnArrayOrUploadedFile() public function testShouldConvertsUploadedFiles() { $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain'); $bag = new FileBag(array('file' => array( 'name' => basename($tmpFile), 'type' => 'text/plain', 'tmp_name' => $tmpFile, 'error' => 0, - 'size' => 100, + 'size' => null, ))); $this->assertEquals($file, $bag->get('file')); @@ -89,7 +89,7 @@ public function testShouldNotRemoveEmptyUploadedFilesForAssociativeArray() public function testShouldConvertUploadedFilesWithPhpBug() { $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain'); $bag = new FileBag(array( 'child' => array( @@ -106,7 +106,7 @@ public function testShouldConvertUploadedFilesWithPhpBug() 'file' => 0, ), 'size' => array( - 'file' => 100, + 'file' => null, ), ), )); @@ -118,7 +118,7 @@ public function testShouldConvertUploadedFilesWithPhpBug() public function testShouldConvertNestedUploadedFilesWithPhpBug() { $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain'); $bag = new FileBag(array( 'child' => array( @@ -135,7 +135,7 @@ public function testShouldConvertNestedUploadedFilesWithPhpBug() 'sub' => array('file' => 0), ), 'size' => array( - 'sub' => array('file' => 100), + 'sub' => array('file' => null), ), ), )); @@ -147,7 +147,7 @@ public function testShouldConvertNestedUploadedFilesWithPhpBug() public function testShouldNotConvertNestedUploadedFiles() { $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain'); $bag = new FileBag(array('image' => array('file' => $file))); $files = $bag->all(); @@ -156,7 +156,10 @@ public function testShouldNotConvertNestedUploadedFiles() protected function createTempFile() { - return tempnam(sys_get_temp_dir().'/form_test', 'FormTest'); + $tempFile = tempnam(sys_get_temp_dir().'/form_test', 'FormTest'); + file_put_contents($tempFile, '1'); + + return $tempFile; } protected function setUp() diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 38b61c24a669c..544467b0451d1 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1072,21 +1072,6 @@ public function testContentAsResource() $this->assertEquals('My other content', $req->getContent()); } - /** - * @expectedException \LogicException - * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider - */ - public function testGetContentCantBeCalledTwiceWithResources($first, $second) - { - if (\PHP_VERSION_ID >= 50600) { - $this->markTestSkipped('PHP >= 5.6 allows to open php://input several times.'); - } - - $req = new Request(); - $req->getContent($first); - $req->getContent($second); - } - public function getContentCantBeCalledTwiceWithResourcesProvider() { return array( @@ -1097,7 +1082,6 @@ public function getContentCantBeCalledTwiceWithResourcesProvider() /** * @dataProvider getContentCanBeCalledTwiceWithResourcesProvider - * @requires PHP 5.6 */ public function testGetContentCanBeCalledTwiceWithResources($first, $second) { @@ -1764,53 +1748,6 @@ public function testTrustedProxiesXForwardedFor() $this->assertTrue($request->isSecure()); } - /** - * @group legacy - * @expectedDeprecation The "Symfony\Component\HttpFoundation\Request::setTrustedHeaderName()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. - */ - public function testLegacyTrustedProxies() - { - $request = Request::create('http://example.com/'); - $request->server->set('REMOTE_ADDR', '3.3.3.3'); - $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2'); - $request->headers->set('X_FORWARDED_HOST', 'foo.example.com, real.example.com:8080'); - $request->headers->set('X_FORWARDED_PROTO', 'https'); - $request->headers->set('X_FORWARDED_PORT', 443); - $request->headers->set('X_MY_FOR', '3.3.3.3, 4.4.4.4'); - $request->headers->set('X_MY_HOST', 'my.example.com'); - $request->headers->set('X_MY_PROTO', 'http'); - $request->headers->set('X_MY_PORT', 81); - - Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL); - - // custom header names - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_MY_FOR'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_MY_HOST'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_MY_PORT'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_MY_PROTO'); - $this->assertEquals('4.4.4.4', $request->getClientIp()); - $this->assertEquals('my.example.com', $request->getHost()); - $this->assertEquals(81, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // disabling via empty header names - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, null); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, null); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, null); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, null); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - //reset - Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO'); - } - public function testTrustedProxiesForwarded() { $request = Request::create('http://example.com/'); @@ -1860,26 +1797,6 @@ public function testTrustedProxiesForwarded() $this->assertTrue($request->isSecure()); } - /** - * @group legacy - * @expectedException \InvalidArgumentException - */ - public function testSetTrustedProxiesInvalidHeaderName() - { - Request::create('http://example.com/'); - Request::setTrustedHeaderName('bogus name', 'X_MY_FOR'); - } - - /** - * @group legacy - * @expectedException \InvalidArgumentException - */ - public function testGetTrustedProxiesInvalidHeaderName() - { - Request::create('http://example.com/'); - Request::getTrustedHeaderName('bogus name'); - } - /** * @dataProvider iisRequestUriProvider */ @@ -2135,14 +2052,13 @@ public function methodSafeProvider() } /** - * @group legacy - * @expectedDeprecation Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead. + * @expectedException \BadMethodCallException */ public function testMethodSafeChecksCacheable() { $request = new Request(); $request->setMethod('OPTIONS'); - $this->assertFalse($request->isMethodSafe()); + $request->isMethodSafe(); } /** @@ -2171,61 +2087,6 @@ public function methodCacheableProvider() ); } - /** - * @group legacy - */ - public function testGetTrustedHeaderName() - { - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL); - - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertSame('X_FORWARDED_FOR', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertSame('X_FORWARDED_HOST', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertSame('X_FORWARDED_PORT', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertSame('X_FORWARDED_PROTO', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED); - - $this->assertSame('FORWARDED', Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'A'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'B'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'C'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'D'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'E'); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED); - - $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL); - - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertSame('B', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertSame('C', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertSame('D', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertSame('E', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED); - - $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - - //reset - Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO'); - } - /** * @dataProvider protocolVersionProvider */ diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index 350d972a94588..b35727962eacd 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -300,7 +300,7 @@ public function testGetMaxAge() $response = new Response(); $response->headers->set('Cache-Control', 'must-revalidate'); $response->headers->set('Expires', -1); - $this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822)); + $this->assertLessThanOrEqual(time() - 2*86400, $response->getExpires()->format('U')); $response = new Response(); $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available'); @@ -653,6 +653,22 @@ public function testIsImmutable() $this->assertTrue($response->isImmutable()); } + public function testSetDate() + { + $response = new Response(); + $response->setDate(\DateTime::createFromFormat(\DateTime::ATOM, '2013-01-26T09:21:56+0100', new \DateTimeZone('Europe/Berlin'))); + + $this->assertEquals('2013-01-26T08:21:56+00:00', $response->getDate()->format(\DateTime::ATOM)); + } + + public function testSetDateWithImmutable() + { + $response = new Response(); + $response->setDate(\DateTimeImmutable::createFromFormat(\DateTime::ATOM, '2013-01-26T09:21:56+0100', new \DateTimeZone('Europe/Berlin'))); + + $this->assertEquals('2013-01-26T08:21:56+00:00', $response->getDate()->format(\DateTime::ATOM)); + } + public function testSetExpires() { $response = new Response(); @@ -666,6 +682,16 @@ public function testSetExpires() $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp()); } + public function testSetExpiresWithImmutable() + { + $response = new Response(); + + $now = $this->createDateTimeImmutableNow(); + $response->setExpires($now); + + $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp()); + } + public function testSetLastModified() { $response = new Response(); @@ -676,6 +702,16 @@ public function testSetLastModified() $this->assertNull($response->getLastModified()); } + public function testSetLastModifiedWithImmutable() + { + $response = new Response(); + $response->setLastModified($this->createDateTimeImmutableNow()); + $this->assertNotNull($response->getLastModified()); + + $response->setLastModified(null); + $this->assertNull($response->getLastModified()); + } + public function testIsInvalid() { $response = new Response(); @@ -912,6 +948,13 @@ protected function createDateTimeNow() return $date->setTimestamp(time()); } + protected function createDateTimeImmutableNow() + { + $date = new \DateTimeImmutable(); + + return $date->setTimestamp(time()); + } + protected function provideResponse() { return new Response(); @@ -990,14 +1033,3 @@ public function __toString() class DefaultResponse extends Response { } - -class ExtendedResponse extends Response -{ - public function setLastModified(\DateTime $date = null) - { - } - - public function getDate() - { - } -} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php new file mode 100644 index 0000000000000..dd72525f5d39d --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php @@ -0,0 +1,177 @@ + + * + * 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\RedisSessionHandler; + +/** + * @requires extension redis + * @group time-sensitive + */ +abstract class AbstractRedisSessionHandlerTestCase extends TestCase +{ + protected const PREFIX = 'prefix_'; + + /** + * @var RedisSessionHandler + */ + protected $storage; + + /** + * @var \Redis|\RedisArray|\RedisCluster|\Predis\Client + */ + protected $redisClient; + + /** + * @var \Redis + */ + protected $validator; + + /** + * @return \Redis|\RedisArray|\RedisCluster|\Predis\Client + */ + abstract protected function createRedisClient(string $host); + + protected function setUp() + { + parent::setUp(); + + if (!extension_loaded('redis')) { + self::markTestSkipped('Extension redis required.'); + } + + $host = getenv('REDIS_HOST') ?: 'localhost'; + + $this->validator = new \Redis(); + $this->validator->connect($host); + + $this->redisClient = $this->createRedisClient($host); + $this->storage = new RedisSessionHandler( + $this->redisClient, + array('prefix' => self::PREFIX) + ); + } + + protected function tearDown() + { + $this->redisClient = null; + $this->storage = null; + + parent::tearDown(); + } + + public function testOpenSession() + { + $this->assertTrue($this->storage->open('', '')); + } + + public function testCloseSession() + { + $this->assertTrue($this->storage->close()); + } + + public function testReadSession() + { + $this->setFixture(self::PREFIX.'id1', null); + $this->setFixture(self::PREFIX.'id2', 'abc123'); + + $this->assertEquals('', $this->storage->read('id1')); + $this->assertEquals('abc123', $this->storage->read('id2')); + } + + public function testWriteSession() + { + $this->assertTrue($this->storage->write('id', 'data')); + + $this->assertTrue($this->hasFixture(self::PREFIX.'id')); + $this->assertEquals('data', $this->getFixture(self::PREFIX.'id')); + } + + public function testUseSessionGcMaxLifetimeAsTimeToLive() + { + $this->storage->write('id', 'data'); + $ttl = $this->fixtureTtl(self::PREFIX.'id'); + + $this->assertLessThanOrEqual(ini_get('session.gc_maxlifetime'), $ttl); + $this->assertGreaterThanOrEqual(0, $ttl); + } + + public function testDestroySession() + { + $this->setFixture(self::PREFIX.'id', 'foo'); + + $this->assertTrue($this->hasFixture(self::PREFIX.'id')); + $this->assertTrue($this->storage->destroy('id')); + $this->assertFalse($this->hasFixture(self::PREFIX.'id')); + } + + public function testGcSession() + { + $this->assertTrue($this->storage->gc(123)); + } + + public function testUpdateTimestamp() + { + $lowTTL = 10; + + $this->setFixture(self::PREFIX.'id', 'foo', $lowTTL); + $this->storage->updateTimestamp('id', array()); + + $this->assertGreaterThan($lowTTL, $this->fixtureTtl(self::PREFIX.'id')); + } + + /** + * @dataProvider getOptionFixtures + */ + public function testSupportedParam(array $options, bool $supported) + { + try { + new RedisSessionHandler($this->redisClient, $options); + $this->assertTrue($supported); + } catch (\InvalidArgumentException $e) { + $this->assertFalse($supported); + } + } + + public function getOptionFixtures(): array + { + return array( + array(array('prefix' => 'session'), true), + array(array('prefix' => 'sfs', 'foo' => 'bar'), false), + ); + } + + protected function setFixture($key, $value, $ttl = null) + { + if (null !== $ttl) { + $this->validator->setex($key, $ttl, $value); + } else { + $this->validator->set($key, $value); + } + } + + protected function getFixture($key) + { + return $this->validator->get($key); + } + + protected function hasFixture($key): bool + { + return $this->validator->exists($key); + } + + protected function fixtureTtl($key): int + { + return $this->validator->ttl($key); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php index 3ac081e3884c1..6566d6eee4f7a 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php @@ -13,9 +13,6 @@ use PHPUnit\Framework\TestCase; -/** - * @requires PHP 7.0 - */ class AbstractSessionHandlerTest extends TestCase { private static $server; diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php deleted file mode 100644 index dda43c805ba7d..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * 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\MemcacheSessionHandler; - -/** - * @requires extension memcache - * @group time-sensitive - * @group legacy - */ -class MemcacheSessionHandlerTest extends TestCase -{ - const PREFIX = 'prefix_'; - const TTL = 1000; - - /** - * @var MemcacheSessionHandler - */ - protected $storage; - - protected $memcache; - - protected function setUp() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } - - parent::setUp(); - $this->memcache = $this->getMockBuilder('Memcache')->getMock(); - $this->storage = new MemcacheSessionHandler( - $this->memcache, - array('prefix' => self::PREFIX, 'expiretime' => self::TTL) - ); - } - - protected function tearDown() - { - $this->memcache = null; - $this->storage = null; - parent::tearDown(); - } - - public function testOpenSession() - { - $this->assertTrue($this->storage->open('', '')); - } - - public function testCloseSession() - { - $this->assertTrue($this->storage->close()); - } - - public function testReadSession() - { - $this->memcache - ->expects($this->once()) - ->method('get') - ->with(self::PREFIX.'id') - ; - - $this->assertEquals('', $this->storage->read('id')); - } - - public function testWriteSession() - { - $this->memcache - ->expects($this->once()) - ->method('set') - ->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2)) - ->will($this->returnValue(true)) - ; - - $this->assertTrue($this->storage->write('id', 'data')); - } - - public function testDestroySession() - { - $this->memcache - ->expects($this->once()) - ->method('delete') - ->with(self::PREFIX.'id') - ->will($this->returnValue(true)) - ; - - $this->assertTrue($this->storage->destroy('id')); - } - - public function testGcSession() - { - $this->assertTrue($this->storage->gc(123)); - } - - /** - * @dataProvider getOptionFixtures - */ - public function testSupportedOptions($options, $supported) - { - try { - new MemcacheSessionHandler($this->memcache, $options); - $this->assertTrue($supported); - } catch (\InvalidArgumentException $e) { - $this->assertFalse($supported); - } - } - - public function getOptionFixtures() - { - return array( - array(array('prefix' => 'session'), true), - array(array('expiretime' => 100), true), - array(array('prefix' => 'session', 'expiretime' => 200), true), - array(array('expiretime' => 100, 'foo' => 'bar'), false), - ); - } - - public function testGetConnection() - { - $method = new \ReflectionMethod($this->storage, 'getMemcache'); - $method->setAccessible(true); - - $this->assertInstanceOf('\Memcache', $method->invoke($this->storage)); - } -} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php index 2e7be359efcff..5bb2db0699292 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php @@ -32,10 +32,6 @@ class MemcachedSessionHandlerTest extends TestCase protected function setUp() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } - parent::setUp(); if (version_compare(phpversion('memcached'), '2.2.0', '>=') && version_compare(phpversion('memcached'), '3.0.0b1', '<')) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php index da051096c8730..55a3864a9ff29 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -17,7 +17,7 @@ /** * @author Markus Bachmann * @group time-sensitive - * @group legacy + * @requires extension mongodb */ class MongoDbSessionHandlerTest extends TestCase { @@ -32,21 +32,11 @@ protected function setUp() { parent::setUp(); - if (extension_loaded('mongodb')) { - if (!class_exists('MongoDB\Client')) { - $this->markTestSkipped('The mongodb/mongodb package is required.'); - } - } elseif (!extension_loaded('mongo')) { - $this->markTestSkipped('The Mongo or MongoDB extension is required.'); + if (!class_exists(\MongoDB\Client::class)) { + $this->markTestSkipped('The mongodb/mongodb package is required.'); } - if (phpversion('mongodb')) { - $mongoClass = 'MongoDB\Client'; - } else { - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; - } - - $this->mongo = $this->getMockBuilder($mongoClass) + $this->mongo = $this->getMockBuilder(\MongoDB\Client::class) ->disableOriginalConstructor() ->getMock(); @@ -62,14 +52,6 @@ protected function setUp() $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); } - /** - * @expectedException \InvalidArgumentException - */ - public function testConstructorShouldThrowExceptionForInvalidMongo() - { - new MongoDbSessionHandler(new \stdClass(), $this->options); - } - /** * @expectedException \InvalidArgumentException */ @@ -110,27 +92,14 @@ public function testRead() $this->assertArrayHasKey($this->options['expiry_field'], $criteria); $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]); - if (phpversion('mongodb')) { - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout); - } else { - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); - } + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $criteria[$this->options['expiry_field']]['$gte']); + $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout); - $fields = array( + return array( $this->options['id_field'] => 'foo', + $this->options['expiry_field'] => new \MongoDB\BSON\UTCDateTime(), + $this->options['data_field'] => new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY), ); - - if (phpversion('mongodb')) { - $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY); - $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000); - } else { - $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY); - $fields[$this->options['id_field']] = new \MongoDate(); - } - - return $fields; })); $this->assertEquals('bar', $this->storage->read('foo')); @@ -145,89 +114,22 @@ public function testWrite() ->with($this->options['database'], $this->options['collection']) ->will($this->returnValue($collection)); - $data = array(); - - $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; - $collection->expects($this->once()) - ->method($methodName) - ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { + ->method('updateOne') + ->will($this->returnCallback(function ($criteria, $updateData, $options) { $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - - if (phpversion('mongodb')) { - $this->assertEquals(array('upsert' => true), $options); - } else { - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); - } + $this->assertEquals(array('upsert' => true), $options); $data = $updateData['$set']; + $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); + $this->assertInstanceOf(\MongoDB\BSON\Binary::class, $data[$this->options['data_field']]); + $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $data[$this->options['time_field']]); + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $data[$this->options['expiry_field']]); + $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000)); })); - $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); $this->assertTrue($this->storage->write('foo', 'bar')); - - if (phpversion('mongodb')) { - $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); - $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000)); - } else { - $this->assertEquals('bar', $data[$this->options['data_field']]->bin); - $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); - $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec); - } - } - - public function testWriteWhenUsingExpiresField() - { - $this->options = array( - 'id_field' => '_id', - 'data_field' => 'data', - 'time_field' => 'time', - 'database' => 'sf2-test', - 'collection' => 'session-test', - 'expiry_field' => 'expiresAt', - ); - - $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); - - $collection = $this->createMongoCollectionMock(); - - $this->mongo->expects($this->once()) - ->method('selectCollection') - ->with($this->options['database'], $this->options['collection']) - ->will($this->returnValue($collection)); - - $data = array(); - - $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; - - $collection->expects($this->once()) - ->method($methodName) - ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { - $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - - if (phpversion('mongodb')) { - $this->assertEquals(array('upsert' => true), $options); - } else { - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); - } - - $data = $updateData['$set']; - })); - - $this->assertTrue($this->storage->write('foo', 'bar')); - - if (phpversion('mongodb')) { - $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); - } else { - $this->assertEquals('bar', $data[$this->options['data_field']]->bin); - $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); - } } public function testReplaceSessionData() @@ -241,10 +143,8 @@ public function testReplaceSessionData() $data = array(); - $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; - $collection->expects($this->exactly(2)) - ->method($methodName) + ->method('updateOne') ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { $data = $updateData; })); @@ -252,11 +152,7 @@ public function testReplaceSessionData() $this->storage->write('foo', 'bar'); $this->storage->write('foo', 'foobar'); - if (phpversion('mongodb')) { - $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData()); - } else { - $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin); - } + $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData()); } public function testDestroy() @@ -268,10 +164,8 @@ public function testDestroy() ->with($this->options['database'], $this->options['collection']) ->will($this->returnValue($collection)); - $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove'; - $collection->expects($this->once()) - ->method($methodName) + ->method('deleteOne') ->with(array($this->options['id_field'] => 'foo')); $this->assertTrue($this->storage->destroy('foo')); @@ -286,18 +180,11 @@ public function testGc() ->with($this->options['database'], $this->options['collection']) ->will($this->returnValue($collection)); - $methodName = phpversion('mongodb') ? 'deleteMany' : 'remove'; - $collection->expects($this->once()) - ->method($methodName) + ->method('deleteMany') ->will($this->returnCallback(function ($criteria) { - if (phpversion('mongodb')) { - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000)); - } else { - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); - } + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $criteria[$this->options['expiry_field']]['$lt']); + $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000)); })); $this->assertTrue($this->storage->gc(1)); @@ -308,23 +195,12 @@ public function testGetConnection() $method = new \ReflectionMethod($this->storage, 'getMongo'); $method->setAccessible(true); - if (phpversion('mongodb')) { - $mongoClass = 'MongoDB\Client'; - } else { - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; - } - - $this->assertInstanceOf($mongoClass, $method->invoke($this->storage)); + $this->assertInstanceOf(\MongoDB\Client::class, $method->invoke($this->storage)); } private function createMongoCollectionMock() { - $collectionClass = 'MongoCollection'; - if (phpversion('mongodb')) { - $collectionClass = 'MongoDB\Collection'; - } - - $collection = $this->getMockBuilder($collectionClass) + $collection = $this->getMockBuilder(\MongoDB\Collection::class) ->disableOriginalConstructor() ->getMock(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php index a6264e51d2a72..95e725f4bc8a6 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php @@ -29,7 +29,6 @@ public function testConstruct() { $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir())); - $this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName()); $this->assertEquals('user', ini_get('session.save_handler')); $this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path')); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php deleted file mode 100644 index 4a9fb600d2ddd..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * 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\NativeSessionHandler; - -/** - * Test class for NativeSessionHandler. - * - * @author Drak - * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - * @group legacy - */ -class NativeSessionHandlerTest extends TestCase -{ - /** - * @expectedDeprecation The Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead. - */ - public function testConstruct() - { - $handler = new NativeSessionHandler(); - - $this->assertInstanceOf('SessionHandler', $handler); - $this->assertTrue($handler instanceof NativeSessionHandler); - } -} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index a0d7529f06a86..75a65597a5264 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -136,10 +136,6 @@ public function testReadWriteReadWithNullByte() public function testReadConvertsStreamToString() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } - $pdo = new MockPdo('pgsql'); $pdo->prepareResult = $this->getMockBuilder('PDOStatement')->getMock(); @@ -157,9 +153,6 @@ public function testReadConvertsStreamToString() public function testReadLockedConvertsStreamToString() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } if (ini_get('session.use_strict_mode')) { $this->markTestSkipped('Strict mode needs no locking for new sessions.'); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php new file mode 100644 index 0000000000000..ffb2d41a536ce --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php @@ -0,0 +1,22 @@ + + * + * 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 Predis\Client; + +class PredisClusterSessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): Client + { + return new Client(array(array('host' => $host))); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php new file mode 100644 index 0000000000000..a9db4eb1bfa01 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php @@ -0,0 +1,22 @@ + + * + * 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 Predis\Client; + +class PredisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): Client + { + return new Client(array('host' => $host)); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php new file mode 100644 index 0000000000000..d263e18ff7828 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php @@ -0,0 +1,20 @@ + + * + * 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; + +class RedisArraySessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): \RedisArray + { + return new \RedisArray(array($host)); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php new file mode 100644 index 0000000000000..afdb6c503b659 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php @@ -0,0 +1,23 @@ + + * + * 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; + +class RedisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): \Redis + { + $client = new \Redis(); + $client->connect($host); + + return $client; + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php deleted file mode 100644 index 898a7d11a5ae2..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * 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 - * - * @group legacy - */ -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')); - } -} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php index 958dc0bc08984..0b3b7e141e0fa 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php @@ -64,13 +64,12 @@ public function testPhpSession() { $storage = $this->getStorage(); - $this->assertFalse($storage->getSaveHandler()->isActive()); + $this->assertNotSame(\PHP_SESSION_ACTIVE, session_status()); $this->assertFalse($storage->isStarted()); session_start(); $this->assertTrue(isset($_SESSION)); - // in PHP 5.4 we can reliably detect a session started - $this->assertTrue($storage->getSaveHandler()->isActive()); + $this->assertSame(\PHP_SESSION_ACTIVE, session_status()); // PHP session might have started, but the storage driver has not, so false is correct here $this->assertFalse($storage->isStarted()); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/NativeProxyTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/NativeProxyTest.php deleted file mode 100644 index ed4fee6bfec0f..0000000000000 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/NativeProxyTest.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * 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\NativeProxy; - -/** - * Test class for NativeProxy. - * - * @group legacy - * - * @author Drak - */ -class NativeProxyTest extends TestCase -{ - public function testIsWrapper() - { - $proxy = new NativeProxy(); - $this->assertFalse($proxy->isWrapper()); - } - - public function testGetSaveHandlerName() - { - $name = ini_get('session.save_handler'); - $proxy = new NativeProxy(); - $this->assertEquals($name, $proxy->getSaveHandlerName()); - } -} diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index f6c6f2e623fe6..ef4bf826e5454 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -16,12 +16,12 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php70": "~1.6" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { - "symfony/expression-language": "~2.8|~3.0|~4.0" + "predis/predis": "~1.0", + "symfony/expression-language": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/HttpKernel/Bundle/Bundle.php b/src/Symfony/Component/HttpKernel/Bundle/Bundle.php index cfa576c367919..46a0c893d8df0 100644 --- a/src/Symfony/Component/HttpKernel/Bundle/Bundle.php +++ b/src/Symfony/Component/HttpKernel/Bundle/Bundle.php @@ -15,7 +15,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Console\Application; -use Symfony\Component\Finder\Finder; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; /** @@ -127,15 +126,6 @@ public function getPath() return $this->path; } - /** - * Returns the bundle parent name. - * - * @return string|null The Bundle parent name it overrides or null if no parent - */ - public function getParent() - { - } - /** * Returns the bundle name (the class short name). * @@ -150,48 +140,8 @@ final public function getName() return $this->name; } - /** - * Finds and registers Commands. - * - * Override this method if your bundle commands do not follow the conventions: - * - * * Commands are in the 'Command' sub-directory - * * Commands extend Symfony\Component\Console\Command\Command - */ public function registerCommands(Application $application) { - if (!is_dir($dir = $this->getPath().'/Command')) { - return; - } - - if (!class_exists('Symfony\Component\Finder\Finder')) { - throw new \RuntimeException('You need the symfony/finder component to register bundle commands.'); - } - - $finder = new Finder(); - $finder->files()->name('*Command.php')->in($dir); - - $prefix = $this->getNamespace().'\\Command'; - foreach ($finder as $file) { - $ns = $prefix; - if ($relativePath = $file->getRelativePath()) { - $ns .= '\\'.str_replace('/', '\\', $relativePath); - } - $class = $ns.'\\'.$file->getBasename('.php'); - if ($this->container) { - $commandIds = $this->container->hasParameter('console.command.ids') ? $this->container->getParameter('console.command.ids') : array(); - $alias = 'console.command.'.strtolower(str_replace('\\', '_', $class)); - if (isset($commandIds[$alias]) || $this->container->has($alias)) { - continue; - } - } - $r = new \ReflectionClass($class); - if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) { - @trigger_error(sprintf('Auto-registration of the command "%s" is deprecated since Symfony 3.4 and won\'t be supported in 4.0. Use PSR-4 based service discovery instead.', $class), E_USER_DEPRECATED); - - $application->add($r->newInstance()); - } - } } /** diff --git a/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php b/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php index 14a7f6f4fd3a3..88a95d8332942 100644 --- a/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php +++ b/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php @@ -46,19 +46,6 @@ public function build(ContainerBuilder $container); */ public function getContainerExtension(); - /** - * Returns the bundle name that this bundle overrides. - * - * Despite its name, this method does not imply any parent/child relationship - * between the bundles, just a way to extend and override an existing - * bundle. - * - * @return string The Bundle name it overrides or null if no parent - * - * @deprecated This method is deprecated as of 3.4 and will be removed in 4.0. - */ - public function getParent(); - /** * Returns the bundle name (the class short name). * diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index fb29f76962928..e22c724085cb4 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -1,6 +1,36 @@ CHANGELOG ========= +4.1.0 +----- + + * added orphaned events support to `EventDataCollector` + * `ExceptionListener` now logs and collects exceptions at priority `2048` (previously logged at `-128` and collected at `0`) + +4.0.0 +----- + + * removed the `DataCollector::varToString()` method, use `DataCollector::cloneVar()` + instead + * using the `DataCollector::cloneVar()` method requires the VarDumper component + * removed the `ValueExporter` class + * removed `ControllerResolverInterface::getArguments()` + * removed `TraceableControllerResolver::getArguments()` + * removed `ControllerResolver::getArguments()` and the ability to resolve arguments + * removed the `argument_resolver` service dependency from the `debug.controller_resolver` + * removed `LazyLoadingFragmentHandler::addRendererService()` + * removed `Psr6CacheClearer::addPool()` + * removed `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` + * removed `Kernel::loadClassCache()`, `Kernel::doLoadClassCache()`, `Kernel::setClassCache()`, + and `Kernel::getEnvParameters()` + * support for the `X-Status-Code` when handling exceptions in the `HttpKernel` + has been dropped, use the `HttpKernel::allowCustomResponseCode()` method + instead + * removed convention-based commands registration + * removed the `ChainCacheClearer::add()` method + * removed the `CacheaWarmerAggregate::add()` and `setWarmers()` methods + * made `CacheWarmerAggregate` and `ChainCacheClearer` classes final + 3.4.0 ----- diff --git a/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php b/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php index 8ee4275b16867..6aabdc0917efa 100644 --- a/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php +++ b/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php @@ -20,14 +20,9 @@ */ class ChainCacheClearer implements CacheClearerInterface { - protected $clearers; + private $clearers; - /** - * Constructs a new instance of ChainCacheClearer. - * - * @param array $clearers The initial clearers - */ - public function __construct($clearers = array()) + public function __construct(iterable $clearers = array()) { $this->clearers = $clearers; } @@ -41,16 +36,4 @@ public function clear($cacheDir) $clearer->clear($cacheDir); } } - - /** - * Adds a cache clearer to the aggregate. - * - * @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead. - */ - public function add(CacheClearerInterface $clearer) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), E_USER_DEPRECATED); - - $this->clearers[] = $clearer; - } } diff --git a/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php b/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php index d413d2c9d641d..f54ca96e994e7 100644 --- a/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php +++ b/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php @@ -11,8 +11,6 @@ namespace Symfony\Component\HttpKernel\CacheClearer; -use Psr\Cache\CacheItemPoolInterface; - /** * @author Nicolas Grekas */ @@ -25,13 +23,6 @@ public function __construct(array $pools = array()) $this->pools = $pools; } - public function addPool(CacheItemPoolInterface $pool) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass an array of pools indexed by name to the constructor instead.', __METHOD__), E_USER_DEPRECATED); - - $this->pools[] = $pool; - } - public function hasPool($name) { return isset($this->pools[$name]); diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php index ca3911ed5f9dc..b188fc5860b2b 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php +++ b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php @@ -20,16 +20,12 @@ */ class CacheWarmerAggregate implements CacheWarmerInterface { - protected $warmers = array(); - protected $optionalsEnabled = false; - private $triggerDeprecation = false; + private $warmers; + private $optionalsEnabled = false; - public function __construct($warmers = array()) + public function __construct(iterable $warmers = array()) { - foreach ($warmers as $warmer) { - $this->add($warmer); - } - $this->triggerDeprecation = true; + $this->warmers = $warmers; } public function enableOptionalWarmers() @@ -62,29 +58,4 @@ public function isOptional() { return false; } - - /** - * @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead. - */ - public function setWarmers(array $warmers) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), E_USER_DEPRECATED); - - $this->warmers = array(); - foreach ($warmers as $warmer) { - $this->add($warmer); - } - } - - /** - * @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead. - */ - public function add(CacheWarmerInterface $warmer) - { - if ($this->triggerDeprecation) { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), E_USER_DEPRECATED); - } - - $this->warmers[] = $warmer; - } } diff --git a/src/Symfony/Component/HttpKernel/Client.php b/src/Symfony/Component/HttpKernel/Client.php index 7979722d164f9..cc744b398c94f 100644 --- a/src/Symfony/Component/HttpKernel/Client.php +++ b/src/Symfony/Component/HttpKernel/Client.php @@ -168,7 +168,6 @@ protected function filterFiles(array $files) '', $value->getClientOriginalName(), $value->getClientMimeType(), - 0, UPLOAD_ERR_INI_SIZE, true ); @@ -177,7 +176,6 @@ protected function filterFiles(array $files) $value->getPathname(), $value->getClientOriginalName(), $value->getClientMimeType(), - $value->getClientSize(), $value->getError(), true ); diff --git a/src/Symfony/Component/HttpKernel/Config/EnvParametersResource.php b/src/Symfony/Component/HttpKernel/Config/EnvParametersResource.php deleted file mode 100644 index f8d2a72b2a9b9..0000000000000 --- a/src/Symfony/Component/HttpKernel/Config/EnvParametersResource.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpKernel\Config; - -use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; - -/** - * EnvParametersResource represents resources stored in prefixed environment variables. - * - * @author Chris Wilkinson - * - * @deprecated since version 3.4, to be removed in 4.0 - */ -class EnvParametersResource implements SelfCheckingResourceInterface, \Serializable -{ - /** - * @var string - */ - private $prefix; - - /** - * @var string - */ - private $variables; - - /** - * @param string $prefix - */ - public function __construct($prefix) - { - $this->prefix = $prefix; - $this->variables = $this->findVariables(); - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return serialize($this->getResource()); - } - - /** - * @return array An array with two keys: 'prefix' for the prefix used and 'variables' containing all the variables watched by this resource - */ - public function getResource() - { - return array('prefix' => $this->prefix, 'variables' => $this->variables); - } - - /** - * {@inheritdoc} - */ - public function isFresh($timestamp) - { - return $this->findVariables() === $this->variables; - } - - public function serialize() - { - return serialize(array('prefix' => $this->prefix, 'variables' => $this->variables)); - } - - public function unserialize($serialized) - { - if (\PHP_VERSION_ID >= 70000) { - $unserialized = unserialize($serialized, array('allowed_classes' => false)); - } else { - $unserialized = unserialize($serialized); - } - - $this->prefix = $unserialized['prefix']; - $this->variables = $unserialized['variables']; - } - - private function findVariables() - { - $variables = array(); - - foreach ($_SERVER as $key => $value) { - if (0 === strpos($key, $this->prefix)) { - $variables[$key] = $value; - } - } - - ksort($variables); - - return $variables; - } -} diff --git a/src/Symfony/Component/HttpKernel/Config/FileLocator.php b/src/Symfony/Component/HttpKernel/Config/FileLocator.php index fb1f913bdff5d..926f0d1a28e14 100644 --- a/src/Symfony/Component/HttpKernel/Config/FileLocator.php +++ b/src/Symfony/Component/HttpKernel/Config/FileLocator.php @@ -29,7 +29,7 @@ class FileLocator extends BaseFileLocator * @param null|string $path The path the global resource directory * @param array $paths An array of paths where to look for resources */ - public function __construct(KernelInterface $kernel, $path = null, array $paths = array()) + public function __construct(KernelInterface $kernel, string $path = null, array $paths = array()) { $this->kernel = $kernel; if (null !== $path) { diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php index 2c17125c5aca1..186007ebe49c9 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php @@ -34,7 +34,7 @@ final class ArgumentResolver implements ArgumentResolverInterface */ private $argumentValueResolvers; - public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, $argumentValueResolvers = array()) + public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, iterable $argumentValueResolvers = array()) { $this->argumentMetadataFactory = $argumentMetadataFactory ?: new ArgumentMetadataFactory(); $this->argumentValueResolvers = $argumentValueResolvers ?: self::getDefaultArgumentValueResolvers(); @@ -81,7 +81,7 @@ public function getArguments(Request $request, $controller) return $arguments; } - public static function getDefaultArgumentValueResolvers() + public static function getDefaultArgumentValueResolvers(): iterable { return array( new RequestAttributeValueResolver(), diff --git a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php index 186583d7be966..426e6728839cc 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php @@ -99,8 +99,6 @@ protected function instantiateController($class) try { return parent::instantiateController($class); } catch (\ArgumentCountError $e) { - } catch (\ErrorException $e) { - } catch (\TypeError $e) { } $this->throwExceptionIfControllerWasRemoved($class, $e); diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerReference.php b/src/Symfony/Component/HttpKernel/Controller/ControllerReference.php index fae4e7fa449bc..f424fdc833ab7 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerReference.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerReference.php @@ -35,7 +35,7 @@ class ControllerReference * @param array $attributes An array of parameters to add to the Request attributes * @param array $query An array of parameters to add to the Request query string */ - public function __construct($controller, array $attributes = array(), array $query = array()) + public function __construct(string $controller, array $attributes = array(), array $query = array()) { $this->controller = $controller; $this->attributes = $attributes; diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php index b9d9f9fa254d9..4d4d5ac129dd6 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php @@ -21,32 +21,13 @@ * * @author Fabien Potencier */ -class ControllerResolver implements ArgumentResolverInterface, ControllerResolverInterface +class ControllerResolver implements ControllerResolverInterface { private $logger; - /** - * If the ...$arg functionality is available. - * - * Requires at least PHP 5.6.0 or HHVM 3.9.1 - * - * @var bool - */ - private $supportsVariadic; - - /** - * If scalar types exists. - * - * @var bool - */ - private $supportsScalarTypes; - public function __construct(LoggerInterface $logger = null) { $this->logger = $logger; - - $this->supportsVariadic = method_exists('ReflectionParameter', 'isVariadic'); - $this->supportsScalarTypes = method_exists('ReflectionParameter', 'getType'); } /** @@ -94,71 +75,6 @@ public function getController(Request $request) return $callable; } - /** - * {@inheritdoc} - * - * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface and inject it in the HttpKernel instead. - */ - public function getArguments(Request $request, $controller) - { - @trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED); - - if (is_array($controller)) { - $r = new \ReflectionMethod($controller[0], $controller[1]); - } elseif (is_object($controller) && !$controller instanceof \Closure) { - $r = new \ReflectionObject($controller); - $r = $r->getMethod('__invoke'); - } else { - $r = new \ReflectionFunction($controller); - } - - return $this->doGetArguments($request, $controller, $r->getParameters()); - } - - /** - * @param Request $request - * @param callable $controller - * @param \ReflectionParameter[] $parameters - * - * @return array The arguments to use when calling the action - * - * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface and inject it in the HttpKernel instead. - */ - protected function doGetArguments(Request $request, $controller, array $parameters) - { - @trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED); - - $attributes = $request->attributes->all(); - $arguments = array(); - foreach ($parameters as $param) { - if (array_key_exists($param->name, $attributes)) { - if ($this->supportsVariadic && $param->isVariadic() && is_array($attributes[$param->name])) { - $arguments = array_merge($arguments, array_values($attributes[$param->name])); - } else { - $arguments[] = $attributes[$param->name]; - } - } elseif ($param->getClass() && $param->getClass()->isInstance($request)) { - $arguments[] = $request; - } elseif ($param->isDefaultValueAvailable()) { - $arguments[] = $param->getDefaultValue(); - } elseif ($this->supportsScalarTypes && $param->hasType() && $param->allowsNull()) { - $arguments[] = null; - } else { - if (is_array($controller)) { - $repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]); - } elseif (is_object($controller)) { - $repr = get_class($controller); - } else { - $repr = $controller; - } - - throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name)); - } - } - - return $arguments; - } - /** * Returns a callable for the given controller. * diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php index afe9fb733748f..30b73f6b53b9e 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php @@ -40,18 +40,4 @@ interface ControllerResolverInterface * @throws \LogicException If the controller can't be found */ public function getController(Request $request); - - /** - * Returns the arguments to pass to the controller. - * - * @param Request $request A Request instance - * @param callable $controller A PHP callable - * - * @return array An array of arguments to pass to the controller - * - * @throws \RuntimeException When value for argument given is not provided - * - * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Please use the {@see ArgumentResolverInterface} instead. - */ - public function getArguments(Request $request, $controller); } diff --git a/src/Symfony/Component/HttpKernel/Controller/TraceableControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/TraceableControllerResolver.php index 750107714026d..adaaa26d08bbf 100644 --- a/src/Symfony/Component/HttpKernel/Controller/TraceableControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/TraceableControllerResolver.php @@ -17,26 +17,15 @@ /** * @author Fabien Potencier */ -class TraceableControllerResolver implements ControllerResolverInterface, ArgumentResolverInterface +class TraceableControllerResolver implements ControllerResolverInterface { private $resolver; private $stopwatch; - private $argumentResolver; - public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch, ArgumentResolverInterface $argumentResolver = null) + public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch) { $this->resolver = $resolver; $this->stopwatch = $stopwatch; - $this->argumentResolver = $argumentResolver; - - // BC - if (null === $this->argumentResolver) { - $this->argumentResolver = $resolver; - } - - if (!$this->argumentResolver instanceof TraceableArgumentResolver) { - $this->argumentResolver = new TraceableArgumentResolver($this->argumentResolver, $this->stopwatch); - } } /** @@ -52,18 +41,4 @@ public function getController(Request $request) return $ret; } - - /** - * {@inheritdoc} - * - * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. - */ - public function getArguments(Request $request, $controller) - { - @trigger_error(sprintf('The %s method is deprecated as of 3.1 and will be removed in 4.0. Please use the %s instead.', __METHOD__, TraceableArgumentResolver::class), E_USER_DEPRECATED); - - $ret = $this->argumentResolver->getArguments($request, $controller); - - return $ret; - } } diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php index 32316a8d519e7..4ea8ea643feb1 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php @@ -25,15 +25,7 @@ class ArgumentMetadata private $defaultValue; private $isNullable; - /** - * @param string $name - * @param string $type - * @param bool $isVariadic - * @param bool $hasDefaultValue - * @param mixed $defaultValue - * @param bool $isNullable - */ - public function __construct($name, $type, $isVariadic, $hasDefaultValue, $defaultValue, $isNullable = false) + public function __construct(string $name, ?string $type, bool $isVariadic, bool $hasDefaultValue, $defaultValue, bool $isNullable = false) { $this->name = $name; $this->type = $type; diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php index d1e7af206804b..646adc074bf6a 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -18,30 +18,6 @@ */ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface { - /** - * If the ...$arg functionality is available. - * - * Requires at least PHP 5.6.0 or HHVM 3.9.1 - * - * @var bool - */ - private $supportsVariadic; - - /** - * If the reflection supports the getType() method to resolve types. - * - * Requires at least PHP 7.0.0 or HHVM 3.11.0 - * - * @var bool - */ - private $supportsParameterType; - - public function __construct() - { - $this->supportsVariadic = method_exists('ReflectionParameter', 'isVariadic'); - $this->supportsParameterType = method_exists('ReflectionParameter', 'getType'); - } - /** * {@inheritdoc} */ @@ -58,48 +34,12 @@ public function createArgumentMetadata($controller) } foreach ($reflection->getParameters() as $param) { - $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull()); + $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $param->isVariadic(), $param->isDefaultValueAvailable(), $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, $param->allowsNull()); } return $arguments; } - /** - * Returns whether an argument is variadic. - * - * @param \ReflectionParameter $parameter - * - * @return bool - */ - private function isVariadic(\ReflectionParameter $parameter) - { - return $this->supportsVariadic && $parameter->isVariadic(); - } - - /** - * Determines whether an argument has a default value. - * - * @param \ReflectionParameter $parameter - * - * @return bool - */ - private function hasDefaultValue(\ReflectionParameter $parameter) - { - return $parameter->isDefaultValueAvailable(); - } - - /** - * Returns a default value if available. - * - * @param \ReflectionParameter $parameter - * - * @return mixed|null - */ - private function getDefaultValue(\ReflectionParameter $parameter) - { - return $this->hasDefaultValue($parameter) ? $parameter->getDefaultValue() : null; - } - /** * Returns an associated type to the given parameter if available. * @@ -109,21 +49,10 @@ private function getDefaultValue(\ReflectionParameter $parameter) */ private function getType(\ReflectionParameter $parameter) { - if ($this->supportsParameterType) { - if (!$type = $parameter->getType()) { - return; - } - $typeName = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); - if ('array' === $typeName && !$type->isBuiltin()) { - // Special case for HHVM with variadics - return; - } - - return $typeName; + if (!$type = $parameter->getType()) { + return; } - if (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $parameter, $info)) { - return $info[1]; - } + return $type->getName(); } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php index 5675f073639a6..14abce761f889 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php @@ -34,7 +34,7 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte * @param string $name The name of the application using the web profiler * @param string $version The version of the application using the web profiler */ - public function __construct($name = null, $version = null) + public function __construct(string $name = null, string $version = null) { $this->name = $name; $this->version = $version; diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 30887ab91c727..faa0d9e37042b 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -11,7 +11,6 @@ namespace Symfony\Component\HttpKernel\DataCollector; -use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; use Symfony\Component\VarDumper\Caster\CutStub; use Symfony\Component\VarDumper\Cloner\ClonerInterface; use Symfony\Component\VarDumper\Cloner\Data; @@ -30,11 +29,6 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable { protected $data = array(); - /** - * @var ValueExporter - */ - private $valueExporter; - /** * @var ClonerInterface */ @@ -66,46 +60,17 @@ protected function cloneVar($var) return $var; } if (null === $this->cloner) { - if (class_exists(CutStub::class)) { - $this->cloner = new VarCloner(); - $this->cloner->setMaxItems(-1); - $this->cloner->addCasters($this->getCasters()); - } else { - @trigger_error(sprintf('Using the %s() method without the VarDumper component is deprecated since Symfony 3.2 and won\'t be supported in 4.0. Install symfony/var-dumper version 3.2 or above.', __METHOD__), E_USER_DEPRECATED); - $this->cloner = false; + if (!class_exists(CutStub::class)) { + throw new \LogicException(sprintf('The VarDumper component is needed for the %s() method. Install symfony/var-dumper version 3.4 or above.', __METHOD__)); } - } - if (false === $this->cloner) { - if (null === $this->valueExporter) { - $this->valueExporter = new ValueExporter(); - } - - return $this->valueExporter->exportValue($var); + $this->cloner = new VarCloner(); + $this->cloner->setMaxItems(-1); + $this->cloner->addCasters($this->getCasters()); } return $this->cloner->cloneVar($var); } - /** - * Converts a PHP variable to a string. - * - * @param mixed $var A PHP variable - * - * @return string The string representation of the variable - * - * @deprecated since version 3.2, to be removed in 4.0. Use cloneVar() instead. - */ - protected function varToString($var) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use cloneVar() instead.', __METHOD__), E_USER_DEPRECATED); - - if (null === $this->valueExporter) { - $this->valueExporter = new ValueExporter(); - } - - return $this->valueExporter->exportValue($var); - } - /** * @return callable[] The casters to add to the cloner */ diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php index 2470089b810b2..549fd5d3a95f5 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php @@ -18,8 +18,6 @@ * DataCollectorInterface. * * @author Fabien Potencier - * - * @method reset() Resets this data collector to its initial state. */ interface DataCollectorInterface { @@ -34,4 +32,9 @@ public function collect(Request $request, Response $response, \Exception $except * @return string The collector name */ public function getName(); + + /** + * Resets this data collector to its initial state. + */ + public function reset(); } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index d0ca671e056b4..e0297ea395967 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -39,7 +39,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface private $dumper; private $dumperIsInjected; - public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null) + public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, string $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null) { $this->stopwatch = $stopwatch; $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php index 0a095ab685408..f9d5bed130cbd 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php @@ -11,10 +11,11 @@ namespace Symfony\Component\HttpKernel\DataCollector; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; /** * EventDataCollector. @@ -27,9 +28,6 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter public function __construct(EventDispatcherInterface $dispatcher = null) { - if ($dispatcher instanceof TraceableEventDispatcherInterface && !method_exists($dispatcher, 'reset')) { - @trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', TraceableEventDispatcherInterface::class, \get_class($dispatcher)), E_USER_DEPRECATED); - } $this->dispatcher = $dispatcher; } @@ -41,6 +39,7 @@ public function collect(Request $request, Response $response, \Exception $except $this->data = array( 'called_listeners' => array(), 'not_called_listeners' => array(), + 'orphaned_events' => array(), ); } @@ -49,10 +48,6 @@ public function reset() $this->data = array(); if ($this->dispatcher instanceof TraceableEventDispatcherInterface) { - if (!method_exists($this->dispatcher, 'reset')) { - return; // @deprecated - } - $this->dispatcher->reset(); } } @@ -63,6 +58,11 @@ public function lateCollect() $this->setCalledListeners($this->dispatcher->getCalledListeners()); $this->setNotCalledListeners($this->dispatcher->getNotCalledListeners()); } + + if ($this->dispatcher instanceof TraceableEventDispatcher) { + $this->setOrphanedEvents($this->dispatcher->getOrphanedEvents()); + } + $this->data = $this->cloneVar($this->data); } @@ -71,7 +71,7 @@ public function lateCollect() * * @param array $listeners An array of called listeners * - * @see TraceableEventDispatcherInterface + * @see TraceableEventDispatcher */ public function setCalledListeners(array $listeners) { @@ -83,7 +83,7 @@ public function setCalledListeners(array $listeners) * * @return array An array of called listeners * - * @see TraceableEventDispatcherInterface + * @see TraceableEventDispatcher */ public function getCalledListeners() { @@ -93,9 +93,9 @@ public function getCalledListeners() /** * Sets the not called listeners. * - * @param array $listeners An array of not called listeners + * @param array $listeners * - * @see TraceableEventDispatcherInterface + * @see TraceableEventDispatcher */ public function setNotCalledListeners(array $listeners) { @@ -105,15 +105,39 @@ public function setNotCalledListeners(array $listeners) /** * Gets the not called listeners. * - * @return array An array of not called listeners + * @return array * - * @see TraceableEventDispatcherInterface + * @see TraceableEventDispatcher */ public function getNotCalledListeners() { return $this->data['not_called_listeners']; } + /** + * Sets the orphaned events. + * + * @param array $events An array of orphaned events + * + * @see TraceableEventDispatcher + */ + public function setOrphanedEvents(array $events) + { + $this->data['orphaned_events'] = $events; + } + + /** + * Gets the orphaned events. + * + * @return array An array of orphaned events + * + * @see TraceableEventDispatcher + */ + public function getOrphanedEvents() + { + return $this->data['orphaned_events']; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php index 03bfa5d842ea8..3f6fc32e04be9 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -26,13 +26,9 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte private $logger; private $containerPathPrefix; - public function __construct($logger = null, $containerPathPrefix = null) + public function __construct($logger = null, string $containerPathPrefix = null) { if (null !== $logger && $logger instanceof DebugLoggerInterface) { - if (!method_exists($logger, 'clear')) { - @trigger_error(sprintf('Implementing "%s" without the "clear()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', DebugLoggerInterface::class, \get_class($logger)), E_USER_DEPRECATED); - } - $this->logger = $logger; } @@ -52,7 +48,7 @@ public function collect(Request $request, Response $response, \Exception $except */ public function reset() { - if ($this->logger && method_exists($this->logger, 'clear')) { + if ($this->logger instanceof DebugLoggerInterface) { $this->logger->clear(); } $this->data = array(); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 54d17d28c7cac..36e5634eeb878 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -79,6 +79,13 @@ public function collect(Request $request, Response $response, \Exception $except $responseCookies[$cookie->getName()] = $cookie; } + $dotenvVars = array(); + foreach (explode(',', getenv('SYMFONY_DOTENV_VARS')) as $name) { + if ('' !== $name && false !== $value = getenv($name)) { + $dotenvVars[$name] = $value; + } + } + $this->data = array( 'method' => $request->getMethod(), 'format' => $request->getRequestFormat(), @@ -101,6 +108,7 @@ public function collect(Request $request, Response $response, \Exception $except 'path_info' => $request->getPathInfo(), 'controller' => 'n/a', 'locale' => $request->getLocale(), + 'dotenv_vars' => $dotenvVars, ); if (isset($this->data['request_headers']['php-auth-pw'])) { @@ -150,6 +158,10 @@ public function collect(Request $request, Response $response, \Exception $except } $this->data['identifier'] = $this->data['route'] ?: (is_array($this->data['controller']) ? $this->data['controller']['class'].'::'.$this->data['controller']['method'].'()' : $this->data['controller']); + + if ($response->headers->has('x-previous-debug-token')) { + $this->data['forward_token'] = $response->headers->get('x-previous-debug-token'); + } } public function lateCollect() @@ -258,6 +270,11 @@ public function getLocale() return $this->data['locale']; } + public function getDotenvVars() + { + return new ParameterBag($this->data['dotenv_vars']->getValue()); + } + /** * Gets the route name. * @@ -309,6 +326,11 @@ public function getRedirect() return isset($this->data['redirect']) ? $this->data['redirect'] : false; } + public function getForwardToken() + { + return isset($this->data['forward_token']) ? $this->data['forward_token'] : null; + } + public function onKernelController(FilterControllerEvent $event) { $this->controllers[$event->getRequest()] = $event->getController(); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php b/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php deleted file mode 100644 index a71c00db99e5a..0000000000000 --- a/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpKernel\DataCollector\Util; - -@trigger_error('The '.__NAMESPACE__.'\ValueExporter class is deprecated since Symfony 3.2 and will be removed in 4.0. Use the VarDumper component instead.', E_USER_DEPRECATED); - -/** - * @author Bernhard Schussek - * - * @deprecated since version 3.2, to be removed in 4.0. Use the VarDumper component instead. - */ -class ValueExporter -{ - /** - * Converts a PHP value to a string. - * - * @param mixed $value The PHP value - * @param int $depth Only for internal usage - * @param bool $deep Only for internal usage - * - * @return string The string representation of the given value - */ - public function exportValue($value, $depth = 1, $deep = false) - { - if ($value instanceof \__PHP_Incomplete_Class) { - return sprintf('__PHP_Incomplete_Class(%s)', $this->getClassNameFromIncomplete($value)); - } - - if (is_object($value)) { - if ($value instanceof \DateTimeInterface) { - return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ATOM)); - } - - return sprintf('Object(%s)', get_class($value)); - } - - if (is_array($value)) { - if (empty($value)) { - return '[]'; - } - - $indent = str_repeat(' ', $depth); - - $a = array(); - foreach ($value as $k => $v) { - if (is_array($v)) { - $deep = true; - } - $a[] = sprintf('%s => %s', $k, $this->exportValue($v, $depth + 1, $deep)); - } - - if ($deep) { - return sprintf("[\n%s%s\n%s]", $indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)); - } - - $s = sprintf('[%s]', implode(', ', $a)); - - if (80 > strlen($s)) { - return $s; - } - - return sprintf("[\n%s%s\n]", $indent, implode(sprintf(",\n%s", $indent), $a)); - } - - if (is_resource($value)) { - return sprintf('Resource(%s#%d)', get_resource_type($value), $value); - } - - if (null === $value) { - return 'null'; - } - - if (false === $value) { - return 'false'; - } - - if (true === $value) { - return 'true'; - } - - return (string) $value; - } - - private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) - { - $array = new \ArrayObject($value); - - return $array['__PHP_Incomplete_Class_Name']; - } -} diff --git a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php index c340d9b67200a..2f1806689a24c 100644 --- a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php +++ b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php @@ -26,7 +26,7 @@ class FileLinkFormatter implements \Serializable private $baseDir; private $urlFormat; - public function __construct($fileLinkFormat = null, RequestStack $requestStack = null, $baseDir = null, $urlFormat = null) + public function __construct($fileLinkFormat = null, RequestStack $requestStack = null, string $baseDir = null, string $urlFormat = null) { $fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); if ($fileLinkFormat && !is_array($fileLinkFormat)) { @@ -63,11 +63,7 @@ public function serialize() public function unserialize($serialized) { - if (\PHP_VERSION_ID >= 70000) { - $this->fileLinkFormat = unserialize($serialized, array('allowed_classes' => false)); - } else { - $this->fileLinkFormat = unserialize($serialized); - } + $this->fileLinkFormat = unserialize($serialized, array('allowed_classes' => false)); } private function getFileLinkFormat() diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php index b1ecebdc969a2..0f9540972f140 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php @@ -36,23 +36,15 @@ public function __construct(Kernel $kernel) */ public function process(ContainerBuilder $container) { - $classes = array(); $annotatedClasses = array(); foreach ($container->getExtensions() as $extension) { if ($extension instanceof Extension) { - if (\PHP_VERSION_ID < 70000) { - $classes = array_merge($classes, $extension->getClassesToCompile()); - } $annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile()); } } $existingClasses = $this->getClassesInComposerClassMaps(); - if (\PHP_VERSION_ID < 70000) { - $classes = $container->getParameterBag()->resolveValue($classes); - $this->kernel->setClassCache($this->expandClasses($classes, $existingClasses)); - } $annotatedClasses = $container->getParameterBag()->resolveValue($annotatedClasses); $this->kernel->setAnnotatedClassCache($this->expandClasses($annotatedClasses, $existingClasses)); } diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.php deleted file mode 100644 index 8ae78e0d841d7..0000000000000 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpKernel\DependencyInjection; - -@trigger_error('The '.__NAMESPACE__.'\AddClassesToCachePass class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); - -/** - * Sets the classes to compile in the cache for the container. - * - * @author Fabien Potencier - * - * @deprecated since version 3.3, to be removed in 4.0. - */ -class AddClassesToCachePass extends AddAnnotatedClassesToCachePass -{ -} diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php index 343e217b9687b..b3a25068fa345 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php @@ -28,7 +28,7 @@ class ControllerArgumentValueResolverPass implements CompilerPassInterface private $argumentResolverService; private $argumentValueResolverTag; - public function __construct($argumentResolverService = 'argument_resolver', $argumentValueResolverTag = 'controller.argument_value_resolver') + public function __construct(string $argumentResolverService = 'argument_resolver', string $argumentValueResolverTag = 'controller.argument_value_resolver') { $this->argumentResolverService = $argumentResolverService; $this->argumentValueResolverTag = $argumentValueResolverTag; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php b/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php index a382c15d67b26..647875554b00c 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php @@ -20,25 +20,8 @@ */ abstract class Extension extends BaseExtension { - private $classes = array(); private $annotatedClasses = array(); - /** - * Gets the classes to cache. - * - * @return array An array of classes - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function getClassesToCompile() - { - if (\PHP_VERSION_ID >= 70000) { - @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); - } - - return $this->classes; - } - /** * Gets the annotated classes to cache. * @@ -49,22 +32,6 @@ public function getAnnotatedClassesToCompile() return $this->annotatedClasses; } - /** - * Adds classes to the class cache. - * - * @param array $classes An array of class patterns - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function addClassesToCompile(array $classes) - { - if (\PHP_VERSION_ID >= 70000) { - @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); - } - - $this->classes = array_merge($this->classes, $classes); - } - /** * Adds annotated classes to the class cache. * diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php index ac52c8732bc4a..825038b695e99 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php @@ -28,11 +28,7 @@ class FragmentRendererPass implements CompilerPassInterface private $handlerService; private $rendererTag; - /** - * @param string $handlerService Service name of the fragment handler in the container - * @param string $rendererTag Tag name used for fragments - */ - public function __construct($handlerService = 'fragment.handler', $rendererTag = 'kernel.fragment_renderer') + public function __construct(string $handlerService = 'fragment.handler', string $rendererTag = 'kernel.fragment_renderer') { $this->handlerService = $handlerService; $this->rendererTag = $rendererTag; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php b/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php index 00b05959cf385..4bb47902b5050 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php @@ -23,52 +23,20 @@ class LazyLoadingFragmentHandler extends FragmentHandler { private $container; - /** - * @deprecated since version 3.3, to be removed in 4.0 - */ - private $rendererIds = array(); private $initialized = array(); - /** - * @param ContainerInterface $container A container - * @param RequestStack $requestStack The Request stack that controls the lifecycle of requests - * @param bool $debug Whether the debug mode is enabled or not - */ - public function __construct(ContainerInterface $container, RequestStack $requestStack, $debug = false) + public function __construct(ContainerInterface $container, RequestStack $requestStack, bool $debug = false) { $this->container = $container; parent::__construct($requestStack, array(), $debug); } - /** - * Adds a service as a fragment renderer. - * - * @param string $name The service name - * @param string $renderer The render service id - * - * @deprecated since version 3.3, to be removed in 4.0 - */ - public function addRendererService($name, $renderer) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - $this->rendererIds[$name] = $renderer; - } - /** * {@inheritdoc} */ public function render($uri, $renderer = 'inline', array $options = array()) { - // BC 3.x, to be removed in 4.0 - if (isset($this->rendererIds[$renderer])) { - $this->addRenderer($this->container->get($this->rendererIds[$renderer])); - unset($this->rendererIds[$renderer]); - - return parent::render($uri, $renderer, $options); - } - if (!isset($this->initialized[$renderer]) && $this->container->has($renderer)) { $this->addRenderer($this->container->get($renderer)); $this->initialized[$renderer] = true; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index cb88594e3a042..8cb19c479041d 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -34,7 +34,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface private $resolverServiceId; private $controllerTag; - public function __construct($resolverServiceId = 'argument_resolver.service', $controllerTag = 'controller.service_arguments') + public function __construct(string $resolverServiceId = 'argument_resolver.service', string $controllerTag = 'controller.service_arguments') { $this->resolverServiceId = $resolverServiceId; $this->controllerTag = $controllerTag; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php index ab50cec3531bf..52c6d69c1f71c 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php @@ -23,7 +23,7 @@ class RemoveEmptyControllerArgumentLocatorsPass implements CompilerPassInterface { private $resolverServiceId; - public function __construct($resolverServiceId = 'argument_resolver.service') + public function __construct(string $resolverServiceId = 'argument_resolver.service') { $this->resolverServiceId = $resolverServiceId; } diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php index 29433a6d5b195..564f879ca73ee 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php @@ -25,7 +25,7 @@ class ResettableServicePass implements CompilerPassInterface { private $tagName; - public function __construct($tagName = 'kernel.reset') + public function __construct(string $tagName = 'kernel.reset') { $this->tagName = $tagName; } diff --git a/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php index 2b08f2d770020..309647bc8e9b4 100644 --- a/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php @@ -30,7 +30,7 @@ class FilterControllerArgumentsEvent extends FilterControllerEvent { private $arguments; - public function __construct(HttpKernelInterface $kernel, callable $controller, array $arguments, Request $request, $requestType) + public function __construct(HttpKernelInterface $kernel, callable $controller, array $arguments, Request $request, ?int $requestType) { parent::__construct($kernel, $controller, $request, $requestType); diff --git a/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php index 84cbc2eaf8365..cbbfd0fe44886 100644 --- a/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php @@ -29,7 +29,7 @@ class FilterControllerEvent extends KernelEvent { private $controller; - public function __construct(HttpKernelInterface $kernel, callable $controller, Request $request, $requestType) + public function __construct(HttpKernelInterface $kernel, callable $controller, Request $request, ?int $requestType) { parent::__construct($kernel, $request, $requestType); diff --git a/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php index 53a7efce76cae..4cdc207f56b46 100644 --- a/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php @@ -28,7 +28,7 @@ class FilterResponseEvent extends KernelEvent { private $response; - public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, Response $response) + public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, Response $response) { parent::__construct($kernel, $request, $requestType); diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php index f70ce09e0a40a..592653d77fa0c 100644 --- a/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php @@ -32,7 +32,7 @@ class GetResponseForControllerResultEvent extends GetResponseEvent */ private $controllerResult; - public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, $controllerResult) + public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, $controllerResult) { parent::__construct($kernel, $request, $requestType); diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php index 751b74515b48b..cf0fcd16233ef 100644 --- a/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php @@ -41,7 +41,7 @@ class GetResponseForExceptionEvent extends GetResponseEvent */ private $allowCustomResponseCode = false; - public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, \Exception $e) + public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, \Exception $e) { parent::__construct($kernel, $request, $requestType); diff --git a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php index 992f6b4dc033e..0dcf879f3da73 100644 --- a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php @@ -32,7 +32,7 @@ class KernelEvent extends Event * @param int $requestType The request type the kernel is currently processing; one of * HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST */ - public function __construct(HttpKernelInterface $kernel, Request $request, $requestType) + public function __construct(HttpKernelInterface $kernel, Request $request, ?int $requestType) { $this->kernel = $kernel; $this->request = $request; diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index dff29ee80b418..9865d6a79aba3 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpKernel\EventListener; +use Psr\Container\ContainerInterface; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; @@ -22,9 +23,17 @@ * Sets the session in the request. * * @author Johannes M. Schmitt + * @author Tobias Schultze */ abstract class AbstractSessionListener implements EventSubscriberInterface { + protected $container; + + public function __construct(ContainerInterface $container = null) + { + $this->container = $container; + } + public function onKernelRequest(GetResponseEvent $event) { if (!$event->isMasterRequest()) { @@ -32,12 +41,13 @@ public function onKernelRequest(GetResponseEvent $event) } $request = $event->getRequest(); - $session = $this->getSession(); - if (null === $session || $request->hasSession()) { - return; + if ($request->hasSession()) { + // no-op + } elseif (method_exists($request, 'setSessionFactory')) { + $request->setSessionFactory(function () { return $this->getSession(); }); + } elseif ($session = $this->getSession()) { + $request->setSession($session); } - - $request->setSession($session); } public function onKernelResponse(FilterResponseEvent $event) @@ -46,7 +56,7 @@ public function onKernelResponse(FilterResponseEvent $event) return; } - if (!$session = $event->getRequest()->getSession()) { + if (!$session = $this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : $event->getRequest()->getSession()) { return; } @@ -56,13 +66,42 @@ public function onKernelResponse(FilterResponseEvent $event) ->setMaxAge(0) ->headers->addCacheControlDirective('must-revalidate'); } + + if ($session->isStarted()) { + /* + * Saves the session, in case it is still open, before sending the response/headers. + * + * This ensures several things in case the developer did not save the session explicitly: + * + * * If a session save handler without locking is used, it ensures the data is available + * on the next request, e.g. after a redirect. PHPs auto-save at script end via + * session_register_shutdown is executed after fastcgi_finish_request. So in this case + * the data could be missing the next request because it might not be saved the moment + * the new request is processed. + * * A locking save handler (e.g. the native 'files') circumvents concurrency problems like + * the one above. But by saving the session before long-running things in the terminate event, + * we ensure the session is not blocked longer than needed. + * * When regenerating the session ID no locking is involved in PHPs session design. See + * https://bugs.php.net/bug.php?id=61470 for a discussion. So in this case, the session must + * be saved anyway before sending the headers with the new session ID. Otherwise session + * data could get lost again for concurrent requests with the new ID. One result could be + * that you get logged out after just logging in. + * + * This listener should be executed as one of the last listeners, so that previous listeners + * can still operate on the open session. This prevents the overhead of restarting it. + * Listeners after closing the session can still work with the session as usual because + * Symfonys session implementation starts the session on demand. So writing to it after + * it is saved will just restart it. + */ + $session->save(); + } } public static function getSubscribedEvents() { return array( KernelEvents::REQUEST => array('onKernelRequest', 128), - // low priority to come after regular response listeners, same as SaveSessionListener + // low priority to come after regular response listeners, but higher than StreamedResponseListener KernelEvents::RESPONSE => array('onKernelResponse', -1000), ); } diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index 2911caa115bf2..4c2398cc0dde1 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -48,15 +48,15 @@ class DebugHandlersListener implements EventSubscriberInterface * @param string|array $fileLinkFormat The format for links to source files * @param bool $scope Enables/disables scoping mode */ - public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, $throwAt = E_ALL, $scream = true, $fileLinkFormat = null, $scope = true) + public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true) { $this->exceptionHandler = $exceptionHandler; $this->logger = $logger; $this->levels = null === $levels ? E_ALL : $levels; - $this->throwAt = is_numeric($throwAt) ? (int) $throwAt : (null === $throwAt ? null : ($throwAt ? E_ALL : null)); - $this->scream = (bool) $scream; + $this->throwAt = \is_int($throwAt) ? $throwAt : (null === $throwAt ? null : ($throwAt ? E_ALL : null)); + $this->scream = $scream; $this->fileLinkFormat = $fileLinkFormat; - $this->scope = (bool) $scope; + $this->scope = $scope; } /** diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php index f18e42c7d3693..fc749de599a48 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php @@ -41,15 +41,19 @@ public function __construct($controller, LoggerInterface $logger = null, $debug $this->debug = $debug; } - public function onKernelException(GetResponseForExceptionEvent $event) + public function logKernelException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); $request = $event->getRequest(); - $eventDispatcher = func_num_args() > 2 ? func_get_arg(2) : null; $this->logException($exception, sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine())); + } - $request = $this->duplicateRequest($exception, $request); + public function onKernelException(GetResponseForExceptionEvent $event) + { + $exception = $event->getException(); + $request = $this->duplicateRequest($exception, $event->getRequest()); + $eventDispatcher = func_num_args() > 2 ? func_get_arg(2) : null; try { $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false); @@ -85,7 +89,10 @@ public function onKernelException(GetResponseForExceptionEvent $event) public static function getSubscribedEvents() { return array( - KernelEvents::EXCEPTION => array('onKernelException', -128), + KernelEvents::EXCEPTION => array( + array('logKernelException', 2048), + array('onKernelException', -128), + ), ); } diff --git a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php index da518fcc8e374..f6bfd6cc6d802 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php @@ -38,7 +38,7 @@ class FragmentListener implements EventSubscriberInterface * @param UriSigner $signer A UriSigner instance * @param string $fragmentPath The path that triggers this listener */ - public function __construct(UriSigner $signer, $fragmentPath = '/_fragment') + public function __construct(UriSigner $signer, string $fragmentPath = '/_fragment') { $this->signer = $signer; $this->fragmentPath = $fragmentPath; diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php index 427ea82fc8bf7..6c2b5098dcf4e 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php @@ -35,7 +35,7 @@ class LocaleListener implements EventSubscriberInterface * @param string $defaultLocale The default locale * @param RequestContextAwareInterface|null $router The router */ - public function __construct(RequestStack $requestStack, $defaultLocale = 'en', RequestContextAwareInterface $router = null) + public function __construct(RequestStack $requestStack, string $defaultLocale = 'en', RequestContextAwareInterface $router = null) { $this->defaultLocale = $defaultLocale; $this->requestStack = $requestStack; diff --git a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php index e3e4e7620e4ee..9cc554db72ab0 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php @@ -43,12 +43,12 @@ class ProfilerListener implements EventSubscriberInterface * @param bool $onlyException True if the profiler only collects data when an exception occurs, false otherwise * @param bool $onlyMasterRequests True if the profiler only collects data when the request is a master request, false otherwise */ - public function __construct(Profiler $profiler, RequestStack $requestStack, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false) + public function __construct(Profiler $profiler, RequestStack $requestStack, RequestMatcherInterface $matcher = null, bool $onlyException = false, bool $onlyMasterRequests = false) { $this->profiler = $profiler; $this->matcher = $matcher; - $this->onlyException = (bool) $onlyException; - $this->onlyMasterRequests = (bool) $onlyMasterRequests; + $this->onlyException = $onlyException; + $this->onlyMasterRequests = $onlyMasterRequests; $this->profiles = new \SplObjectStorage(); $this->parents = new \SplObjectStorage(); $this->requestStack = $requestStack; @@ -121,7 +121,7 @@ public static function getSubscribedEvents() { return array( KernelEvents::RESPONSE => array('onKernelResponse', -100), - KernelEvents::EXCEPTION => 'onKernelException', + KernelEvents::EXCEPTION => array('onKernelException', 2048), KernelEvents::TERMINATE => array('onKernelTerminate', -1024), ); } diff --git a/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php b/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php index 6d56197a737e7..01475c2a77d3d 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php @@ -24,7 +24,7 @@ class ResponseListener implements EventSubscriberInterface { private $charset; - public function __construct($charset) + public function __construct(string $charset) { $this->charset = $charset; } diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index caaf80f86b892..98a397a2e6841 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -56,7 +56,7 @@ class RouterListener implements EventSubscriberInterface * * @throws \InvalidArgumentException */ - public function __construct($matcher, RequestStack $requestStack, RequestContext $context = null, LoggerInterface $logger = null, $projectDir = null, $debug = true) + public function __construct($matcher, RequestStack $requestStack, RequestContext $context = null, LoggerInterface $logger = null, string $projectDir = null, bool $debug = true) { if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); diff --git a/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php index 36809b59af914..a29121a3c66aa 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php @@ -11,36 +11,16 @@ namespace Symfony\Component\HttpKernel\EventListener; +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.1 and will be removed in 5.0. Use AbstractSessionListener instead.', SaveSessionListener::class), E_USER_DEPRECATED); + use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; /** - * Saves the session, in case it is still open, before sending the response/headers. - * - * This ensures several things in case the developer did not save the session explicitly: - * - * * If a session save handler without locking is used, it ensures the data is available - * on the next request, e.g. after a redirect. PHPs auto-save at script end via - * session_register_shutdown is executed after fastcgi_finish_request. So in this case - * the data could be missing the next request because it might not be saved the moment - * the new request is processed. - * * A locking save handler (e.g. the native 'files') circumvents concurrency problems like - * the one above. But by saving the session before long-running things in the terminate event, - * we ensure the session is not blocked longer than needed. - * * When regenerating the session ID no locking is involved in PHPs session design. See - * https://bugs.php.net/bug.php?id=61470 for a discussion. So in this case, the session must - * be saved anyway before sending the headers with the new session ID. Otherwise session - * data could get lost again for concurrent requests with the new ID. One result could be - * that you get logged out after just logging in. - * - * This listener should be executed as one of the last listeners, so that previous listeners - * can still operate on the open session. This prevents the overhead of restarting it. - * Listeners after closing the session can still work with the session as usual because - * Symfonys session implementation starts the session on demand. So writing to it after - * it is saved will just restart it. - * * @author Tobias Schultze + * + * @deprecated since Symfony 4.1, to be removed in 5.0. Use AbstractSessionListener instead. */ class SaveSessionListener implements EventSubscriberInterface { diff --git a/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php index 39ebfd922fac6..9f30eea38b42e 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php @@ -22,8 +22,6 @@ */ class SessionListener extends AbstractSessionListener { - private $container; - public function __construct(ContainerInterface $container) { $this->container = $container; diff --git a/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php b/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php index 39418842666b2..3aedbf97fec3e 100644 --- a/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php @@ -21,9 +21,10 @@ class AccessDeniedHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(403, $message, $previous, array(), $code); + parent::__construct(403, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/BadRequestHttpException.php b/src/Symfony/Component/HttpKernel/Exception/BadRequestHttpException.php index c28d83739c7a5..ff215db45f7d1 100644 --- a/src/Symfony/Component/HttpKernel/Exception/BadRequestHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/BadRequestHttpException.php @@ -20,9 +20,10 @@ class BadRequestHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(400, $message, $previous, array(), $code); + parent::__construct(400, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/ConflictHttpException.php b/src/Symfony/Component/HttpKernel/Exception/ConflictHttpException.php index 79f24f2e56dc0..60195daf7b09c 100644 --- a/src/Symfony/Component/HttpKernel/Exception/ConflictHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/ConflictHttpException.php @@ -20,9 +20,10 @@ class ConflictHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(409, $message, $previous, array(), $code); + parent::__construct(409, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/GoneHttpException.php b/src/Symfony/Component/HttpKernel/Exception/GoneHttpException.php index 84e6915df70f7..f2f3515f77ab0 100644 --- a/src/Symfony/Component/HttpKernel/Exception/GoneHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/GoneHttpException.php @@ -20,9 +20,10 @@ class GoneHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(410, $message, $previous, array(), $code); + parent::__construct(410, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/HttpException.php b/src/Symfony/Component/HttpKernel/Exception/HttpException.php index e8e37605838cc..dab73120d096d 100644 --- a/src/Symfony/Component/HttpKernel/Exception/HttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/HttpException.php @@ -21,7 +21,7 @@ class HttpException extends \RuntimeException implements HttpExceptionInterface private $statusCode; private $headers; - public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = array(), $code = 0) + public function __construct(int $statusCode, string $message = null, \Exception $previous = null, array $headers = array(), ?int $code = 0) { $this->statusCode = $statusCode; $this->headers = $headers; diff --git a/src/Symfony/Component/HttpKernel/Exception/LengthRequiredHttpException.php b/src/Symfony/Component/HttpKernel/Exception/LengthRequiredHttpException.php index 645efe87d7c23..46b76ba6a3197 100644 --- a/src/Symfony/Component/HttpKernel/Exception/LengthRequiredHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/LengthRequiredHttpException.php @@ -20,9 +20,10 @@ class LengthRequiredHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(411, $message, $previous, array(), $code); + parent::__construct(411, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/MethodNotAllowedHttpException.php b/src/Symfony/Component/HttpKernel/Exception/MethodNotAllowedHttpException.php index e308a5fee2767..b0085c16fa0e2 100644 --- a/src/Symfony/Component/HttpKernel/Exception/MethodNotAllowedHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/MethodNotAllowedHttpException.php @@ -21,10 +21,11 @@ class MethodNotAllowedHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct(array $allow, $message = null, \Exception $previous = null, $code = 0) + public function __construct(array $allow, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) { - $headers = array('Allow' => strtoupper(implode(', ', $allow))); + $headers['Allow'] = strtoupper(implode(', ', $allow)); parent::__construct(405, $message, $previous, $headers, $code); } diff --git a/src/Symfony/Component/HttpKernel/Exception/NotAcceptableHttpException.php b/src/Symfony/Component/HttpKernel/Exception/NotAcceptableHttpException.php index 097a13fd9bc05..32c3089374bca 100644 --- a/src/Symfony/Component/HttpKernel/Exception/NotAcceptableHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/NotAcceptableHttpException.php @@ -20,9 +20,10 @@ class NotAcceptableHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(406, $message, $previous, array(), $code); + parent::__construct(406, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/NotFoundHttpException.php b/src/Symfony/Component/HttpKernel/Exception/NotFoundHttpException.php index 878173cc7b9ac..433ff9b9e0d64 100644 --- a/src/Symfony/Component/HttpKernel/Exception/NotFoundHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/NotFoundHttpException.php @@ -20,9 +20,10 @@ class NotFoundHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(404, $message, $previous, array(), $code); + parent::__construct(404, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/PreconditionFailedHttpException.php b/src/Symfony/Component/HttpKernel/Exception/PreconditionFailedHttpException.php index 9f13a624cc0fb..108178889cb05 100644 --- a/src/Symfony/Component/HttpKernel/Exception/PreconditionFailedHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/PreconditionFailedHttpException.php @@ -20,9 +20,10 @@ class PreconditionFailedHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(412, $message, $previous, array(), $code); + parent::__construct(412, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.php b/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.php index 9d0a36d7d34d6..30783282410fc 100644 --- a/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.php @@ -22,9 +22,10 @@ class PreconditionRequiredHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(428, $message, $previous, array(), $code); + parent::__construct(428, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/ServiceUnavailableHttpException.php b/src/Symfony/Component/HttpKernel/Exception/ServiceUnavailableHttpException.php index b2767c3fd5bcc..667764779f8e7 100644 --- a/src/Symfony/Component/HttpKernel/Exception/ServiceUnavailableHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/ServiceUnavailableHttpException.php @@ -21,12 +21,12 @@ class ServiceUnavailableHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0) + public function __construct($retryAfter = null, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) { - $headers = array(); if ($retryAfter) { - $headers = array('Retry-After' => $retryAfter); + $headers['Retry-After'] = $retryAfter; } parent::__construct(503, $message, $previous, $headers, $code); diff --git a/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.php b/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.php index 7d8a803323b7c..60b024c330171 100644 --- a/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.php @@ -23,12 +23,12 @@ class TooManyRequestsHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0) + public function __construct($retryAfter = null, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) { - $headers = array(); if ($retryAfter) { - $headers = array('Retry-After' => $retryAfter); + $headers['Retry-After'] = $retryAfter; } parent::__construct(429, $message, $previous, $headers, $code); diff --git a/src/Symfony/Component/HttpKernel/Exception/UnauthorizedHttpException.php b/src/Symfony/Component/HttpKernel/Exception/UnauthorizedHttpException.php index 05ac875c552e2..17ebb254640b8 100644 --- a/src/Symfony/Component/HttpKernel/Exception/UnauthorizedHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/UnauthorizedHttpException.php @@ -21,10 +21,11 @@ class UnauthorizedHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($challenge, $message = null, \Exception $previous = null, $code = 0) + public function __construct(string $challenge, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) { - $headers = array('WWW-Authenticate' => $challenge); + $headers['WWW-Authenticate'] = $challenge; parent::__construct(401, $message, $previous, $headers, $code); } diff --git a/src/Symfony/Component/HttpKernel/Exception/UnprocessableEntityHttpException.php b/src/Symfony/Component/HttpKernel/Exception/UnprocessableEntityHttpException.php index 01b8b8465a1a3..3a4b40c98457d 100644 --- a/src/Symfony/Component/HttpKernel/Exception/UnprocessableEntityHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/UnprocessableEntityHttpException.php @@ -20,9 +20,10 @@ class UnprocessableEntityHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(422, $message, $previous, array(), $code); + parent::__construct(422, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php b/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php index 6913504e832dc..ed6861154adab 100644 --- a/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php @@ -20,9 +20,10 @@ class UnsupportedMediaTypeHttpException extends HttpException * @param string $message The internal exception message * @param \Exception $previous The previous exception * @param int $code The internal exception code + * @param array $headers */ - public function __construct($message = null, \Exception $previous = null, $code = 0) + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) { - parent::__construct(415, $message, $previous, array(), $code); + parent::__construct(415, $message, $previous, $headers, $code); } } diff --git a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php index a8dca41ba20c1..ba1f3687038aa 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php @@ -63,7 +63,7 @@ public function render($uri, Request $request, array $options = array()) { if (!$this->surrogate || !$this->surrogate->hasSurrogateCapability($request)) { if ($uri instanceof ControllerReference && $this->containsNonScalars($uri->attributes)) { - @trigger_error('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated since Symfony 3.1, and will be removed in 4.0. Use a different rendering strategy or pass scalar values.', E_USER_DEPRECATED); + throw new \InvalidArgumentException('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is not supported. Use a different rendering strategy or pass scalar values.'); } return $this->inlineStrategy->render($uri, $request, $options); @@ -83,7 +83,7 @@ public function render($uri, Request $request, array $options = array()) return new Response($tag); } - private function generateSignedFragmentUri($uri, Request $request) + private function generateSignedFragmentUri($uri, Request $request): string { if (null === $this->signer) { throw new \LogicException('You must use a URI when using the ESI rendering strategy or set a URL signer.'); @@ -95,7 +95,7 @@ private function generateSignedFragmentUri($uri, Request $request) return substr($fragmentUri, strlen($request->getSchemeAndHttpHost())); } - private function containsNonScalars(array $values) + private function containsNonScalars(array $values): bool { foreach ($values as $value) { if (is_array($value)) { diff --git a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php index c9d952ffbe044..aa61bd1f7dd67 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php +++ b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php @@ -37,7 +37,7 @@ class FragmentHandler * @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances * @param bool $debug Whether the debug mode is enabled or not */ - public function __construct(RequestStack $requestStack, array $renderers = array(), $debug = false) + public function __construct(RequestStack $requestStack, array $renderers = array(), bool $debug = false) { $this->requestStack = $requestStack; foreach ($renderers as $renderer) { diff --git a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php index 7e957d4c95f71..e737a141e93b7 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php @@ -38,7 +38,7 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer * @param string $globalDefaultTemplate The global default content (it can be a template name or the content) * @param string $charset */ - public function __construct($templating = null, UriSigner $signer = null, $globalDefaultTemplate = null, $charset = 'utf-8') + public function __construct($templating = null, UriSigner $signer = null, string $globalDefaultTemplate = null, string $charset = 'utf-8') { $this->setTemplating($templating); $this->globalDefaultTemplate = $globalDefaultTemplate; @@ -121,12 +121,7 @@ public function render($uri, Request $request, array $options = array()) return new Response(sprintf('%s', $uri, $renderedAttributes, $content)); } - /** - * @param string $template - * - * @return bool - */ - private function templateExists($template) + private function templateExists(string $template): bool { if ($this->templating instanceof EngineInterface) { try { diff --git a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php index 3f3a51a59e145..e742845858dcc 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php @@ -109,21 +109,10 @@ protected function createSubRequest($uri, Request $request) $cookies = $request->cookies->all(); $server = $request->server->all(); - // Override the arguments to emulate a sub-request. - // Sub-request object will point to localhost as client ip and real client ip - // will be included into trusted header for client ip - try { - if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { - $currentXForwardedFor = $request->headers->get('X_FORWARDED_FOR', ''); - - $server['HTTP_X_FORWARDED_FOR'] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); - } elseif (method_exists(Request::class, 'getTrustedHeaderName') && $trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP, false)) { - $currentXForwardedFor = $request->headers->get($trustedHeaderName, ''); + if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { + $currentXForwardedFor = $request->headers->get('X_FORWARDED_FOR', ''); - $server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); - } - } catch (\InvalidArgumentException $e) { - // Do nothing + $server['HTTP_X_FORWARDED_FOR'] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); } $server['REMOTE_ADDR'] = '127.0.0.1'; @@ -135,9 +124,12 @@ protected function createSubRequest($uri, Request $request) $subRequest->headers->set('Surrogate-Capability', $request->headers->get('Surrogate-Capability')); } - if ($session = $request->getSession()) { - $subRequest->setSession($session); + static $setSession; + + if (null === $setSession) { + $setSession = \Closure::bind(function ($subRequest, $request) { $subRequest->session = $request->session; }, null, Request::class); } + $setSession($subRequest, $request); return $subRequest; } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 7c5a44f05ba91..ad554587be93b 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -649,23 +649,16 @@ private function isPrivateRequest(Request $request) /** * Records that an event took place. - * - * @param Request $request A Request instance - * @param string $event The event name */ - private function record(Request $request, $event) + private function record(Request $request, string $event) { $this->traces[$this->getTraceKey($request)][] = $event; } /** * Calculates the key we use in the "trace" array for a given request. - * - * @param Request $request - * - * @return string */ - private function getTraceKey(Request $request) + private function getTraceKey(Request $request): string { $path = $request->getPathInfo(); if ($qs = $request->getQueryString()) { @@ -678,12 +671,8 @@ private function getTraceKey(Request $request) /** * Checks whether the given (cached) response may be served as "stale" when a revalidation * is currently in progress. - * - * @param Response $entry - * - * @return bool true when the stale response may be served, false otherwise */ - private function mayServeStaleWhileRevalidate(Response $entry) + private function mayServeStaleWhileRevalidate(Response $entry): bool { $timeout = $entry->headers->getCacheControlDirective('stale-while-revalidate'); @@ -696,12 +685,8 @@ private function mayServeStaleWhileRevalidate(Response $entry) /** * Waits for the store to release a locked entry. - * - * @param Request $request The request to wait for - * - * @return bool true if the lock was released before the internal timeout was hit; false if the wait timeout was exceeded */ - private function waitForLock(Request $request) + private function waitForLock(Request $request): bool { $wait = 0; while ($this->store->isLocked($request) && $wait < 100) { diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php index fa0d4a86712b6..c92b0f24c265e 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -29,11 +29,9 @@ class Store implements StoreInterface private $locks; /** - * @param string $root The path to the cache directory - * * @throws \RuntimeException */ - public function __construct($root) + public function __construct(string $root) { $this->root = $root; if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) { diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 41f8913bed40c..d4ef09e8478ff 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -51,9 +51,7 @@ public function __construct(EventDispatcherInterface $dispatcher, ControllerReso $this->argumentResolver = $argumentResolver; if (null === $this->argumentResolver) { - @trigger_error(sprintf('As of 3.1 an %s is used to resolve arguments. In 4.0 the $argumentResolver becomes the %s if no other is provided instead of using the $resolver argument.', ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED); - // fallback in case of deprecations - $this->argumentResolver = $resolver; + $this->argumentResolver = new ArgumentResolver(); } } @@ -118,7 +116,7 @@ public function terminateWithException(\Exception $exception, Request $request = * @throws \LogicException If one of the listener does not behave as expected * @throws NotFoundHttpException When controller cannot be found */ - private function handleRaw(Request $request, $type = self::MASTER_REQUEST) + private function handleRaw(Request $request, int $type = self::MASTER_REQUEST) { $this->requestStack->push($request); @@ -157,9 +155,7 @@ private function handleRaw(Request $request, $type = self::MASTER_REQUEST) if ($event->hasResponse()) { $response = $event->getResponse(); - } - - if (!$response instanceof Response) { + } else { $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response)); // the user may have forgotten to return something @@ -184,7 +180,7 @@ private function handleRaw(Request $request, $type = self::MASTER_REQUEST) * * @throws \RuntimeException if the passed object is not a Response instance */ - private function filterResponse(Response $response, Request $request, $type) + private function filterResponse(Response $response, Request $request, int $type) { $event = new FilterResponseEvent($this, $request, $type, $response); @@ -201,11 +197,8 @@ private function filterResponse(Response $response, Request $request, $type) * Note that the order of the operations is important here, otherwise * operations such as {@link RequestStack::getParentRequest()} can lead to * weird results. - * - * @param Request $request - * @param int $type */ - private function finishRequest(Request $request, $type) + private function finishRequest(Request $request, int $type) { $this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type)); $this->requestStack->pop(); @@ -216,13 +209,11 @@ private function finishRequest(Request $request, $type) * * @param \Exception $e An \Exception instance * @param Request $request A Request instance - * @param int $type The type of the request - * - * @return Response A Response instance + * @param int $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) * * @throws \Exception */ - private function handleException(\Exception $e, $request, $type) + private function handleException(\Exception $e, Request $request, int $type): Response { $event = new GetResponseForExceptionEvent($this, $request, $type, $e); $this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event); @@ -239,13 +230,7 @@ private function handleException(\Exception $e, $request, $type) $response = $event->getResponse(); // the developer asked for a specific status code - if ($response->headers->has('X-Status-Code')) { - @trigger_error(sprintf('Using the X-Status-Code header is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s::allowCustomResponseCode() instead.', GetResponseForExceptionEvent::class), E_USER_DEPRECATED); - - $response->setStatusCode($response->headers->get('X-Status-Code')); - - $response->headers->remove('X-Status-Code'); - } elseif (!$event->isAllowingCustomResponseCode() && !$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) { + if (!$event->isAllowingCustomResponseCode() && !$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) { // ensure that we actually have an error response if ($e instanceof HttpExceptionInterface) { // keep the HTTP status code and headers @@ -263,7 +248,7 @@ private function handleException(\Exception $e, $request, $type) } } - private function varToString($var) + private function varToString($var): string { if (is_object($var)) { return sprintf('Object(%s)', get_class($var)); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index cd1db6c036447..61c35201a8aef 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -29,14 +29,12 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Bundle\BundleInterface; -use Symfony\Component\HttpKernel\Config\EnvParametersResource; use Symfony\Component\HttpKernel\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\ConfigCache; -use Symfony\Component\ClassLoader\ClassCollectionLoader; /** * The Kernel is the heart of the Symfony system. @@ -52,7 +50,6 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ protected $bundles = array(); - protected $bundleMap; protected $container; protected $rootDir; protected $environment; @@ -60,31 +57,26 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl protected $booted = false; protected $name; protected $startTime; - protected $loadClassCache; private $projectDir; private $warmupDir; private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.5-DEV'; - const VERSION_ID = 30405; - const MAJOR_VERSION = 3; - const MINOR_VERSION = 4; - const RELEASE_VERSION = 5; + const VERSION = '4.1.0-DEV'; + const VERSION_ID = 40100; + const MAJOR_VERSION = 4; + const MINOR_VERSION = 1; + const RELEASE_VERSION = 0; const EXTRA_VERSION = 'DEV'; - const END_OF_MAINTENANCE = '11/2020'; - const END_OF_LIFE = '11/2021'; + const END_OF_MAINTENANCE = '01/2019'; + const END_OF_LIFE = '07/2019'; - /** - * @param string $environment The environment - * @param bool $debug Whether to enable debugging or not - */ - public function __construct($environment, $debug) + public function __construct(string $environment, bool $debug) { $this->environment = $environment; - $this->debug = (bool) $debug; + $this->debug = $debug; $this->rootDir = $this->getRootDir(); $this->name = $this->getName(); @@ -126,10 +118,6 @@ public function boot() $_SERVER['SHELL_VERBOSITY'] = 3; } - if ($this->loadClassCache) { - $this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]); - } - // init bundles $this->initializeBundles(); @@ -226,26 +214,13 @@ public function getBundles() /** * {@inheritdoc} */ - public function getBundle($name, $first = true/*, $noDeprecation = false */) + public function getBundle($name) { - $noDeprecation = false; - if (func_num_args() >= 3) { - $noDeprecation = func_get_arg(2); - } - - if (!$first && !$noDeprecation) { - @trigger_error(sprintf('Passing "false" as the second argument to %s() is deprecated as of 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - } - - if (!isset($this->bundleMap[$name])) { + if (!isset($this->bundles[$name])) { throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, get_class($this))); } - if (true === $first) { - return $this->bundleMap[$name][0]; - } - - return $this->bundleMap[$name]; + return $this->bundles[$name]; } /** @@ -272,32 +247,27 @@ public function locateResource($name, $dir = null, $first = true) $isResource = 0 === strpos($path, 'Resources') && null !== $dir; $overridePath = substr($path, 9); $resourceBundle = null; - $bundles = $this->getBundle($bundleName, false, true); + $bundle = $this->getBundle($bundleName); $files = array(); - foreach ($bundles as $bundle) { - if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { - if (null !== $resourceBundle) { - throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', - $file, - $resourceBundle, - $dir.'/'.$bundles[0]->getName().$overridePath - )); - } - - if ($first) { - return $file; - } - $files[] = $file; + if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { + if (null !== $resourceBundle) { + throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', + $file, + $resourceBundle, + $dir.'/'.$bundle->getName().$overridePath + )); } - if (file_exists($file = $bundle->getPath().'/'.$path)) { - if ($first && !$isResource) { - return $file; - } - $files[] = $file; - $resourceBundle = $bundle->getName(); + $files[] = $file; + } + + if (file_exists($file = $bundle->getPath().'/'.$path)) { + if ($first && !$isResource) { + return $file; } + $files[] = $file; + $resourceBundle = $bundle->getName(); } if (count($files) > 0) { @@ -381,43 +351,6 @@ public function getContainer() return $this->container; } - /** - * Loads the PHP class cache. - * - * This methods only registers the fact that you want to load the cache classes. - * The cache will actually only be loaded when the Kernel is booted. - * - * That optimization is mainly useful when using the HttpCache class in which - * case the class cache is not loaded if the Response is in the cache. - * - * @param string $name The cache name prefix - * @param string $extension File extension of the resulting file - * - * @deprecated since version 3.3, to be removed in 4.0. The class cache is not needed anymore when using PHP 7.0. - */ - public function loadClassCache($name = 'classes', $extension = '.php') - { - if (\PHP_VERSION_ID >= 70000) { - @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); - } - - $this->loadClassCache = array($name, $extension); - } - - /** - * @internal - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function setClassCache(array $classes) - { - if (\PHP_VERSION_ID >= 70000) { - @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); - } - - file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/classes.map', sprintf('= 70000) { - @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); - } - $cacheDir = $this->warmupDir ?: $this->getCacheDir(); - - if (!$this->booted && is_file($cacheDir.'/classes.map')) { - ClassCollectionLoader::load(include($cacheDir.'/classes.map'), $cacheDir, $name, $this->debug, false, $extension); - } - } - - /** - * Initializes the data structures related to the bundle management. - * - * - the bundles property maps a bundle name to the bundle instance, - * - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first). + * Initializes bundles. * * @throws \LogicException if two bundles share a common name - * @throws \LogicException if a bundle tries to extend a non-registered bundle - * @throws \LogicException if a bundle tries to extend itself - * @throws \LogicException if two bundles extend the same ancestor */ protected function initializeBundles() { // init bundles $this->bundles = array(); - $topMostBundles = array(); - $directChildren = array(); - foreach ($this->registerBundles() as $bundle) { $name = $bundle->getName(); if (isset($this->bundles[$name])) { throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); } $this->bundles[$name] = $bundle; - - if ($parentName = $bundle->getParent()) { - @trigger_error('Bundle inheritance is deprecated as of 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); - - if (isset($directChildren[$parentName])) { - throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName])); - } - if ($parentName == $name) { - throw new \LogicException(sprintf('Bundle "%s" can not extend itself.', $name)); - } - $directChildren[$parentName] = $name; - } else { - $topMostBundles[$name] = $bundle; - } - } - - // look for orphans - if (!empty($directChildren) && count($diff = array_diff_key($directChildren, $this->bundles))) { - $diff = array_keys($diff); - - throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); - } - - // inheritance - $this->bundleMap = array(); - foreach ($topMostBundles as $name => $bundle) { - $bundleMap = array($bundle); - $hierarchy = array($name); - - while (isset($directChildren[$name])) { - $name = $directChildren[$name]; - array_unshift($bundleMap, $this->bundles[$name]); - $hierarchy[] = $name; - } - - foreach ($hierarchy as $hierarchyBundle) { - $this->bundleMap[$hierarchyBundle] = $bundleMap; - array_pop($bundleMap); - } } } @@ -698,56 +568,26 @@ protected function getKernelParameters() foreach ($this->bundles as $name => $bundle) { $bundles[$name] = get_class($bundle); $bundlesMetadata[$name] = array( - 'parent' => $bundle->getParent(), 'path' => $bundle->getPath(), 'namespace' => $bundle->getNamespace(), ); } - return array_merge( - array( - 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, - 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(), - 'kernel.environment' => $this->environment, - 'kernel.debug' => $this->debug, - 'kernel.name' => $this->name, - 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir, - 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), - 'kernel.bundles' => $bundles, - 'kernel.bundles_metadata' => $bundlesMetadata, - 'kernel.charset' => $this->getCharset(), - 'kernel.container_class' => $this->getContainerClass(), - ), - $this->getEnvParameters(false) + return array( + 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, + 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(), + 'kernel.environment' => $this->environment, + 'kernel.debug' => $this->debug, + 'kernel.name' => $this->name, + 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir, + 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), + 'kernel.bundles' => $bundles, + 'kernel.bundles_metadata' => $bundlesMetadata, + 'kernel.charset' => $this->getCharset(), + 'kernel.container_class' => $this->getContainerClass(), ); } - /** - * Gets the environment parameters. - * - * Only the parameters starting with "SYMFONY__" are considered. - * - * @return array An array of parameters - * - * @deprecated since version 3.3, to be removed in 4.0 - */ - protected function getEnvParameters() - { - if (0 === func_num_args() || func_get_arg(0)) { - @trigger_error(sprintf('The %s() method is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax to get the value of any environment variable from configuration files instead.', __METHOD__), E_USER_DEPRECATED); - } - - $parameters = array(); - foreach ($_SERVER as $key => $value) { - if (0 === strpos($key, 'SYMFONY__')) { - @trigger_error(sprintf('The support of special environment variables that start with SYMFONY__ (such as "%s") is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax instead to get the value of environment variables in configuration files.', $key), E_USER_DEPRECATED); - $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value; - } - } - - return $parameters; - } - /** * Builds the service container. * @@ -776,7 +616,6 @@ protected function buildContainer() } $container->addCompilerPass(new AddAnnotatedClassesToCachePass($this)); - $container->addResource(new EnvParametersResource('SYMFONY__')); return $container; } @@ -854,7 +693,6 @@ protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container 'file' => $cache->getPath(), 'as_files' => true, 'debug' => $this->debug, - 'inline_class_loader_parameter' => \PHP_VERSION_ID >= 70000 && !$this->loadClassCache && !class_exists(ClassCollectionLoader::class, false) ? 'container.dumper.inline_class_loader' : null, 'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(), )); @@ -946,11 +784,9 @@ public static function stripComments($source) $output .= $rawChunk; - if (\PHP_VERSION_ID >= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - unset($tokens, $rawChunk); - gc_mem_caches(); - } + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + unset($tokens, $rawChunk); + gc_mem_caches(); return $output; } @@ -962,11 +798,7 @@ public function serialize() public function unserialize($data) { - if (\PHP_VERSION_ID >= 70000) { - list($environment, $debug) = unserialize($data, array('allowed_classes' => false)); - } else { - list($environment, $debug) = unserialize($data); - } + list($environment, $debug) = unserialize($data, array('allowed_classes' => false)); $this->__construct($environment, $debug); } diff --git a/src/Symfony/Component/HttpKernel/KernelInterface.php b/src/Symfony/Component/HttpKernel/KernelInterface.php index b341e3e9fcda9..e58fbd2ed5b64 100644 --- a/src/Symfony/Component/HttpKernel/KernelInterface.php +++ b/src/Symfony/Component/HttpKernel/KernelInterface.php @@ -56,19 +56,15 @@ public function shutdown(); public function getBundles(); /** - * Returns a bundle and optionally its descendants by its name. + * Returns a bundle. * - * The second argument is deprecated as of 3.4 and will be removed in 4.0. This method - * will always return an instance of BundleInterface in 4.0. + * @param string $name Bundle name * - * @param string $name Bundle name - * @param bool $first Whether to return the first bundle only or together with its descendants - * - * @return BundleInterface|BundleInterface[] A BundleInterface instance or an array of BundleInterface instances if $first is false + * @return BundleInterface A BundleInterface instance * * @throws \InvalidArgumentException when the bundle is not enabled */ - public function getBundle($name, $first = true); + public function getBundle($name); /** * Returns the file path for a given resource. diff --git a/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php b/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php index f0606d3b0e5ee..1d955c48296ae 100644 --- a/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php +++ b/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php @@ -15,8 +15,6 @@ * DebugLoggerInterface. * * @author Fabien Potencier - * - * @method clear() Removes all log records. */ interface DebugLoggerInterface { @@ -37,4 +35,9 @@ public function getLogs(); * @return int The number of errors */ public function countErrors(); + + /** + * Removes all log records. + */ + public function clear(); } diff --git a/src/Symfony/Component/HttpKernel/Log/Logger.php b/src/Symfony/Component/HttpKernel/Log/Logger.php index 617efcf13e5a4..bf6ab49232fac 100644 --- a/src/Symfony/Component/HttpKernel/Log/Logger.php +++ b/src/Symfony/Component/HttpKernel/Log/Logger.php @@ -37,7 +37,7 @@ class Logger extends AbstractLogger private $formatter; private $handle; - public function __construct($minLevel = null, $output = 'php://stderr', callable $formatter = null) + public function __construct(string $minLevel = null, $output = 'php://stderr', callable $formatter = null) { if (null === $minLevel) { $minLevel = LogLevel::WARNING; @@ -80,14 +80,7 @@ public function log($level, $message, array $context = array()) fwrite($this->handle, $formatter($level, $message, $context)); } - /** - * @param string $level - * @param string $message - * @param array $context - * - * @return string - */ - private function format($level, $message, array $context) + private function format(string $level, string $message, array $context): string { if (false !== strpos($message, '{')) { $replacements = array(); diff --git a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php index e24b2e0183684..d78411d3f6b71 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php @@ -30,11 +30,9 @@ class FileProfilerStorage implements ProfilerStorageInterface * * Example : "file:/path/to/the/storage/folder" * - * @param string $dsn The DSN - * * @throws \RuntimeException */ - public function __construct($dsn) + public function __construct(string $dsn) { if (0 !== strpos($dsn, 'file:')) { throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use FileStorage with an invalid dsn "%s". The expected format is "file:/path/to/the/storage/folder".', $dsn)); diff --git a/src/Symfony/Component/HttpKernel/Profiler/Profile.php b/src/Symfony/Component/HttpKernel/Profiler/Profile.php index c21c9d38a343d..f03c872672f5f 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/Profile.php +++ b/src/Symfony/Component/HttpKernel/Profiler/Profile.php @@ -43,10 +43,7 @@ class Profile */ private $children = array(); - /** - * @param string $token The token - */ - public function __construct($token) + public function __construct(string $token) { $this->token = $token; } @@ -219,6 +216,17 @@ public function addChild(Profile $child) $child->setParent($this); } + public function getChildByToken(string $token): ?self + { + foreach ($this->children as $child) { + if ($token === $child->getToken()) { + return $child; + } + } + + return null; + } + /** * Gets a Collector by name. * diff --git a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php index 15bfbc5a07777..a97d63b05f8f6 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php +++ b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php @@ -36,14 +36,11 @@ class Profiler private $initiallyEnabled = true; private $enabled = true; - /** - * @param bool $enable The initial enabled state - */ - public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null, $enable = true) + public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null, bool $enable = true) { $this->storage = $storage; $this->logger = $logger; - $this->initiallyEnabled = $this->enabled = (bool) $enable; + $this->initiallyEnabled = $this->enabled = $enable; } /** @@ -159,6 +156,10 @@ public function collect(Request $request, Response $response, \Exception $except $profile->setIp('Unknown'); } + if ($prevToken = $response->headers->get('X-Debug-Token')) { + $response->headers->set('X-Previous-Debug-Token', $prevToken); + } + $response->headers->set('X-Debug-Token', $profile->getToken()); foreach ($this->collectors as $collector) { @@ -174,10 +175,6 @@ public function collect(Request $request, Response $response, \Exception $except public function reset() { foreach ($this->collectors as $collector) { - if (!method_exists($collector, 'reset')) { - continue; - } - $collector->reset(); } $this->enabled = $this->initiallyEnabled; @@ -211,10 +208,6 @@ public function set(array $collectors = array()) */ public function add(DataCollectorInterface $collector) { - if (!method_exists($collector, 'reset')) { - @trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', DataCollectorInterface::class, \get_class($collector)), E_USER_DEPRECATED); - } - $this->collectors[$collector->getName()] = $collector; } diff --git a/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php b/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php index 8e52b097d6946..394e7ed7c6d51 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php @@ -12,12 +12,9 @@ namespace Symfony\Component\HttpKernel\Tests\Bundle; use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle\ExtensionNotValidBundle; use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\ExtensionPresentBundle; -use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionAbsentBundle\ExtensionAbsentBundle; -use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand; class BundleTest extends TestCase { @@ -31,24 +28,6 @@ public function testGetContainerExtension() ); } - /** - * @group legacy - * @expectedDeprecation Auto-registration of the command "Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand" is deprecated since Symfony 3.4 and won't be supported in 4.0. Use PSR-4 based service discovery instead. - */ - public function testRegisterCommands() - { - $cmd = new FooCommand(); - $app = $this->getMockBuilder('Symfony\Component\Console\Application')->getMock(); - $app->expects($this->once())->method('add')->with($this->equalTo($cmd)); - - $bundle = new ExtensionPresentBundle(); - $bundle->registerCommands($app); - - $bundle2 = new ExtensionAbsentBundle(); - - $this->assertNull($bundle2->registerCommands($app)); - } - /** * @expectedException \LogicException * @expectedExceptionMessage must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface @@ -59,20 +38,6 @@ public function testGetContainerExtensionWithInvalidClass() $bundle->getContainerExtension(); } - public function testHttpKernelRegisterCommandsIgnoresCommandsThatAreRegisteredAsServices() - { - $container = new ContainerBuilder(); - $container->register('console.command.symfony_component_httpkernel_tests_fixtures_extensionpresentbundle_command_foocommand', 'Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand'); - - $application = $this->getMockBuilder('Symfony\Component\Console\Application')->getMock(); - // add() is never called when the found command classes are already registered as services - $application->expects($this->never())->method('add'); - - $bundle = new ExtensionPresentBundle(); - $bundle->setContainer($container); - $bundle->registerCommands($application); - } - public function testBundleNameIsGuessedFromClass() { $bundle = new GuessedNameBundle(); diff --git a/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.php b/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.php index ec2ecff948c81..06400b444a8a2 100644 --- a/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.php @@ -39,21 +39,6 @@ public function testInjectClearersInConstructor() $chainClearer->clear(self::$cacheDir); } - /** - * @group legacy - */ - public function testInjectClearerUsingAdd() - { - $clearer = $this->getMockClearer(); - $clearer - ->expects($this->once()) - ->method('clear'); - - $chainClearer = new ChainCacheClearer(); - $chainClearer->add($clearer); - $chainClearer->clear(self::$cacheDir); - } - protected function getMockClearer() { return $this->getMockBuilder('Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface')->getMock(); diff --git a/src/Symfony/Component/HttpKernel/Tests/CacheClearer/Psr6CacheClearerTest.php b/src/Symfony/Component/HttpKernel/Tests/CacheClearer/Psr6CacheClearerTest.php index 3e20efbf1f2c5..588fd3007e465 100644 --- a/src/Symfony/Component/HttpKernel/Tests/CacheClearer/Psr6CacheClearerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/CacheClearer/Psr6CacheClearerTest.php @@ -45,25 +45,4 @@ public function testClearPoolThrowsExceptionOnUnreferencedPool() { (new Psr6CacheClearer())->clearPool('unknown'); } - - /** - * @group legacy - * @expectedDeprecation The Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer::addPool() method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass an array of pools indexed by name to the constructor instead. - */ - public function testClearPoolsInjectedByAdder() - { - $pool1 = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); - $pool1 - ->expects($this->once()) - ->method('clear'); - - $pool2 = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); - $pool2 - ->expects($this->once()) - ->method('clear'); - - $clearer = new Psr6CacheClearer(array('pool1' => $pool1)); - $clearer->addPool($pool2); - $clearer->clear(''); - } } diff --git a/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php b/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php index ba159124c93e9..cfa998c1de143 100644 --- a/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php @@ -38,34 +38,6 @@ public function testInjectWarmersUsingConstructor() $aggregate->warmUp(self::$cacheDir); } - /** - * @group legacy - */ - public function testInjectWarmersUsingAdd() - { - $warmer = $this->getCacheWarmerMock(); - $warmer - ->expects($this->once()) - ->method('warmUp'); - $aggregate = new CacheWarmerAggregate(); - $aggregate->add($warmer); - $aggregate->warmUp(self::$cacheDir); - } - - /** - * @group legacy - */ - public function testInjectWarmersUsingSetWarmers() - { - $warmer = $this->getCacheWarmerMock(); - $warmer - ->expects($this->once()) - ->method('warmUp'); - $aggregate = new CacheWarmerAggregate(); - $aggregate->setWarmers(array($warmer)); - $aggregate->warmUp(self::$cacheDir); - } - public function testWarmupDoesCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsEnabled() { $warmer = $this->getCacheWarmerMock(); diff --git a/src/Symfony/Component/HttpKernel/Tests/ClientTest.php b/src/Symfony/Component/HttpKernel/Tests/ClientTest.php index 051d5d47c0265..2febf4b9d6061 100644 --- a/src/Symfony/Component/HttpKernel/Tests/ClientTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/ClientTest.php @@ -105,8 +105,8 @@ public function testUploadedFile() $client = new Client($kernel); $files = array( - array('tmp_name' => $source, 'name' => 'original', 'type' => 'mime/original', 'size' => 1, 'error' => UPLOAD_ERR_OK), - new UploadedFile($source, 'original', 'mime/original', 1, UPLOAD_ERR_OK, true), + array('tmp_name' => $source, 'name' => 'original', 'type' => 'mime/original', 'size' => null, 'error' => UPLOAD_ERR_OK), + new UploadedFile($source, 'original', 'mime/original', UPLOAD_ERR_OK, true), ); $file = null; @@ -121,8 +121,7 @@ public function testUploadedFile() $this->assertEquals('original', $file->getClientOriginalName()); $this->assertEquals('mime/original', $file->getClientMimeType()); - $this->assertSame(1, $file->getClientSize()); - $this->assertTrue($file->isValid()); + $this->assertEquals(1, $file->getSize()); } $file->move(dirname($target), basename($target)); @@ -155,15 +154,19 @@ public function testUploadedFileWhenSizeExceedsUploadMaxFileSize() $file = $this ->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') - ->setConstructorArgs(array($source, 'original', 'mime/original', 123, UPLOAD_ERR_OK, true)) - ->setMethods(array('getSize')) + ->setConstructorArgs(array($source, 'original', 'mime/original', UPLOAD_ERR_OK, true)) + ->setMethods(array('getSize', 'getClientSize')) ->getMock() ; - - $file->expects($this->once()) + /* should be modified when the getClientSize will be removed */ + $file->expects($this->any()) ->method('getSize') ->will($this->returnValue(INF)) ; + $file->expects($this->any()) + ->method('getClientSize') + ->will($this->returnValue(INF)) + ; $client->request('POST', '/', array(), array($file)); @@ -177,7 +180,7 @@ public function testUploadedFileWhenSizeExceedsUploadMaxFileSize() $this->assertEquals(UPLOAD_ERR_INI_SIZE, $file->getError()); $this->assertEquals('mime/original', $file->getClientMimeType()); $this->assertEquals('original', $file->getClientOriginalName()); - $this->assertEquals(0, $file->getClientSize()); + $this->assertEquals(0, $file->getSize()); unlink($source); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Config/EnvParametersResourceTest.php b/src/Symfony/Component/HttpKernel/Tests/Config/EnvParametersResourceTest.php deleted file mode 100644 index 986e05d8b888a..0000000000000 --- a/src/Symfony/Component/HttpKernel/Tests/Config/EnvParametersResourceTest.php +++ /dev/null @@ -1,110 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpKernel\Tests\Config; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpKernel\Config\EnvParametersResource; - -/** - * @group legacy - */ -class EnvParametersResourceTest extends TestCase -{ - protected $prefix = '__DUMMY_'; - protected $initialEnv; - protected $resource; - - protected function setUp() - { - $this->initialEnv = array( - $this->prefix.'1' => 'foo', - $this->prefix.'2' => 'bar', - ); - - foreach ($this->initialEnv as $key => $value) { - $_SERVER[$key] = $value; - } - - $this->resource = new EnvParametersResource($this->prefix); - } - - protected function tearDown() - { - foreach ($_SERVER as $key => $value) { - if (0 === strpos($key, $this->prefix)) { - unset($_SERVER[$key]); - } - } - } - - public function testGetResource() - { - $this->assertSame( - array('prefix' => $this->prefix, 'variables' => $this->initialEnv), - $this->resource->getResource(), - '->getResource() returns the resource' - ); - } - - public function testToString() - { - $this->assertSame( - serialize(array('prefix' => $this->prefix, 'variables' => $this->initialEnv)), - (string) $this->resource - ); - } - - public function testIsFreshNotChanged() - { - $this->assertTrue( - $this->resource->isFresh(time()), - '->isFresh() returns true if the variables have not changed' - ); - } - - public function testIsFreshValueChanged() - { - reset($this->initialEnv); - $_SERVER[key($this->initialEnv)] = 'baz'; - - $this->assertFalse( - $this->resource->isFresh(time()), - '->isFresh() returns false if a variable has been changed' - ); - } - - public function testIsFreshValueRemoved() - { - reset($this->initialEnv); - unset($_SERVER[key($this->initialEnv)]); - - $this->assertFalse( - $this->resource->isFresh(time()), - '->isFresh() returns false if a variable has been removed' - ); - } - - public function testIsFreshValueAdded() - { - $_SERVER[$this->prefix.'3'] = 'foo'; - - $this->assertFalse( - $this->resource->isFresh(time()), - '->isFresh() returns false if a variable has been added' - ); - } - - public function testSerializeUnserialize() - { - $this->assertEquals($this->resource, unserialize(serialize($this->resource))); - } -} diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php index 0804139030128..6a5926f85c7e3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php @@ -152,9 +152,6 @@ public function testGetArgumentsInjectsExtendingRequest() $this->assertEquals(array($request), self::$resolver->getArguments($request, $controller), '->getArguments() injects the request when extended'); } - /** - * @requires PHP 5.6 - */ public function testGetVariadicArguments() { $request = Request::create('/'); @@ -166,7 +163,6 @@ public function testGetVariadicArguments() } /** - * @requires PHP 5.6 * @expectedException \InvalidArgumentException */ public function testGetVariadicArgumentsWithoutArrayInRequest() @@ -180,7 +176,6 @@ public function testGetVariadicArgumentsWithoutArrayInRequest() } /** - * @requires PHP 5.6 * @expectedException \InvalidArgumentException */ public function testGetArgumentWithoutArray() @@ -210,9 +205,6 @@ public function testIfExceptionIsThrownWhenMissingAnArgument() self::$resolver->getArguments($request, $controller); } - /** - * @requires PHP 7.1 - */ public function testGetNullableArguments() { $request = Request::create('/'); @@ -224,9 +216,6 @@ public function testGetNullableArguments() $this->assertEquals(array('foo', new \stdClass(), 'value', 'mandatory'), self::$resolver->getArguments($request, $controller)); } - /** - * @requires PHP 7.1 - */ public function testGetNullableArgumentsWithDefaults() { $request = Request::create('/'); diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php index 0019123b6819c..6f203d3a987c3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php @@ -141,17 +141,7 @@ public function testNonConstructController() $request = Request::create('/'); $request->attributes->set('_controller', array(ImpossibleConstructController::class, 'action')); - if (\PHP_VERSION_ID < 70100) { - ErrorHandler::register(); - try { - $resolver->getController($request); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } else { - $resolver->getController($request); - } + $resolver->getController($request); } public function testNonInstantiableControllerWithCorrespondingService() diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php index 190e15ad67bca..203a162fa815f 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php @@ -14,8 +14,6 @@ use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Controller\ControllerResolver; -use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\NullableController; -use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController; use Symfony\Component\HttpFoundation\Request; class ControllerResolverTest extends TestCase @@ -144,139 +142,6 @@ public function getUndefinedControllers() ); } - /** - * @group legacy - */ - public function testGetArguments() - { - $resolver = $this->createControllerResolver(); - - $request = Request::create('/'); - $controller = array(new self(), 'testGetArguments'); - $this->assertEquals(array(), $resolver->getArguments($request, $controller), '->getArguments() returns an empty array if the method takes no arguments'); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $controller = array(new self(), 'controllerMethod1'); - $this->assertEquals(array('foo'), $resolver->getArguments($request, $controller), '->getArguments() returns an array of arguments for the controller method'); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $controller = array(new self(), 'controllerMethod2'); - $this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller), '->getArguments() uses default values if present'); - - $request->attributes->set('bar', 'bar'); - $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller), '->getArguments() overrides default values if provided in the request attributes'); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $controller = function ($foo) {}; - $this->assertEquals(array('foo'), $resolver->getArguments($request, $controller)); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $controller = function ($foo, $bar = 'bar') {}; - $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller)); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $controller = new self(); - $this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller)); - $request->attributes->set('bar', 'bar'); - $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller)); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $request->attributes->set('foobar', 'foobar'); - $controller = 'Symfony\Component\HttpKernel\Tests\Controller\some_controller_function'; - $this->assertEquals(array('foo', 'foobar'), $resolver->getArguments($request, $controller)); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $request->attributes->set('foobar', 'foobar'); - $controller = array(new self(), 'controllerMethod3'); - - try { - $resolver->getArguments($request, $controller); - $this->fail('->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); - } catch (\Exception $e) { - $this->assertInstanceOf('\RuntimeException', $e, '->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); - } - - $request = Request::create('/'); - $controller = array(new self(), 'controllerMethod5'); - $this->assertEquals(array($request), $resolver->getArguments($request, $controller), '->getArguments() injects the request'); - } - - /** - * @requires PHP 5.6 - * @group legacy - */ - public function testGetVariadicArguments() - { - $resolver = new ControllerResolver(); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $request->attributes->set('bar', array('foo', 'bar')); - $controller = array(new VariadicController(), 'action'); - $this->assertEquals(array('foo', 'foo', 'bar'), $resolver->getArguments($request, $controller)); - } - - public function testCreateControllerCanReturnAnyCallable() - { - $mock = $this->getMockBuilder('Symfony\Component\HttpKernel\Controller\ControllerResolver')->setMethods(array('createController'))->getMock(); - $mock->expects($this->once())->method('createController')->will($this->returnValue('Symfony\Component\HttpKernel\Tests\Controller\some_controller_function')); - - $request = Request::create('/'); - $request->attributes->set('_controller', 'foobar'); - $mock->getController($request); - } - - /** - * @expectedException \RuntimeException - * @group legacy - */ - public function testIfExceptionIsThrownWhenMissingAnArgument() - { - $resolver = new ControllerResolver(); - $request = Request::create('/'); - - $controller = array($this, 'controllerMethod1'); - - $resolver->getArguments($request, $controller); - } - - /** - * @requires PHP 7.1 - * @group legacy - */ - public function testGetNullableArguments() - { - $resolver = new ControllerResolver(); - - $request = Request::create('/'); - $request->attributes->set('foo', 'foo'); - $request->attributes->set('bar', new \stdClass()); - $request->attributes->set('mandatory', 'mandatory'); - $controller = array(new NullableController(), 'action'); - $this->assertEquals(array('foo', new \stdClass(), 'value', 'mandatory'), $resolver->getArguments($request, $controller)); - } - - /** - * @requires PHP 7.1 - * @group legacy - */ - public function testGetNullableArgumentsWithDefaults() - { - $resolver = new ControllerResolver(); - - $request = Request::create('/'); - $request->attributes->set('mandatory', 'mandatory'); - $controller = array(new NullableController(), 'action'); - $this->assertEquals(array(null, null, 'value', 'mandatory'), $resolver->getArguments($request, $controller)); - } - protected function createControllerResolver(LoggerInterface $logger = null) { return new ControllerResolver($logger); @@ -290,21 +155,9 @@ public function controllerMethod1($foo) { } - protected function controllerMethod2($foo, $bar = null) - { - } - - protected function controllerMethod3($foo, $bar, $foobar) - { - } - protected static function controllerMethod4() { } - - protected function controllerMethod5(Request $request) - { - } } function some_controller_function($foo, $foobar) diff --git a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php index b4b449f358611..2ddc6e7a5ee10 100644 --- a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php @@ -84,9 +84,6 @@ public function testSignature5() ), $arguments); } - /** - * @requires PHP 5.6 - */ public function testVariadicSignature() { $arguments = $this->factory->createArgumentMetadata(array(new VariadicController(), 'action')); @@ -97,9 +94,6 @@ public function testVariadicSignature() ), $arguments); } - /** - * @requires PHP 7.0 - */ public function testBasicTypesSignature() { $arguments = $this->factory->createArgumentMetadata(array(new BasicTypesController(), 'action')); @@ -111,9 +105,6 @@ public function testBasicTypesSignature() ), $arguments); } - /** - * @requires PHP 7.1 - */ public function testNullableTypesSignature() { $arguments = $this->factory->createArgumentMetadata(array(new NullableController(), 'action')); diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php deleted file mode 100644 index 5fe92d60e0491..0000000000000 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpKernel\Tests\DataCollector\Util; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; - -/** - * @group legacy - */ -class ValueExporterTest extends TestCase -{ - /** - * @var ValueExporter - */ - private $valueExporter; - - protected function setUp() - { - $this->valueExporter = new ValueExporter(); - } - - public function testDateTime() - { - $dateTime = new \DateTime('2014-06-10 07:35:40', new \DateTimeZone('UTC')); - $this->assertSame('Object(DateTime) - 2014-06-10T07:35:40+00:00', $this->valueExporter->exportValue($dateTime)); - } - - public function testDateTimeImmutable() - { - $dateTime = new \DateTimeImmutable('2014-06-10 07:35:40', new \DateTimeZone('UTC')); - $this->assertSame('Object(DateTimeImmutable) - 2014-06-10T07:35:40+00:00', $this->valueExporter->exportValue($dateTime)); - } - - public function testIncompleteClass() - { - $foo = new \__PHP_Incomplete_Class(); - $array = new \ArrayObject($foo); - $array['__PHP_Incomplete_Class_Name'] = 'AppBundle/Foo'; - $this->assertSame('__PHP_Incomplete_Class(AppBundle/Foo)', $this->valueExporter->exportValue($foo)); - } -} diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php index 73021483738f1..7e664954dd6dc 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php @@ -18,31 +18,6 @@ class LazyLoadingFragmentHandlerTest extends TestCase { - /** - * @group legacy - * @expectedDeprecation The Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler::addRendererService() method is deprecated since Symfony 3.3 and will be removed in 4.0. - */ - public function testRenderWithLegacyMapping() - { - $renderer = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface')->getMock(); - $renderer->expects($this->once())->method('getName')->will($this->returnValue('foo')); - $renderer->expects($this->any())->method('render')->will($this->returnValue(new Response())); - - $requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock(); - $requestStack->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->once())->method('get')->will($this->returnValue($renderer)); - - $handler = new LazyLoadingFragmentHandler($container, $requestStack, false); - $handler->addRendererService('foo', 'foo'); - - $handler->render('/foo', 'foo'); - - // second call should not lazy-load anymore (see once() above on the get() method) - $handler->render('/foo', 'foo'); - } - public function testRender() { $renderer = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface')->getMock(); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php index 3cb0b298bb07a..330dbe9769a1c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php @@ -54,11 +54,13 @@ public function testHandleWithoutLogger($event, $event2) $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); $l = new ExceptionListener('foo'); + $l->logKernelException($event); $l->onKernelException($event); $this->assertEquals(new Response('foo'), $event->getResponse()); try { + $l->logKernelException($event2); $l->onKernelException($event2); $this->fail('RuntimeException expected'); } catch (\RuntimeException $e) { @@ -75,11 +77,13 @@ public function testHandleWithLogger($event, $event2) $logger = new TestLogger(); $l = new ExceptionListener('foo', $logger); + $l->logKernelException($event); $l->onKernelException($event); $this->assertEquals(new Response('foo'), $event->getResponse()); try { + $l->logKernelException($event2); $l->onKernelException($event2); $this->fail('RuntimeException expected'); } catch (\RuntimeException $e) { diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php index 342dfc367527d..2668ede8e8e4d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php @@ -70,12 +70,7 @@ public function getPortData() ); } - /** - * @param string $uri - * - * @return GetResponseEvent - */ - private function createGetResponseEventForUri($uri) + private function createGetResponseEventForUri(string $uri): GetResponseEvent { $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); $request = Request::create($uri); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SaveSessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SaveSessionListenerTest.php index 5492c3d784805..a903fd5891693 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SaveSessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SaveSessionListenerTest.php @@ -19,6 +19,9 @@ use Symfony\Component\HttpKernel\EventListener\SaveSessionListener; use Symfony\Component\HttpKernel\HttpKernelInterface; +/** + * @group legacy + */ class SaveSessionListenerTest extends TestCase { public function testOnlyTriggeredOnMasterRequest() diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php index 34598363c8914..79fd88e4ab1cd 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Session; @@ -58,22 +59,33 @@ public function testSessionIsSet() public function testResponseIsPrivate() { $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); - $session->expects($this->once())->method('isStarted')->willReturn(false); + $session->expects($this->exactly(2))->method('isStarted')->willReturn(false); $session->expects($this->once())->method('hasBeenStarted')->willReturn(true); $container = new Container(); - $container->set('session', $session); + $container->set('initialized_session', $session); $listener = new SessionListener($container); $kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock(); - $request = new Request(); $response = new Response(); - $listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST)); - $listener->onKernelResponse(new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response)); + $listener->onKernelResponse(new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response)); $this->assertTrue($response->headers->hasCacheControlDirective('private')); $this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate')); $this->assertSame('0', $response->headers->getCacheControlDirective('max-age')); } + + public function testUninitilizedSession() + { + $event = $this->getMockBuilder(FilterResponseEvent::class)->disableOriginalConstructor()->getMock(); + $event->expects($this->once())->method('isMasterRequest')->willReturn(true); + + $container = new ServiceLocator(array( + 'initialized_session' => function () {}, + )); + + $listener = new SessionListener($container); + $listener->onKernelResponse($event); + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/MethodNotAllowedHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/MethodNotAllowedHttpExceptionTest.php index b5def13ce38d7..ea82014952518 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/MethodNotAllowedHttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/MethodNotAllowedHttpExceptionTest.php @@ -12,6 +12,19 @@ public function testHeadersDefault() $this->assertSame(array('Allow' => 'GET, PUT'), $exception->getHeaders()); } + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=1200', + ); + + $exception = new MethodNotAllowedHttpException(array('get'), null, null, null, $headers); + + $headers['Allow'] = 'GET'; + + $this->assertSame($headers, $exception->getHeaders()); + } + /** * @dataProvider headerDataProvider */ diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/ServiceUnavailableHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/ServiceUnavailableHttpExceptionTest.php index e41a23d4e7719..83cbdc2bccc5b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/ServiceUnavailableHttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/ServiceUnavailableHttpExceptionTest.php @@ -12,6 +12,19 @@ public function testHeadersDefaultRetryAfter() $this->assertSame(array('Retry-After' => 10), $exception->getHeaders()); } + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=1337', + ); + + $exception = new ServiceUnavailableHttpException(1337, null, null, null, $headers); + + $headers['Retry-After'] = 1337; + + $this->assertSame($headers, $exception->getHeaders()); + } + /** * @dataProvider headerDataProvider */ diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php index 2079bb3380d20..6ec7f3d0796d2 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php @@ -12,6 +12,19 @@ public function testHeadersDefaultRertyAfter() $this->assertSame(array('Retry-After' => 10), $exception->getHeaders()); } + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=69', + ); + + $exception = new TooManyRequestsHttpException(69, null, null, null, $headers); + + $headers['Retry-After'] = 69; + + $this->assertSame($headers, $exception->getHeaders()); + } + /** * @dataProvider headerDataProvider */ diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/UnauthorizedHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/UnauthorizedHttpExceptionTest.php index 37a0028dc8257..1e93d25b1a9b8 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/UnauthorizedHttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/UnauthorizedHttpExceptionTest.php @@ -12,6 +12,19 @@ public function testHeadersDefault() $this->assertSame(array('WWW-Authenticate' => 'Challenge'), $exception->getHeaders()); } + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=1200', + ); + + $exception = new UnauthorizedHttpException('Challenge', null, null, null, $headers); + + $headers['WWW-Authenticate'] = 'Challenge'; + + $this->assertSame($headers, $exception->getHeaders()); + } + /** * @dataProvider headerDataProvider */ diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php index ca2e6a693da6e..d5456fe5dcadc 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php @@ -11,10 +11,9 @@ namespace Symfony\Component\HttpKernel\Tests\Fixtures; -use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; -class TestEventDispatcher extends EventDispatcher implements TraceableEventDispatcherInterface +class TestEventDispatcher extends TraceableEventDispatcher { public function getCalledListeners() { @@ -29,4 +28,9 @@ public function getNotCalledListeners() public function reset() { } + + public function getOrphanedEvents() + { + return array(); + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php index 7099b4d7e8bf6..8a40dcd5bb892 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php @@ -26,19 +26,7 @@ public function testRenderFallbackToInlineStrategyIfEsiNotSupported() $strategy->render('/', Request::create('/')); } - /** - * @group legacy - * @expectedDeprecation Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated %s. - */ - public function testRenderFallbackWithObjectAttributesIsDeprecated() - { - $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); - $request = Request::create('/'); - $reference = new ControllerReference('main_controller', array('foo' => new \stdClass()), array()); - $strategy->render($reference, $request); - } - - public function testRenderFallbackWithScalarIsNotDeprecated() + public function testRenderFallbackWithScalar() { $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); $request = Request::create('/'); diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php index 18e55a5be0df2..844b53b9a1f06 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -13,8 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpKernel\Controller\ArgumentResolver; -use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer; @@ -53,48 +51,6 @@ public function testRenderWithObjectsAsAttributes() $this->assertSame('foo', $strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/'))->getContent()); } - /** - * @group legacy - */ - public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheControllerLegacy() - { - $resolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver')->setMethods(array('getController'))->getMock(); - $resolver - ->expects($this->once()) - ->method('getController') - ->will($this->returnValue(function (\stdClass $object, Bar $object1) { - return new Response($object1->getBar()); - })) - ; - - $kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack()); - $renderer = new InlineFragmentRenderer($kernel); - - $response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/')); - $this->assertEquals('bar', $response->getContent()); - } - - /** - * @group legacy - */ - public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheController() - { - $resolver = $this->getMockBuilder(ControllerResolverInterface::class)->getMock(); - $resolver - ->expects($this->once()) - ->method('getController') - ->will($this->returnValue(function (\stdClass $object, Bar $object1) { - return new Response($object1->getBar()); - })) - ; - - $kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack(), new ArgumentResolver()); - $renderer = new InlineFragmentRenderer($kernel); - - $response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/')); - $this->assertEquals('bar', $response->getContent()); - } - public function testRenderWithTrustedHeaderDisabled() { Request::setTrustedProxies(array(), 0); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php index 7aed26aa59c9c..0adef984c6f5c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php @@ -111,24 +111,6 @@ public function testHandleHttpException() $this->assertEquals('POST', $response->headers->get('Allow')); } - /** - * @group legacy - * @dataProvider getStatusCodes - */ - public function testLegacyHandleWhenAnExceptionIsHandledWithASpecificStatusCode($responseStatusCode, $expectedStatusCode) - { - $dispatcher = new EventDispatcher(); - $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) use ($responseStatusCode, $expectedStatusCode) { - $event->setResponse(new Response('', $responseStatusCode, array('X-Status-Code' => $expectedStatusCode))); - }); - - $kernel = $this->getHttpKernel($dispatcher, function () { throw new \RuntimeException(); }); - $response = $kernel->handle(new Request()); - - $this->assertEquals($expectedStatusCode, $response->getStatusCode()); - $this->assertFalse($response->headers->has('X-Status-Code')); - } - public function getStatusCodes() { return array( diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index a16ac37deebce..9dab664cf5cdf 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -17,7 +17,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Bundle\BundleInterface; -use Symfony\Component\HttpKernel\Config\EnvParametersResource; use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass; use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter; use Symfony\Component\HttpKernel\Kernel; @@ -124,20 +123,6 @@ public function testBootSetsTheBootedFlagToTrue() $this->assertTrue($kernel->isBooted()); } - /** - * @group legacy - */ - public function testClassCacheIsLoaded() - { - $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); - $kernel->loadClassCache('name', '.extension'); - $kernel->expects($this->once()) - ->method('doLoadClassCache') - ->with('name', '.extension'); - - $kernel->boot(); - } - public function testClassCacheIsNotLoadedByDefault() { $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); @@ -147,53 +132,6 @@ public function testClassCacheIsNotLoadedByDefault() $kernel->boot(); } - /** - * @group legacy - */ - public function testClassCacheIsNotLoadedWhenKernelIsNotBooted() - { - $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); - $kernel->loadClassCache(); - $kernel->expects($this->never()) - ->method('doLoadClassCache'); - } - - public function testEnvParametersResourceIsAdded() - { - $container = new ContainerBuilder(); - $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') - ->disableOriginalConstructor() - ->setMethods(array('getContainerBuilder', 'prepareContainer', 'getCacheDir', 'getLogDir')) - ->getMock(); - $kernel->expects($this->any()) - ->method('getContainerBuilder') - ->will($this->returnValue($container)); - $kernel->expects($this->any()) - ->method('prepareContainer') - ->will($this->returnValue(null)); - $kernel->expects($this->any()) - ->method('getCacheDir') - ->will($this->returnValue(sys_get_temp_dir())); - $kernel->expects($this->any()) - ->method('getLogDir') - ->will($this->returnValue(sys_get_temp_dir())); - - $reflection = new \ReflectionClass(get_class($kernel)); - $method = $reflection->getMethod('buildContainer'); - $method->setAccessible(true); - $method->invoke($kernel); - - $found = false; - foreach ($container->getResources() as $resource) { - if ($resource instanceof EnvParametersResource) { - $found = true; - break; - } - } - - $this->assertTrue($found); - } - public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce() { $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); @@ -421,7 +359,7 @@ public function testLocateResourceThrowsExceptionWhenResourceDoesNotExist() $kernel ->expects($this->once()) ->method('getBundle') - ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'))) ; $kernel->locateResource('@Bundle1Bundle/config/routing.xml'); @@ -433,80 +371,19 @@ public function testLocateResourceReturnsTheFirstThatMatches() $kernel ->expects($this->once()) ->method('getBundle') - ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'))) ; $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt', $kernel->locateResource('@Bundle1Bundle/foo.txt')); } - /** - * @group legacy - */ - public function testLocateResourceReturnsTheFirstThatMatchesWithParent() - { - $parent = $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'); - $child = $this->getBundle(__DIR__.'/Fixtures/Bundle2Bundle'); - - $kernel = $this->getKernel(array('getBundle')); - $kernel - ->expects($this->exactly(2)) - ->method('getBundle') - ->will($this->returnValue(array($child, $parent))) - ; - - $this->assertEquals(__DIR__.'/Fixtures/Bundle2Bundle/foo.txt', $kernel->locateResource('@ParentAABundle/foo.txt')); - $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/bar.txt', $kernel->locateResource('@ParentAABundle/bar.txt')); - } - - /** - * @group legacy - */ - public function testLocateResourceReturnsAllMatches() - { - $parent = $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'); - $child = $this->getBundle(__DIR__.'/Fixtures/Bundle2Bundle'); - - $kernel = $this->getKernel(array('getBundle')); - $kernel - ->expects($this->once()) - ->method('getBundle') - ->will($this->returnValue(array($child, $parent))) - ; - - $this->assertEquals(array( - __DIR__.'/Fixtures/Bundle2Bundle/foo.txt', - __DIR__.'/Fixtures/Bundle1Bundle/foo.txt', ), - $kernel->locateResource('@Bundle1Bundle/foo.txt', null, false)); - } - - /** - * @group legacy - */ - public function testLocateResourceReturnsAllMatchesBis() - { - $kernel = $this->getKernel(array('getBundle')); - $kernel - ->expects($this->once()) - ->method('getBundle') - ->will($this->returnValue(array( - $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'), - $this->getBundle(__DIR__.'/Foobar'), - ))) - ; - - $this->assertEquals( - array(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt'), - $kernel->locateResource('@Bundle1Bundle/foo.txt', null, false) - ); - } - public function testLocateResourceIgnoresDirOnNonResource() { $kernel = $this->getKernel(array('getBundle')); $kernel ->expects($this->once()) ->method('getBundle') - ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'))) ; $this->assertEquals( @@ -521,7 +398,7 @@ public function testLocateResourceReturnsTheDirOneForResources() $kernel ->expects($this->once()) ->method('getBundle') - ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle')))) + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle'))) ; $this->assertEquals( @@ -530,73 +407,13 @@ public function testLocateResourceReturnsTheDirOneForResources() ); } - /** - * @group legacy - */ - public function testLocateResourceReturnsTheDirOneForResourcesAndBundleOnes() - { - $kernel = $this->getKernel(array('getBundle')); - $kernel - ->expects($this->once()) - ->method('getBundle') - ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle')))) - ; - - $this->assertEquals(array( - __DIR__.'/Fixtures/Resources/Bundle1Bundle/foo.txt', - __DIR__.'/Fixtures/Bundle1Bundle/Resources/foo.txt', ), - $kernel->locateResource('@Bundle1Bundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources', false) - ); - } - - /** - * @group legacy - */ - public function testLocateResourceOverrideBundleAndResourcesFolders() - { - $parent = $this->getBundle(__DIR__.'/Fixtures/BaseBundle', null, 'BaseBundle', 'BaseBundle'); - $child = $this->getBundle(__DIR__.'/Fixtures/ChildBundle', 'ParentBundle', 'ChildBundle', 'ChildBundle'); - - $kernel = $this->getKernel(array('getBundle')); - $kernel - ->expects($this->exactly(4)) - ->method('getBundle') - ->will($this->returnValue(array($child, $parent))) - ; - - $this->assertEquals(array( - __DIR__.'/Fixtures/Resources/ChildBundle/foo.txt', - __DIR__.'/Fixtures/ChildBundle/Resources/foo.txt', - __DIR__.'/Fixtures/BaseBundle/Resources/foo.txt', - ), - $kernel->locateResource('@BaseBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources', false) - ); - - $this->assertEquals( - __DIR__.'/Fixtures/Resources/ChildBundle/foo.txt', - $kernel->locateResource('@BaseBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources') - ); - - try { - $kernel->locateResource('@BaseBundle/Resources/hide.txt', __DIR__.'/Fixtures/Resources', false); - $this->fail('Hidden resources should raise an exception when returning an array of matching paths'); - } catch (\RuntimeException $e) { - } - - try { - $kernel->locateResource('@BaseBundle/Resources/hide.txt', __DIR__.'/Fixtures/Resources', true); - $this->fail('Hidden resources should raise an exception when returning the first matching path'); - } catch (\RuntimeException $e) { - } - } - public function testLocateResourceOnDirectories() { $kernel = $this->getKernel(array('getBundle')); $kernel ->expects($this->exactly(2)) ->method('getBundle') - ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle')))) + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle'))) ; $this->assertEquals( @@ -612,7 +429,7 @@ public function testLocateResourceOnDirectories() $kernel ->expects($this->exactly(2)) ->method('getBundle') - ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle')))) + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle'))) ; $this->assertEquals( @@ -626,103 +443,6 @@ public function testLocateResourceOnDirectories() } /** - * @group legacy - */ - public function testInitializeBundles() - { - $parent = $this->getBundle(null, null, 'ParentABundle'); - $child = $this->getBundle(null, 'ParentABundle', 'ChildABundle'); - - // use test kernel so we can access getBundleMap() - $kernel = $this->getKernelForTest(array('registerBundles')); - $kernel - ->expects($this->once()) - ->method('registerBundles') - ->will($this->returnValue(array($parent, $child))) - ; - $kernel->boot(); - - $map = $kernel->getBundleMap(); - $this->assertEquals(array($child, $parent), $map['ParentABundle']); - } - - /** - * @group legacy - */ - public function testInitializeBundlesSupportInheritanceCascade() - { - $grandparent = $this->getBundle(null, null, 'GrandParentBBundle'); - $parent = $this->getBundle(null, 'GrandParentBBundle', 'ParentBBundle'); - $child = $this->getBundle(null, 'ParentBBundle', 'ChildBBundle'); - - // use test kernel so we can access getBundleMap() - $kernel = $this->getKernelForTest(array('registerBundles')); - $kernel - ->expects($this->once()) - ->method('registerBundles') - ->will($this->returnValue(array($grandparent, $parent, $child))) - ; - $kernel->boot(); - - $map = $kernel->getBundleMap(); - $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentBBundle']); - $this->assertEquals(array($child, $parent), $map['ParentBBundle']); - $this->assertEquals(array($child), $map['ChildBBundle']); - } - - /** - * @group legacy - * @expectedException \LogicException - * @expectedExceptionMessage Bundle "ChildCBundle" extends bundle "FooBar", which is not registered. - */ - public function testInitializeBundlesThrowsExceptionWhenAParentDoesNotExists() - { - $child = $this->getBundle(null, 'FooBar', 'ChildCBundle'); - $kernel = $this->getKernel(array(), array($child)); - $kernel->boot(); - } - - /** - * @group legacy - */ - public function testInitializeBundlesSupportsArbitraryBundleRegistrationOrder() - { - $grandparent = $this->getBundle(null, null, 'GrandParentCBundle'); - $parent = $this->getBundle(null, 'GrandParentCBundle', 'ParentCBundle'); - $child = $this->getBundle(null, 'ParentCBundle', 'ChildCBundle'); - - // use test kernel so we can access getBundleMap() - $kernel = $this->getKernelForTest(array('registerBundles')); - $kernel - ->expects($this->once()) - ->method('registerBundles') - ->will($this->returnValue(array($parent, $grandparent, $child))) - ; - $kernel->boot(); - - $map = $kernel->getBundleMap(); - $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentCBundle']); - $this->assertEquals(array($child, $parent), $map['ParentCBundle']); - $this->assertEquals(array($child), $map['ChildCBundle']); - } - - /** - * @group legacy - * @expectedException \LogicException - * @expectedExceptionMessage Bundle "ParentCBundle" is directly extended by two bundles "ChildC2Bundle" and "ChildC1Bundle". - */ - public function testInitializeBundlesThrowsExceptionWhenABundleIsDirectlyExtendedByTwoBundles() - { - $parent = $this->getBundle(null, null, 'ParentCBundle'); - $child1 = $this->getBundle(null, 'ParentCBundle', 'ChildC1Bundle'); - $child2 = $this->getBundle(null, 'ParentCBundle', 'ChildC2Bundle'); - - $kernel = $this->getKernel(array(), array($parent, $child1, $child2)); - $kernel->boot(); - } - - /** - * @group legacy * @expectedException \LogicException * @expectedExceptionMessage Trying to register two bundles with the same name "DuplicateName" */ @@ -735,19 +455,6 @@ public function testInitializeBundleThrowsExceptionWhenRegisteringTwoBundlesWith $kernel->boot(); } - /** - * @group legacy - * @expectedException \LogicException - * @expectedExceptionMessage Bundle "CircularRefBundle" can not extend itself. - */ - public function testInitializeBundleThrowsExceptionWhenABundleExtendsItself() - { - $circularRef = $this->getBundle(null, 'CircularRefBundle', 'CircularRefBundle'); - - $kernel = $this->getKernel(array(), array($circularRef)); - $kernel->boot(); - } - public function testTerminateReturnsSilentlyIfKernelIsNotBooted() { $kernel = $this->getKernel(array('getHttpKernel')); @@ -807,25 +514,6 @@ public function testKernelRootDirNameStartingWithANumber() $this->assertEquals('_123', $kernel->getName()); } - /** - * @group legacy - * @expectedDeprecation The Symfony\Component\HttpKernel\Kernel::getEnvParameters() method is deprecated as of 3.3 and will be removed in 4.0. Use the %cenv()%c syntax to get the value of any environment variable from configuration files instead. - * @expectedDeprecation The support of special environment variables that start with SYMFONY__ (such as "SYMFONY__FOO__BAR") is deprecated as of 3.3 and will be removed in 4.0. Use the %cenv()%c syntax instead to get the value of environment variables in configuration files. - */ - public function testSymfonyEnvironmentVariables() - { - $_SERVER['SYMFONY__FOO__BAR'] = 'baz'; - - $kernel = $this->getKernel(); - $method = new \ReflectionMethod($kernel, 'getEnvParameters'); - $method->setAccessible(true); - - $envParameters = $method->invoke($kernel); - $this->assertSame('baz', $envParameters['foo.bar']); - - unset($_SERVER['SYMFONY__FOO__BAR']); - } - public function testProjectDirExtension() { $kernel = new CustomProjectDirKernel(); diff --git a/src/Symfony/Component/HttpKernel/UriSigner.php b/src/Symfony/Component/HttpKernel/UriSigner.php index 28459b4ecd394..de73039b342c4 100644 --- a/src/Symfony/Component/HttpKernel/UriSigner.php +++ b/src/Symfony/Component/HttpKernel/UriSigner.php @@ -25,7 +25,7 @@ class UriSigner * @param string $secret A secret * @param string $parameter Query string parameter to use */ - public function __construct($secret, $parameter = '_hash') + public function __construct(string $secret, string $parameter = '_hash') { $this->secret = $secret; $this->parameter = $parameter; diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 26b782bb383b7..7bfc6323342d2 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -16,37 +16,36 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "^3.4.4|^4.0.4", - "symfony/debug": "~2.8|~3.0|~4.0", + "php": "^7.1.3", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~4.1", + "symfony/debug": "~3.4|~4.0", "psr/log": "~1.0" }, "require-dev": { - "symfony/browser-kit": "~2.8|~3.0|~4.0", - "symfony/class-loader": "~2.8|~3.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/console": "~2.8|~3.0|~4.0", - "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/browser-kit": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~3.4|~4.0", "symfony/dependency-injection": "^3.4.5|^4.0.5", - "symfony/dom-crawler": "~2.8|~3.0|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/process": "~2.8|~3.0|~4.0", + "symfony/dom-crawler": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0", "symfony/routing": "~3.4|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/translation": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~3.3|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0", "psr/cache": "~1.0" }, "provide": { "psr/log-implementation": "1.0" }, "conflict": { - "symfony/config": "<2.8", + "symfony/config": "<3.4", "symfony/dependency-injection": "<3.4.5|<4.0.5,>=4", - "symfony/var-dumper": "<3.3", + "symfony/var-dumper": "<3.4", "twig/twig": "<1.34|<2.4,>=2" }, "suggest": { @@ -54,7 +53,6 @@ "symfony/config": "", "symfony/console": "", "symfony/dependency-injection": "", - "symfony/finder": "", "symfony/var-dumper": "" }, "autoload": { @@ -66,7 +64,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Inflector/Inflector.php b/src/Symfony/Component/Inflector/Inflector.php index fbe8cc8a45c72..cf685159a7f41 100644 --- a/src/Symfony/Component/Inflector/Inflector.php +++ b/src/Symfony/Component/Inflector/Inflector.php @@ -91,7 +91,7 @@ final class Inflector // accesses (access), addresses (address), kisses (kiss) array('sess', 4, true, false, 'ss'), - // analyses (analysis), ellipses (ellipsis), funguses (fungus), + // analyses (analysis), ellipses (ellipsis), fungi (fungus), // neuroses (neurosis), theses (thesis), emphases (emphasis), // oases (oasis), crises (crisis), houses (house), bases (base), // atlases (atlas) @@ -159,7 +159,7 @@ private function __construct() * * @internal */ - public static function singularize($plural) + public static function singularize(string $plural) { $pluralRev = strrev($plural); $lowerPluralRev = strtolower($pluralRev); diff --git a/src/Symfony/Component/Inflector/composer.json b/src/Symfony/Component/Inflector/composer.json index 365206e71b274..68550038cfb04 100644 --- a/src/Symfony/Component/Inflector/composer.json +++ b/src/Symfony/Component/Inflector/composer.json @@ -23,7 +23,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "autoload": { "psr-4": { "Symfony\\Component\\Inflector\\": "" }, @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Intl/Collator/Collator.php b/src/Symfony/Component/Intl/Collator/Collator.php index 4b19dd31ec440..3caf48a2de196 100644 --- a/src/Symfony/Component/Intl/Collator/Collator.php +++ b/src/Symfony/Component/Intl/Collator/Collator.php @@ -74,7 +74,7 @@ class Collator * * @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed */ - public function __construct($locale) + public function __construct(?string $locale) { if ('en' !== $locale && null !== $locale) { throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); diff --git a/src/Symfony/Component/Intl/Data/Bundle/Compiler/GenrbCompiler.php b/src/Symfony/Component/Intl/Data/Bundle/Compiler/GenrbCompiler.php index ef54e402e9294..13f423342b1ea 100644 --- a/src/Symfony/Component/Intl/Data/Bundle/Compiler/GenrbCompiler.php +++ b/src/Symfony/Component/Intl/Data/Bundle/Compiler/GenrbCompiler.php @@ -32,7 +32,7 @@ class GenrbCompiler implements BundleCompilerInterface * * @throws RuntimeException if the "genrb" cannot be found */ - public function __construct($genrb = 'genrb', $envVars = '') + public function __construct(string $genrb = 'genrb', string $envVars = '') { exec('which '.$genrb, $output, $status); diff --git a/src/Symfony/Component/Intl/Data/Bundle/Reader/BufferedBundleReader.php b/src/Symfony/Component/Intl/Data/Bundle/Reader/BufferedBundleReader.php index 5fee153259281..ae5a74a0ca526 100644 --- a/src/Symfony/Component/Intl/Data/Bundle/Reader/BufferedBundleReader.php +++ b/src/Symfony/Component/Intl/Data/Bundle/Reader/BufferedBundleReader.php @@ -29,7 +29,7 @@ class BufferedBundleReader implements BundleReaderInterface * @param BundleReaderInterface $reader The reader to buffer * @param int $bufferSize The number of entries to store in the buffer */ - public function __construct(BundleReaderInterface $reader, $bufferSize) + public function __construct(BundleReaderInterface $reader, int $bufferSize) { $this->reader = $reader; $this->buffer = new RingBuffer($bufferSize); diff --git a/src/Symfony/Component/Intl/Data/Bundle/Reader/IntlBundleReader.php b/src/Symfony/Component/Intl/Data/Bundle/Reader/IntlBundleReader.php index 9f800ccace7ef..375d3fe84658a 100644 --- a/src/Symfony/Component/Intl/Data/Bundle/Reader/IntlBundleReader.php +++ b/src/Symfony/Component/Intl/Data/Bundle/Reader/IntlBundleReader.php @@ -34,7 +34,6 @@ public function read($path, $locale) // Never enable fallback. We want to know if a bundle cannot be found $bundle = new \ResourceBundle($locale, $path, false); } catch (\Exception $e) { - // HHVM compatibility: constructor throws on invalid resource $bundle = null; } diff --git a/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php index 68a27c432389e..d13596bc1783d 100644 --- a/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/AbstractDataGenerator.php @@ -29,10 +29,10 @@ abstract class AbstractDataGenerator private $compiler; private $dirName; - public function __construct(GenrbCompiler $compiler, $dirName) + public function __construct(GenrbCompiler $compiler, string $dirName) { $this->compiler = $compiler; - $this->dirName = (string) $dirName; + $this->dirName = $dirName; } public function generateData(GeneratorConfig $config) diff --git a/src/Symfony/Component/Intl/Data/Generator/GeneratorConfig.php b/src/Symfony/Component/Intl/Data/Generator/GeneratorConfig.php index 9bb9304db0159..5b9e2b2ea95f8 100644 --- a/src/Symfony/Component/Intl/Data/Generator/GeneratorConfig.php +++ b/src/Symfony/Component/Intl/Data/Generator/GeneratorConfig.php @@ -30,11 +30,7 @@ class GeneratorConfig */ private $bundleWriters = array(); - /** - * @param string $sourceDir - * @param string $icuVersion - */ - public function __construct($sourceDir, $icuVersion) + public function __construct(string $sourceDir, string $icuVersion) { $this->sourceDir = $sourceDir; $this->icuVersion = $icuVersion; diff --git a/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php index 5951a77043eeb..c89ac337c4478 100644 --- a/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php @@ -34,9 +34,9 @@ class LocaleDataGenerator private $scriptDataProvider; private $regionDataProvider; - public function __construct($dirName, LanguageDataProvider $languageDataProvider, ScriptDataProvider $scriptDataProvider, RegionDataProvider $regionDataProvider) + public function __construct(string $dirName, LanguageDataProvider $languageDataProvider, ScriptDataProvider $scriptDataProvider, RegionDataProvider $regionDataProvider) { - $this->dirName = (string) $dirName; + $this->dirName = $dirName; $this->languageDataProvider = $languageDataProvider; $this->scriptDataProvider = $scriptDataProvider; $this->regionDataProvider = $regionDataProvider; diff --git a/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php index 6818a7948d579..61bc4fa9175f0 100644 --- a/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/CurrencyDataProvider.php @@ -39,7 +39,7 @@ class CurrencyDataProvider * @param string $path The path to the resource bundle * @param BundleEntryReaderInterface $reader The reader for reading the resource bundle */ - public function __construct($path, BundleEntryReaderInterface $reader) + public function __construct(string $path, BundleEntryReaderInterface $reader) { $this->path = $path; $this->reader = $reader; diff --git a/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php index 0eb5987d10ff5..4a64937782b03 100644 --- a/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/LanguageDataProvider.php @@ -32,7 +32,7 @@ class LanguageDataProvider * @param string $path The path to the directory containing the .res files * @param BundleEntryReaderInterface $reader The reader for reading the .res files */ - public function __construct($path, BundleEntryReaderInterface $reader) + public function __construct(string $path, BundleEntryReaderInterface $reader) { $this->path = $path; $this->reader = $reader; diff --git a/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php index 0eb82f621e155..cdd73716ceb7f 100644 --- a/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/LocaleDataProvider.php @@ -32,7 +32,7 @@ class LocaleDataProvider * @param string $path The path to the directory containing the .res files * @param BundleEntryReaderInterface $reader The reader for reading the .res files */ - public function __construct($path, BundleEntryReaderInterface $reader) + public function __construct(string $path, BundleEntryReaderInterface $reader) { $this->path = $path; $this->reader = $reader; diff --git a/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php index d3c8164158a97..80880989a7b9e 100644 --- a/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/RegionDataProvider.php @@ -32,7 +32,7 @@ class RegionDataProvider * @param string $path The path to the directory containing the .res files * @param BundleEntryReaderInterface $reader The reader for reading the .res files */ - public function __construct($path, BundleEntryReaderInterface $reader) + public function __construct(string $path, BundleEntryReaderInterface $reader) { $this->path = $path; $this->reader = $reader; diff --git a/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php b/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php index 29ff1d63ae9b1..a4c5ce3a17472 100644 --- a/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php +++ b/src/Symfony/Component/Intl/Data/Provider/ScriptDataProvider.php @@ -32,7 +32,7 @@ class ScriptDataProvider * @param string $path The path to the directory containing the .res files * @param BundleEntryReaderInterface $reader The reader for reading the .res files */ - public function __construct($path, BundleEntryReaderInterface $reader) + public function __construct(string $path, BundleEntryReaderInterface $reader) { $this->path = $path; $this->reader = $reader; diff --git a/src/Symfony/Component/Intl/Data/Util/RingBuffer.php b/src/Symfony/Component/Intl/Data/Util/RingBuffer.php index b63c31e91970f..d7facfd249ba3 100644 --- a/src/Symfony/Component/Intl/Data/Util/RingBuffer.php +++ b/src/Symfony/Component/Intl/Data/Util/RingBuffer.php @@ -34,7 +34,7 @@ class RingBuffer implements \ArrayAccess private $size; - public function __construct($size) + public function __construct(int $size) { $this->size = $size; } diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php index 66376475c666b..69d8a03a3aa9f 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php @@ -23,7 +23,7 @@ class AmPmTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { return $dateTime->format('A'); } @@ -31,7 +31,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return 'AM|PM'; } @@ -39,7 +39,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'marker' => $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php index a174fdcabc570..0bbc5cb44f47d 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php @@ -23,7 +23,7 @@ class DayOfWeekTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $dayOfWeek = $dateTime->format('l'); switch ($length) { @@ -41,7 +41,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { switch ($length) { case 4: @@ -58,7 +58,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array(); } diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php index 5af6dd724336b..23000311c7eb0 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php @@ -23,7 +23,7 @@ class DayOfYearTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $dayOfYear = $dateTime->format('z') + 1; @@ -33,7 +33,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return '\d{'.$length.'}'; } @@ -41,7 +41,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array(); } diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php index f0ded907c33c5..f05157f40269e 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php @@ -23,7 +23,7 @@ class DayTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { return $this->padLeft($dateTime->format('j'), $length); } @@ -31,7 +31,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; } @@ -39,7 +39,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'day' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php index fa312d7f11d98..c66699a95e2ae 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php @@ -40,7 +40,7 @@ class FullTransformer * @param string $pattern The pattern to be used to format and/or parse values * @param string $timezone The timezone to perform the date/time calculations */ - public function __construct($pattern, $timezone) + public function __construct(string $pattern, string $timezone) { $this->pattern = $pattern; $this->timezone = $timezone; diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php index 948e3450443ef..dd3a1a8e71368 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php @@ -23,7 +23,7 @@ class Hour1200Transformer extends HourTransformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $hourOfDay = $dateTime->format('g'); $hourOfDay = '12' == $hourOfDay ? '0' : $hourOfDay; @@ -34,7 +34,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function normalizeHour($hour, $marker = null) + public function normalizeHour(int $hour, string $marker = null): int { if ('PM' === $marker) { $hour += 12; @@ -46,7 +46,7 @@ public function normalizeHour($hour, $marker = null) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return '\d{1,2}'; } @@ -54,7 +54,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'hour' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php index 19c4d203abe09..63aea50498ec0 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php @@ -23,7 +23,7 @@ class Hour1201Transformer extends HourTransformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { return $this->padLeft($dateTime->format('g'), $length); } @@ -31,7 +31,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function normalizeHour($hour, $marker = null) + public function normalizeHour(int $hour, string $marker = null): int { if ('PM' !== $marker && 12 === $hour) { $hour = 0; @@ -46,7 +46,7 @@ public function normalizeHour($hour, $marker = null) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return '\d{1,2}'; } @@ -54,7 +54,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'hour' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php index e43d0ee8b2aec..78233f3d3806d 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php @@ -23,7 +23,7 @@ class Hour2400Transformer extends HourTransformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { return $this->padLeft($dateTime->format('G'), $length); } @@ -31,7 +31,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function normalizeHour($hour, $marker = null) + public function normalizeHour(int $hour, string $marker = null): int { if ('AM' == $marker) { $hour = 0; @@ -45,7 +45,7 @@ public function normalizeHour($hour, $marker = null) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return '\d{1,2}'; } @@ -53,7 +53,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'hour' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php index df4e671aaf02b..e82c04da95314 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php @@ -23,7 +23,7 @@ class Hour2401Transformer extends HourTransformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $hourOfDay = $dateTime->format('G'); $hourOfDay = ('0' == $hourOfDay) ? '24' : $hourOfDay; @@ -34,7 +34,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function normalizeHour($hour, $marker = null) + public function normalizeHour(int $hour, string $marker = null): int { if ((null === $marker && 24 === $hour) || 'AM' == $marker) { $hour = 0; @@ -48,7 +48,7 @@ public function normalizeHour($hour, $marker = null) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return '\d{1,2}'; } @@ -56,7 +56,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'hour' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php index 349d8e29ae7a9..349cd794de3ae 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php @@ -28,5 +28,5 @@ abstract class HourTransformer extends Transformer * * @return int The normalized hour value */ - abstract public function normalizeHour($hour, $marker = null); + abstract public function normalizeHour(int $hour, string $marker = null): int; } diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php index 08b5356e3fbd9..1f4dec8d1a161 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php @@ -23,7 +23,7 @@ class MinuteTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $minuteOfHour = (int) $dateTime->format('i'); @@ -33,7 +33,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; } @@ -41,7 +41,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'minute' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php index 391e035025a6f..ed669398f0ca2 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php @@ -65,7 +65,7 @@ public function __construct() /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $matchLengthMap = array( 1 => 'n', @@ -88,7 +88,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { switch ($length) { case 1: @@ -114,7 +114,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { if (!is_numeric($matched)) { if (3 === $length) { diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php index fa7e91d038f8c..ec02e7f3059ba 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php @@ -23,7 +23,7 @@ class QuarterTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $month = (int) $dateTime->format('n'); $quarter = (int) floor(($month - 1) / 3) + 1; @@ -43,7 +43,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { switch ($length) { case 1: @@ -59,7 +59,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array(); } diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php index dd2e7bd9f9bb5..6b1ffd0ce15d2 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php @@ -23,7 +23,7 @@ class SecondTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $secondOfMinute = (int) $dateTime->format('s'); @@ -33,7 +33,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; } @@ -41,7 +41,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'second' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/TimezoneTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/TimezoneTransformer.php index dc4fa7f53dfa8..77a6e860256bc 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/TimezoneTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/TimezoneTransformer.php @@ -27,7 +27,7 @@ class TimezoneTransformer extends Transformer * * @throws NotImplementedException When time zone is different than UTC or GMT (Etc/GMT) */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { $timeZone = substr($dateTime->getTimezone()->getName(), 0, 3); @@ -63,7 +63,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return 'GMT[+-]\d{2}:?\d{2}'; } @@ -71,7 +71,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'timezone' => self::getEtcTimeZoneId($matched), diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php index 26a25db355ca9..5bdb7267afd97 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php @@ -29,7 +29,7 @@ abstract class Transformer * * @return string The formatted value */ - abstract public function format(\DateTime $dateTime, $length); + abstract public function format(\DateTime $dateTime, int $length): string; /** * Returns a reverse matching regular expression of a string generated by format(). @@ -38,7 +38,7 @@ abstract public function format(\DateTime $dateTime, $length); * * @return string The reverse matching regular expression */ - abstract public function getReverseMatchingRegExp($length); + abstract public function getReverseMatchingRegExp(int $length): string; /** * Extract date options from a matched value returned by the processing of the reverse matching @@ -49,7 +49,7 @@ abstract public function getReverseMatchingRegExp($length); * * @return array An associative array */ - abstract public function extractDateOptions($matched, $length); + abstract public function extractDateOptions(string $matched, int $length): array; /** * Pad a string with zeros to the left. @@ -59,7 +59,7 @@ abstract public function extractDateOptions($matched, $length); * * @return string The padded string */ - protected function padLeft($value, $length) + protected function padLeft(string $value, int $length): string { return str_pad($value, $length, '0', STR_PAD_LEFT); } diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php index 0b546b774a40c..b41420b722179 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php @@ -23,7 +23,7 @@ class YearTransformer extends Transformer /** * {@inheritdoc} */ - public function format(\DateTime $dateTime, $length) + public function format(\DateTime $dateTime, int $length): string { if (2 === $length) { return $dateTime->format('y'); @@ -35,7 +35,7 @@ public function format(\DateTime $dateTime, $length) /** * {@inheritdoc} */ - public function getReverseMatchingRegExp($length) + public function getReverseMatchingRegExp(int $length): string { return 2 === $length ? '\d{2}' : '\d{4}'; } @@ -43,7 +43,7 @@ public function getReverseMatchingRegExp($length) /** * {@inheritdoc} */ - public function extractDateOptions($matched, $length) + public function extractDateOptions(string $matched, int $length): array { return array( 'year' => (int) $matched, diff --git a/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php b/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php index 7b3dfdfc22195..90db708294825 100644 --- a/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php +++ b/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php @@ -132,7 +132,7 @@ class IntlDateFormatter * @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed */ - public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null) + public function __construct(?string $locale, int $datetype, int $timetype, $timezone = null, ?int $calendar = self::GREGORIAN, string $pattern = null) { if ('en' !== $locale && null !== $locale) { throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); diff --git a/src/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php b/src/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php index f3ed0fa928594..bd6e4791debb1 100644 --- a/src/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php +++ b/src/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php @@ -20,7 +20,7 @@ class MethodArgumentNotImplementedException extends NotImplementedException * @param string $methodName The method name that raised the exception * @param string $argName The argument name that is not implemented */ - public function __construct($methodName, $argName) + public function __construct(string $methodName, string $argName) { $message = sprintf('The %s() method\'s argument $%s behavior is not implemented.', $methodName, $argName); parent::__construct($message); diff --git a/src/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php b/src/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php index 52228218ff33a..ee9ebe5e2fb55 100644 --- a/src/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php +++ b/src/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php @@ -19,10 +19,10 @@ class MethodArgumentValueNotImplementedException extends NotImplementedException /** * @param string $methodName The method name that raised the exception * @param string $argName The argument name - * @param string $argValue The argument value that is not implemented + * @param mixed $argValue The argument value that is not implemented * @param string $additionalMessage An optional additional message to append to the exception message */ - public function __construct($methodName, $argName, $argValue, $additionalMessage = '') + public function __construct(string $methodName, string $argName, $argValue, string $additionalMessage = '') { $message = sprintf( 'The %s() method\'s argument $%s value %s behavior is not implemented.%s', diff --git a/src/Symfony/Component/Intl/Exception/MethodNotImplementedException.php b/src/Symfony/Component/Intl/Exception/MethodNotImplementedException.php index 544e0e40a4821..6a45d71a483b2 100644 --- a/src/Symfony/Component/Intl/Exception/MethodNotImplementedException.php +++ b/src/Symfony/Component/Intl/Exception/MethodNotImplementedException.php @@ -19,7 +19,7 @@ class MethodNotImplementedException extends NotImplementedException /** * @param string $methodName The name of the method */ - public function __construct($methodName) + public function __construct(string $methodName) { parent::__construct(sprintf('The %s() is not implemented.', $methodName)); } diff --git a/src/Symfony/Component/Intl/Exception/NotImplementedException.php b/src/Symfony/Component/Intl/Exception/NotImplementedException.php index 47ac32b3201e9..1413886b3e86d 100644 --- a/src/Symfony/Component/Intl/Exception/NotImplementedException.php +++ b/src/Symfony/Component/Intl/Exception/NotImplementedException.php @@ -23,7 +23,7 @@ class NotImplementedException extends RuntimeException /** * @param string $message The exception message. A note to install the intl extension is appended to this string */ - public function __construct($message) + public function __construct(string $message) { parent::__construct($message.' '.self::INTL_INSTALL_MESSAGE); } diff --git a/src/Symfony/Component/Intl/Exception/UnexpectedTypeException.php b/src/Symfony/Component/Intl/Exception/UnexpectedTypeException.php index 645d4926dad15..739152346cc29 100644 --- a/src/Symfony/Component/Intl/Exception/UnexpectedTypeException.php +++ b/src/Symfony/Component/Intl/Exception/UnexpectedTypeException.php @@ -18,7 +18,7 @@ */ class UnexpectedTypeException extends InvalidArgumentException { - public function __construct($value, $expectedType) + public function __construct($value, string $expectedType) { parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); } diff --git a/src/Symfony/Component/Intl/Locale.php b/src/Symfony/Component/Intl/Locale.php index 6dbd71a4ba221..9b74fa4da3fa4 100644 --- a/src/Symfony/Component/Intl/Locale.php +++ b/src/Symfony/Component/Intl/Locale.php @@ -21,7 +21,7 @@ final class Locale extends \Locale { /** - * @var string + * @var string|null */ private static $defaultFallback = 'en'; @@ -31,11 +31,11 @@ final class Locale extends \Locale * The default fallback locale is used as fallback for locales that have no * fallback otherwise. * - * @param string $locale The default fallback locale + * @param string|null $locale The default fallback locale * * @see getFallback() */ - public static function setDefaultFallback($locale) + public static function setDefaultFallback(?string $locale) { self::$defaultFallback = $locale; } @@ -43,12 +43,12 @@ public static function setDefaultFallback($locale) /** * Returns the default fallback locale. * - * @return string The default fallback locale + * @return string|null The default fallback locale * * @see setDefaultFallback() * @see getFallback() */ - public static function getDefaultFallback() + public static function getDefaultFallback(): ?string { return self::$defaultFallback; } @@ -65,7 +65,7 @@ public static function getDefaultFallback() * @return string|null The ICU locale code of the fallback locale, or null * if no fallback exists */ - public static function getFallback($locale) + public static function getFallback($locale): ?string { if (function_exists('locale_parse')) { $localeSubTags = locale_parse($locale); @@ -105,6 +105,8 @@ public static function getFallback($locale) if (strlen($locale) < 4) { return self::$defaultFallback; } + + return null; } /** diff --git a/src/Symfony/Component/Intl/Locale/Locale.php b/src/Symfony/Component/Intl/Locale/Locale.php index 2aa9eb7b090af..053f1db56ffd8 100644 --- a/src/Symfony/Component/Intl/Locale/Locale.php +++ b/src/Symfony/Component/Intl/Locale/Locale.php @@ -16,8 +16,8 @@ /** * Replacement for PHP's native {@link \Locale} class. * - * The only method supported in this class is {@link getDefault}. This method - * will always return "en". All other methods will throw an exception when used. + * The only methods supported in this class are `getDefault` and `canonicalize`. + * All other methods will throw an exception when used. * * @author Eriksen Costa * @author Bernhard Schussek @@ -57,6 +57,39 @@ public static function acceptFromHttp($header) throw new MethodNotImplementedException(__METHOD__); } + /** + * Returns a canonicalized locale string. + * + * This polyfill doesn't implement the full-spec algorithm. It only + * canonicalizes locale strings handled by the `LocaleBundle` class. + * + * @param string $locale + * + * @return string + */ + public static function canonicalize($locale) + { + $locale = (string) $locale; + + if ('' === $locale || '.' === $locale[0]) { + return self::getDefault(); + } + + if (!preg_match('/^([a-z]{2})[-_]([a-z]{2})(?:([a-z]{2})(?:[-_]([a-z]{2}))?)?(?:\..*)?$/i', $locale, $m)) { + return $locale; + } + + if (!empty($m[4])) { + return strtolower($m[1]).'_'.ucfirst(strtolower($m[2].$m[3])).'_'.strtoupper($m[4]); + } + + if (!empty($m[3])) { + return strtolower($m[1]).'_'.ucfirst(strtolower($m[2].$m[3])); + } + + return strtolower($m[1]).'_'.strtoupper($m[2]); + } + /** * Not supported. Returns a correctly ordered and delimited locale code. * diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index 844d61cecbc75..874a7e290b776 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -257,7 +257,7 @@ class NumberFormatter * @throws MethodArgumentValueNotImplementedException When the $style is not supported * @throws MethodArgumentNotImplementedException When the pattern value is different than null */ - public function __construct($locale = 'en', $style = null, $pattern = null) + public function __construct(?string $locale = 'en', int $style = null, $pattern = null) { if ('en' !== $locale && null !== $locale) { throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); diff --git a/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php b/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php index b528b3b0a7fe3..11ae995630912 100644 --- a/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php +++ b/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php @@ -27,14 +27,7 @@ class CurrencyBundle extends CurrencyDataProvider implements CurrencyBundleInter { private $localeProvider; - /** - * Creates a new currency bundle. - * - * @param string $path - * @param BundleEntryReaderInterface $reader - * @param LocaleDataProvider $localeProvider - */ - public function __construct($path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider) + public function __construct(string $path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider) { parent::__construct($path, $reader); diff --git a/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php b/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php index ee319544e5414..1ef5862dac9f7 100644 --- a/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php +++ b/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php @@ -29,15 +29,7 @@ class LanguageBundle extends LanguageDataProvider implements LanguageBundleInter private $localeProvider; private $scriptProvider; - /** - * Creates a new language bundle. - * - * @param string $path - * @param BundleEntryReaderInterface $reader - * @param LocaleDataProvider $localeProvider - * @param ScriptDataProvider $scriptProvider - */ - public function __construct($path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider, ScriptDataProvider $scriptProvider) + public function __construct(string $path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider, ScriptDataProvider $scriptProvider) { parent::__construct($path, $reader); diff --git a/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php b/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php index d709ba55626f6..436d211cad354 100644 --- a/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php +++ b/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php @@ -27,14 +27,7 @@ class RegionBundle extends RegionDataProvider implements RegionBundleInterface { private $localeProvider; - /** - * Creates a new region bundle. - * - * @param string $path - * @param BundleEntryReaderInterface $reader - * @param LocaleDataProvider $localeProvider - */ - public function __construct($path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider) + public function __construct(string $path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider) { parent::__construct($path, $reader); diff --git a/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/IntlBundleReaderTest.php b/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/IntlBundleReaderTest.php index 3e992db42a1f8..3a779f6fc6a7f 100644 --- a/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/IntlBundleReaderTest.php +++ b/src/Symfony/Component/Intl/Tests/Data/Bundle/Reader/IntlBundleReaderTest.php @@ -51,10 +51,6 @@ public function testReadFollowsAlias() public function testReadDoesNotFollowFallback() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('ResourceBundle does not support disabling fallback properly on HHVM.'); - } - // "ro_MD" -> "ro" $data = $this->reader->read(__DIR__.'/Fixtures/res', 'ro_MD'); @@ -67,10 +63,6 @@ public function testReadDoesNotFollowFallback() public function testReadDoesNotFollowFallbackAlias() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('ResourceBundle does not support disabling fallback properly on HHVM.'); - } - // "mo" = "ro_MD" -> "ro" $data = $this->reader->read(__DIR__.'/Fixtures/res', 'mo'); diff --git a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php index 00c9ce0d1380f..7a4f01b66ae5d 100644 --- a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php @@ -247,9 +247,6 @@ public function formatProvider() return $formatData; } - /** - * @requires PHP 5.5.10 - */ public function testFormatUtcAndGmtAreSplit() { $pattern = "yyyy.MM.dd 'at' HH:mm:ss zzz"; @@ -322,7 +319,6 @@ public function formatWithTimezoneProvider() /** * @dataProvider formatTimezoneProvider - * @requires PHP 5.5.10 */ public function testFormatTimezone($pattern, $timezone, $expected) { @@ -334,7 +330,7 @@ public function testFormatTimezone($pattern, $timezone, $expected) public function formatTimezoneProvider() { - $cases = array( + return array( array('z', 'GMT', 'GMT'), array('zz', 'GMT', 'GMT'), array('zzz', 'GMT', 'GMT'), @@ -373,20 +369,13 @@ public function formatTimezoneProvider() array('zzzzz', 'Etc/Zulu', 'Coordinated Universal Time'), array('zzzzz', 'Etc/UCT', 'Coordinated Universal Time'), array('zzzzz', 'Etc/Greenwich', 'Greenwich Mean Time'), - ); - - if (!defined('HHVM_VERSION')) { - // these timezones are not considered valid in HHVM - $cases = array_merge($cases, array( - array('z', 'GMT+03:00', 'GMT+3'), - array('zz', 'GMT+03:00', 'GMT+3'), - array('zzz', 'GMT+03:00', 'GMT+3'), - array('zzzz', 'GMT+03:00', 'GMT+03:00'), - array('zzzzz', 'GMT+03:00', 'GMT+03:00'), - )); - } - return $cases; + array('z', 'GMT+03:00', 'GMT+3'), + array('zz', 'GMT+03:00', 'GMT+3'), + array('zzz', 'GMT+03:00', 'GMT+3'), + array('zzzz', 'GMT+03:00', 'GMT+03:00'), + array('zzzzz', 'GMT+03:00', 'GMT+03:00'), + ); } public function testFormatWithGmtTimezone() @@ -427,9 +416,6 @@ public function testFormatWithConstructorTimezone() ); } - /** - * @requires PHP 5.5.10 - */ public function testFormatWithDateTimeZoneGmt() { $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('GMT'), IntlDateFormatter::GREGORIAN, 'zzz'); @@ -439,10 +425,6 @@ public function testFormatWithDateTimeZoneGmt() public function testFormatWithDateTimeZoneGmtOffset() { - if (defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) { - $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); - } - $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('GMT+03:00'), IntlDateFormatter::GREGORIAN, 'zzzz'); $this->assertEquals('GMT+03:00', $formatter->format(0)); diff --git a/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php b/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php index 5ee414a1c2506..b0388620ba4b7 100644 --- a/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php +++ b/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php @@ -21,6 +21,17 @@ public function testAcceptFromHttp() $this->call('acceptFromHttp', 'pt-br,en-us;q=0.7,en;q=0.5'); } + public function testCanonicalize() + { + $this->assertSame('en', $this->call('canonicalize', '')); + $this->assertSame('en', $this->call('canonicalize', '.utf8')); + $this->assertSame('fr_FR', $this->call('canonicalize', 'FR-fr')); + $this->assertSame('fr_FR', $this->call('canonicalize', 'FR-fr.utf8')); + $this->assertSame('uz_Latn', $this->call('canonicalize', 'UZ-lATN')); + $this->assertSame('uz_Cyrl_UZ', $this->call('canonicalize', 'UZ-cYRL-uz')); + $this->assertSame('123', $this->call('canonicalize', 123)); + } + /** * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException */ diff --git a/src/Symfony/Component/Intl/Util/IntlTestHelper.php b/src/Symfony/Component/Intl/Util/IntlTestHelper.php index 12c3ff05913c5..ca9a0b59be437 100644 --- a/src/Symfony/Component/Intl/Util/IntlTestHelper.php +++ b/src/Symfony/Component/Intl/Util/IntlTestHelper.php @@ -42,7 +42,7 @@ public static function requireIntl(TestCase $testCase, $minimumIcuVersion = null // * the intl extension is loaded with version Intl::getIcuStubVersion() // * the intl extension is not loaded - if (($minimumIcuVersion || defined('HHVM_VERSION_ID')) && IcuVersion::compare(Intl::getIcuVersion(), $minimumIcuVersion, '<', 1)) { + if ($minimumIcuVersion && IcuVersion::compare(Intl::getIcuVersion(), $minimumIcuVersion, '<', 1)) { $testCase->markTestSkipped('ICU version '.$minimumIcuVersion.' is required.'); } diff --git a/src/Symfony/Component/Intl/Util/SvnRepository.php b/src/Symfony/Component/Intl/Util/SvnRepository.php index 9716a5425ead7..00049791d9f84 100644 --- a/src/Symfony/Component/Intl/Util/SvnRepository.php +++ b/src/Symfony/Component/Intl/Util/SvnRepository.php @@ -75,7 +75,7 @@ public static function download($url, $targetDir) * * @param string $path The path to the repository */ - public function __construct($path) + public function __construct(string $path) { $this->path = $path; } diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index 582049066431d..874be36cb3bb6 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -24,11 +24,11 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "symfony/polyfill-intl-icu": "~1.0" }, "require-dev": { - "symfony/filesystem": "~2.8|~3.0|~4.0" + "symfony/filesystem": "~3.4|~4.0" }, "suggest": { "ext-intl": "to use the component with locales other than \"en\"" @@ -43,7 +43,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Ldap/Adapter/AbstractQuery.php b/src/Symfony/Component/Ldap/Adapter/AbstractQuery.php index 5cacad79b249e..660e52dcb75fa 100644 --- a/src/Symfony/Component/Ldap/Adapter/AbstractQuery.php +++ b/src/Symfony/Component/Ldap/Adapter/AbstractQuery.php @@ -24,7 +24,7 @@ abstract class AbstractQuery implements QueryInterface protected $query; protected $options; - public function __construct(ConnectionInterface $connection, $dn, $query, array $options = array()) + public function __construct(ConnectionInterface $connection, string $dn, string $query, array $options = array()) { $resolver = new OptionsResolver(); $resolver->setDefaults(array( diff --git a/src/Symfony/Component/Ldap/Adapter/EntryManagerInterface.php b/src/Symfony/Component/Ldap/Adapter/EntryManagerInterface.php index 9538abfae2b25..82c023d5ec06d 100644 --- a/src/Symfony/Component/Ldap/Adapter/EntryManagerInterface.php +++ b/src/Symfony/Component/Ldap/Adapter/EntryManagerInterface.php @@ -20,6 +20,7 @@ * * @author Charles Sarrazin * @author Bob van de Vijver + * @author Kevin Schuurmans */ interface EntryManagerInterface { @@ -43,6 +44,15 @@ public function add(Entry $entry); */ public function update(Entry $entry); + /** + * Renames an entry on the Ldap server. + * + * @param Entry $entry + * @param string $newRdn + * @param bool $removeOldRdn + */ + public function rename(Entry $entry, $newRdn, $removeOldRdn = true); + /** * Removes an entry from the Ldap server. * diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php index 871c4b049562d..b3959c669164e 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Adapter\EntryManagerInterface; -use Symfony\Component\Ldap\Adapter\RenameEntryInterface; use Symfony\Component\Ldap\Entry; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; @@ -21,7 +20,7 @@ * @author Charles Sarrazin * @author Bob van de Vijver */ -class EntryManager implements EntryManagerInterface, RenameEntryInterface +class EntryManager implements EntryManagerInterface { private $connection; diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php index 09cb42c7d38ee..05e5878bb2a48 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php @@ -27,7 +27,7 @@ class Query extends AbstractQuery /** @var resource */ private $search; - public function __construct(Connection $connection, $dn, $query, array $options = array()) + public function __construct(Connection $connection, string $dn, string $query, array $options = array()) { parent::__construct($connection, $dn, $query, $options); } diff --git a/src/Symfony/Component/Ldap/Adapter/RenameEntryInterface.php b/src/Symfony/Component/Ldap/Adapter/RenameEntryInterface.php deleted file mode 100644 index c15cb16e77e80..0000000000000 --- a/src/Symfony/Component/Ldap/Adapter/RenameEntryInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * @deprecated since version 3.3, will be merged with {@link EntryManagerInterface} in 4.0. - */ -interface RenameEntryInterface -{ - /** - * Renames an entry on the Ldap server. - * - * @param Entry $entry - * @param string $newRdn - * @param bool $removeOldRdn - */ - public function rename(Entry $entry, $newRdn, $removeOldRdn = true); -} diff --git a/src/Symfony/Component/Ldap/CHANGELOG.md b/src/Symfony/Component/Ldap/CHANGELOG.md index 5d90f278f567a..fd68ace6b2f27 100644 --- a/src/Symfony/Component/Ldap/CHANGELOG.md +++ b/src/Symfony/Component/Ldap/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.0.0 +----- + + * removed the `LdapClient` class and the `LdapClientInterface` + * removed the `RenameEntryInterface` interface and merged with `EntryManagerInterface` + 3.3.0 ----- diff --git a/src/Symfony/Component/Ldap/Entry.php b/src/Symfony/Component/Ldap/Entry.php index 42745c2b8928c..aac189624af9a 100644 --- a/src/Symfony/Component/Ldap/Entry.php +++ b/src/Symfony/Component/Ldap/Entry.php @@ -19,7 +19,7 @@ class Entry private $dn; private $attributes; - public function __construct($dn, array $attributes = array()) + public function __construct(string $dn, array $attributes = array()) { $this->dn = $dn; $this->attributes = $attributes; diff --git a/src/Symfony/Component/Ldap/Ldap.php b/src/Symfony/Component/Ldap/Ldap.php index 514f51d22c21a..26fd4f946ccc9 100644 --- a/src/Symfony/Component/Ldap/Ldap.php +++ b/src/Symfony/Component/Ldap/Ldap.php @@ -70,7 +70,7 @@ public function escape($subject, $ignore = '', $flags = 0) * * @return static */ - public static function create($adapter, array $config = array()) + public static function create($adapter, array $config = array()): Ldap { if (!isset(self::$adapterMap[$adapter])) { throw new DriverNotFoundException(sprintf( diff --git a/src/Symfony/Component/Ldap/LdapClient.php b/src/Symfony/Component/Ldap/LdapClient.php deleted file mode 100644 index 4776879e8b122..0000000000000 --- a/src/Symfony/Component/Ldap/LdapClient.php +++ /dev/null @@ -1,127 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Ldap; - -@trigger_error('The '.__NAMESPACE__.'\LdapClient class is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Ldap class directly instead.', E_USER_DEPRECATED); - -/** - * @author Grégoire Pineau - * @author Francis Besset - * @author Charles Sarrazin - * - * @deprecated since version 3.1, to be removed in 4.0. Use the Ldap class instead. - */ -final class LdapClient implements LdapClientInterface -{ - private $ldap; - - public function __construct($host = null, $port = 389, $version = 3, $useSsl = false, $useStartTls = false, $optReferrals = false, LdapInterface $ldap = null) - { - $config = $this->normalizeConfig($host, $port, $version, $useSsl, $useStartTls, $optReferrals); - - $this->ldap = null !== $ldap ? $ldap : Ldap::create('ext_ldap', $config); - } - - /** - * {@inheritdoc} - */ - public function bind($dn = null, $password = null) - { - $this->ldap->bind($dn, $password); - } - - /** - * {@inheritdoc} - */ - public function query($dn, $query, array $options = array()) - { - return $this->ldap->query($dn, $query, $options); - } - - /** - * {@inheritdoc} - */ - public function getEntryManager() - { - return $this->ldap->getEntryManager(); - } - - /** - * {@inheritdoc} - */ - public function find($dn, $query, $filter = '*') - { - @trigger_error('The "find" method is deprecated since Symfony 3.1 and will be removed in 4.0. Use the "query" method instead.', E_USER_DEPRECATED); - - $query = $this->ldap->query($dn, $query, array('filter' => $filter)); - $entries = $query->execute(); - $result = array( - 'count' => 0, - ); - - foreach ($entries as $entry) { - $resultEntry = array(); - - foreach ($entry->getAttributes() as $attribute => $values) { - $resultAttribute = array( - 'count' => count($values), - ); - - foreach ($values as $val) { - $resultAttribute[] = $val; - } - $attributeName = strtolower($attribute); - - $resultAttribute['count'] = count($values); - $resultEntry[$attributeName] = $resultAttribute; - $resultEntry[] = $attributeName; - } - - $resultEntry['count'] = count($resultEntry) / 2; - $resultEntry['dn'] = $entry->getDn(); - $result[] = $resultEntry; - } - - $result['count'] = count($result) - 1; - - return $result; - } - - /** - * {@inheritdoc} - */ - public function escape($subject, $ignore = '', $flags = 0) - { - return $this->ldap->escape($subject, $ignore, $flags); - } - - private function normalizeConfig($host, $port, $version, $useSsl, $useStartTls, $optReferrals) - { - if ((bool) $useSsl) { - $encryption = 'ssl'; - } elseif ((bool) $useStartTls) { - $encryption = 'tls'; - } else { - $encryption = 'none'; - } - - return array( - 'host' => $host, - 'port' => $port, - 'encryption' => $encryption, - 'options' => array( - 'protocol_version' => $version, - 'referrals' => (bool) $optReferrals, - ), - ); - } -} diff --git a/src/Symfony/Component/Ldap/LdapClientInterface.php b/src/Symfony/Component/Ldap/LdapClientInterface.php deleted file mode 100644 index 0872ee082e813..0000000000000 --- a/src/Symfony/Component/Ldap/LdapClientInterface.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Ldap; - -/** - * Ldap interface. - * - * This interface is used for the BC layer with branch 2.8 and 3.0. - * - * @author Grégoire Pineau - * @author Charles Sarrazin - * - * @deprecated since version 3.1, to be removed in 4.0. Use the LdapInterface instead. - */ -interface LdapClientInterface extends LdapInterface -{ - /** - * Find a username into ldap connection. - * - * @param string $dn - * @param string $query - * @param mixed $filter - * - * @return array|null - */ - public function find($dn, $query, $filter = '*'); -} diff --git a/src/Symfony/Component/Ldap/Tests/LdapClientTest.php b/src/Symfony/Component/Ldap/Tests/LdapClientTest.php deleted file mode 100644 index 176c8f16f9320..0000000000000 --- a/src/Symfony/Component/Ldap/Tests/LdapClientTest.php +++ /dev/null @@ -1,229 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Ldap\Tests; - -use Symfony\Component\Ldap\Adapter\CollectionInterface; -use Symfony\Component\Ldap\Adapter\QueryInterface; -use Symfony\Component\Ldap\Entry; -use Symfony\Component\Ldap\LdapClient; -use Symfony\Component\Ldap\LdapInterface; - -/** - * @group legacy - */ -class LdapClientTest extends LdapTestCase -{ - /** @var LdapClient */ - private $client; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $ldap; - - protected function setUp() - { - $this->ldap = $this->getMockBuilder(LdapInterface::class)->getMock(); - - $this->client = new LdapClient(null, 389, 3, false, false, false, $this->ldap); - } - - public function testLdapBind() - { - $this->ldap - ->expects($this->once()) - ->method('bind') - ->with('foo', 'bar') - ; - $this->client->bind('foo', 'bar'); - } - - public function testLdapEscape() - { - $this->ldap - ->expects($this->once()) - ->method('escape') - ->with('foo', 'bar', 'baz') - ; - $this->client->escape('foo', 'bar', 'baz'); - } - - public function testLdapQuery() - { - $this->ldap - ->expects($this->once()) - ->method('query') - ->with('foo', 'bar', array('baz')) - ; - $this->client->query('foo', 'bar', array('baz')); - } - - public function testLdapFind() - { - $collection = $this->getMockBuilder(CollectionInterface::class)->getMock(); - $collection - ->expects($this->once()) - ->method('getIterator') - ->will($this->returnValue(new \ArrayIterator(array( - new Entry('cn=qux,dc=foo,dc=com', array( - 'cn' => array('qux'), - 'dc' => array('com', 'foo'), - 'givenName' => array('Qux'), - )), - new Entry('cn=baz,dc=foo,dc=com', array( - 'cn' => array('baz'), - 'dc' => array('com', 'foo'), - 'givenName' => array('Baz'), - )), - )))) - ; - $query = $this->getMockBuilder(QueryInterface::class)->getMock(); - $query - ->expects($this->once()) - ->method('execute') - ->will($this->returnValue($collection)) - ; - $this->ldap - ->expects($this->once()) - ->method('query') - ->with('dc=foo,dc=com', 'bar', array('filter' => 'baz')) - ->willReturn($query) - ; - - $expected = array( - 'count' => 2, - 0 => array( - 'count' => 3, - 0 => 'cn', - 'cn' => array( - 'count' => 1, - 0 => 'qux', - ), - 1 => 'dc', - 'dc' => array( - 'count' => 2, - 0 => 'com', - 1 => 'foo', - ), - 2 => 'givenname', - 'givenname' => array( - 'count' => 1, - 0 => 'Qux', - ), - 'dn' => 'cn=qux,dc=foo,dc=com', - ), - 1 => array( - 'count' => 3, - 0 => 'cn', - 'cn' => array( - 'count' => 1, - 0 => 'baz', - ), - 1 => 'dc', - 'dc' => array( - 'count' => 2, - 0 => 'com', - 1 => 'foo', - ), - 2 => 'givenname', - 'givenname' => array( - 'count' => 1, - 0 => 'Baz', - ), - 'dn' => 'cn=baz,dc=foo,dc=com', - ), - ); - $this->assertEquals($expected, $this->client->find('dc=foo,dc=com', 'bar', 'baz')); - } - - /** - * @dataProvider provideConfig - */ - public function testLdapClientConfig($args, $expected) - { - $reflObj = new \ReflectionObject($this->client); - $reflMethod = $reflObj->getMethod('normalizeConfig'); - $reflMethod->setAccessible(true); - array_unshift($args, $this->client); - $this->assertEquals($expected, call_user_func_array(array($reflMethod, 'invoke'), $args)); - } - - /** - * @group functional - * @requires extension ldap - */ - public function testLdapClientFunctional() - { - $config = $this->getLdapConfig(); - $ldap = new LdapClient($config['host'], $config['port']); - $ldap->bind('cn=admin,dc=symfony,dc=com', 'symfony'); - $result = $ldap->find('dc=symfony,dc=com', '(&(objectclass=person)(ou=Maintainers))'); - - $con = @ldap_connect($config['host'], $config['port']); - @ldap_bind($con, 'cn=admin,dc=symfony,dc=com', 'symfony'); - $search = @ldap_search($con, 'dc=symfony,dc=com', '(&(objectclass=person)(ou=Maintainers))', array('*')); - $expected = @ldap_get_entries($con, $search); - - $this->assertSame($expected, $result); - } - - public function provideConfig() - { - return array( - array( - array('localhost', 389, 3, true, false, false), - array( - 'host' => 'localhost', - 'port' => 389, - 'encryption' => 'ssl', - 'options' => array( - 'protocol_version' => 3, - 'referrals' => false, - ), - ), - ), - array( - array('localhost', 389, 3, false, true, false), - array( - 'host' => 'localhost', - 'port' => 389, - 'encryption' => 'tls', - 'options' => array( - 'protocol_version' => 3, - 'referrals' => false, - ), - ), - ), - array( - array('localhost', 389, 3, false, false, false), - array( - 'host' => 'localhost', - 'port' => 389, - 'encryption' => 'none', - 'options' => array( - 'protocol_version' => 3, - 'referrals' => false, - ), - ), - ), - array( - array('localhost', 389, 3, false, false, false), - array( - 'host' => 'localhost', - 'port' => 389, - 'encryption' => 'none', - 'options' => array( - 'protocol_version' => 3, - 'referrals' => false, - ), - ), - ), - ); - } -} diff --git a/src/Symfony/Component/Ldap/composer.json b/src/Symfony/Component/Ldap/composer.json index 6e4e266923db5..10bb67cf3fa89 100644 --- a/src/Symfony/Component/Ldap/composer.json +++ b/src/Symfony/Component/Ldap/composer.json @@ -16,9 +16,8 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-php56": "~1.0", - "symfony/options-resolver": "~2.8|~3.0|~4.0", + "php": "^7.1.3", + "symfony/options-resolver": "~3.4|~4.0", "ext-ldap": "*" }, "autoload": { @@ -30,7 +29,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Lock/Key.php b/src/Symfony/Component/Lock/Key.php index 8be4f3a255975..7fddafe9e2a1e 100644 --- a/src/Symfony/Component/Lock/Key.php +++ b/src/Symfony/Component/Lock/Key.php @@ -22,12 +22,9 @@ final class Key private $expiringTime; private $state = array(); - /** - * @param string $resource - */ - public function __construct($resource) + public function __construct(string $resource) { - $this->resource = (string) $resource; + $this->resource = $resource; } public function __toString() @@ -35,39 +32,22 @@ public function __toString() return $this->resource; } - /** - * @param string $stateKey - * - * @return bool - */ - public function hasState($stateKey) + public function hasState(string $stateKey): bool { return isset($this->state[$stateKey]); } - /** - * @param string $stateKey - * @param mixed $state - */ - public function setState($stateKey, $state) + public function setState(string $stateKey, $state): void { $this->state[$stateKey] = $state; } - /** - * @param string $stateKey - */ - public function removeState($stateKey) + public function removeState(string $stateKey): void { unset($this->state[$stateKey]); } - /** - * @param $stateKey - * - * @return mixed - */ - public function getState($stateKey) + public function getState(string $stateKey) { return $this->state[$stateKey]; } diff --git a/src/Symfony/Component/Lock/Lock.php b/src/Symfony/Component/Lock/Lock.php index 77fd032bf1075..7936a5f7b6d20 100644 --- a/src/Symfony/Component/Lock/Lock.php +++ b/src/Symfony/Component/Lock/Lock.php @@ -41,12 +41,12 @@ final class Lock implements LockInterface, LoggerAwareInterface * @param float|null $ttl Maximum expected lock duration in seconds * @param bool $autoRelease Whether to automatically release the lock or not when the lock instance is destroyed */ - public function __construct(Key $key, StoreInterface $store, $ttl = null, $autoRelease = true) + public function __construct(Key $key, StoreInterface $store, float $ttl = null, bool $autoRelease = true) { $this->store = $store; $this->key = $key; $this->ttl = $ttl; - $this->autoRelease = (bool) $autoRelease; + $this->autoRelease = $autoRelease; $this->logger = new NullLogger(); } @@ -154,7 +154,7 @@ public function release() } /** - * @return bool + * {@inheritdoc} */ public function isExpired() { @@ -162,9 +162,7 @@ public function isExpired() } /** - * Returns the remaining lifetime. - * - * @return float|null Remaining lifetime in seconds. Null when the lock won't expire. + * {@inheritdoc} */ public function getRemainingLifetime() { diff --git a/src/Symfony/Component/Lock/LockInterface.php b/src/Symfony/Component/Lock/LockInterface.php index 437a4cc2770c8..75a76426dc5a1 100644 --- a/src/Symfony/Component/Lock/LockInterface.php +++ b/src/Symfony/Component/Lock/LockInterface.php @@ -24,7 +24,7 @@ interface LockInterface { /** * Acquires the lock. If the lock is acquired by someone else, the parameter `blocking` determines whether or not - * the the call should block until the release of the lock. + * the call should block until the release of the lock. * * @param bool $blocking Whether or not the Lock should wait for the release of someone else * @@ -56,4 +56,16 @@ public function isAcquired(); * @throws LockReleasingException If the lock can not be released */ public function release(); + + /** + * @return bool + */ + public function isExpired(); + + /** + * Returns the remaining lifetime. + * + * @return float|null Remaining lifetime in seconds. Null when the lock won't expire. + */ + public function getRemainingLifetime(); } diff --git a/src/Symfony/Component/Lock/Store/FlockStore.php b/src/Symfony/Component/Lock/Store/FlockStore.php index 5babc7f610bce..bc03b859d00f3 100644 --- a/src/Symfony/Component/Lock/Store/FlockStore.php +++ b/src/Symfony/Component/Lock/Store/FlockStore.php @@ -36,7 +36,7 @@ class FlockStore implements StoreInterface * * @throws LockStorageException If the lock directory could not be created or is not writable */ - public function __construct($lockPath = null) + public function __construct(string $lockPath = null) { if (null === $lockPath) { $lockPath = sys_get_temp_dir(); diff --git a/src/Symfony/Component/Lock/Store/MemcachedStore.php b/src/Symfony/Component/Lock/Store/MemcachedStore.php index 8e9db10cd036f..f1c5c11208f3e 100644 --- a/src/Symfony/Component/Lock/Store/MemcachedStore.php +++ b/src/Symfony/Component/Lock/Store/MemcachedStore.php @@ -38,7 +38,7 @@ public static function isSupported() * @param \Memcached $memcached * @param int $initialTtl the expiration delay of locks in seconds */ - public function __construct(\Memcached $memcached, $initialTtl = 300) + public function __construct(\Memcached $memcached, int $initialTtl = 300) { if (!static::isSupported()) { throw new InvalidArgumentException('Memcached extension is required'); @@ -149,12 +149,8 @@ public function exists(Key $key) /** * Retrieve an unique token for the given key. - * - * @param Key $key - * - * @return string */ - private function getToken(Key $key) + private function getToken(Key $key): string { if (!$key->hasState(__CLASS__)) { $token = base64_encode(random_bytes(32)); diff --git a/src/Symfony/Component/Lock/Store/RedisStore.php b/src/Symfony/Component/Lock/Store/RedisStore.php index 4d057337b60db..291e0f5254ffa 100644 --- a/src/Symfony/Component/Lock/Store/RedisStore.php +++ b/src/Symfony/Component/Lock/Store/RedisStore.php @@ -32,7 +32,7 @@ class RedisStore implements StoreInterface * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient * @param float $initialTtl the expiration delay of locks in seconds */ - public function __construct($redisClient, $initialTtl = 300.0) + public function __construct($redisClient, float $initialTtl = 300.0) { if (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \RedisCluster && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) { throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, is_object($redisClient) ? get_class($redisClient) : gettype($redisClient))); @@ -124,13 +124,9 @@ public function exists(Key $key) /** * Evaluates a script in the corresponding redis client. * - * @param string $script - * @param string $resource - * @param array $args - * * @return mixed */ - private function evaluate($script, $resource, array $args) + private function evaluate(string $script, string $resource, array $args) { if ($this->redis instanceof \Redis || $this->redis instanceof \RedisCluster || $this->redis instanceof RedisProxy) { return $this->redis->eval($script, array_merge(array($resource), $args), 1); @@ -149,12 +145,8 @@ private function evaluate($script, $resource, array $args) /** * Retrieves an unique token for the given key. - * - * @param Key $key - * - * @return string */ - private function getToken(Key $key) + private function getToken(Key $key): string { if (!$key->hasState(__CLASS__)) { $token = base64_encode(random_bytes(32)); diff --git a/src/Symfony/Component/Lock/Store/RetryTillSaveStore.php b/src/Symfony/Component/Lock/Store/RetryTillSaveStore.php index dfc3b266687d9..bf69a75f97fbe 100644 --- a/src/Symfony/Component/Lock/Store/RetryTillSaveStore.php +++ b/src/Symfony/Component/Lock/Store/RetryTillSaveStore.php @@ -37,7 +37,7 @@ class RetryTillSaveStore implements StoreInterface, LoggerAwareInterface * @param int $retrySleep Duration in ms between 2 retry * @param int $retryCount Maximum amount of retry */ - public function __construct(StoreInterface $decorated, $retrySleep = 100, $retryCount = PHP_INT_MAX) + public function __construct(StoreInterface $decorated, int $retrySleep = 100, int $retryCount = PHP_INT_MAX) { $this->decorated = $decorated; $this->retrySleep = $retrySleep; diff --git a/src/Symfony/Component/Lock/Store/SemaphoreStore.php b/src/Symfony/Component/Lock/Store/SemaphoreStore.php index a6cc9ea8b9f1d..8913e39122405 100644 --- a/src/Symfony/Component/Lock/Store/SemaphoreStore.php +++ b/src/Symfony/Component/Lock/Store/SemaphoreStore.php @@ -13,7 +13,6 @@ use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\Exception\LockConflictedException; -use Symfony\Component\Lock\Exception\NotSupportedException; use Symfony\Component\Lock\Key; use Symfony\Component\Lock\StoreInterface; @@ -27,23 +26,13 @@ class SemaphoreStore implements StoreInterface /** * Returns whether or not the store is supported. * - * @param bool|null $blocking when not null, checked again the blocking mode - * * @return bool * * @internal */ - public static function isSupported($blocking = null) + public static function isSupported() { - if (!extension_loaded('sysvsem')) { - return false; - } - - if (false === $blocking && \PHP_VERSION_ID < 50601) { - return false; - } - - return true; + return extension_loaded('sysvsem'); } public function __construct() @@ -76,16 +65,7 @@ private function lock(Key $key, $blocking) } $resource = sem_get(crc32($key)); - - if (\PHP_VERSION_ID < 50601) { - if (!$blocking) { - throw new NotSupportedException(sprintf('The store "%s" does not supports non blocking locks.', get_class($this))); - } - - $acquired = sem_acquire($resource); - } else { - $acquired = sem_acquire($resource, !$blocking); - } + $acquired = sem_acquire($resource, !$blocking); if (!$acquired) { throw new LockConflictedException(); diff --git a/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php b/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php index 93e5a54793f31..2fe15cd921bf9 100644 --- a/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php +++ b/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php @@ -38,12 +38,6 @@ public function testBlockingLocks() // Amount a microsecond used to order async actions $clockDelay = 50000; - if (\PHP_VERSION_ID < 50600 || defined('HHVM_VERSION_ID')) { - $this->markTestSkipped('The PHP engine does not keep resource in child forks'); - - return; - } - /** @var StoreInterface $store */ $store = $this->getStore(); $key = new Key(uniqid(__METHOD__, true)); diff --git a/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php index bb37ec1fe1a1b..eeb95d5810e85 100644 --- a/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php @@ -27,10 +27,6 @@ class SemaphoreStoreTest extends AbstractStoreTest */ protected function getStore() { - if (\PHP_VERSION_ID < 50601) { - $this->markTestSkipped('Non blocking semaphore are supported by PHP version greater or equals than 5.6.1'); - } - return new SemaphoreStore(); } } diff --git a/src/Symfony/Component/Lock/composer.json b/src/Symfony/Component/Lock/composer.json index 9a6e768723bb9..1ca9a4da40220 100644 --- a/src/Symfony/Component/Lock/composer.json +++ b/src/Symfony/Component/Lock/composer.json @@ -16,8 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-php70": "~1.0", + "php": "^7.1.3", "psr/log": "~1.0" }, "require-dev": { @@ -32,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php index 60317243e9c4f..4d9699df4c9c5 100644 --- a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php +++ b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php @@ -41,61 +41,49 @@ public function __construct(OptionsResolver $optionsResolver) } /** - * @param string $option - * * @return mixed * * @throws NoConfigurationException on no configured value */ - public function getDefault($option) + public function getDefault(string $option) { return call_user_func($this->get, 'defaults', $option, sprintf('No default value was set for the "%s" option.', $option)); } /** - * @param string $option - * * @return \Closure[] * * @throws NoConfigurationException on no configured closures */ - public function getLazyClosures($option) + public function getLazyClosures(string $option): array { return call_user_func($this->get, 'lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option)); } /** - * @param string $option - * * @return string[] * * @throws NoConfigurationException on no configured types */ - public function getAllowedTypes($option) + public function getAllowedTypes(string $option): array { return call_user_func($this->get, 'allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option)); } /** - * @param string $option - * * @return mixed[] * * @throws NoConfigurationException on no configured values */ - public function getAllowedValues($option) + public function getAllowedValues(string $option): array { return call_user_func($this->get, 'allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option)); } /** - * @param string $option - * - * @return \Closure - * * @throws NoConfigurationException on no configured normalizer */ - public function getNormalizer($option) + public function getNormalizer(string $option): \Closure { return call_user_func($this->get, 'normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option)); } diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 95a492de94bb8..8edd6b5c89791 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -976,15 +976,12 @@ public function count() * non-technical people. * * @param mixed $value The value to return the type of - * @param string $type - * - * @return string The type of the value */ - private function formatTypeOf($value, $type) + private function formatTypeOf($value, ?string $type): string { $suffix = ''; - if ('[]' === substr($type, -2)) { + if (null !== $type && '[]' === substr($type, -2)) { $suffix = '[]'; $type = substr($type, 0, -2); while ('[]' === substr($type, -2)) { @@ -1017,10 +1014,8 @@ private function formatTypeOf($value, $type) * in double quotes ("). * * @param mixed $value The value to format as string - * - * @return string The string representation of the passed value */ - private function formatValue($value) + private function formatValue($value): string { if (is_object($value)) { return get_class($value); @@ -1059,13 +1054,9 @@ private function formatValue($value) * Each of the values is converted to a string using * {@link formatValue()}. The values are then concatenated with commas. * - * @param array $values A list of values - * - * @return string The string representation of the value list - * * @see formatValue() */ - private function formatValues(array $values) + private function formatValues(array $values): string { foreach ($values as $key => $value) { $values[$key] = $this->formatValue($value); diff --git a/src/Symfony/Component/OptionsResolver/composer.json b/src/Symfony/Component/OptionsResolver/composer.json index 895847ea5add7..e78b66f25c105 100644 --- a/src/Symfony/Component/OptionsResolver/composer.json +++ b/src/Symfony/Component/OptionsResolver/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" }, @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Process/CHANGELOG.md b/src/Symfony/Component/Process/CHANGELOG.md index c5cdb9944164b..354db592a1526 100644 --- a/src/Symfony/Component/Process/CHANGELOG.md +++ b/src/Symfony/Component/Process/CHANGELOG.md @@ -1,6 +1,30 @@ CHANGELOG ========= +4.1.0 +----- + + * added the `Process::isTtySupported()` method that allows to check for TTY support + * made `PhpExecutableFinder` look for the `PHP_BINARY` env var when searching the php binary + * added the `ProcessSignaledException` class to properly catch signaled process errors + +4.0.0 +----- + + * environment variables will always be inherited + * added a second `array $env = array()` argument to the `start()`, `run()`, + `mustRun()`, and `restart()` methods of the `Process` class + * added a second `array $env = array()` argument to the `start()` method of the + `PhpProcess` class + * the `ProcessUtils::escapeArgument()` method has been removed + * the `areEnvironmentVariablesInherited()`, `getOptions()`, and `setOptions()` + methods of the `Process` class have been removed + * support for passing `proc_open()` options has been removed + * removed the `ProcessBuilder` class, use the `Process` class instead + * removed the `getEnhanceWindowsCompatibility()` and `setEnhanceWindowsCompatibility()` methods of the `Process` class + * passing a not existing working directory to the constructor of the `Symfony\Component\Process\Process` class is not + supported anymore + 3.4.0 ----- diff --git a/src/Symfony/Component/Process/Exception/ProcessSignaledException.php b/src/Symfony/Component/Process/Exception/ProcessSignaledException.php new file mode 100644 index 0000000000000..d4d322756f39b --- /dev/null +++ b/src/Symfony/Component/Process/Exception/ProcessSignaledException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +use Symfony\Component\Process\Process; + +/** + * Exception that is thrown when a process has been signaled. + * + * @author Sullivan Senechal + */ +final class ProcessSignaledException extends RuntimeException +{ + private $process; + + public function __construct(Process $process) + { + $this->process = $process; + + parent::__construct(sprintf('The process has been signaled with signal "%s".', $process->getTermSignal())); + } + + public function getProcess(): Process + { + return $this->process; + } + + public function getSignal(): int + { + return $this->getProcess()->getTermSignal(); + } +} diff --git a/src/Symfony/Component/Process/Exception/ProcessTimedOutException.php b/src/Symfony/Component/Process/Exception/ProcessTimedOutException.php index fef4a8ae867b8..e1f6445a4b3cb 100644 --- a/src/Symfony/Component/Process/Exception/ProcessTimedOutException.php +++ b/src/Symfony/Component/Process/Exception/ProcessTimedOutException.php @@ -26,7 +26,7 @@ class ProcessTimedOutException extends RuntimeException private $process; private $timeoutType; - public function __construct(Process $process, $timeoutType) + public function __construct(Process $process, int $timeoutType) { $this->process = $process; $this->timeoutType = $timeoutType; diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php index 65e31d3e1f42c..ef79065ff7e59 100644 --- a/src/Symfony/Component/Process/PhpExecutableFinder.php +++ b/src/Symfony/Component/Process/PhpExecutableFinder.php @@ -35,14 +35,17 @@ public function __construct() */ public function find($includeArgs = true) { - $args = $this->findArguments(); - $args = $includeArgs && $args ? ' '.implode(' ', $args) : ''; + if ($php = getenv('PHP_BINARY')) { + if (!is_executable($php)) { + return false; + } - // HHVM support - if (defined('HHVM_VERSION')) { - return (getenv('PHP_BINARY') ?: PHP_BINARY).$args; + return $php; } + $args = $this->findArguments(); + $args = $includeArgs && $args ? ' '.implode(' ', $args) : ''; + // PHP_BINARY return the current sapi executable if (PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) && is_file(PHP_BINARY)) { return PHP_BINARY.$args; @@ -82,10 +85,7 @@ public function find($includeArgs = true) public function findArguments() { $arguments = array(); - - if (defined('HHVM_VERSION')) { - $arguments[] = '--php'; - } elseif ('phpdbg' === PHP_SAPI) { + if ('phpdbg' === PHP_SAPI) { $arguments[] = '-qrr'; } diff --git a/src/Symfony/Component/Process/PhpProcess.php b/src/Symfony/Component/Process/PhpProcess.php index d3fd384afc4a3..4c560ef9b2227 100644 --- a/src/Symfony/Component/Process/PhpProcess.php +++ b/src/Symfony/Component/Process/PhpProcess.php @@ -29,9 +29,8 @@ class PhpProcess extends Process * @param string|null $cwd The working directory or null to use the working dir of the current PHP process * @param array|null $env The environment variables or null to use the same environment as the current PHP process * @param int $timeout The timeout in seconds - * @param array $options An array of options for proc_open */ - public function __construct($script, $cwd = null, array $env = null, $timeout = 60, array $options = null) + public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60) { $executableFinder = new PhpExecutableFinder(); if (false === $php = $executableFinder->find(false)) { @@ -46,11 +45,8 @@ public function __construct($script, $cwd = null, array $env = null, $timeout = $php[] = $file; $script = null; } - if (null !== $options) { - @trigger_error(sprintf('The $options parameter of the %s constructor is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - } - parent::__construct($php, $cwd, $env, $script, $timeout, $options); + parent::__construct($php, $cwd, $env, $script, $timeout); } /** @@ -64,12 +60,11 @@ public function setPhpBinary($php) /** * {@inheritdoc} */ - public function start(callable $callback = null/*, array $env = array()*/) + public function start(callable $callback = null, array $env = array()) { if (null === $this->getCommandLine()) { throw new RuntimeException('Unable to find the PHP executable.'); } - $env = 1 < func_num_args() ? func_get_arg(1) : null; parent::start($callback, $env); } diff --git a/src/Symfony/Component/Process/Pipes/UnixPipes.php b/src/Symfony/Component/Process/Pipes/UnixPipes.php index 78ffee7b031c1..badaf34d42012 100644 --- a/src/Symfony/Component/Process/Pipes/UnixPipes.php +++ b/src/Symfony/Component/Process/Pipes/UnixPipes.php @@ -26,11 +26,11 @@ class UnixPipes extends AbstractPipes private $ptyMode; private $haveReadSupport; - public function __construct($ttyMode, $ptyMode, $input, $haveReadSupport) + public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveReadSupport) { - $this->ttyMode = (bool) $ttyMode; - $this->ptyMode = (bool) $ptyMode; - $this->haveReadSupport = (bool) $haveReadSupport; + $this->ttyMode = $ttyMode; + $this->ptyMode = $ptyMode; + $this->haveReadSupport = $haveReadSupport; parent::__construct($input); } diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php index d5fa2fdeef67c..845d2bf70ebf1 100644 --- a/src/Symfony/Component/Process/Pipes/WindowsPipes.php +++ b/src/Symfony/Component/Process/Pipes/WindowsPipes.php @@ -34,9 +34,9 @@ class WindowsPipes extends AbstractPipes ); private $haveReadSupport; - public function __construct($input, $haveReadSupport) + public function __construct($input, bool $haveReadSupport) { - $this->haveReadSupport = (bool) $haveReadSupport; + $this->haveReadSupport = $haveReadSupport; if ($this->haveReadSupport) { // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 830c623e0dc3b..3d9a3374f904c 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -14,6 +14,7 @@ use Symfony\Component\Process\Exception\InvalidArgumentException; use Symfony\Component\Process\Exception\LogicException; use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Exception\ProcessSignaledException; use Symfony\Component\Process\Exception\ProcessTimedOutException; use Symfony\Component\Process\Exception\RuntimeException; use Symfony\Component\Process\Pipes\PipesInterface; @@ -58,22 +59,18 @@ class Process implements \IteratorAggregate private $lastOutputTime; private $timeout; private $idleTimeout; - private $options = array('suppress_errors' => true); private $exitcode; private $fallbackStatus = array(); private $processInformation; private $outputDisabled = false; private $stdout; private $stderr; - private $enhanceWindowsCompatibility = true; - private $enhanceSigchildCompatibility; private $process; private $status = self::STATUS_READY; private $incrementalOutputOffset = 0; private $incrementalErrorOutputOffset = 0; private $tty; private $pty; - private $inheritEnv = false; private $useFileHandles = false; /** @var PipesInterface */ @@ -137,11 +134,10 @@ class Process implements \IteratorAggregate * @param array|null $env The environment variables or null to use the same environment as the current PHP process * @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input * @param int|float|null $timeout The timeout in seconds or null to disable - * @param array $options An array of options for proc_open * * @throws RuntimeException When proc_open is not installed */ - public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = null) + public function __construct($commandline, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) { if (!function_exists('proc_open')) { throw new RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.'); @@ -165,11 +161,6 @@ public function __construct($commandline, $cwd = null, array $env = null, $input $this->setTimeout($timeout); $this->useFileHandles = '\\' === DIRECTORY_SEPARATOR; $this->pty = false; - $this->enhanceSigchildCompatibility = '\\' !== DIRECTORY_SEPARATOR && $this->isSigchildEnabled(); - if (null !== $options) { - @trigger_error(sprintf('The $options parameter of the %s constructor is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - $this->options = array_replace($this->options, $options); - } } public function __destruct() @@ -204,9 +195,8 @@ public function __clone() * * @final since version 3.3 */ - public function run($callback = null/*, array $env = array()*/) + public function run(callable $callback = null, array $env = array()): int { - $env = 1 < func_num_args() ? func_get_arg(1) : null; $this->start($callback, $env); return $this->wait(); @@ -223,18 +213,12 @@ public function run($callback = null/*, array $env = array()*/) * * @return self * - * @throws RuntimeException if PHP was compiled with --enable-sigchild and the enhanced sigchild compatibility mode is not enabled * @throws ProcessFailedException if the process didn't terminate successfully * * @final since version 3.3 */ - public function mustRun(callable $callback = null/*, array $env = array()*/) + public function mustRun(callable $callback = null, array $env = array()) { - if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); - } - $env = 1 < func_num_args() ? func_get_arg(1) : null; - if (0 !== $this->run($callback, $env)) { throw new ProcessFailedException($this); } @@ -262,29 +246,17 @@ public function mustRun(callable $callback = null/*, array $env = array()*/) * @throws RuntimeException When process is already running * @throws LogicException In case a callback is provided and output has been disabled */ - public function start(callable $callback = null/*, array $env = array()*/) + public function start(callable $callback = null, array $env = array()) { if ($this->isRunning()) { throw new RuntimeException('Process is already running'); } - if (2 <= func_num_args()) { - $env = func_get_arg(1); - } else { - if (__CLASS__ !== static::class) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName() && (2 > $r->getNumberOfParameters() || 'env' !== $r->getParameters()[0]->name)) { - @trigger_error(sprintf('The %s::start() method expects a second "$env" argument since Symfony 3.3. It will be made mandatory in 4.0.', static::class), E_USER_DEPRECATED); - } - } - $env = null; - } $this->resetProcessData(); $this->starttime = $this->lastOutputTime = microtime(true); $this->callback = $this->buildCallback($callback); $this->hasCallback = null !== $callback; $descriptors = $this->getDescriptors(); - $inheritEnv = $this->inheritEnv; if (is_array($commandline = $this->commandline)) { $commandline = implode(' ', array_map(array($this, 'escapeArgument'), $commandline)); @@ -295,26 +267,17 @@ public function start(callable $callback = null/*, array $env = array()*/) } } - if (null === $env) { - $env = $this->env; - } else { - if ($this->env) { - $env += $this->env; - } - $inheritEnv = true; + if ($this->env) { + $env += $this->env; } + $env += $this->getDefaultEnv(); - if (null !== $env && $inheritEnv) { - $env += $this->getDefaultEnv(); - } elseif (null !== $env) { - @trigger_error('Not inheriting environment variables is deprecated since Symfony 3.3 and will always happen in 4.0. Set "Process::inheritEnvironmentVariables()" to true instead.', E_USER_DEPRECATED); - } else { - $env = $this->getDefaultEnv(); - } - if ('\\' === DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) { - $this->options['bypass_shell'] = true; + $options = array('suppress_errors' => true); + + if ('\\' === DIRECTORY_SEPARATOR) { + $options['bypass_shell'] = true; $commandline = $this->prepareWindowsCommandLine($commandline, $env); - } elseif (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + } elseif (!$this->useFileHandles && $this->isSigchildEnabled()) { // last exit code is output on the fourth pipe and caught to work around --enable-sigchild $descriptors[3] = array('pipe', 'w'); @@ -326,22 +289,19 @@ public function start(callable $callback = null/*, array $env = array()*/) // @see : https://bugs.php.net/69442 $ptsWorkaround = fopen(__FILE__, 'r'); } - if (defined('HHVM_VERSION')) { - $envPairs = $env; - } else { - $envPairs = array(); - foreach ($env as $k => $v) { - if (false !== $v) { - $envPairs[] = $k.'='.$v; - } + + $envPairs = array(); + foreach ($env as $k => $v) { + if (false !== $v) { + $envPairs[] = $k.'='.$v; } } if (!is_dir($this->cwd)) { - @trigger_error('The provided cwd does not exist. Command is currently ran against getcwd(). This behavior is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); + throw new RuntimeException('The provided cwd does not exist.'); } - $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options); + $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $options); if (!is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); @@ -378,12 +338,11 @@ public function start(callable $callback = null/*, array $env = array()*/) * * @final since version 3.3 */ - public function restart(callable $callback = null/*, array $env = array()*/) + public function restart(callable $callback = null, array $env = array()) { if ($this->isRunning()) { throw new RuntimeException('Process is already running'); } - $env = 1 < func_num_args() ? func_get_arg(1) : null; $process = clone $this; $process->start($callback, $env); @@ -431,7 +390,7 @@ public function wait(callable $callback = null) } if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) { - throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig'])); + throw new ProcessSignaledException($this); } return $this->exitcode; @@ -693,15 +652,9 @@ public function clearErrorOutput() * Returns the exit code returned by the process. * * @return null|int The exit status code, null if the Process is not terminated - * - * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled */ public function getExitCode() { - if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); - } - $this->updateStatus(false); return $this->exitcode; @@ -744,17 +697,12 @@ public function isSuccessful() * * @return bool * - * @throws RuntimeException In case --enable-sigchild is activated - * @throws LogicException In case the process is not terminated + * @throws LogicException In case the process is not terminated */ public function hasBeenSignaled() { $this->requireProcessIsTerminated(__FUNCTION__); - if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); - } - return $this->processInformation['signaled']; } @@ -772,7 +720,7 @@ public function getTermSignal() { $this->requireProcessIsTerminated(__FUNCTION__); - if ($this->isSigchildEnabled() && (!$this->enhanceSigchildCompatibility || -1 === $this->processInformation['termsig'])) { + if ($this->isSigchildEnabled() && -1 === $this->processInformation['termsig']) { throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); } @@ -904,10 +852,8 @@ public function stop($timeout = 10, $signal = null) * Adds a line to the STDOUT stream. * * @internal - * - * @param string $line The line to append */ - public function addOutput($line) + public function addOutput(string $line) { $this->lastOutputTime = microtime(true); @@ -920,10 +866,8 @@ public function addOutput($line) * Adds a line to the STDERR stream. * * @internal - * - * @param string $line The line to append */ - public function addErrorOutput($line) + public function addErrorOutput(string $line) { $this->lastOutputTime = microtime(true); @@ -1031,16 +975,9 @@ public function setTty($tty) if ('\\' === DIRECTORY_SEPARATOR && $tty) { throw new RuntimeException('TTY mode is not supported on Windows platform.'); } - if ($tty) { - static $isTtySupported; - if (null === $isTtySupported) { - $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', array(array('file', '/dev/tty', 'r'), array('file', '/dev/tty', 'w'), array('file', '/dev/tty', 'w')), $pipes); - } - - if (!$isTtySupported) { - throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.'); - } + if ($tty && !self::isTtySupported()) { + throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.'); } $this->tty = (bool) $tty; @@ -1181,108 +1118,6 @@ public function setInput($input) return $this; } - /** - * Gets the options for proc_open. - * - * @return array The current options - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function getOptions() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - return $this->options; - } - - /** - * Sets the options for proc_open. - * - * @param array $options The new options - * - * @return self The current Process instance - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function setOptions(array $options) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); - - $this->options = $options; - - return $this; - } - - /** - * Gets whether or not Windows compatibility is enabled. - * - * This is true by default. - * - * @return bool - * - * @deprecated since version 3.3, to be removed in 4.0. Enhanced Windows compatibility will always be enabled. - */ - public function getEnhanceWindowsCompatibility() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Enhanced Windows compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED); - - return $this->enhanceWindowsCompatibility; - } - - /** - * Sets whether or not Windows compatibility is enabled. - * - * @param bool $enhance - * - * @return self The current Process instance - * - * @deprecated since version 3.3, to be removed in 4.0. Enhanced Windows compatibility will always be enabled. - */ - public function setEnhanceWindowsCompatibility($enhance) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Enhanced Windows compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED); - - $this->enhanceWindowsCompatibility = (bool) $enhance; - - return $this; - } - - /** - * Returns whether sigchild compatibility mode is activated or not. - * - * @return bool - * - * @deprecated since version 3.3, to be removed in 4.0. Sigchild compatibility will always be enabled. - */ - public function getEnhanceSigchildCompatibility() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Sigchild compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED); - - return $this->enhanceSigchildCompatibility; - } - - /** - * Activates sigchild compatibility mode. - * - * Sigchild compatibility mode is required to get the exit code and - * determine the success of a process when PHP has been compiled with - * the --enable-sigchild option - * - * @param bool $enhance - * - * @return self The current Process instance - * - * @deprecated since version 3.3, to be removed in 4.0. - */ - public function setEnhanceSigchildCompatibility($enhance) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Sigchild compatibility will always be enabled.', __METHOD__), E_USER_DEPRECATED); - - $this->enhanceSigchildCompatibility = (bool) $enhance; - - return $this; - } - /** * Sets whether environment variables will be inherited or not. * @@ -1293,28 +1128,12 @@ public function setEnhanceSigchildCompatibility($enhance) public function inheritEnvironmentVariables($inheritEnv = true) { if (!$inheritEnv) { - @trigger_error('Not inheriting environment variables is deprecated since Symfony 3.3 and will always happen in 4.0. Set "Process::inheritEnvironmentVariables()" to true instead.', E_USER_DEPRECATED); + throw new InvalidArgumentException('Not inheriting environment variables is not supported.'); } - $this->inheritEnv = (bool) $inheritEnv; - return $this; } - /** - * Returns whether environment variables will be inherited or not. - * - * @return bool - * - * @deprecated since version 3.3, to be removed in 4.0. Environment variables will always be inherited. - */ - public function areEnvironmentVariablesInherited() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Environment variables will always be inherited.', __METHOD__), E_USER_DEPRECATED); - - return $this->inheritEnv; - } - /** * Performs a check between the timeout definition and the time the process started. * @@ -1342,6 +1161,20 @@ public function checkTimeout() } } + /** + * Returns whether TTY is supported on the current operating system. + */ + public static function isTtySupported(): bool + { + static $isTtySupported; + + if (null === $isTtySupported) { + $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', array(array('file', '/dev/tty', 'r'), array('file', '/dev/tty', 'w'), array('file', '/dev/tty', 'w')), $pipes); + } + + return $isTtySupported; + } + /** * Returns whether PTY is supported on the current operating system. * @@ -1364,10 +1197,8 @@ public static function isPtySupported() /** * Creates the descriptors needed by the proc_open. - * - * @return array */ - private function getDescriptors() + private function getDescriptors(): array { if ($this->input instanceof \Iterator) { $this->input->rewind(); @@ -1432,7 +1263,7 @@ protected function updateStatus($blocking) $this->readPipes($running && $blocking, '\\' !== DIRECTORY_SEPARATOR || !$running); - if ($this->fallbackStatus && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + if ($this->fallbackStatus && $this->isSigchildEnabled()) { $this->processInformation = $this->fallbackStatus + $this->processInformation; } @@ -1452,7 +1283,7 @@ protected function isSigchildEnabled() return self::$sigchild; } - if (!function_exists('phpinfo') || defined('HHVM_VERSION')) { + if (!function_exists('phpinfo')) { return self::$sigchild = false; } @@ -1470,7 +1301,7 @@ protected function isSigchildEnabled() * * @throws LogicException in case output has been disabled or process is not started */ - private function readPipesForOutput($caller, $blocking = false) + private function readPipesForOutput(string $caller, bool $blocking = false) { if ($this->outputDisabled) { throw new LogicException('Output has been disabled.'); @@ -1484,13 +1315,9 @@ private function readPipesForOutput($caller, $blocking = false) /** * Validates and returns the filtered timeout. * - * @param int|float|null $timeout - * - * @return float|null - * * @throws InvalidArgumentException if the given timeout is a negative number */ - private function validateTimeout($timeout) + private function validateTimeout(?float $timeout): ?float { $timeout = (float) $timeout; @@ -1509,7 +1336,7 @@ private function validateTimeout($timeout) * @param bool $blocking Whether to use blocking calls or not * @param bool $close Whether to close file handles or not */ - private function readPipes($blocking, $close) + private function readPipes(bool $blocking, bool $close) { $result = $this->processPipes->readAndWrite($blocking, $close); @@ -1528,7 +1355,7 @@ private function readPipes($blocking, $close) * * @return int The exitcode */ - private function close() + private function close(): int { $this->processPipes->close(); if (is_resource($this->process)) { @@ -1541,7 +1368,7 @@ private function close() if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) { // if process has been signaled, no exitcode but a valid termsig, apply Unix convention $this->exitcode = 128 + $this->processInformation['termsig']; - } elseif ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + } elseif ($this->isSigchildEnabled()) { $this->processInformation['signaled'] = true; $this->processInformation['termsig'] = -1; } @@ -1586,7 +1413,7 @@ private function resetProcessData() * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed * @throws RuntimeException In case of failure */ - private function doSignal($signal, $throwException) + private function doSignal(int $signal, bool $throwException): bool { if (null === $pid = $this->getPid()) { if ($throwException) { @@ -1606,7 +1433,7 @@ private function doSignal($signal, $throwException) return false; } } else { - if (!$this->enhanceSigchildCompatibility || !$this->isSigchildEnabled()) { + if (!$this->isSigchildEnabled()) { $ok = @proc_terminate($this->process, $signal); } elseif (function_exists('posix_kill')) { $ok = @posix_kill($pid, $signal); @@ -1630,7 +1457,7 @@ private function doSignal($signal, $throwException) return true; } - private function prepareWindowsCommandLine($cmd, array &$env) + private function prepareWindowsCommandLine(string $cmd, array &$env) { $uid = uniqid('', true); $varCount = 0; @@ -1679,11 +1506,9 @@ function ($m) use (&$env, &$varCache, &$varCount, $uid) { /** * Ensures the process is running or terminated, throws a LogicException if the process has a not started. * - * @param string $functionName The function name that was called - * * @throws LogicException if the process has not run */ - private function requireProcessIsStarted($functionName) + private function requireProcessIsStarted(string $functionName) { if (!$this->isStarted()) { throw new LogicException(sprintf('Process must be started before calling %s.', $functionName)); @@ -1693,11 +1518,9 @@ private function requireProcessIsStarted($functionName) /** * Ensures the process is terminated, throws a LogicException if the process has a status different than `terminated`. * - * @param string $functionName The function name that was called - * * @throws LogicException if the process is not yet terminated */ - private function requireProcessIsTerminated($functionName) + private function requireProcessIsTerminated(string $functionName) { if (!$this->isTerminated()) { throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName)); @@ -1706,12 +1529,8 @@ private function requireProcessIsTerminated($functionName) /** * Escapes a string to be used as a shell argument. - * - * @param string $argument The argument that will be escaped - * - * @return string The escaped argument */ - private function escapeArgument($argument) + private function escapeArgument(string $argument): string { if ('\\' !== DIRECTORY_SEPARATOR) { return "'".str_replace("'", "'\\''", $argument)."'"; diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php deleted file mode 100644 index a91147cb9f0d2..0000000000000 --- a/src/Symfony/Component/Process/ProcessBuilder.php +++ /dev/null @@ -1,280 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the Process class instead.', ProcessBuilder::class), E_USER_DEPRECATED); - -use Symfony\Component\Process\Exception\InvalidArgumentException; -use Symfony\Component\Process\Exception\LogicException; - -/** - * @author Kris Wallsmith - * - * @deprecated since version 3.4, to be removed in 4.0. Use the Process class instead. - */ -class ProcessBuilder -{ - private $arguments; - private $cwd; - private $env = array(); - private $input; - private $timeout = 60; - private $options; - private $inheritEnv = true; - private $prefix = array(); - private $outputDisabled = false; - - /** - * @param string[] $arguments An array of arguments - */ - public function __construct(array $arguments = array()) - { - $this->arguments = $arguments; - } - - /** - * Creates a process builder instance. - * - * @param string[] $arguments An array of arguments - * - * @return static - */ - public static function create(array $arguments = array()) - { - return new static($arguments); - } - - /** - * Adds an unescaped argument to the command string. - * - * @param string $argument A command argument - * - * @return $this - */ - public function add($argument) - { - $this->arguments[] = $argument; - - return $this; - } - - /** - * Adds a prefix to the command string. - * - * The prefix is preserved when resetting arguments. - * - * @param string|array $prefix A command prefix or an array of command prefixes - * - * @return $this - */ - public function setPrefix($prefix) - { - $this->prefix = is_array($prefix) ? $prefix : array($prefix); - - return $this; - } - - /** - * Sets the arguments of the process. - * - * Arguments must not be escaped. - * Previous arguments are removed. - * - * @param string[] $arguments - * - * @return $this - */ - public function setArguments(array $arguments) - { - $this->arguments = $arguments; - - return $this; - } - - /** - * Sets the working directory. - * - * @param null|string $cwd The working directory - * - * @return $this - */ - public function setWorkingDirectory($cwd) - { - $this->cwd = $cwd; - - return $this; - } - - /** - * Sets whether environment variables will be inherited or not. - * - * @param bool $inheritEnv - * - * @return $this - */ - public function inheritEnvironmentVariables($inheritEnv = true) - { - $this->inheritEnv = $inheritEnv; - - return $this; - } - - /** - * Sets an environment variable. - * - * Setting a variable overrides its previous value. Use `null` to unset a - * defined environment variable. - * - * @param string $name The variable name - * @param null|string $value The variable value - * - * @return $this - */ - public function setEnv($name, $value) - { - $this->env[$name] = $value; - - return $this; - } - - /** - * Adds a set of environment variables. - * - * Already existing environment variables with the same name will be - * overridden by the new values passed to this method. Pass `null` to unset - * a variable. - * - * @param array $variables The variables - * - * @return $this - */ - public function addEnvironmentVariables(array $variables) - { - $this->env = array_replace($this->env, $variables); - - return $this; - } - - /** - * Sets the input of the process. - * - * @param resource|string|int|float|bool|\Traversable|null $input The input content - * - * @return $this - * - * @throws InvalidArgumentException In case the argument is invalid - */ - public function setInput($input) - { - $this->input = ProcessUtils::validateInput(__METHOD__, $input); - - return $this; - } - - /** - * Sets the process timeout. - * - * To disable the timeout, set this value to null. - * - * @param float|null $timeout - * - * @return $this - * - * @throws InvalidArgumentException - */ - public function setTimeout($timeout) - { - if (null === $timeout) { - $this->timeout = null; - - return $this; - } - - $timeout = (float) $timeout; - - if ($timeout < 0) { - throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); - } - - $this->timeout = $timeout; - - return $this; - } - - /** - * Adds a proc_open option. - * - * @param string $name The option name - * @param string $value The option value - * - * @return $this - */ - public function setOption($name, $value) - { - $this->options[$name] = $value; - - return $this; - } - - /** - * Disables fetching output and error output from the underlying process. - * - * @return $this - */ - public function disableOutput() - { - $this->outputDisabled = true; - - return $this; - } - - /** - * Enables fetching output and error output from the underlying process. - * - * @return $this - */ - public function enableOutput() - { - $this->outputDisabled = false; - - return $this; - } - - /** - * Creates a Process instance and returns it. - * - * @return Process - * - * @throws LogicException In case no arguments have been provided - */ - public function getProcess() - { - if (0 === count($this->prefix) && 0 === count($this->arguments)) { - throw new LogicException('You must add() command arguments before calling getProcess().'); - } - - $arguments = array_merge($this->prefix, $this->arguments); - $process = new Process($arguments, $this->cwd, $this->env, $this->input, $this->timeout, $this->options); - // to preserve the BC with symfony <3.3, we convert the array structure - // to a string structure to avoid the prefixing with the exec command - $process->setCommandLine($process->getCommandLine()); - - if ($this->inheritEnv) { - $process->inheritEnvironmentVariables(); - } - if ($this->outputDisabled) { - $process->disableOutput(); - } - - return $process; - } -} diff --git a/src/Symfony/Component/Process/ProcessUtils.php b/src/Symfony/Component/Process/ProcessUtils.php index c30950c1da2f2..e0d9c08ab463f 100644 --- a/src/Symfony/Component/Process/ProcessUtils.php +++ b/src/Symfony/Component/Process/ProcessUtils.php @@ -29,55 +29,6 @@ private function __construct() { } - /** - * Escapes a string to be used as a shell argument. - * - * @param string $argument The argument that will be escaped - * - * @return string The escaped argument - * - * @deprecated since version 3.3, to be removed in 4.0. Use a command line array or give env vars to the `Process::start/run()` method instead. - */ - public static function escapeArgument($argument) - { - @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use a command line array or give env vars to the Process::start/run() method instead.', E_USER_DEPRECATED); - - //Fix for PHP bug #43784 escapeshellarg removes % from given string - //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows - //@see https://bugs.php.net/bug.php?id=43784 - //@see https://bugs.php.net/bug.php?id=49446 - if ('\\' === DIRECTORY_SEPARATOR) { - if ('' === $argument) { - return escapeshellarg($argument); - } - - $escapedArgument = ''; - $quote = false; - foreach (preg_split('/(")/', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { - if ('"' === $part) { - $escapedArgument .= '\\"'; - } elseif (self::isSurroundedBy($part, '%')) { - // Avoid environment variable expansion - $escapedArgument .= '^%"'.substr($part, 1, -1).'"^%'; - } else { - // escape trailing backslash - if ('\\' === substr($part, -1)) { - $part .= '\\'; - } - $quote = true; - $escapedArgument .= $part; - } - } - if ($quote) { - $escapedArgument = '"'.$escapedArgument.'"'; - } - - return $escapedArgument; - } - - return "'".str_replace("'", "'\\''", $argument)."'"; - } - /** * Validates and normalizes a Process input. * @@ -115,9 +66,4 @@ public static function validateInput($caller, $input) return $input; } - - private static function isSurroundedBy($arg, $char) - { - return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; - } } diff --git a/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php index bc692f6a75df6..68d6110e3a8d1 100644 --- a/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php @@ -91,7 +91,7 @@ public function testFindWithOpenBaseDir() $this->markTestSkipped('Cannot test when open_basedir is set'); } - $this->iniSet('open_basedir', dirname(PHP_BINARY).(!defined('HHVM_VERSION') || HHVM_VERSION_ID >= 30800 ? PATH_SEPARATOR.'/' : '')); + $this->iniSet('open_basedir', dirname(PHP_BINARY).PATH_SEPARATOR.'/'); $finder = new ExecutableFinder(); $result = $finder->find($this->getPhpBinaryName()); @@ -109,7 +109,7 @@ public function testFindProcessInOpenBasedir() } $this->setPath(''); - $this->iniSet('open_basedir', PHP_BINARY.(!defined('HHVM_VERSION') || HHVM_VERSION_ID >= 30800 ? PATH_SEPARATOR.'/' : '')); + $this->iniSet('open_basedir', PHP_BINARY.PATH_SEPARATOR.'/'); $finder = new ExecutableFinder(); $result = $finder->find($this->getPhpBinaryName(), false); diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php index b08ad5d3b734f..1055384cc59bb 100644 --- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -24,10 +24,6 @@ class PhpExecutableFinderTest extends TestCase */ public function testFind() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Should not be executed in HHVM context.'); - } - $f = new PhpExecutableFinder(); $current = PHP_BINARY; @@ -37,23 +33,6 @@ public function testFind() $this->assertEquals($current, $f->find(false), '::find() returns the executable PHP'); } - /** - * tests find() with the env var / constant PHP_BINARY with HHVM. - */ - public function testFindWithHHVM() - { - if (!defined('HHVM_VERSION')) { - $this->markTestSkipped('Should be executed in HHVM context.'); - } - - $f = new PhpExecutableFinder(); - - $current = getenv('PHP_BINARY') ?: PHP_BINARY; - - $this->assertEquals($current.' --php', $f->find(), '::find() returns the executable PHP'); - $this->assertEquals($current, $f->find(false), '::find() returns the executable PHP'); - } - /** * tests find() with the env var PHP_PATH. */ @@ -61,9 +40,7 @@ public function testFindArguments() { $f = new PhpExecutableFinder(); - if (defined('HHVM_VERSION')) { - $this->assertEquals($f->findArguments(), array('--php'), '::findArguments() returns HHVM arguments'); - } elseif ('phpdbg' === PHP_SAPI) { + if ('phpdbg' === PHP_SAPI) { $this->assertEquals($f->findArguments(), array('-qrr'), '::findArguments() returns phpdbg arguments'); } else { $this->assertEquals($f->findArguments(), array(), '::findArguments() returns no arguments'); diff --git a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php b/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php deleted file mode 100644 index 36c40bf03fcc3..0000000000000 --- a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php +++ /dev/null @@ -1,226 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Process\ProcessBuilder; - -/** - * @group legacy - */ -class ProcessBuilderTest extends TestCase -{ - public function testInheritEnvironmentVars() - { - $proc = ProcessBuilder::create() - ->add('foo') - ->getProcess(); - - $this->assertTrue($proc->areEnvironmentVariablesInherited()); - - $proc = ProcessBuilder::create() - ->add('foo') - ->inheritEnvironmentVariables(false) - ->getProcess(); - - $this->assertFalse($proc->areEnvironmentVariablesInherited()); - } - - public function testAddEnvironmentVariables() - { - $pb = new ProcessBuilder(); - $env = array( - 'foo' => 'bar', - 'foo2' => 'bar2', - ); - $proc = $pb - ->add('command') - ->setEnv('foo', 'bar2') - ->addEnvironmentVariables($env) - ->getProcess() - ; - - $this->assertSame($env, $proc->getEnv()); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException - */ - public function testNegativeTimeoutFromSetter() - { - $pb = new ProcessBuilder(); - $pb->setTimeout(-1); - } - - public function testNullTimeout() - { - $pb = new ProcessBuilder(); - $pb->setTimeout(10); - $pb->setTimeout(null); - - $r = new \ReflectionObject($pb); - $p = $r->getProperty('timeout'); - $p->setAccessible(true); - - $this->assertNull($p->getValue($pb)); - } - - public function testShouldSetArguments() - { - $pb = new ProcessBuilder(array('initial')); - $pb->setArguments(array('second')); - - $proc = $pb->getProcess(); - - $this->assertContains('second', $proc->getCommandLine()); - } - - public function testPrefixIsPrependedToAllGeneratedProcess() - { - $pb = new ProcessBuilder(); - $pb->setPrefix('/usr/bin/php'); - - $proc = $pb->setArguments(array('-v'))->getProcess(); - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertEquals('"/usr/bin/php" -v', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' '-v'", $proc->getCommandLine()); - } - - $proc = $pb->setArguments(array('-i'))->getProcess(); - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertEquals('"/usr/bin/php" -i', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' '-i'", $proc->getCommandLine()); - } - } - - public function testArrayPrefixesArePrependedToAllGeneratedProcess() - { - $pb = new ProcessBuilder(); - $pb->setPrefix(array('/usr/bin/php', 'composer.phar')); - - $proc = $pb->setArguments(array('-v'))->getProcess(); - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertEquals('"/usr/bin/php" composer.phar -v', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' 'composer.phar' '-v'", $proc->getCommandLine()); - } - - $proc = $pb->setArguments(array('-i'))->getProcess(); - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertEquals('"/usr/bin/php" composer.phar -i', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' 'composer.phar' '-i'", $proc->getCommandLine()); - } - } - - public function testShouldEscapeArguments() - { - $pb = new ProcessBuilder(array('%path%', 'foo " bar', '%baz%baz')); - $proc = $pb->getProcess(); - - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertSame('""^%"path"^%"" "foo "" bar" ""^%"baz"^%"baz"', $proc->getCommandLine()); - } else { - $this->assertSame("'%path%' 'foo \" bar' '%baz%baz'", $proc->getCommandLine()); - } - } - - public function testShouldEscapeArgumentsAndPrefix() - { - $pb = new ProcessBuilder(array('arg')); - $pb->setPrefix('%prefix%'); - $proc = $pb->getProcess(); - - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertSame('""^%"prefix"^%"" arg', $proc->getCommandLine()); - } else { - $this->assertSame("'%prefix%' 'arg'", $proc->getCommandLine()); - } - } - - /** - * @expectedException \Symfony\Component\Process\Exception\LogicException - */ - public function testShouldThrowALogicExceptionIfNoPrefixAndNoArgument() - { - ProcessBuilder::create()->getProcess(); - } - - public function testShouldNotThrowALogicExceptionIfNoArgument() - { - $process = ProcessBuilder::create() - ->setPrefix('/usr/bin/php') - ->getProcess(); - - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertEquals('"/usr/bin/php"', $process->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php'", $process->getCommandLine()); - } - } - - public function testShouldNotThrowALogicExceptionIfNoPrefix() - { - $process = ProcessBuilder::create(array('/usr/bin/php')) - ->getProcess(); - - if ('\\' === DIRECTORY_SEPARATOR) { - $this->assertEquals('"/usr/bin/php"', $process->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php'", $process->getCommandLine()); - } - } - - public function testShouldReturnProcessWithDisabledOutput() - { - $process = ProcessBuilder::create(array('/usr/bin/php')) - ->disableOutput() - ->getProcess(); - - $this->assertTrue($process->isOutputDisabled()); - } - - public function testShouldReturnProcessWithEnabledOutput() - { - $process = ProcessBuilder::create(array('/usr/bin/php')) - ->disableOutput() - ->enableOutput() - ->getProcess(); - - $this->assertFalse($process->isOutputDisabled()); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException - * @expectedExceptionMessage Symfony\Component\Process\ProcessBuilder::setInput only accepts strings, Traversable objects or stream resources. - */ - public function testInvalidInput() - { - $builder = ProcessBuilder::create(); - $builder->setInput(array()); - } - - public function testDoesNotPrefixExec() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test cannot run on Windows.'); - } - - $builder = ProcessBuilder::create(array('command', '-v', 'ls')); - $process = $builder->getProcess(); - $process->run(); - - $this->assertTrue($process->isSuccessful()); - } -} diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index bca7ddd9af10d..9d36d247c5dfa 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -28,7 +28,6 @@ class ProcessTest extends TestCase private static $phpBin; private static $process; private static $sigchild; - private static $notEnhancedSigchild = false; public static function setUpBeforeClass() { @@ -49,19 +48,19 @@ protected function tearDown() } /** - * @group legacy - * @expectedDeprecation The provided cwd does not exist. Command is currently ran against getcwd(). This behavior is deprecated since Symfony 3.4 and will be removed in 4.0. + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage The provided cwd does not exist. */ public function testInvalidCwd() { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('False-positive on Windows/appveyor.'); + try { + // Check that it works fine if the CWD exists + $cmd = new Process('echo test', __DIR__); + $cmd->run(); + } catch (\Exception $e) { + $this->fail($e); } - // Check that it works fine if the CWD exists - $cmd = new Process('echo test', __DIR__); - $cmd->run(); - $cmd = new Process('echo test', __DIR__.'/notfound/'); $cmd->run(); } @@ -118,9 +117,14 @@ public function testStopWithTimeoutIsActuallyWorking() $p = $this->getProcess(array(self::$phpBin, __DIR__.'/NonStopableProcess.php', 30)); $p->start(); - while (false === strpos($p->getOutput(), 'received')) { + while ($p->isRunning() && false === strpos($p->getOutput(), 'received')) { usleep(1000); } + + if (!$p->isRunning()) { + throw new \LogicException('Process is not running: '.$p->getErrorOutput()); + } + $start = microtime(true); $p->stop(0.1); @@ -438,7 +442,6 @@ public function testExitCodeCommandFailed() if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('Windows does not support POSIX exit code'); } - $this->skipIfNotEnhancedSigchild(); // such command run in bash return an exitcode 127 $process = $this->getProcess('nonexistingcommandIhopeneversomeonewouldnameacommandlikethis'); @@ -473,7 +476,6 @@ public function testTTYCommandExitCode() if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('Windows does have /dev/tty support'); } - $this->skipIfNotEnhancedSigchild(); $process = $this->getProcess('echo "foo" >> /dev/null'); $process->setTty(true); @@ -499,8 +501,6 @@ public function testTTYInWindowsEnvironment() public function testExitCodeTextIsNullWhenExitCodeIsNull() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(''); $this->assertNull($process->getExitCodeText()); } @@ -521,8 +521,6 @@ public function testPTYCommand() public function testMustRun() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess('echo foo'); $this->assertSame($process, $process->mustRun()); @@ -531,8 +529,6 @@ public function testMustRun() public function testSuccessfulMustRunHasCorrectExitCode() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess('echo foo')->mustRun(); $this->assertEquals(0, $process->getExitCode()); } @@ -542,16 +538,12 @@ public function testSuccessfulMustRunHasCorrectExitCode() */ public function testMustRunThrowsException() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess('exit 1'); $process->mustRun(); } public function testExitCodeText() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(''); $r = new \ReflectionObject($process); $p = $r->getProperty('exitcode'); @@ -580,8 +572,6 @@ public function testUpdateStatus() public function testGetExitCodeIsNullOnStart() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcessForCode('usleep(100000);'); $this->assertNull($process->getExitCode()); $process->start(); @@ -592,8 +582,6 @@ public function testGetExitCodeIsNullOnStart() public function testGetExitCodeIsNullOnWhenStartingAgain() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcessForCode('usleep(100000);'); $process->run(); $this->assertEquals(0, $process->getExitCode()); @@ -605,8 +593,6 @@ public function testGetExitCodeIsNullOnWhenStartingAgain() public function testGetExitCode() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess('echo foo'); $process->run(); $this->assertSame(0, $process->getExitCode()); @@ -642,8 +628,6 @@ public function testStop() public function testIsSuccessful() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess('echo foo'); $process->run(); $this->assertTrue($process->isSuccessful()); @@ -651,8 +635,6 @@ public function testIsSuccessful() public function testIsSuccessfulOnlyAfterTerminated() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcessForCode('usleep(100000);'); $process->start(); @@ -665,8 +647,6 @@ public function testIsSuccessfulOnlyAfterTerminated() public function testIsNotSuccessful() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcessForCode('throw new \Exception(\'BOUM\');'); $process->run(); $this->assertFalse($process->isSuccessful()); @@ -677,7 +657,6 @@ public function testProcessIsNotSignaled() if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('Windows does not support POSIX signals'); } - $this->skipIfNotEnhancedSigchild(); $process = $this->getProcess('echo foo'); $process->run(); @@ -689,7 +668,6 @@ public function testProcessWithoutTermSignal() if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('Windows does not support POSIX signals'); } - $this->skipIfNotEnhancedSigchild(); $process = $this->getProcess('echo foo'); $process->run(); @@ -701,7 +679,6 @@ public function testProcessIsSignaledIfStopped() if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('Windows does not support POSIX signals'); } - $this->skipIfNotEnhancedSigchild(); $process = $this->getProcessForCode('sleep(32);'); $process->start(); @@ -711,15 +688,18 @@ public function testProcessIsSignaledIfStopped() } /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage The process has been signaled + * @expectedException \Symfony\Component\Process\Exception\ProcessSignaledException + * @expectedExceptionMessage The process has been signaled with signal "9". */ public function testProcessThrowsExceptionWhenExternallySignaled() { if (!function_exists('posix_kill')) { $this->markTestSkipped('Function posix_kill is required.'); } - $this->skipIfNotEnhancedSigchild(false); + + if (self::$sigchild) { + $this->markTestSkipped('PHP is compiled with --enable-sigchild.'); + } $process = $this->getProcessForCode('sleep(32.1);'); $process->start(); @@ -930,8 +910,6 @@ public function testSignal() */ public function testExitCodeIsAvailableAfterSignal() { - $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess('sleep 4'); $process->start(); $process->signal(SIGKILL); @@ -1015,10 +993,9 @@ public function provideMethodsThatNeedATerminatedProcess() } /** - * @dataProvider provideWrongSignal * @expectedException \Symfony\Component\Process\Exception\RuntimeException */ - public function testWrongSignal($signal) + public function testWrongSignal() { if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('POSIX signals do not work on Windows'); @@ -1027,7 +1004,7 @@ public function testWrongSignal($signal) $process = $this->getProcessForCode('sleep(38);'); $process->start(); try { - $process->signal($signal); + $process->signal(-4); $this->fail('A RuntimeException must have been thrown'); } catch (RuntimeException $e) { $process->stop(0); @@ -1036,14 +1013,6 @@ public function testWrongSignal($signal) throw $e; } - public function provideWrongSignal() - { - return array( - array(-4), - array('Céphalopodes'), - ); - } - public function testDisableOutputDisablesTheOutput() { $p = $this->getProcess('foo'); @@ -1457,31 +1426,6 @@ public function testEnvIsInherited() unset($_ENV['FOO']); } - /** - * @group legacy - */ - public function testInheritEnvDisabled() - { - $process = $this->getProcessForCode('echo serialize($_SERVER);', null, array('BAR' => 'BAZ')); - - putenv('FOO=BAR'); - $_ENV['FOO'] = 'BAR'; - - $this->assertSame($process, $process->inheritEnvironmentVariables(false)); - $this->assertFalse($process->areEnvironmentVariablesInherited()); - - $process->run(); - - $expected = array('BAR' => 'BAZ', 'FOO' => 'BAR'); - $env = array_intersect_key(unserialize($process->getOutput()), $expected); - unset($expected['FOO']); - - $this->assertSame($expected, $env); - - putenv('FOO'); - unset($_ENV['FOO']); - } - public function testGetCommandLine() { $p = new Process(array('/usr/bin/php')); @@ -1501,19 +1445,6 @@ public function testEscapeArgument($arg) $this->assertSame($arg, $p->getOutput()); } - /** - * @dataProvider provideEscapeArgument - * @group legacy - */ - public function testEscapeArgumentWhenInheritEnvDisabled($arg) - { - $p = new Process(array(self::$phpBin, '-r', 'echo $argv[1];', $arg), null, array('BAR' => 'BAZ')); - $p->inheritEnvironmentVariables(false); - $p->run(); - - $this->assertSame($arg, $p->getOutput()); - } - public function testRawCommandLine() { $p = new Process(sprintf('"%s" -r %s "a" "" "b"', self::$phpBin, escapeshellarg('print_r($argv);'))); @@ -1569,21 +1500,6 @@ private function getProcess($commandline, $cwd = null, array $env = null, $input $process = new Process($commandline, $cwd, $env, $input, $timeout); $process->inheritEnvironmentVariables(); - if (false !== $enhance = getenv('ENHANCE_SIGCHLD')) { - try { - $process->setEnhanceSigchildCompatibility(false); - $process->getExitCode(); - $this->fail('ENHANCE_SIGCHLD must be used together with a sigchild-enabled PHP.'); - } catch (RuntimeException $e) { - $this->assertSame('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.', $e->getMessage()); - if ($enhance) { - $process->setEnhanceSigchildCompatibility(true); - } else { - self::$notEnhancedSigchild = true; - } - } - } - if (self::$process) { self::$process->stop(0); } @@ -1598,22 +1514,6 @@ private function getProcessForCode($code, $cwd = null, array $env = null, $input { return $this->getProcess(array(self::$phpBin, '-r', $code), $cwd, $env, $input, $timeout); } - - private function skipIfNotEnhancedSigchild($expectException = true) - { - if (self::$sigchild) { - if (!$expectException) { - $this->markTestSkipped('PHP is compiled with --enable-sigchild.'); - } elseif (self::$notEnhancedSigchild) { - if (method_exists($this, 'expectException')) { - $this->expectException('Symfony\Component\Process\Exception\RuntimeException'); - $this->expectExceptionMessage('This PHP has been compiled with --enable-sigchild.'); - } else { - $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild.'); - } - } - } - } } class NonStringifiable diff --git a/src/Symfony/Component/Process/Tests/ProcessUtilsTest.php b/src/Symfony/Component/Process/Tests/ProcessUtilsTest.php deleted file mode 100644 index 82fd8cfa8c898..0000000000000 --- a/src/Symfony/Component/Process/Tests/ProcessUtilsTest.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Process\ProcessUtils; - -/** - * @group legacy - */ -class ProcessUtilsTest extends TestCase -{ - /** - * @dataProvider dataArguments - */ - public function testEscapeArgument($result, $argument) - { - $this->assertSame($result, ProcessUtils::escapeArgument($argument)); - } - - public function dataArguments() - { - if ('\\' === DIRECTORY_SEPARATOR) { - return array( - array('"\"php\" \"-v\""', '"php" "-v"'), - array('"foo bar"', 'foo bar'), - array('^%"path"^%', '%path%'), - array('"<|>\\" \\"\'f"', '<|>" "\'f'), - array('""', ''), - array('"with\trailingbs\\\\"', 'with\trailingbs\\'), - ); - } - - return array( - array("'\"php\" \"-v\"'", '"php" "-v"'), - array("'foo bar'", 'foo bar'), - array("'%path%'", '%path%'), - array("'<|>\" \"'\\''f'", '<|>" "\'f'), - array("''", ''), - array("'with\\trailingbs\\'", 'with\trailingbs\\'), - array("'withNonAsciiAccentLikeéÉèÈàÀöä'", 'withNonAsciiAccentLikeéÉèÈàÀöä'), - ); - } -} diff --git a/src/Symfony/Component/Process/composer.json b/src/Symfony/Component/Process/composer.json index b8867db368038..58f4005b594b7 100644 --- a/src/Symfony/Component/Process/composer.json +++ b/src/Symfony/Component/Process/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" }, @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/PropertyAccess/CHANGELOG.md b/src/Symfony/Component/PropertyAccess/CHANGELOG.md index 416287e2a0e82..970f3545b5702 100644 --- a/src/Symfony/Component/PropertyAccess/CHANGELOG.md +++ b/src/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.0.0 +----- + + * removed the `StringUtil` class, use `Symfony\Component\Inflector\Inflector` + 3.1.0 ----- diff --git a/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php b/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php index d238d3276d17a..c2b0492130cc8 100644 --- a/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php +++ b/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php @@ -25,7 +25,7 @@ class UnexpectedTypeException extends RuntimeException * @param PropertyPathInterface $path The property path * @param int $pathIndex The property path index when the unexpected value was found */ - public function __construct($value, PropertyPathInterface $path, $pathIndex) + public function __construct($value, PropertyPathInterface $path, int $pathIndex) { $message = sprintf( 'PropertyAccessor requires a graph of objects or arrays to operate on, '. diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccess.php b/src/Symfony/Component/PropertyAccess/PropertyAccess.php index 3c8dc1c56cc18..e929347e6536c 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccess.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccess.php @@ -23,17 +23,12 @@ final class PropertyAccess * * @return PropertyAccessor */ - public static function createPropertyAccessor() + public static function createPropertyAccessor(): PropertyAccessor { return self::createPropertyAccessorBuilder()->getPropertyAccessor(); } - /** - * Creates a property accessor builder. - * - * @return PropertyAccessorBuilder - */ - public static function createPropertyAccessorBuilder() + public static function createPropertyAccessorBuilder(): PropertyAccessorBuilder { return new PropertyAccessorBuilder(); } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index d0db3946e47f7..37637e67833a4 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -33,90 +33,23 @@ */ class PropertyAccessor implements PropertyAccessorInterface { - /** - * @internal - */ - const VALUE = 0; - - /** - * @internal - */ - const REF = 1; - - /** - * @internal - */ - const IS_REF_CHAINED = 2; - - /** - * @internal - */ - const ACCESS_HAS_PROPERTY = 0; - - /** - * @internal - */ - const ACCESS_TYPE = 1; - - /** - * @internal - */ - const ACCESS_NAME = 2; - - /** - * @internal - */ - const ACCESS_REF = 3; - - /** - * @internal - */ - const ACCESS_ADDER = 4; - - /** - * @internal - */ - const ACCESS_REMOVER = 5; - - /** - * @internal - */ - const ACCESS_TYPE_METHOD = 0; - - /** - * @internal - */ - const ACCESS_TYPE_PROPERTY = 1; - - /** - * @internal - */ - const ACCESS_TYPE_MAGIC = 2; - - /** - * @internal - */ - const ACCESS_TYPE_ADDER_AND_REMOVER = 3; - - /** - * @internal - */ - const ACCESS_TYPE_NOT_FOUND = 4; - - /** - * @internal - */ - const CACHE_PREFIX_READ = 'r'; - - /** - * @internal - */ - const CACHE_PREFIX_WRITE = 'w'; - - /** - * @internal - */ - const CACHE_PREFIX_PROPERTY_PATH = 'p'; + private const VALUE = 0; + private const REF = 1; + private const IS_REF_CHAINED = 2; + private const ACCESS_HAS_PROPERTY = 0; + private const ACCESS_TYPE = 1; + private const ACCESS_NAME = 2; + private const ACCESS_REF = 3; + private const ACCESS_ADDER = 4; + private const ACCESS_REMOVER = 5; + private const ACCESS_TYPE_METHOD = 0; + private const ACCESS_TYPE_PROPERTY = 1; + private const ACCESS_TYPE_MAGIC = 2; + private const ACCESS_TYPE_ADDER_AND_REMOVER = 3; + private const ACCESS_TYPE_NOT_FOUND = 4; + private const CACHE_PREFIX_READ = 'r'; + private const CACHE_PREFIX_WRITE = 'w'; + private const CACHE_PREFIX_PROPERTY_PATH = 'p'; /** * @var bool @@ -131,21 +64,13 @@ class PropertyAccessor implements PropertyAccessorInterface private $readPropertyCache = array(); private $writePropertyCache = array(); - private $propertyPathCache = array(); - - private static $previousErrorHandler = false; - private static $errorHandler = array(__CLASS__, 'handleError'); private static $resultProto = array(self::VALUE => null); /** * Should not be used by application code. Use * {@link PropertyAccess::createPropertyAccessor()} instead. - * - * @param bool $magicCall - * @param bool $throwExceptionOnInvalidIndex - * @param CacheItemPoolInterface $cacheItemPool */ - public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null) + public function __construct(bool $magicCall = false, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; @@ -182,10 +107,6 @@ public function setValue(&$objectOrArray, $propertyPath, $value) $overwrite = true; try { - if (\PHP_VERSION_ID < 70000 && false === self::$previousErrorHandler) { - self::$previousErrorHandler = set_error_handler(self::$errorHandler); - } - for ($i = count($propertyValues) - 1; 0 <= $i; --$i) { $zval = $propertyValues[$i]; unset($propertyValues[$i]); @@ -234,26 +155,9 @@ public function setValue(&$objectOrArray, $propertyPath, $value) // It wasn't thrown in this class so rethrow it throw $e; - } finally { - if (\PHP_VERSION_ID < 70000 && false !== self::$previousErrorHandler) { - restore_error_handler(); - self::$previousErrorHandler = false; - } } } - /** - * @internal - */ - public static function handleError($type, $message, $file, $line, $context = array()) - { - if (E_RECOVERABLE_ERROR === $type) { - self::throwInvalidArgumentException($message, debug_backtrace(false), 1); - } - - return null !== self::$previousErrorHandler && false !== call_user_func(self::$previousErrorHandler, $type, $message, $file, $line, $context); - } - private static function throwInvalidArgumentException($message, $trace, $i) { if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && isset($trace[$i]['args'][0])) { @@ -262,7 +166,7 @@ private static function throwInvalidArgumentException($message, $trace, $i) $type = $trace[$i]['args'][0]; $type = is_object($type) ? get_class($type) : gettype($type); - throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given', substr($message, $pos, strpos($message, ',', $pos) - $pos), $type)); + throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given.', substr($message, $pos, strpos($message, ',', $pos) - $pos), $type)); } } @@ -595,7 +499,7 @@ private function getReadAccessInfo($class, $property) private function writeIndex($zval, $index, $value) { if (!$zval[self::VALUE] instanceof \ArrayAccess && !is_array($zval[self::VALUE])) { - throw new NoSuchIndexException(sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($zval[self::VALUE]))); + throw new NoSuchIndexException(sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, get_class($zval[self::VALUE]))); } $zval[self::REF][$index] = $value; @@ -636,7 +540,7 @@ private function writeProperty($zval, $property, $value) } elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) { $object->{$access[self::ACCESS_NAME]}($value); } elseif (self::ACCESS_TYPE_NOT_FOUND === $access[self::ACCESS_TYPE]) { - throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s".', $property, get_class($object))); + throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s"%s.', $property, get_class($object), isset($access[self::ACCESS_NAME]) ? ': '.$access[self::ACCESS_NAME] : '')); } else { throw new NoSuchPropertyException($access[self::ACCESS_NAME]); } @@ -684,13 +588,9 @@ private function writeCollection($zval, $property, $collection, $addMethod, $rem /** * Guesses how to write the property value. * - * @param string $class - * @param string $property - * @param mixed $value - * - * @return array + * @param mixed $value */ - private function getWriteAccessInfo($class, $property, $value) + private function getWriteAccessInfo(string $class, string $property, $value): array { $key = (false !== strpos($class, '@') ? rawurlencode($class) : $class).'..'.$property; @@ -779,12 +679,9 @@ private function getWriteAccessInfo($class, $property, $value) /** * Returns whether a property is writable in the given object. * - * @param object $object The object to write to - * @param string $property The property to write - * - * @return bool Whether the property is writable + * @param object $object The object to write to */ - private function isPropertyWritable($object, $property) + private function isPropertyWritable($object, string $property): bool { if (!is_object($object)) { return false; @@ -801,12 +698,8 @@ private function isPropertyWritable($object, $property) /** * Camelizes a given string. - * - * @param string $string Some string - * - * @return string The camelized version of the string */ - private function camelize($string) + private function camelize(string $string): string { return str_replace(' ', '', ucwords(str_replace('_', ' ', $string))); } @@ -836,14 +729,8 @@ private function findAdderAndRemover(\ReflectionClass $reflClass, array $singula /** * Returns whether a method is public and has the number of required parameters. - * - * @param \ReflectionClass $class The class of the method - * @param string $methodName The method name - * @param int $parameters The number of parameters - * - * @return bool Whether the method is public and has $parameters required parameters */ - private function isMethodAccessible(\ReflectionClass $class, $methodName, $parameters) + private function isMethodAccessible(\ReflectionClass $class, string $methodName, int $parameters): bool { if ($class->hasMethod($methodName)) { $method = $class->getMethod($methodName); @@ -862,10 +749,8 @@ private function isMethodAccessible(\ReflectionClass $class, $methodName, $param * Gets a PropertyPath instance and caches it. * * @param string|PropertyPath $propertyPath - * - * @return PropertyPath */ - private function getPropertyPath($propertyPath) + private function getPropertyPath($propertyPath): PropertyPath { if ($propertyPath instanceof PropertyPathInterface) { // Don't call the copy constructor has it is not needed here diff --git a/src/Symfony/Component/PropertyAccess/StringUtil.php b/src/Symfony/Component/PropertyAccess/StringUtil.php deleted file mode 100644 index 02e598f709e23..0000000000000 --- a/src/Symfony/Component/PropertyAccess/StringUtil.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess; - -use Symfony\Component\Inflector\Inflector; - -/** - * Creates singulars from plurals. - * - * @author Bernhard Schussek - * - * @deprecated since version 3.1, to be removed in 4.0. Use {@see Symfony\Component\Inflector\Inflector} instead. - */ -class StringUtil -{ - /** - * This class should not be instantiated. - */ - private function __construct() - { - } - - /** - * Returns the singular form of a word. - * - * If the method can't determine the form with certainty, an array of the - * possible singulars is returned. - * - * @param string $plural A word in plural form - * - * @return string|array The singular form or an array of possible singular - * forms - * - * @deprecated since version 3.1, to be removed in 4.0. Use {@see Symfony\Component\Inflector\Inflector::singularize} instead. - */ - public static function singularify($plural) - { - @trigger_error('StringUtil::singularify() is deprecated since Symfony 3.1 and will be removed in 4.0. Use Symfony\Component\Inflector\Inflector::singularize instead.', E_USER_DEPRECATED); - - return Inflector::singularize($plural); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/ReturnTyped.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/ReturnTyped.php deleted file mode 100644 index b6a9852715d79..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/ReturnTyped.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Tests\Fixtures; - -/** - * @author Kévin Dunglas - */ -class ReturnTyped -{ - public function getFoos(): array - { - return 'It doesn\'t respect the return type on purpose'; - } - - public function addFoo(\DateTime $dateTime) - { - } - - public function removeFoo(\DateTime $dateTime) - { - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClassTypeErrorInsideCall.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClassTypeErrorInsideCall.php deleted file mode 100644 index 44a93900fae34..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClassTypeErrorInsideCall.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Tests\Fixtures; - -class TestClassTypeErrorInsideCall -{ - public function expectsDateTime(\DateTime $date) - { - } - - public function getProperty() - { - } - - public function setProperty($property) - { - $this->expectsDateTime(null); // throws TypeError - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index b8356500a5881..f160be61f325f 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -15,14 +15,12 @@ use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; use Symfony\Component\PropertyAccess\PropertyAccessor; -use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped; use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass; use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall; use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet; use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object; use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassSetValue; use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable; -use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassTypeErrorInsideCall; use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted; class PropertyAccessorTest extends TestCase @@ -581,9 +579,6 @@ public function testThrowTypeErrorWithInterface() $this->propertyAccessor->setValue($object, 'countable', 'This is a string, \Countable expected.'); } - /** - * @requires PHP 7.0 - */ public function testAnonymousClassRead() { $value = 'bar'; @@ -595,9 +590,6 @@ public function testAnonymousClassRead() $this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo')); } - /** - * @requires PHP 7.0 - */ public function testAnonymousClassWrite() { $value = 'bar'; @@ -640,27 +632,4 @@ public function setFoo($foo) return $obj; } - - /** - * @requires PHP 7.0 - * @expectedException \TypeError - */ - public function testThrowTypeErrorInsideSetterCall() - { - $object = new TestClassTypeErrorInsideCall(); - - $this->propertyAccessor->setValue($object, 'property', 'foo'); - } - - /** - * @requires PHP 7 - * - * @expectedException \TypeError - */ - public function testDoNotDiscardReturnTypeError() - { - $object = new ReturnTyped(); - - $this->propertyAccessor->setValue($object, 'foos', array(new \DateTime())); - } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php b/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php deleted file mode 100644 index 7728e15f834c8..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\PropertyAccess\StringUtil; - -/** - * @group legacy - */ -class StringUtilTest extends TestCase -{ - public function singularifyProvider() - { - // This is only a stub to make sure the BC layer works - // Actual tests are in the Symfony Inflector component - return array( - array('axes', array('ax', 'axe', 'axis')), - ); - } - - /** - * @dataProvider singularifyProvider - */ - public function testSingularify($plural, $singular) - { - $single = StringUtil::singularify($plural); - if (is_string($singular) && is_array($single)) { - $this->fail("--- Expected\n`string`: ".$singular."\n+++ Actual\n`array`: ".implode(', ', $single)); - } elseif (is_array($singular) && is_string($single)) { - $this->fail("--- Expected\n`array`: ".implode(', ', $singular)."\n+++ Actual\n`string`: ".$single); - } - - $this->assertEquals($singular, $single); - } -} diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index d6e7afb69c48c..bacba378a4434 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -16,12 +16,11 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-php70": "~1.0", - "symfony/inflector": "~3.1|~4.0" + "php": "^7.1.3", + "symfony/inflector": "~3.4|~4.0" }, "require-dev": { - "symfony/cache": "~3.1|~4.0" + "symfony/cache": "~3.4|~4.0" }, "suggest": { "psr/cache-implementation": "To cache access methods." @@ -35,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php b/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php index c4bc893f72e9b..dd547f562be0f 100644 --- a/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php +++ b/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php @@ -31,7 +31,7 @@ class PropertyInfoPass implements CompilerPassInterface private $descriptionExtractorTag; private $accessExtractorTag; - public function __construct($propertyInfoService = 'property_info', $listExtractorTag = 'property_info.list_extractor', $typeExtractorTag = 'property_info.type_extractor', $descriptionExtractorTag = 'property_info.description_extractor', $accessExtractorTag = 'property_info.access_extractor') + public function __construct(string $propertyInfoService = 'property_info', string $listExtractorTag = 'property_info.list_extractor', string $typeExtractorTag = 'property_info.type_extractor', string $descriptionExtractorTag = 'property_info.description_extractor', string $accessExtractorTag = 'property_info.access_extractor') { $this->propertyInfoService = $propertyInfoService; $this->listExtractorTag = $listExtractorTag; diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 9494333a2e316..ca4f71d8ea10a 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -147,15 +147,7 @@ public function getTypes($class, $property, array $context = array()) return array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0])); } - /** - * Gets the DocBlock for this property. - * - * @param string $class - * @param string $property - * - * @return array - */ - private function getDocBlock($class, $property) + private function getDocBlock(string $class, string $property): array { $propertyHash = sprintf('%s::%s', $class, $property); @@ -189,36 +181,19 @@ private function getDocBlock($class, $property) return $this->docBlocks[$propertyHash] = $data; } - /** - * Gets the DocBlock from a property. - * - * @param string $class - * @param string $property - * - * @return DocBlock|null - */ - private function getDocBlockFromProperty($class, $property) + private function getDocBlockFromProperty(string $class, string $property): ?DocBlock { // Use a ReflectionProperty instead of $class to get the parent class if applicable try { $reflectionProperty = new \ReflectionProperty($class, $property); } catch (\ReflectionException $e) { - return; + return null; } return $this->docBlockFactory->create($reflectionProperty, $this->contextFactory->createFromReflector($reflectionProperty->getDeclaringClass())); } - /** - * Gets DocBlock from accessor or mutator method. - * - * @param string $class - * @param string $ucFirstProperty - * @param int $type - * - * @return array|null - */ - private function getDocBlockFromMethod($class, $ucFirstProperty, $type) + private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array { $prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes; $prefix = null; @@ -244,7 +219,7 @@ private function getDocBlockFromMethod($class, $ucFirstProperty, $type) } if (!isset($reflectionMethod)) { - return; + return null; } return array($this->docBlockFactory->create($reflectionMethod, $this->contextFactory->createFromReflector($reflectionMethod)), $prefix); diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 47081b1d77381..74d867c31ee45 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -34,31 +34,15 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp /** * @internal */ - public static $defaultAccessorPrefixes = array('is', 'can', 'get'); + public static $defaultAccessorPrefixes = array('is', 'can', 'get', 'has'); /** * @internal */ public static $defaultArrayMutatorPrefixes = array('add', 'remove'); - /** - * @var bool - */ - private $supportsParameterType; - - /** - * @var string[] - */ private $mutatorPrefixes; - - /** - * @var string[] - */ private $accessorPrefixes; - - /** - * @var string[] - */ private $arrayMutatorPrefixes; /** @@ -68,7 +52,6 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp */ public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null) { - $this->supportsParameterType = method_exists('ReflectionParameter', 'getType'); $this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : self::$defaultMutatorPrefixes; $this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : self::$defaultAccessorPrefixes; $this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes; @@ -155,44 +138,22 @@ public function isWritable($class, $property, array $context = array()) } /** - * Tries to extract type information from mutators. - * - * @param string $class - * @param string $property - * * @return Type[]|null */ - private function extractFromMutator($class, $property) + private function extractFromMutator(string $class, string $property): ?array { list($reflectionMethod, $prefix) = $this->getMutatorMethod($class, $property); if (null === $reflectionMethod) { - return; + return null; } $reflectionParameters = $reflectionMethod->getParameters(); $reflectionParameter = $reflectionParameters[0]; - if ($this->supportsParameterType) { - if (!$reflectionType = $reflectionParameter->getType()) { - return; - } - $type = $this->extractFromReflectionType($reflectionType); - - // HHVM reports variadics with "array" but not builtin type hints - if (!$reflectionType->isBuiltin() && Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) { - return; - } - } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $reflectionParameter, $info)) { - if (Type::BUILTIN_TYPE_ARRAY === $info[1]) { - $type = new Type(Type::BUILTIN_TYPE_ARRAY, $reflectionParameter->allowsNull(), null, true); - } elseif (Type::BUILTIN_TYPE_CALLABLE === $info[1]) { - $type = new Type(Type::BUILTIN_TYPE_CALLABLE, $reflectionParameter->allowsNull()); - } else { - $type = new Type(Type::BUILTIN_TYPE_OBJECT, $reflectionParameter->allowsNull(), $info[1]); - } - } else { - return; + if (!$reflectionType = $reflectionParameter->getType()) { + return null; } + $type = $this->extractFromReflectionType($reflectionType); if (in_array($prefix, $this->arrayMutatorPrefixes)) { $type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type); @@ -204,37 +165,29 @@ private function extractFromMutator($class, $property) /** * Tries to extract type information from accessors. * - * @param string $class - * @param string $property - * * @return Type[]|null */ - private function extractFromAccessor($class, $property) + private function extractFromAccessor(string $class, string $property): ?array { list($reflectionMethod, $prefix) = $this->getAccessorMethod($class, $property); if (null === $reflectionMethod) { - return; + return null; } - if ($this->supportsParameterType && $reflectionType = $reflectionMethod->getReturnType()) { + if ($reflectionType = $reflectionMethod->getReturnType()) { return array($this->extractFromReflectionType($reflectionType)); } - if (in_array($prefix, array('is', 'can'))) { + if (in_array($prefix, array('is', 'can', 'has'))) { return array(new Type(Type::BUILTIN_TYPE_BOOL)); } + + return null; } - /** - * Extracts data from the PHP 7 reflection type. - * - * @param \ReflectionType $reflectionType - * - * @return Type - */ - private function extractFromReflectionType(\ReflectionType $reflectionType) + private function extractFromReflectionType(\ReflectionType $reflectionType): Type { - $phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : $reflectionType->__toString(); + $phpTypeOrClass = $reflectionType->getName(); $nullable = $reflectionType->allowsNull(); if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) { @@ -250,15 +203,7 @@ private function extractFromReflectionType(\ReflectionType $reflectionType) return $type; } - /** - * Does the class have the given public property? - * - * @param string $class - * @param string $property - * - * @return bool - */ - private function isPublicProperty($class, $property) + private function isPublicProperty(string $class, string $property): bool { try { $reflectionProperty = new \ReflectionProperty($class, $property); @@ -276,13 +221,8 @@ private function isPublicProperty($class, $property) * * Returns an array with a the instance of \ReflectionMethod as first key * and the prefix of the method as second or null if not found. - * - * @param string $class - * @param string $property - * - * @return array|null */ - private function getAccessorMethod($class, $property) + private function getAccessorMethod(string $class, string $property): ?array { $ucProperty = ucfirst($property); @@ -300,20 +240,15 @@ private function getAccessorMethod($class, $property) // Return null if the property doesn't exist } } + + return null; } /** - * Gets the mutator method. - * * Returns an array with a the instance of \ReflectionMethod as first key * and the prefix of the method as second or null if not found. - * - * @param string $class - * @param string $property - * - * @return array */ - private function getMutatorMethod($class, $property) + private function getMutatorMethod(string $class, string $property): ?array { $ucProperty = ucfirst($property); $ucSingulars = (array) Inflector::singularize($ucProperty); @@ -340,17 +275,11 @@ private function getMutatorMethod($class, $property) } } } + + return null; } - /** - * Extracts a property name from a method name. - * - * @param string $methodName - * @param \ReflectionProperty[] $reflectionProperties - * - * @return string - */ - private function getPropertyName($methodName, array $reflectionProperties) + private function getPropertyName(string $methodName, array $reflectionProperties): ?string { $pattern = implode('|', array_merge($this->accessorPrefixes, $this->mutatorPrefixes)); @@ -369,5 +298,7 @@ private function getPropertyName($methodName, array $reflectionProperties) return $matches[2]; } + + return null; } } diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php index c1010832df471..71edb47e5b4eb 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php @@ -83,12 +83,9 @@ public function getTypes($class, $property, array $context = array()) /** * Retrieves the cached data if applicable or delegates to the decorated extractor. * - * @param string $method - * @param array $arguments - * * @return mixed */ - private function extract($method, array $arguments) + private function extract(string $method, array $arguments) { try { $serializedArguments = serialize($arguments); diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php index 22e409c1011eb..f2d3a82964dc0 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php @@ -31,7 +31,7 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface * @param iterable|PropertyDescriptionExtractorInterface[] $descriptionExtractors * @param iterable|PropertyAccessExtractorInterface[] $accessExtractors */ - public function __construct($listExtractors = array(), $typeExtractors = array(), $descriptionExtractors = array(), $accessExtractors = array()) + public function __construct(iterable $listExtractors = array(), iterable $typeExtractors = array(), iterable $descriptionExtractors = array(), iterable $accessExtractors = array()) { $this->listExtractors = $listExtractors; $this->typeExtractors = $typeExtractors; @@ -90,13 +90,9 @@ public function isWritable($class, $property, array $context = array()) /** * Iterates over registered extractors and return the first value found. * - * @param iterable $extractors - * @param string $method - * @param array $arguments - * * @return mixed */ - private function extract($extractors, $method, array $arguments) + private function extract(iterable $extractors, string $method, array $arguments) { foreach ($extractors as $extractor) { $value = call_user_func_array(array($extractor, $method), $arguments); diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php index dab463f7e5105..38a9ff9e632d3 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php @@ -40,31 +40,6 @@ public function testExtract($property, array $type = null, $shortDescription, $l $this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); } - public function testParamTagTypeIsOmitted() - { - $this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType')); - } - - /** - * @dataProvider typesWithCustomPrefixesProvider - */ - public function testExtractTypesWithCustomPrefixes($property, array $type = null) - { - $customExtractor = new PhpDocExtractor(null, array('add', 'remove'), array('is', 'can')); - - $this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); - } - - /** - * @dataProvider typesWithNoPrefixesProvider - */ - public function testExtractTypesWithNoPrefixes($property, array $type = null) - { - $noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array()); - - $this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); - } - public function typesProvider() { return array( @@ -93,7 +68,8 @@ public function typesProvider() array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null), array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null), array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), - array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), + array('g', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null), + array('array', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), array('donotexist', null, null, null), array('staticGetter', null, null, null), array('staticSetter', null, null, null), @@ -101,6 +77,21 @@ public function typesProvider() ); } + public function testParamTagTypeIsOmitted() + { + $this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType')); + } + + /** + * @dataProvider typesWithCustomPrefixesProvider + */ + public function testExtractTypesWithCustomPrefixes($property, array $type = null) + { + $customExtractor = new PhpDocExtractor(null, array('add', 'remove'), array('is', 'can')); + + $this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); + } + public function typesWithCustomPrefixesProvider() { return array( @@ -129,13 +120,23 @@ public function typesWithCustomPrefixesProvider() array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null), array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null), array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), - array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), + array('g', null), array('donotexist', null, null, null), array('staticGetter', null, null, null), array('staticSetter', null, null, null), ); } + /** + * @dataProvider typesWithNoPrefixesProvider + */ + public function testExtractTypesWithNoPrefixes($property, array $type = null) + { + $noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array()); + + $this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); + } + public function typesWithNoPrefixesProvider() { return array( @@ -164,7 +165,7 @@ public function typesWithNoPrefixesProvider() array('d', null, null, null), array('e', null, null, null), array('f', null, null, null), - array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), + array('g', null), array('donotexist', null, null, null), array('staticGetter', null, null, null), array('staticSetter', null, null, null), diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php index 02729b4bf90e4..1acb48f355dc6 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php @@ -40,7 +40,7 @@ public function testGetProperties() 'collection', 'B', 'Guid', - 'g', + 'array', 'emptyVar', 'foo', 'foo2', @@ -56,6 +56,7 @@ public function testGetProperties() 'd', 'e', 'f', + 'g', ), $this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy') ); @@ -72,7 +73,7 @@ public function testGetPropertiesWithCustomPrefixes() 'collection', 'B', 'Guid', - 'g', + 'array', 'emptyVar', 'foo', 'foo2', @@ -100,7 +101,7 @@ public function testGetPropertiesWithNoPrefixes() 'collection', 'B', 'Guid', - 'g', + 'array', 'emptyVar', 'foo', 'foo2', @@ -138,7 +139,6 @@ public function typesProvider() /** * @dataProvider php7TypesProvider - * @requires PHP 7.0 */ public function testExtractPhp7Type($property, array $type = null) { @@ -157,7 +157,6 @@ public function php7TypesProvider() /** * @dataProvider php71TypesProvider - * @requires PHP 7.1 */ public function testExtractPhp71Type($property, array $type = null) { @@ -198,10 +197,12 @@ public function getReadableProperties() array('d', true), array('e', false), array('f', false), + array('g', true), array('Id', true), array('id', true), array('Guid', true), array('guid', false), + array('guid', false), ); } @@ -228,6 +229,7 @@ public function getWritableProperties() array('d', false), array('e', true), array('f', true), + array('g', false), array('Id', false), array('Guid', true), array('guid', false), diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index 4e558eca014e5..0916217020835 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -66,7 +66,7 @@ class Dummy extends ParentDummy * * @var array|null */ - public $g; + public $array; /** * This should not be removed. diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php index 330496827cfc4..dc9109d7d1722 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php @@ -75,4 +75,11 @@ public function addE($e) public function removeF(\DateTime $f) { } + + /** + * @return bool|null + */ + public function hasG() + { + } } diff --git a/src/Symfony/Component/PropertyInfo/Type.php b/src/Symfony/Component/PropertyInfo/Type.php index 86bc2f7643db4..51c9a2d8684cc 100644 --- a/src/Symfony/Component/PropertyInfo/Type.php +++ b/src/Symfony/Component/PropertyInfo/Type.php @@ -57,16 +57,9 @@ class Type private $collectionValueType; /** - * @param string $builtinType - * @param bool $nullable - * @param string|null $class - * @param bool $collection - * @param Type|null $collectionKeyType - * @param Type|null $collectionValueType - * * @throws \InvalidArgumentException */ - public function __construct($builtinType, $nullable = false, $class = null, $collection = false, Type $collectionKeyType = null, Type $collectionValueType = null) + public function __construct(string $builtinType, bool $nullable = false, string $class = null, bool $collection = false, Type $collectionKeyType = null, Type $collectionValueType = null) { if (!in_array($builtinType, self::$builtinTypes)) { throw new \InvalidArgumentException(sprintf('"%s" is not a valid PHP type.', $builtinType)); @@ -84,20 +77,13 @@ public function __construct($builtinType, $nullable = false, $class = null, $col * Gets built-in type. * * Can be bool, int, float, string, array, object, resource, null, callback or iterable. - * - * @return string */ - public function getBuiltinType() + public function getBuiltinType(): string { return $this->builtinType; } - /** - * Allows null value? - * - * @return bool - */ - public function isNullable() + public function isNullable(): bool { return $this->nullable; } @@ -106,20 +92,13 @@ public function isNullable() * Gets the class name. * * Only applicable if the built-in type is object. - * - * @return string|null */ - public function getClassName() + public function getClassName(): ?string { return $this->class; } - /** - * Is collection? - * - * @return bool - */ - public function isCollection() + public function isCollection(): bool { return $this->collection; } @@ -128,10 +107,8 @@ public function isCollection() * Gets collection key type. * * Only applicable for a collection type. - * - * @return self|null */ - public function getCollectionKeyType() + public function getCollectionKeyType(): ?self { return $this->collectionKeyType; } @@ -140,10 +117,8 @@ public function getCollectionKeyType() * Gets collection value type. * * Only applicable for a collection type. - * - * @return self|null */ - public function getCollectionValueType() + public function getCollectionValueType(): ?self { return $this->collectionValueType; } diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index bc14cd8b69b1b..939fb7c195932 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -27,9 +27,9 @@ final class PhpDocTypeHelper /** * Creates a {@see Type} from a PHPDoc type. * - * @return Type + * @return Type[] */ - public function getTypes(DocType $varType) + public function getTypes(DocType $varType): array { $types = array(); $nullable = false; @@ -73,17 +73,12 @@ public function getTypes(DocType $varType) /** * Creates a {@see Type} from a PHPDoc type. - * - * @param string $docType - * @param bool $nullable - * - * @return Type|null */ - private function createType($docType, $nullable) + private function createType(string $docType, bool $nullable): ?Type { // Cannot guess if (!$docType || 'mixed' === $docType) { - return; + return null; } if ($collection = '[]' === substr($docType, -2)) { @@ -110,14 +105,7 @@ private function createType($docType, $nullable) return new Type($phpType, $nullable, $class); } - /** - * Normalizes the type. - * - * @param string $docType - * - * @return string - */ - private function normalizeType($docType) + private function normalizeType(string $docType): string { switch ($docType) { case 'integer': @@ -141,14 +129,7 @@ private function normalizeType($docType) } } - /** - * Gets an array containing the PHP type and the class. - * - * @param string $docType - * - * @return array - */ - private function getPhpTypeAndClass($docType) + private function getPhpTypeAndClass(string $docType): array { if (in_array($docType, Type::$builtinTypes)) { return array($docType, null); diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index ce41fabd09365..0350fb411f315 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -23,20 +23,20 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/inflector": "~3.1|~4.0" + "php": "^7.1.3", + "symfony/inflector": "~3.4|~4.0" }, "require-dev": { - "symfony/serializer": "~2.8|~3.0|~4.0", - "symfony/cache": "~3.1|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", + "symfony/serializer": "~3.4|~4.0", + "symfony/cache": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0", "doctrine/annotations": "~1.0" }, "conflict": { "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", "phpdocumentor/type-resolver": "<0.2.1", - "symfony/dependency-injection": "<3.3" + "symfony/dependency-injection": "<3.4" }, "suggest": { "psr/cache-implementation": "To cache results", @@ -53,7 +53,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md index e278f8b1e5936..d088e2eddc117 100644 --- a/src/Symfony/Component/Routing/CHANGELOG.md +++ b/src/Symfony/Component/Routing/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.0.0 +----- + + * dropped support for using UTF-8 route patterns without using the `utf8` option + * dropped support for using UTF-8 route requirements without using the `utf8` option + 3.4.0 ----- diff --git a/src/Symfony/Component/Routing/CompiledRoute.php b/src/Symfony/Component/Routing/CompiledRoute.php index 8ecf5153ce2f7..44484c0e6ebd5 100644 --- a/src/Symfony/Component/Routing/CompiledRoute.php +++ b/src/Symfony/Component/Routing/CompiledRoute.php @@ -37,9 +37,9 @@ class CompiledRoute implements \Serializable * @param array $hostVariables An array of host variables * @param array $variables An array of variables (variables defined in the path and in the host patterns) */ - public function __construct($staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array()) + public function __construct(string $staticPrefix, string $regex, array $tokens, array $pathVariables, string $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array()) { - $this->staticPrefix = (string) $staticPrefix; + $this->staticPrefix = $staticPrefix; $this->regex = $regex; $this->tokens = $tokens; $this->pathVariables = $pathVariables; @@ -71,11 +71,7 @@ public function serialize() */ public function unserialize($serialized) { - if (\PHP_VERSION_ID >= 70000) { - $data = unserialize($serialized, array('allowed_classes' => false)); - } else { - $data = unserialize($serialized); - } + $data = unserialize($serialized, array('allowed_classes' => false)); $this->variables = $data['vars']; $this->staticPrefix = $data['path_prefix']; diff --git a/src/Symfony/Component/Routing/DependencyInjection/RoutingResolverPass.php b/src/Symfony/Component/Routing/DependencyInjection/RoutingResolverPass.php index 4af0a5a28668b..a5116c99f23af 100644 --- a/src/Symfony/Component/Routing/DependencyInjection/RoutingResolverPass.php +++ b/src/Symfony/Component/Routing/DependencyInjection/RoutingResolverPass.php @@ -28,7 +28,7 @@ class RoutingResolverPass implements CompilerPassInterface private $resolverServiceId; private $loaderTag; - public function __construct($resolverServiceId = 'routing.resolver', $loaderTag = 'routing.loader') + public function __construct(string $resolverServiceId = 'routing.resolver', string $loaderTag = 'routing.loader') { $this->resolverServiceId = $resolverServiceId; $this->loaderTag = $loaderTag; diff --git a/src/Symfony/Component/Routing/Exception/MethodNotAllowedException.php b/src/Symfony/Component/Routing/Exception/MethodNotAllowedException.php index 712412fecec58..22ca6c393cc85 100644 --- a/src/Symfony/Component/Routing/Exception/MethodNotAllowedException.php +++ b/src/Symfony/Component/Routing/Exception/MethodNotAllowedException.php @@ -22,7 +22,7 @@ class MethodNotAllowedException extends \RuntimeException implements ExceptionIn { protected $allowedMethods = array(); - public function __construct(array $allowedMethods, $message = null, $code = 0, \Exception $previous = null) + public function __construct(array $allowedMethods, string $message = null, int $code = 0, \Exception $previous = null) { $this->allowedMethods = array_map('strtoupper', $allowedMethods); diff --git a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php index 60bdf1da3522c..0cb87f1163f3b 100644 --- a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php +++ b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Routing\Generator\Dumper; +use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; + /** * PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes. * @@ -88,7 +90,7 @@ private function generateDeclaredRoutes() $properties[] = $compiledRoute->getHostTokens(); $properties[] = $route->getSchemes(); - $routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true))); + $routes .= sprintf(" '%s' => %s,\n", $name, PhpMatcherDumper::export($properties)); } $routes .= ' )'; diff --git a/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php index cf9f0704c4987..b1cb104b58e73 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php @@ -59,10 +59,9 @@ public function load($file, $type = null) $collection->addResource(new FileResource($path)); $collection->addCollection($this->loader->load($class, $type)); } - if (\PHP_VERSION_ID >= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - gc_mem_caches(); - } + + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + gc_mem_caches(); return $collection; } diff --git a/src/Symfony/Component/Routing/Loader/Configurator/CollectionConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/CollectionConfigurator.php index 38d86cb895cb4..4166fce62694b 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/CollectionConfigurator.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/CollectionConfigurator.php @@ -24,7 +24,7 @@ class CollectionConfigurator private $parent; - public function __construct(RouteCollection $parent, $name) + public function __construct(RouteCollection $parent, string $name) { $this->parent = $parent; $this->name = $name; @@ -40,13 +40,8 @@ public function __destruct() /** * Adds a route. - * - * @param string $name - * @param string $path - * - * @return RouteConfigurator */ - final public function add($name, $path) + final public function add(string $name, string $path): RouteConfigurator { $this->collection->add($this->name.$name, $route = clone $this->route); @@ -66,11 +61,9 @@ final public function collection($name = '') /** * Sets the prefix to add to the path of all child routes. * - * @param string $prefix - * * @return $this */ - final public function prefix($prefix) + final public function prefix(string $prefix) { $this->route->setPath($prefix); diff --git a/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php index d0a3c373ff23a..f978497dd20d9 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php @@ -36,11 +36,9 @@ public function __destruct() /** * Sets the prefix to add to the path of all child routes. * - * @param string $prefix - * * @return $this */ - final public function prefix($prefix) + final public function prefix(string $prefix) { $this->route->addPrefix($prefix); diff --git a/src/Symfony/Component/Routing/Loader/Configurator/RouteConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/RouteConfigurator.php index b8d87025435e0..df54a489e6fad 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/RouteConfigurator.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/RouteConfigurator.php @@ -22,7 +22,7 @@ class RouteConfigurator use Traits\AddTrait; use Traits\RouteTrait; - public function __construct(RouteCollection $collection, Route $route, $name = '') + public function __construct(RouteCollection $collection, Route $route, string $name = '') { $this->collection = $collection; $this->route = $route; diff --git a/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php index 4591a86ba5cf9..713fcd2e05190 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php @@ -25,7 +25,7 @@ class RoutingConfigurator private $path; private $file; - public function __construct(RouteCollection $collection, PhpFileLoader $loader, $path, $file) + public function __construct(RouteCollection $collection, PhpFileLoader $loader, string $path, string $file) { $this->collection = $collection; $this->loader = $loader; diff --git a/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php b/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php index 7171fd241f6d0..779bacfa0eb45 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/Traits/AddTrait.php @@ -26,13 +26,8 @@ trait AddTrait /** * Adds a route. - * - * @param string $name - * @param string $path - * - * @return RouteConfigurator */ - final public function add($name, $path) + final public function add(string $name, string $path): RouteConfigurator { $this->collection->add($this->name.$name, $route = new Route($path)); @@ -41,13 +36,8 @@ final public function add($name, $path) /** * Adds a route. - * - * @param string $name - * @param string $path - * - * @return RouteConfigurator */ - final public function __invoke($name, $path) + final public function __invoke(string $name, string $path): RouteConfigurator { return $this->add($name, $path); } diff --git a/src/Symfony/Component/Routing/Loader/Configurator/Traits/RouteTrait.php b/src/Symfony/Component/Routing/Loader/Configurator/Traits/RouteTrait.php index 4d2e255b14076..d3ced4d63807b 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/Traits/RouteTrait.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/Traits/RouteTrait.php @@ -60,11 +60,9 @@ final public function options(array $options) /** * Sets the condition. * - * @param string $condition - * * @return $this */ - final public function condition($condition) + final public function condition(string $condition) { $this->route->setCondition($condition); @@ -74,11 +72,9 @@ final public function condition($condition) /** * Sets the pattern for the host. * - * @param string $pattern - * * @return $this */ - final public function host($pattern) + final public function host(string $pattern) { $this->route->setHost($pattern); @@ -128,4 +124,14 @@ final public function controller($controller) return $this; } + + /** + * Adds a prefix to the name of all the routes within the collection. + */ + final public function addNamePrefix(string $prefix): self + { + $this->route->addNamePrefix($prefix); + + return $this; + } } diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php index 3a77890703ce2..31d69ca6d8e8c 100644 --- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php @@ -165,6 +165,10 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, $ $subCollection->addRequirements($requirements); $subCollection->addOptions($options); + if ($namePrefix = $node->getAttribute('name-prefix')) { + $subCollection->addNamePrefix($namePrefix); + } + $collection->addCollection($subCollection); } diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php index 037d581d05eab..b3ed099f8149e 100644 --- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php @@ -16,6 +16,7 @@ use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser as YamlParser; +use Symfony\Component\Yaml\Yaml; use Symfony\Component\Config\Loader\FileLoader; /** @@ -27,7 +28,7 @@ class YamlFileLoader extends FileLoader { private static $availableKeys = array( - 'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', + 'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', ); private $yamlParser; @@ -57,18 +58,10 @@ public function load($file, $type = null) $this->yamlParser = new YamlParser(); } - $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) { - $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message; - - return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false; - }); - try { - $parsedConfig = $this->yamlParser->parseFile($path); + $parsedConfig = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e); - } finally { - restore_error_handler(); } $collection = new RouteCollection(); @@ -177,6 +170,10 @@ protected function parseImport(RouteCollection $collection, array $config, $path $subCollection->addRequirements($requirements); $subCollection->addOptions($options); + if (isset($config['name_prefix'])) { + $subCollection->addNamePrefix($config['name_prefix']); + } + $collection->addCollection($subCollection); } diff --git a/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd b/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd index a97111aaa55e3..fd461154dfe44 100644 --- a/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd +++ b/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd @@ -50,6 +50,7 @@ + diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php b/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php deleted file mode 100644 index 6916297b8c174..0000000000000 --- a/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php +++ /dev/null @@ -1,159 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Routing\Matcher\Dumper; - -/** - * Collection of routes. - * - * @author Arnaud Le Blanc - * - * @internal - */ -class DumperCollection implements \IteratorAggregate -{ - /** - * @var DumperCollection|null - */ - private $parent; - - /** - * @var DumperCollection[]|DumperRoute[] - */ - private $children = array(); - - /** - * @var array - */ - private $attributes = array(); - - /** - * Returns the children routes and collections. - * - * @return self[]|DumperRoute[] - */ - public function all() - { - return $this->children; - } - - /** - * Adds a route or collection. - * - * @param DumperRoute|DumperCollection The route or collection - */ - public function add($child) - { - if ($child instanceof self) { - $child->setParent($this); - } - $this->children[] = $child; - } - - /** - * Sets children. - * - * @param array $children The children - */ - public function setAll(array $children) - { - foreach ($children as $child) { - if ($child instanceof self) { - $child->setParent($this); - } - } - $this->children = $children; - } - - /** - * Returns an iterator over the children. - * - * @return \Iterator|DumperCollection[]|DumperRoute[] The iterator - */ - public function getIterator() - { - return new \ArrayIterator($this->children); - } - - /** - * Returns the root of the collection. - * - * @return self The root collection - */ - public function getRoot() - { - return (null !== $this->parent) ? $this->parent->getRoot() : $this; - } - - /** - * Returns the parent collection. - * - * @return self|null The parent collection or null if the collection has no parent - */ - protected function getParent() - { - return $this->parent; - } - - /** - * Sets the parent collection. - */ - protected function setParent(DumperCollection $parent) - { - $this->parent = $parent; - } - - /** - * Returns true if the attribute is defined. - * - * @param string $name The attribute name - * - * @return bool true if the attribute is defined, false otherwise - */ - public function hasAttribute($name) - { - return array_key_exists($name, $this->attributes); - } - - /** - * Returns an attribute by name. - * - * @param string $name The attribute name - * @param mixed $default Default value is the attribute doesn't exist - * - * @return mixed The attribute value - */ - public function getAttribute($name, $default = null) - { - return $this->hasAttribute($name) ? $this->attributes[$name] : $default; - } - - /** - * Sets an attribute by name. - * - * @param string $name The attribute name - * @param mixed $value The attribute value - */ - public function setAttribute($name, $value) - { - $this->attributes[$name] = $value; - } - - /** - * Sets multiple attributes. - * - * @param array $attributes The attributes - */ - public function setAttributes($attributes) - { - $this->attributes = $attributes; - } -} diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php b/src/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php deleted file mode 100644 index c71989a3a1532..0000000000000 --- a/src/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Routing\Matcher\Dumper; - -use Symfony\Component\Routing\Route; - -/** - * Container for a Route. - * - * @author Arnaud Le Blanc - * - * @internal - */ -class DumperRoute -{ - private $name; - private $route; - - /** - * @param string $name The route name - * @param Route $route The route - */ - public function __construct($name, Route $route) - { - $this->name = $name; - $this->route = $route; - } - - /** - * Returns the route name. - * - * @return string The route name - */ - public function getName() - { - return $this->name; - } - - /** - * Returns the route. - * - * @return Route The route - */ - public function getRoute() - { - return $this->route; - } -} diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index 5d1839b2de02a..42e5ed80b0153 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -22,6 +22,7 @@ * @author Fabien Potencier * @author Tobias Schultze * @author Arnaud Le Blanc + * @author Nicolas Grekas */ class PhpMatcherDumper extends MatcherDumper { @@ -93,7 +94,21 @@ public function addExpressionLanguageProvider(ExpressionFunctionProviderInterfac */ private function generateMatchMethod($supportsRedirections) { - $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n"); + // Group hosts by same-suffix, re-order when possible + $matchHost = false; + $routes = new StaticPrefixCollection(); + foreach ($this->getRoutes()->all() as $name => $route) { + if ($host = $route->getHost()) { + $matchHost = true; + $host = '/'.str_replace('.', '/', rtrim(explode('}', strrev($host), 2)[0], '.')); + } + + $routes->addRoute($host ?: '/', array($name, $route)); + } + $routes = $matchHost ? $routes->populateCollection(new RouteCollection()) : $this->getRoutes(); + + $code = rtrim($this->compileRoutes($routes, $supportsRedirections, $matchHost), "\n"); + $fetchHost = $matchHost ? " \$host = strtolower(\$context->getHost());\n" : ''; return <<context; - \$request = \$this->request ?: \$this->createRequest(\$pathinfo); \$requestMethod = \$canonicalMethod = \$context->getMethod(); - +{$fetchHost} if ('HEAD' === \$requestMethod) { \$canonicalMethod = 'GET'; } $code - throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException(); + throw \$allow ? new MethodNotAllowedException(array_keys(\$allow)) : new ResourceNotFoundException(); } EOF; } @@ -124,34 +138,12 @@ public function match(\$rawPathinfo) * * @return string PHP code */ - private function compileRoutes(RouteCollection $routes, $supportsRedirections) + private function compileRoutes(RouteCollection $routes, $supportsRedirections, $matchHost) { - $fetchedHost = false; - $groups = $this->groupRoutesByHostRegex($routes); - $code = ''; - - foreach ($groups as $collection) { - if (null !== $regex = $collection->getAttribute('host_regex')) { - if (!$fetchedHost) { - $code .= " \$host = \$context->getHost();\n\n"; - $fetchedHost = true; - } - - $code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true)); - } - - $tree = $this->buildStaticPrefixCollection($collection); - $groupCode = $this->compileStaticPrefixRoutes($tree, $supportsRedirections); + list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes, $supportsRedirections); - if (null !== $regex) { - // apply extra indention at each line (except empty ones) - $groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode); - $code .= $groupCode; - $code .= " }\n\n"; - } else { - $code .= $groupCode; - } - } + $code = $this->compileStaticRoutes($staticRoutes, $supportsRedirections, $matchHost); + $code .= $this->compileDynamicRoutes($dynamicRoutes, $supportsRedirections, $matchHost); if ('' === $code) { $code .= " if ('/' === \$pathinfo) {\n"; @@ -162,55 +154,391 @@ private function compileRoutes(RouteCollection $routes, $supportsRedirections) return $code; } - private function buildStaticPrefixCollection(DumperCollection $collection) + /** + * Splits static routes from dynamic routes, so that they can be matched first, using a simple switch. + */ + private function groupStaticRoutes(RouteCollection $collection, bool $supportsRedirections): array { - $prefixCollection = new StaticPrefixCollection(); + $staticRoutes = $dynamicRegex = array(); + $dynamicRoutes = new RouteCollection(); + + foreach ($collection->all() as $name => $route) { + $compiledRoute = $route->compile(); + $hostRegex = $compiledRoute->getHostRegex(); + $regex = $compiledRoute->getRegex(); + if ($hasTrailingSlash = $supportsRedirections && $pos = strpos($regex, '/$')) { + $regex = substr_replace($regex, '/?$', $pos, 2); + } + if (!$compiledRoute->getPathVariables()) { + $host = !$compiledRoute->getHostVariables() ? $route->getHost() : ''; + $url = $route->getPath(); + if ($hasTrailingSlash) { + $url = rtrim($url, '/'); + } + foreach ($dynamicRegex as list($hostRx, $rx)) { + if (preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) { + $dynamicRegex[] = array($hostRegex, $regex); + $dynamicRoutes->add($name, $route); + continue 2; + } + } - foreach ($collection as $dumperRoute) { - $prefix = $dumperRoute->getRoute()->compile()->getStaticPrefix(); - $prefixCollection->addRoute($prefix, $dumperRoute); + $staticRoutes[$url][$name] = array($hasTrailingSlash, $route); + } else { + $dynamicRegex[] = array($hostRegex, $regex); + $dynamicRoutes->add($name, $route); + } } - $prefixCollection->optimizeGroups(); + return array($staticRoutes, $dynamicRoutes); + } - return $prefixCollection; + /** + * Compiles static routes in a switch statement. + * + * Condition-less paths are put in a static array in the switch's default, with generic matching logic. + * Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases. + * + * @throws \LogicException + */ + private function compileStaticRoutes(array $staticRoutes, bool $supportsRedirections, bool $matchHost): string + { + if (!$staticRoutes) { + return ''; + } + $code = $default = ''; + $checkTrailingSlash = false; + + foreach ($staticRoutes as $url => $routes) { + if (1 === count($routes)) { + foreach ($routes as $name => list($hasTrailingSlash, $route)) { + } + + if (!$route->getCondition()) { + if (!$supportsRedirections && $route->getSchemes()) { + throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); + } + $checkTrailingSlash = $checkTrailingSlash || $hasTrailingSlash; + $default .= sprintf( + "%s => array(%s, %s, %s, %s),\n", + self::export($url), + self::export(array('_route' => $name) + $route->getDefaults()), + self::export(!$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex() ?: null), + self::export(array_flip($route->getMethods()) ?: null), + self::export(array_flip($route->getSchemes()) ?: null).($hasTrailingSlash ? ', true' : '') + ); + continue; + } + } + + $code .= sprintf(" case %s:\n", self::export($url)); + foreach ($routes as $name => list($hasTrailingSlash, $route)) { + $code .= $this->compileRoute($route, $name, $supportsRedirections, $hasTrailingSlash); + } + $code .= " break;\n"; + } + + $matchedPathinfo = $supportsRedirections ? '$trimmedPathinfo' : '$pathinfo'; + + if ($default) { + $code .= <<indent($default, 4)} ); + + if (!isset(\$routes[{$matchedPathinfo}])) { + break; + } + list(\$ret, \$requiredHost, \$requiredMethods, \$requiredSchemes) = \$routes[{$matchedPathinfo}]; +{$this->compileSwitchDefault(false, $matchedPathinfo, $matchHost, $supportsRedirections, $checkTrailingSlash)} +EOF; + } + + return sprintf(" switch (%s) {\n%s }\n\n", $matchedPathinfo, $this->indent($code)); } /** - * Generates PHP code to match a tree of routes. + * Compiles a regular expression followed by a switch statement to match dynamic routes. * - * @param StaticPrefixCollection $collection A StaticPrefixCollection instance - * @param bool $supportsRedirections Whether redirections are supported by the base class - * @param string $ifOrElseIf either "if" or "elseif" to influence chaining + * The regular expression matches both the host and the pathinfo at the same time. For stellar performance, + * it is built as a tree of patterns, with re-ordering logic to group same-prefix routes together when possible. * - * @return string PHP code + * Patterns are named so that we know which one matched (https://pcre.org/current/doc/html/pcre2syntax.html#SEC23). + * This name is used to "switch" to the additional logic required to match the final route. + * + * Condition-less paths are put in a static array in the switch's default, with generic matching logic. + * Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases. + * + * Last but not least: + * - Because it is not possibe to mix unicode/non-unicode patterns in a single regexp, several of them can be generated. + * - The same regexp can be used several times when the logic in the switch rejects the match. When this happens, the + * matching-but-failing subpattern is blacklisted by replacing its name by "(*F)", which forces a failure-to-match. + * To ease this backlisting operation, the name of subpatterns is also the string offset where the replacement should occur. */ - private function compileStaticPrefixRoutes(StaticPrefixCollection $collection, $supportsRedirections, $ifOrElseIf = 'if') + private function compileDynamicRoutes(RouteCollection $collection, bool $supportsRedirections, bool $matchHost): string { + if (!$collection->all()) { + return ''; + } $code = ''; - $prefix = $collection->getPrefix(); + $state = (object) array( + 'switch' => '', + 'default' => '', + 'mark' => 0, + 'markTail' => 0, + 'supportsRedirections' => $supportsRedirections, + 'checkTrailingSlash' => false, + 'hostVars' => array(), + 'vars' => array(), + ); + $state->getVars = static function ($m) use ($state) { + if ('_route' === $m[1]) { + return '?:'; + } - if (!empty($prefix) && '/' !== $prefix) { - $code .= sprintf(" %s (0 === strpos(\$pathinfo, %s)) {\n", $ifOrElseIf, var_export($prefix, true)); + $state->vars[] = $m[1]; + + return ''; + }; + + $prev = null; + $perModifiers = array(); + foreach ($collection->all() as $name => $route) { + preg_match('#[a-zA-Z]*$#', $route->compile()->getRegex(), $rx); + if ($prev !== $rx[0] && $route->compile()->getPathVariables()) { + $routes = new RouteCollection(); + $perModifiers[] = array($rx[0], $routes); + $prev = $rx[0]; + } + $routes->add($name, $route); } - $ifOrElseIf = 'if'; + foreach ($perModifiers as list($modifiers, $routes)) { + $prev = false; + $perHost = array(); + foreach ($routes->all() as $name => $route) { + $regex = $route->compile()->getHostRegex(); + if ($prev !== $regex) { + $routes = new RouteCollection(); + $perHost[] = array($regex, $routes); + $prev = $regex; + } + $routes->add($name, $route); + } + $prev = false; + $code .= "\n {$state->mark} => '{^(?'"; + $state->mark += 4; + + foreach ($perHost as list($hostRegex, $routes)) { + if ($matchHost) { + if ($hostRegex) { + preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $hostRegex, $rx); + $state->vars = array(); + $hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')'; + $state->hostVars = $state->vars; + } else { + $hostRegex = '[^/]*+'; + $state->hostVars = array(); + } + $state->mark += 3 + $prev + strlen($hostRegex); + $code .= "\n .".self::export(($prev ? ')' : '')."|{$hostRegex}(?"); + $prev = true; + } + + $tree = new StaticPrefixCollection(); + foreach ($routes->all() as $name => $route) { + preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $route->compile()->getRegex(), $rx); + + $state->vars = array(); + $regex = preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]); + $tree->addRoute($regex, array($name, $regex, $state->vars, $route)); + } + + $code .= $this->compileStaticPrefixCollection($tree, $state); + } + if ($matchHost) { + $code .= "\n .')'"; + } + $code .= "\n .')$}{$modifiers}',"; + } + + if ($state->default) { + $state->switch .= <<indent($state->default, 4)} ); + + list(\$ret, \$vars, \$requiredMethods, \$requiredSchemes) = \$routes[\$m]; +{$this->compileSwitchDefault(true, '$m', $matchHost, $supportsRedirections, $state->checkTrailingSlash)} +EOF; + } + + $matchedPathinfo = $matchHost ? '$host.$pathinfo' : '$pathinfo'; + unset($state->getVars); + + return << \$regex) { + while (preg_match(\$regex, \$matchedPathinfo, \$matches)) { + switch (\$m = (int) \$matches['MARK']) { +{$this->indent($state->switch, 3)} } + + if ({$state->mark} === \$m) { + break; + } + \$regex = substr_replace(\$regex, 'F', \$m - \$offset, 1 + strlen(\$m)); + \$offset += strlen(\$m); + } + } + +EOF; + } + + /** + * Compiles a regexp tree of subpatterns that matches nested same-prefix routes. + * + * @param \stdClass $state A simple state object that keeps track of the progress of the compilation, + * and gathers the generated switch's "case" and "default" statements + */ + private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \stdClass $state, int $prefixLen = 0) + { + $code = ''; + $prevRegex = null; + $routes = $tree->getRoutes(); - foreach ($collection->getItems() as $route) { + foreach ($routes as $i => $route) { if ($route instanceof StaticPrefixCollection) { - $code .= $this->compileStaticPrefixRoutes($route, $supportsRedirections, $ifOrElseIf); - $ifOrElseIf = 'elseif'; + $prevRegex = null; + $prefix = substr($route->getPrefix(), $prefixLen); + $state->mark += 3 + strlen($prefix); + $code .= "\n .".self::export("|{$prefix}(?"); + $code .= $this->indent($this->compileStaticPrefixCollection($route, $state, $prefixLen + strlen($prefix))); + $code .= "\n .')'"; + $state->markTail += 1; + continue; + } + + list($name, $regex, $vars, $route) = $route; + $compiledRoute = $route->compile(); + $hasTrailingSlash = $state->supportsRedirections && '' !== $regex && '/' === $regex[-1]; + + if ($compiledRoute->getRegex() === $prevRegex) { + $state->switch = substr_replace($state->switch, $this->compileRoute($route, $name, $state->supportsRedirections, $hasTrailingSlash)."\n", -19, 0); + continue; + } + + $methods = array_flip($route->getMethods()); + $hasTrailingSlash = $hasTrailingSlash && (!$methods || isset($methods['GET'])); + $state->mark += 3 + $state->markTail + $hasTrailingSlash + strlen($regex) - $prefixLen; + $state->markTail = 2 + strlen($state->mark); + $code .= "\n ."; + $code .= self::export(sprintf('|%s(*:%s)', substr($regex, $prefixLen).($hasTrailingSlash ? '?' : ''), $state->mark)); + $vars = array_merge($state->hostVars, $vars); + + if (!$route->getCondition() && (!is_array($next = $routes[1 + $i] ?? null) || $regex !== $next[1])) { + $prevRegex = null; + $state->checkTrailingSlash = $state->checkTrailingSlash || $hasTrailingSlash; + $state->default .= sprintf( + "%s => array(%s, %s, %s, %s),\n", + $state->mark, + self::export(array('_route' => $name) + $route->getDefaults()), + self::export($vars), + self::export($methods ?: null), + self::export(array_flip($route->getSchemes()) ?: null).($hasTrailingSlash ? ', true' : '') + ); } else { - $code .= $this->compileRoute($route[1]->getRoute(), $route[1]->getName(), $supportsRedirections, $prefix)."\n"; - $ifOrElseIf = 'if'; + $prevRegex = $compiledRoute->getRegex(); + $combine = ' $matches = array('; + foreach ($vars as $j => $m) { + $combine .= sprintf('%s => $matches[%d] ?? null, ', self::export($m), 1 + $j); + } + $combine = $vars ? substr_replace($combine, ");\n\n", -2) : ''; + + $state->switch .= <<mark}: +{$combine}{$this->compileRoute($route, $name, $state->supportsRedirections, $hasTrailingSlash)} + break; + +EOF; } } - if (!empty($prefix) && '/' !== $prefix) { - $code .= " }\n\n"; - // apply extra indention at each line (except empty ones) - $code = preg_replace('/^.{2,}$/m', ' $0', $code); + return $code; + } + + /** + * A simple helper to compiles the switch's "default" for both static and dynamic routes. + */ + private function compileSwitchDefault(bool $hasVars, string $routesKey, bool $matchHost, bool $supportsRedirections, bool $checkTrailingSlash) + { + if ($hasVars) { + $code = << \$v) { + if (isset(\$matches[1 + \$i])) { + \$ret[\$v] = \$matches[1 + \$i]; + } + } + +EOF; + } elseif ($matchHost) { + $code = <<mergeDefaults(\$hostMatches, \$ret); + } + } + +EOF; + } else { + $code = ''; + } + if ($supportsRedirections && $checkTrailingSlash) { + $code .= <<redirect(\$rawPathinfo.'/', \$ret['_route'])); + } + +EOF; } + if ($supportsRedirections) { + $code .= <<getScheme()])) { + if ('GET' !== \$canonicalMethod) { + \$allow['GET'] = 'GET'; + break; + } + + return array_replace(\$ret, \$this->redirect(\$rawPathinfo, \$ret['_route'], key(\$requiredSchemes))); + } + +EOF; + } + $code .= <<compile(); $conditions = array(); - $hasTrailingSlash = false; - $matches = false; - $hostMatches = false; - $methods = $route->getMethods(); - - $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('GET', $methods)); - $regex = $compiledRoute->getRegex(); - - if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P.*?)\$\1#'.('u' === substr($regex, -1) ? 'u' : ''), $regex, $m)) { - if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) { - $conditions[] = sprintf('%s === $trimmedPathinfo', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); - $hasTrailingSlash = true; - } else { - $conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true)); - } - } else { - if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) { - $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true)); - } - - if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) { - $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2); - $hasTrailingSlash = true; - } - $conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true)); - - $matches = true; + $matches = (bool) $compiledRoute->getPathVariables(); + $hostMatches = (bool) $compiledRoute->getHostVariables(); + $methods = array_flip($route->getMethods()); + $supportsTrailingSlash = $supportsRedirections && (!$methods || isset($methods['GET'])); + + if ($hasTrailingSlash && !$supportsTrailingSlash) { + $hasTrailingSlash = false; + $conditions[] = "'/' === \$pathinfo[-1]"; } - if ($compiledRoute->getHostVariables()) { - $hostMatches = true; + if ($route->getCondition()) { + $expression = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request')); + + if (false !== strpos($expression, '$request')) { + $conditions[] = '($request = $request ?? $this->request ?: $this->createRequest($pathinfo))'; + } + $conditions[] = $expression; } - if ($route->getCondition()) { - $conditions[] = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request')); + if (!$compiledRoute->getHostRegex()) { + // no-op + } elseif ($hostMatches) { + $conditions[] = sprintf('preg_match(%s, $host, $hostMatches)', self::export($compiledRoute->getHostRegex())); + } else { + $conditions[] = sprintf('%s === $host', self::export($route->getHost())); } $conditions = implode(' && ', $conditions); - $code .= <<mergeDefaults(array_replace(%s), %s);\n", implode(', ', $vars), - str_replace("\n", '', var_export($route->getDefaults(), true)) + self::export($route->getDefaults()) ); } elseif ($route->getDefaults()) { - $code .= sprintf(" \$ret = %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true))); + $code .= sprintf(" \$ret = %s;\n", self::export(array_replace($route->getDefaults(), array('_route' => $name)))); } else { $code .= sprintf(" \$ret = array('_route' => '%s');\n", $name); } if ($hasTrailingSlash) { $code .= <<redirect(\$rawPathinfo.'/', '$name')); @@ -323,12 +645,12 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren if (!$supportsRedirections) { throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); } - $schemes = str_replace("\n", '', var_export(array_flip($schemes), true)); + $schemes = self::export(array_flip($schemes)); $code .= <<getScheme()])) { if ('GET' !== \$canonicalMethod) { - \$allow[] = 'GET'; + \$allow['GET'] = 'GET'; goto $gotoname; } @@ -340,56 +662,17 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren } if ($methods) { - if (1 === count($methods)) { - if ('HEAD' === $methods[0]) { - $code .= <<setAttribute('host_regex', null); - $groups->add($currentGroup); - - foreach ($routes as $name => $route) { - $hostRegex = $route->compile()->getHostRegex(); - if ($currentGroup->getAttribute('host_regex') !== $hostRegex) { - $currentGroup = new DumperCollection(); - $currentGroup->setAttribute('host_regex', $hostRegex); - $groups->add($currentGroup); - } - $currentGroup->add(new DumperRoute($name, $route)); - } - - return $groups; + return $conditions ? $this->indent($code) : $code; } private function getExpressionLanguage() @@ -446,4 +704,44 @@ private function getExpressionLanguage() return $this->expressionLanguage; } + + private function indent($code, $level = 1) + { + return preg_replace('/^./m', str_repeat(' ', $level).'$0', $code); + } + + /** + * @internal + */ + public static function export($value): string + { + if (null === $value) { + return 'null'; + } + if (!\is_array($value)) { + return str_replace("\n", '\'."\n".\'', var_export($value, true)); + } + if (!$value) { + return 'array()'; + } + + $i = 0; + $export = 'array('; + + foreach ($value as $k => $v) { + if ($i === $k) { + ++$i; + } else { + $export .= self::export($k).' => '; + + if (\is_int($k) && $i < $k) { + $i = 1 + $k; + } + } + + $export .= self::export($v).', '; + } + + return substr_replace($export, ')', -2); + } } diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php b/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php index 73658086189a0..dbc42caf52bde 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Routing\Matcher\Dumper; +use Symfony\Component\Routing\RouteCollection; + /** * Prefix tree of routes preserving routes order. * @@ -20,35 +22,35 @@ */ class StaticPrefixCollection { - /** - * @var string - */ private $prefix; + private $staticPrefix; + private $matchStart = 0; /** - * @var array[]|StaticPrefixCollection[] + * @var string[] */ - private $items = array(); + private $prefixes = array(); /** - * @var int + * @var array[]|self[] */ - private $matchStart = 0; + private $items = array(); - public function __construct($prefix = '') + public function __construct(string $prefix = '/', string $staticPrefix = '/') { $this->prefix = $prefix; + $this->staticPrefix = $staticPrefix; } - public function getPrefix() + public function getPrefix(): string { return $this->prefix; } /** - * @return mixed[]|StaticPrefixCollection[] + * @return array[]|self[] */ - public function getItems() + public function getRoutes(): array { return $this->items; } @@ -56,28 +58,26 @@ public function getItems() /** * Adds a route to a group. * - * @param string $prefix - * @param mixed $route + * @param array|self $route */ - public function addRoute($prefix, $route) + public function addRoute(string $prefix, $route) { - $prefix = '/' === $prefix ? $prefix : rtrim($prefix, '/'); $this->guardAgainstAddingNotAcceptedRoutes($prefix); + list($prefix, $staticPrefix) = $this->detectCommonPrefix($prefix, $prefix) ?: array(rtrim($prefix, '/') ?: '/', '/'); - if ($this->prefix === $prefix) { + if ($this->staticPrefix === $staticPrefix) { // When a prefix is exactly the same as the base we move up the match start position. // This is needed because otherwise routes that come afterwards have higher precedence // than a possible regular expression, which goes against the input order sorting. - $this->items[] = array($prefix, $route); + $this->prefixes[] = $prefix; + $this->items[] = $route; $this->matchStart = count($this->items); return; } - foreach ($this->items as $i => $item) { - if ($i < $this->matchStart) { - continue; - } + for ($i = $this->matchStart; $i < \count($this->items); ++$i) { + $item = $this->items[$i]; if ($item instanceof self && $item->accepts($prefix)) { $item->addRoute($prefix, $route); @@ -85,9 +85,8 @@ public function addRoute($prefix, $route) return; } - $group = $this->groupWithItem($item, $prefix, $route); - - if ($group instanceof self) { + if ($group = $this->groupWithItem($i, $prefix, $route)) { + $this->prefixes[$i] = $group->getPrefix(); $this->items[$i] = $group; return; @@ -96,33 +95,43 @@ public function addRoute($prefix, $route) // No optimised case was found, in this case we simple add the route for possible // grouping when new routes are added. - $this->items[] = array($prefix, $route); + $this->prefixes[] = $prefix; + $this->items[] = $route; } /** - * Tries to combine a route with another route or group. - * - * @param StaticPrefixCollection|array $item - * @param string $prefix - * @param mixed $route - * - * @return null|StaticPrefixCollection + * Linearizes back a set of nested routes into a collection. */ - private function groupWithItem($item, $prefix, $route) + public function populateCollection(RouteCollection $routes): RouteCollection { - $itemPrefix = $item instanceof self ? $item->prefix : $item[0]; - $commonPrefix = $this->detectCommonPrefix($prefix, $itemPrefix); + foreach ($this->items as $route) { + if ($route instanceof self) { + $route->populateCollection($routes); + } else { + $routes->add(...$route); + } + } - if (!$commonPrefix) { - return; + return $routes; + } + + /** + * Tries to combine a route with another route or group. + */ + private function groupWithItem(int $i, string $prefix, $route): ?self + { + if (!$commonPrefix = $this->detectCommonPrefix($prefix, $this->prefixes[$i])) { + return null; } - $child = new self($commonPrefix); + $child = new self(...$commonPrefix); + $item = $this->items[$i]; if ($item instanceof self) { + $child->prefixes = array($commonPrefix[0]); $child->items = array($item); } else { - $child->addRoute($item[0], $item[1]); + $child->addRoute($this->prefixes[$i], $item); } $child->addRoute($prefix, $route); @@ -132,12 +141,8 @@ private function groupWithItem($item, $prefix, $route) /** * Checks whether a prefix can be contained within the group. - * - * @param string $prefix - * - * @return bool Whether a prefix could belong in a given group */ - private function accepts($prefix) + private function accepts(string $prefix): bool { return '' === $this->prefix || 0 === strpos($prefix, $this->prefix); } @@ -145,89 +150,56 @@ private function accepts($prefix) /** * Detects whether there's a common prefix relative to the group prefix and returns it. * - * @param string $prefix - * @param string $anotherPrefix - * - * @return false|string A common prefix, longer than the base/group prefix, or false when none available + * @return null|array A common prefix, longer than the base/group prefix, or null when none available */ - private function detectCommonPrefix($prefix, $anotherPrefix) + private function detectCommonPrefix(string $prefix, string $anotherPrefix): ?array { $baseLength = strlen($this->prefix); - $commonLength = $baseLength; $end = min(strlen($prefix), strlen($anotherPrefix)); - - for ($i = $baseLength; $i <= $end; ++$i) { - if (substr($prefix, 0, $i) !== substr($anotherPrefix, 0, $i)) { + $staticLength = null; + + for ($i = $baseLength; $i < $end && $prefix[$i] === $anotherPrefix[$i]; ++$i) { + if ('(' === $prefix[$i]) { + $staticLength = $staticLength ?? $i; + for ($j = 1 + $i, $n = 1; $j < $end && 0 < $n; ++$j) { + if ($prefix[$j] !== $anotherPrefix[$j]) { + break 2; + } + if ('(' === $prefix[$j]) { + ++$n; + } elseif (')' === $prefix[$j]) { + --$n; + } elseif ('\\' === $prefix[$j] && (++$j === $end || $prefix[$j] !== $anotherPrefix[$j])) { + --$j; + break; + } + } + if (0 < $n) { + break; + } + $i = $j; + } elseif ('\\' === $prefix[$i] && (++$i === $end || $prefix[$i] !== $anotherPrefix[$i])) { + --$i; break; } - - $commonLength = $i; } - $commonPrefix = rtrim(substr($prefix, 0, $commonLength), '/'); + $staticLength = $staticLength ?? $i; + $commonPrefix = rtrim(substr($prefix, 0, $i), '/'); if (strlen($commonPrefix) > $baseLength) { - return $commonPrefix; - } - - return false; - } - - /** - * Optimizes the tree by inlining items from groups with less than 3 items. - */ - public function optimizeGroups() - { - $index = -1; - - while (isset($this->items[++$index])) { - $item = $this->items[$index]; - - if ($item instanceof self) { - $item->optimizeGroups(); - - // When a group contains only two items there's no reason to optimize because at minimum - // the amount of prefix check is 2. In this case inline the group. - if ($item->shouldBeInlined()) { - array_splice($this->items, $index, 1, $item->items); - - // Lower index to pass through the same index again after optimizing. - // The first item of the replacements might be a group needing optimization. - --$index; - } - } + return array($commonPrefix, rtrim(substr($prefix, 0, $staticLength), '/') ?: '/'); } - } - private function shouldBeInlined() - { - if (count($this->items) >= 3) { - return false; - } - - foreach ($this->items as $item) { - if ($item instanceof self) { - return true; - } - } - - foreach ($this->items as $item) { - if (is_array($item) && $item[0] === $this->prefix) { - return false; - } - } - - return true; + return null; } /** * Guards against adding incompatible prefixes in a group. * - * @param string $prefix - * * @throws \LogicException when a prefix does not belong in a group */ - private function guardAgainstAddingNotAcceptedRoutes($prefix) + private function guardAgainstAddingNotAcceptedRoutes(string $prefix): void { if (!$this->accepts($prefix)) { $message = sprintf('Could not add route with prefix %s to collection with prefix %s', $prefix, $this->prefix); diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index 3cba7e66f8606..4d56af1a87c91 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -213,7 +213,7 @@ protected function handleRouteRequirements($pathinfo, $name, Route $route) protected function mergeDefaults($params, $defaults) { foreach ($params as $key => $value) { - if (!is_int($key)) { + if (!\is_int($key) && null !== $value) { $defaults[$key] = $value; } } diff --git a/src/Symfony/Component/Routing/RequestContext.php b/src/Symfony/Component/Routing/RequestContext.php index d62a7766ef859..0a68fe08f6805 100644 --- a/src/Symfony/Component/Routing/RequestContext.php +++ b/src/Symfony/Component/Routing/RequestContext.php @@ -33,17 +33,7 @@ class RequestContext private $queryString; private $parameters = array(); - /** - * @param string $baseUrl The base URL - * @param string $method The HTTP method - * @param string $host The HTTP host name - * @param string $scheme The HTTP scheme - * @param int $httpPort The HTTP port - * @param int $httpsPort The HTTPS port - * @param string $path The path - * @param string $queryString The query string - */ - public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443, $path = '/', $queryString = '') + public function __construct(string $baseUrl = '', string $method = 'GET', string $host = 'localhost', string $scheme = 'http', int $httpPort = 80, int $httpsPort = 443, string $path = '/', string $queryString = '') { $this->setBaseUrl($baseUrl); $this->setMethod($method); diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php index cd50ac8276f60..29d352aebd17d 100644 --- a/src/Symfony/Component/Routing/Route.php +++ b/src/Symfony/Component/Routing/Route.php @@ -50,7 +50,7 @@ class Route implements \Serializable * @param string|string[] $methods A required HTTP method or an array of restricted methods * @param string $condition A condition that should evaluate to true for the route to match */ - public function __construct($path, array $defaults = array(), array $requirements = array(), array $options = array(), $host = '', $schemes = array(), $methods = array(), $condition = '') + public function __construct(string $path, array $defaults = array(), array $requirements = array(), array $options = array(), ?string $host = '', $schemes = array(), $methods = array(), ?string $condition = '') { $this->setPath($path); $this->setDefaults($defaults); diff --git a/src/Symfony/Component/Routing/RouteCollection.php b/src/Symfony/Component/Routing/RouteCollection.php index feabf234bc6d4..ebe92da714c39 100644 --- a/src/Symfony/Component/Routing/RouteCollection.php +++ b/src/Symfony/Component/Routing/RouteCollection.php @@ -153,6 +153,20 @@ public function addPrefix($prefix, array $defaults = array(), array $requirement } } + /** + * Adds a prefix to the name of all the routes within in the collection. + */ + public function addNamePrefix(string $prefix) + { + $prefixedRoutes = array(); + + foreach ($this->routes as $name => $route) { + $prefixedRoutes[$prefix.$name] = $route; + } + + $this->routes = $prefixedRoutes; + } + /** * Sets the host pattern on all routes. * diff --git a/src/Symfony/Component/Routing/RouteCollectionBuilder.php b/src/Symfony/Component/Routing/RouteCollectionBuilder.php index e8a9a165d6734..d63c6138f7983 100644 --- a/src/Symfony/Component/Routing/RouteCollectionBuilder.php +++ b/src/Symfony/Component/Routing/RouteCollectionBuilder.php @@ -58,7 +58,7 @@ public function __construct(LoaderInterface $loader = null) */ public function import($resource, $prefix = '/', $type = null) { - /** @var RouteCollection[] $collection */ + /** @var RouteCollection[] $collections */ $collections = $this->load($resource, $type); // create a builder from the RouteCollection @@ -255,7 +255,7 @@ public function setMethods($methods) * * @return $this */ - private function addResource(ResourceInterface $resource) + private function addResource(ResourceInterface $resource): RouteCollectionBuilder { $this->resources[] = $resource; @@ -326,10 +326,8 @@ public function build() /** * Generates a route name based on details of this route. - * - * @return string */ - private function generateRouteName(Route $route) + private function generateRouteName(Route $route): string { $methods = implode('_', $route->getMethods()).'_'; @@ -353,7 +351,7 @@ private function generateRouteName(Route $route) * * @throws FileLoaderLoadException If no loader is found */ - private function load($resource, $type = null) + private function load($resource, string $type = null): array { if (null === $this->loader) { throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.'); diff --git a/src/Symfony/Component/Routing/RouteCompiler.php b/src/Symfony/Component/Routing/RouteCompiler.php index dc4e4f8077aba..42ddde0077b1a 100644 --- a/src/Symfony/Component/Routing/RouteCompiler.php +++ b/src/Symfony/Component/Routing/RouteCompiler.php @@ -103,8 +103,7 @@ private static function compilePattern(Route $route, $pattern, $isHost) $needsUtf8 = $route->getOption('utf8'); if (!$needsUtf8 && $useUtf8 && preg_match('/[\x80-\xFF]/', $pattern)) { - $needsUtf8 = true; - @trigger_error(sprintf('Using UTF-8 route patterns without setting the "utf8" option is deprecated since Symfony 3.2 and will throw a LogicException in 4.0. Turn on the "utf8" route option for pattern "%s".', $pattern), E_USER_DEPRECATED); + throw new \LogicException(sprintf('Cannot use UTF-8 route patterns without setting the "utf8" option for route "%s".', $route->getPath())); } if (!$useUtf8 && $needsUtf8) { throw new \LogicException(sprintf('Cannot mix UTF-8 requirements with non-UTF-8 pattern "%s".', $pattern)); @@ -176,8 +175,7 @@ private static function compilePattern(Route $route, $pattern, $isHost) if (!preg_match('//u', $regexp)) { $useUtf8 = false; } elseif (!$needsUtf8 && preg_match('/[\x80-\xFF]|(?hasDefault($tokens[0][3]) || '/' === $tokens[0][1]) ? '' : $tokens[0][1]; @@ -251,14 +247,9 @@ private static function determineStaticPrefix(Route $route, array $tokens) } /** - * Returns the next static character in the Route pattern that will serve as a separator. - * - * @param string $pattern The route pattern - * @param bool $useUtf8 Whether the character is encoded in UTF-8 or not - * - * @return string The next static character that functions as separator (or empty string when none available) + * Returns the next static character in the Route pattern that will serve as a separator (or the empty string when none available) */ - private static function findNextSeparator($pattern, $useUtf8) + private static function findNextSeparator(string $pattern, bool $useUtf8): string { if ('' == $pattern) { // return empty string if pattern is empty or false (false which can be returned by substr) @@ -284,7 +275,7 @@ private static function findNextSeparator($pattern, $useUtf8) * * @return string The regexp pattern for a single token */ - private static function computeRegexp(array $tokens, $index, $firstOptional) + private static function computeRegexp(array $tokens, int $index, int $firstOptional): string { $token = $tokens[$index]; if ('text' === $token[0]) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php index 59253f0749da7..8f32dd2770dbe 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php @@ -21,7 +21,6 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { @@ -32,6 +31,6 @@ public function match($rawPathinfo) throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php index 29f1f5096b599..d3d2826f5fb2b 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php @@ -21,294 +21,209 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); + $host = strtolower($context->getHost()); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/foo')) { - // foo - if (preg_match('#^/foo/(?Pbaz|symfony)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',)); - } - - // foofoo - if ('/foofoo' === $pathinfo) { - return array ( 'def' => 'test', '_route' => 'foofoo',); - } - - } - - elseif (0 === strpos($pathinfo, '/bar')) { - // bar - if (preg_match('#^/bar/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ()); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_bar; + switch ($pathinfo) { + default: + $routes = array( + '/test/baz' => array(array('_route' => 'baz'), null, null, null), + '/test/baz.html' => array(array('_route' => 'baz2'), null, null, null), + '/test/baz3/' => array(array('_route' => 'baz3'), null, null, null), + '/foofoo' => array(array('_route' => 'foofoo', 'def' => 'test'), null, null, null), + '/spa ce' => array(array('_route' => 'space'), null, null, null), + '/multi/new' => array(array('_route' => 'overridden2'), null, null, null), + '/multi/hey/' => array(array('_route' => 'hey'), null, null, null), + '/ababa' => array(array('_route' => 'ababa'), null, null, null), + '/route1' => array(array('_route' => 'route1'), 'a.example.com', null, null), + '/c2/route2' => array(array('_route' => 'route2'), 'a.example.com', null, null), + '/route4' => array(array('_route' => 'route4'), 'a.example.com', null, null), + '/c2/route3' => array(array('_route' => 'route3'), 'b.example.com', null, null), + '/route5' => array(array('_route' => 'route5'), 'c.example.com', null, null), + '/route6' => array(array('_route' => 'route6'), null, null, null), + '/route11' => array(array('_route' => 'route11'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route12' => array(array('_route' => 'route12', 'var1' => 'val'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route17' => array(array('_route' => 'route17'), null, null, null), + ); + + if (!isset($routes[$pathinfo])) { + break; } - - return $ret; - } - not_bar: - - // barhead - if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ()); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_barhead; + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + if ($requiredHost) { + if ('#' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) { + break; + } + if ('#' === $requiredHost[0] && $hostMatches) { + $hostMatches['_route'] = $ret['_route']; + $ret = $this->mergeDefaults($hostMatches, $ret); + } } - return $ret; - } - not_barhead: - - } - - elseif (0 === strpos($pathinfo, '/test')) { - if (0 === strpos($pathinfo, '/test/baz')) { - // baz - if ('/test/baz' === $pathinfo) { - return array('_route' => 'baz'); - } - - // baz2 - if ('/test/baz.html' === $pathinfo) { - return array('_route' => 'baz2'); - } - - // baz3 - if ('/test/baz3/' === $pathinfo) { - return array('_route' => 'baz3'); - } - - } - - // baz4 - if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ()); - } - - // baz5 - if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ()); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_baz5; + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; } return $ret; - } - not_baz5: - - // baz.baz6 - if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ()); - if ('PUT' !== $canonicalMethod) { - $allow[] = 'PUT'; - goto not_bazbaz6; - } - - return $ret; - } - not_bazbaz6: - - } - - // quoter - if (preg_match('#^/(?P[\']+)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ()); - } - - // space - if ('/spa ce' === $pathinfo) { - return array('_route' => 'space'); } - if (0 === strpos($pathinfo, '/a')) { - if (0 === strpos($pathinfo, '/a/b\'b')) { - // foo1 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ()); - } - - // bar1 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ()); - } - - } - - // overridden - if (preg_match('#^/a/(?P.*)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ()); - } - - if (0 === strpos($pathinfo, '/a/b\'b')) { - // foo2 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ()); + $matchedPathinfo = $host.$pathinfo; + $regexList = array( + 0 => '{^(?' + .'|[^/]*+(?' + .'|/foo/(baz|symfony)(*:34)' + .'|/bar(?' + .'|/([^/]++)(*:57)' + .'|head/([^/]++)(*:77)' + .')' + .'|/test/([^/]++)(?' + .'|/(*:103)' + .')' + .'|/([\']+)(*:119)' + .'|/a(?' + .'|/b\'b/([^/]++)(?' + .'|(*:148)' + .'|(*:156)' + .')' + .'|/(.*)(*:170)' + .'|/b\'b/([^/]++)(?' + .'|(*:194)' + .'|(*:202)' + .')' + .')' + .'|/multi/hello(?:/([^/]++))?(*:238)' + .'|/([^/]++)/b/([^/]++)(*:266)' + .'|/([^/]++)/b/([^/]++)(*:294)' + .'|/aba/([^/]++)(*:315)' + .')|(?i:([^\\.]++)\\.example\\.com)(?' + .'|/route1(?' + .'|3/([^/]++)(*:375)' + .'|4/([^/]++)(*:393)' + .')' + .')|(?i:c\\.example\\.com)(?' + .'|/route15/([^/]++)(*:443)' + .')|[^/]*+(?' + .'|/route16/([^/]++)(*:478)' + .'|/a(?' + .'|/a\\.\\.\\.(*:499)' + .'|/b(?' + .'|/([^/]++)(*:521)' + .'|/c/([^/]++)(*:540)' + .')' + .')' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + case 103: + $matches = array('foo' => $matches[1] ?? null); + + // baz4 + return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array()); + + // baz5 + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array()); + if (!isset(($a = array('POST' => 0))[$requestMethod])) { + $allow += $a; + goto not_baz5; + } + + return $ret; + not_baz5: + + // baz.baz6 + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_bazbaz6; + } + + return $ret; + not_bazbaz6: + + break; + case 148: + $matches = array('foo' => $matches[1] ?? null); + + // foo1 + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_foo1; + } + + return $ret; + not_foo1: + + break; + case 194: + $matches = array('foo1' => $matches[1] ?? null); + + // foo2 + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array()); + + break; + case 266: + $matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null); + + // foo3 + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array()); + + break; + default: + $routes = array( + 34 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), + 57 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), + 77 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), + 119 => array(array('_route' => 'quoter'), array('quoter'), null, null), + 156 => array(array('_route' => 'bar1'), array('bar'), null, null), + 170 => array(array('_route' => 'overridden'), array('var'), null, null), + 202 => array(array('_route' => 'bar2'), array('bar1'), null, null), + 238 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), + 294 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), + 315 => array(array('_route' => 'foo4'), array('foo'), null, null), + 375 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), + 393 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), + 443 => array(array('_route' => 'route15'), array('name'), null, null), + 478 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), + 499 => array(array('_route' => 'a'), array(), null, null), + 521 => array(array('_route' => 'b'), array('var'), null, null), + 540 => array(array('_route' => 'c'), array('var'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + + return $ret; } - // bar2 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ()); + if (540 === $m) { + break; } - - } - - } - - elseif (0 === strpos($pathinfo, '/multi')) { - // helloWorld - if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P[^/]++))?$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',)); - } - - // hey - if ('/multi/hey/' === $pathinfo) { - return array('_route' => 'hey'); - } - - // overridden2 - if ('/multi/new' === $pathinfo) { - return array('_route' => 'overridden2'); + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); } - - } - - // foo3 - if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ()); - } - - // bar3 - if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ()); - } - - if (0 === strpos($pathinfo, '/aba')) { - // ababa - if ('/ababa' === $pathinfo) { - return array('_route' => 'ababa'); - } - - // foo4 - if (preg_match('#^/aba/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ()); - } - - } - - $host = $context->getHost(); - - if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { - // route1 - if ('/route1' === $pathinfo) { - return array('_route' => 'route1'); - } - - // route2 - if ('/c2/route2' === $pathinfo) { - return array('_route' => 'route2'); - } - - } - - if (preg_match('#^b\\.example\\.com$#sDi', $host, $hostMatches)) { - // route3 - if ('/c2/route3' === $pathinfo) { - return array('_route' => 'route3'); - } - - } - - if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { - // route4 - if ('/route4' === $pathinfo) { - return array('_route' => 'route4'); - } - - } - - if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { - // route5 - if ('/route5' === $pathinfo) { - return array('_route' => 'route5'); - } - - } - - // route6 - if ('/route6' === $pathinfo) { - return array('_route' => 'route6'); - } - - if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#sDi', $host, $hostMatches)) { - if (0 === strpos($pathinfo, '/route1')) { - // route11 - if ('/route11' === $pathinfo) { - return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ()); - } - - // route12 - if ('/route12' === $pathinfo) { - return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',)); - } - - // route13 - if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ()); - } - - // route14 - if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',)); - } - - } - - } - - if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { - // route15 - if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ()); - } - - } - - // route16 - if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',)); - } - - // route17 - if ('/route17' === $pathinfo) { - return array('_route' => 'route17'); - } - - // a - if ('/a/a...' === $pathinfo) { - return array('_route' => 'a'); - } - - if (0 === strpos($pathinfo, '/a/b')) { - // b - if (preg_match('#^/a/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ()); - } - - // c - if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ()); - } - } - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php index 7f27184d8a2a4..e15535a78e849 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -21,361 +21,253 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); + $host = strtolower($context->getHost()); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/foo')) { - // foo - if (preg_match('#^/foo/(?Pbaz|symfony)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',)); - } - - // foofoo - if ('/foofoo' === $pathinfo) { - return array ( 'def' => 'test', '_route' => 'foofoo',); - } - - } - - elseif (0 === strpos($pathinfo, '/bar')) { - // bar - if (preg_match('#^/bar/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ()); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_bar; - } - - return $ret; - } - not_bar: - - // barhead - if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ()); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_barhead; - } - - return $ret; - } - not_barhead: - - } - - elseif (0 === strpos($pathinfo, '/test')) { - if (0 === strpos($pathinfo, '/test/baz')) { - // baz - if ('/test/baz' === $pathinfo) { - return array('_route' => 'baz'); - } - - // baz2 - if ('/test/baz.html' === $pathinfo) { - return array('_route' => 'baz2'); + switch ($trimmedPathinfo) { + default: + $routes = array( + '/test/baz' => array(array('_route' => 'baz'), null, null, null), + '/test/baz.html' => array(array('_route' => 'baz2'), null, null, null), + '/test/baz3' => array(array('_route' => 'baz3'), null, null, null, true), + '/foofoo' => array(array('_route' => 'foofoo', 'def' => 'test'), null, null, null), + '/spa ce' => array(array('_route' => 'space'), null, null, null), + '/multi/new' => array(array('_route' => 'overridden2'), null, null, null), + '/multi/hey' => array(array('_route' => 'hey'), null, null, null, true), + '/ababa' => array(array('_route' => 'ababa'), null, null, null), + '/route1' => array(array('_route' => 'route1'), 'a.example.com', null, null), + '/c2/route2' => array(array('_route' => 'route2'), 'a.example.com', null, null), + '/route4' => array(array('_route' => 'route4'), 'a.example.com', null, null), + '/c2/route3' => array(array('_route' => 'route3'), 'b.example.com', null, null), + '/route5' => array(array('_route' => 'route5'), 'c.example.com', null, null), + '/route6' => array(array('_route' => 'route6'), null, null, null), + '/route11' => array(array('_route' => 'route11'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route12' => array(array('_route' => 'route12', 'var1' => 'val'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route17' => array(array('_route' => 'route17'), null, null, null), + '/secure' => array(array('_route' => 'secure'), null, null, array('https' => 0)), + '/nonsecure' => array(array('_route' => 'nonsecure'), null, null, array('http' => 0)), + ); + + if (!isset($routes[$trimmedPathinfo])) { + break; } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$trimmedPathinfo]; - // baz3 - if ('/test/baz3' === $trimmedPathinfo) { - $ret = array('_route' => 'baz3'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_baz3; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz3')); + if ($requiredHost) { + if ('#' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) { + break; + } + if ('#' === $requiredHost[0] && $hostMatches) { + $hostMatches['_route'] = $ret['_route']; + $ret = $this->mergeDefaults($hostMatches, $ret); } - - return $ret; } - not_baz3: - - } - // baz4 - if (preg_match('#^/test/(?P[^/]++)/?$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ()); - if ('/' === substr($pathinfo, -1)) { + if (empty($routes[$trimmedPathinfo][4]) || '/' === $pathinfo[-1]) { // no-op } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_baz4; + $allow['GET'] = 'GET'; + break; } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz4')); - } - - return $ret; - } - not_baz4: - - // baz5 - if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ()); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_baz5; - } - - return $ret; - } - not_baz5: - - // baz.baz6 - if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ()); - if ('PUT' !== $canonicalMethod) { - $allow[] = 'PUT'; - goto not_bazbaz6; - } - - return $ret; - } - not_bazbaz6: - - } - - // quoter - if (preg_match('#^/(?P[\']+)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ()); - } - - // space - if ('/spa ce' === $pathinfo) { - return array('_route' => 'space'); - } - - if (0 === strpos($pathinfo, '/a')) { - if (0 === strpos($pathinfo, '/a/b\'b')) { - // foo1 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ()); - } - - // bar1 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ()); + return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route'])); } - } - - // overridden - if (preg_match('#^/a/(?P.*)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ()); - } - - if (0 === strpos($pathinfo, '/a/b\'b')) { - // foo2 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ()); - } + if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) { + if ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + break; + } - // bar2 - if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ()); + return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes))); } - } - - } - - elseif (0 === strpos($pathinfo, '/multi')) { - // helloWorld - if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P[^/]++))?$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',)); - } - - // hey - if ('/multi/hey' === $trimmedPathinfo) { - $ret = array('_route' => 'hey'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_hey; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'hey')); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; } return $ret; - } - not_hey: - - // overridden2 - if ('/multi/new' === $pathinfo) { - return array('_route' => 'overridden2'); - } - } - // foo3 - if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ()); - } - - // bar3 - if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ()); - } - - if (0 === strpos($pathinfo, '/aba')) { - // ababa - if ('/ababa' === $pathinfo) { - return array('_route' => 'ababa'); - } - - // foo4 - if (preg_match('#^/aba/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ()); - } - - } - - $host = $context->getHost(); - - if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { - // route1 - if ('/route1' === $pathinfo) { - return array('_route' => 'route1'); - } - - // route2 - if ('/c2/route2' === $pathinfo) { - return array('_route' => 'route2'); - } - - } - - if (preg_match('#^b\\.example\\.com$#sDi', $host, $hostMatches)) { - // route3 - if ('/c2/route3' === $pathinfo) { - return array('_route' => 'route3'); - } - - } - - if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { - // route4 - if ('/route4' === $pathinfo) { - return array('_route' => 'route4'); - } - - } - - if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { - // route5 - if ('/route5' === $pathinfo) { - return array('_route' => 'route5'); - } - - } - - // route6 - if ('/route6' === $pathinfo) { - return array('_route' => 'route6'); - } - - if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#sDi', $host, $hostMatches)) { - if (0 === strpos($pathinfo, '/route1')) { - // route11 - if ('/route11' === $pathinfo) { - return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ()); - } - - // route12 - if ('/route12' === $pathinfo) { - return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',)); + $matchedPathinfo = $host.$pathinfo; + $regexList = array( + 0 => '{^(?' + .'|[^/]*+(?' + .'|/foo/(baz|symfony)(*:34)' + .'|/bar(?' + .'|/([^/]++)(*:57)' + .'|head/([^/]++)(*:77)' + .')' + .'|/test/([^/]++)(?' + .'|/?(*:104)' + .')' + .'|/([\']+)(*:120)' + .'|/a(?' + .'|/b\'b/([^/]++)(?' + .'|(*:149)' + .'|(*:157)' + .')' + .'|/(.*)(*:171)' + .'|/b\'b/([^/]++)(?' + .'|(*:195)' + .'|(*:203)' + .')' + .')' + .'|/multi/hello(?:/([^/]++))?(*:239)' + .'|/([^/]++)/b/([^/]++)(*:267)' + .'|/([^/]++)/b/([^/]++)(*:295)' + .'|/aba/([^/]++)(*:316)' + .')|(?i:([^\\.]++)\\.example\\.com)(?' + .'|/route1(?' + .'|3/([^/]++)(*:376)' + .'|4/([^/]++)(*:394)' + .')' + .')|(?i:c\\.example\\.com)(?' + .'|/route15/([^/]++)(*:444)' + .')|[^/]*+(?' + .'|/route16/([^/]++)(*:479)' + .'|/a(?' + .'|/a\\.\\.\\.(*:500)' + .'|/b(?' + .'|/([^/]++)(*:522)' + .'|/c/([^/]++)(*:541)' + .')' + .')' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + case 104: + $matches = array('foo' => $matches[1] ?? null); + + // baz4 + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array()); + if ('/' === $pathinfo[-1]) { + // no-op + } elseif ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + goto not_baz4; + } else { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz4')); + } + + return $ret; + not_baz4: + + // baz5 + if ('/' === $pathinfo[-1]) { + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array()); + if (!isset(($a = array('POST' => 0))[$requestMethod])) { + $allow += $a; + goto not_baz5; + } + + return $ret; + } + not_baz5: + + // baz.baz6 + if ('/' === $pathinfo[-1]) { + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_bazbaz6; + } + + return $ret; + } + not_bazbaz6: + + break; + case 149: + $matches = array('foo' => $matches[1] ?? null); + + // foo1 + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_foo1; + } + + return $ret; + not_foo1: + + break; + case 195: + $matches = array('foo1' => $matches[1] ?? null); + + // foo2 + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array()); + + break; + case 267: + $matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null); + + // foo3 + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array()); + + break; + default: + $routes = array( + 34 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), + 57 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), + 77 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), + 120 => array(array('_route' => 'quoter'), array('quoter'), null, null), + 157 => array(array('_route' => 'bar1'), array('bar'), null, null), + 171 => array(array('_route' => 'overridden'), array('var'), null, null), + 203 => array(array('_route' => 'bar2'), array('bar1'), null, null), + 239 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), + 295 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), + 316 => array(array('_route' => 'foo4'), array('foo'), null, null), + 376 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), + 394 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), + 444 => array(array('_route' => 'route15'), array('name'), null, null), + 479 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), + 500 => array(array('_route' => 'a'), array(), null, null), + 522 => array(array('_route' => 'b'), array('var'), null, null), + 541 => array(array('_route' => 'c'), array('var'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) { + if ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + break; + } + + return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes))); + } + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + + return $ret; } - // route13 - if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ()); + if (541 === $m) { + break; } - - // route14 - if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',)); - } - - } - - } - - if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { - // route15 - if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ()); + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); } - - } - - // route16 - if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',)); - } - - // route17 - if ('/route17' === $pathinfo) { - return array('_route' => 'route17'); - } - - // a - if ('/a/a...' === $pathinfo) { - return array('_route' => 'a'); - } - - if (0 === strpos($pathinfo, '/a/b')) { - // b - if (preg_match('#^/a/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ()); - } - - // c - if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ()); - } - - } - - // secure - if ('/secure' === $pathinfo) { - $ret = array('_route' => 'secure'); - $requiredSchemes = array ( 'https' => 0,); - if (!isset($requiredSchemes[$context->getScheme()])) { - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_secure; - } - - return array_replace($ret, $this->redirect($rawPathinfo, 'secure', key($requiredSchemes))); - } - - return $ret; - } - not_secure: - - // nonsecure - if ('/nonsecure' === $pathinfo) { - $ret = array('_route' => 'nonsecure'); - $requiredSchemes = array ( 'http' => 0,); - if (!isset($requiredSchemes[$context->getScheme()])) { - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_nonsecure; - } - - return array_replace($ret, $this->redirect($rawPathinfo, 'nonsecure', key($requiredSchemes))); - } - - return $ret; } - not_nonsecure: - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php index cfa6d131a057a..e49293f03c8ec 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php @@ -21,31 +21,76 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/rootprefix')) { - // static - if ('/rootprefix/test' === $pathinfo) { - return array('_route' => 'static'); - } + switch ($pathinfo) { + case '/with-condition': + // with-condition + if (($context->getMethod() == "GET")) { + return array('_route' => 'with-condition'); + } + break; + default: + $routes = array( + '/rootprefix/test' => array(array('_route' => 'static'), null, null, null), + ); - // dynamic - if (preg_match('#^/rootprefix/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'dynamic')), array ()); - } + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + return $ret; } - // with-condition - if ('/with-condition' === $pathinfo && ($context->getMethod() == "GET")) { - return array('_route' => 'with-condition'); + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/rootprefix/([^/]++)(*:27)' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 27 => array(array('_route' => 'dynamic'), array('var'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + + return $ret; + } + + if (27 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } } - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php index db9741ccabc0c..17a6a7e421a32 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php @@ -21,88 +21,54 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } - // just_head - if ('/just_head' === $pathinfo) { - $ret = array('_route' => 'just_head'); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_just_head; - } - - return $ret; - } - not_just_head: - - // head_and_get - if ('/head_and_get' === $pathinfo) { - $ret = array('_route' => 'head_and_get'); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_head_and_get; - } - - return $ret; - } - not_head_and_get: - - // get_and_head - if ('/get_and_head' === $pathinfo) { - $ret = array('_route' => 'get_and_head'); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_get_and_head; - } - - return $ret; - } - not_get_and_head: - - // post_and_head - if ('/post_and_head' === $pathinfo) { - $ret = array('_route' => 'post_and_head'); - if (!in_array($requestMethod, array('POST', 'HEAD'))) { - $allow = array_merge($allow, array('POST', 'HEAD')); - goto not_post_and_head; - } - - return $ret; - } - not_post_and_head: - - if (0 === strpos($pathinfo, '/put_and_post')) { - // put_and_post - if ('/put_and_post' === $pathinfo) { + switch ($pathinfo) { + case '/put_and_post': + // put_and_post $ret = array('_route' => 'put_and_post'); - if (!in_array($requestMethod, array('PUT', 'POST'))) { - $allow = array_merge($allow, array('PUT', 'POST')); + if (!isset(($a = array('PUT' => 0, 'POST' => 1))[$requestMethod])) { + $allow += $a; goto not_put_and_post; } return $ret; - } - not_put_and_post: - - // put_and_get_and_head - if ('/put_and_post' === $pathinfo) { + not_put_and_post: + // put_and_get_and_head $ret = array('_route' => 'put_and_get_and_head'); - if (!in_array($canonicalMethod, array('PUT', 'GET'))) { - $allow = array_merge($allow, array('PUT', 'GET')); + if (!isset(($a = array('PUT' => 0, 'GET' => 1, 'HEAD' => 2))[$canonicalMethod])) { + $allow += $a; goto not_put_and_get_and_head; } return $ret; - } - not_put_and_get_and_head: + not_put_and_get_and_head: + break; + default: + $routes = array( + '/just_head' => array(array('_route' => 'just_head'), null, array('HEAD' => 0), null), + '/head_and_get' => array(array('_route' => 'head_and_get'), null, array('HEAD' => 0, 'GET' => 1), null), + '/get_and_head' => array(array('_route' => 'get_and_head'), null, array('GET' => 0, 'HEAD' => 1), null), + '/post_and_head' => array(array('_route' => 'post_and_head'), null, array('POST' => 0, 'HEAD' => 1), null), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + return $ret; } - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php index 3edda1b03445c..97aba359841e2 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php @@ -21,194 +21,110 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/a')) { - // a_first - if ('/a/11' === $pathinfo) { - return array('_route' => 'a_first'); - } - - // a_second - if ('/a/22' === $pathinfo) { - return array('_route' => 'a_second'); - } - - // a_third - if ('/a/333' === $pathinfo) { - return array('_route' => 'a_third'); - } - - } - - // a_wildcard - if (preg_match('#^/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'a_wildcard')), array ()); - } - - if (0 === strpos($pathinfo, '/a')) { - // a_fourth - if ('/a/44' === $trimmedPathinfo) { - $ret = array('_route' => 'a_fourth'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_a_fourth; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fourth')); + switch ($trimmedPathinfo) { + default: + $routes = array( + '/a/11' => array(array('_route' => 'a_first'), null, null, null), + '/a/22' => array(array('_route' => 'a_second'), null, null, null), + '/a/333' => array(array('_route' => 'a_third'), null, null, null), + '/a/44' => array(array('_route' => 'a_fourth'), null, null, null, true), + '/a/55' => array(array('_route' => 'a_fifth'), null, null, null, true), + '/a/66' => array(array('_route' => 'a_sixth'), null, null, null, true), + '/nested/group/a' => array(array('_route' => 'nested_a'), null, null, null, true), + '/nested/group/b' => array(array('_route' => 'nested_b'), null, null, null, true), + '/nested/group/c' => array(array('_route' => 'nested_c'), null, null, null, true), + '/slashed/group' => array(array('_route' => 'slashed_a'), null, null, null, true), + '/slashed/group/b' => array(array('_route' => 'slashed_b'), null, null, null, true), + '/slashed/group/c' => array(array('_route' => 'slashed_c'), null, null, null, true), + ); + + if (!isset($routes[$trimmedPathinfo])) { + break; } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$trimmedPathinfo]; - return $ret; - } - not_a_fourth: - - // a_fifth - if ('/a/55' === $trimmedPathinfo) { - $ret = array('_route' => 'a_fifth'); - if ('/' === substr($pathinfo, -1)) { + if (empty($routes[$trimmedPathinfo][4]) || '/' === $pathinfo[-1]) { // no-op } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_a_fifth; + $allow['GET'] = 'GET'; + break; } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fifth')); + return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route'])); } - return $ret; - } - not_a_fifth: + if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) { + if ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + break; + } - // a_sixth - if ('/a/66' === $trimmedPathinfo) { - $ret = array('_route' => 'a_sixth'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_a_sixth; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_sixth')); + return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes))); } - return $ret; - } - not_a_sixth: - - } - - // nested_wildcard - if (0 === strpos($pathinfo, '/nested') && preg_match('#^/nested/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'nested_wildcard')), array ()); - } - - if (0 === strpos($pathinfo, '/nested/group')) { - // nested_a - if ('/nested/group/a' === $trimmedPathinfo) { - $ret = array('_route' => 'nested_a'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_nested_a; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_a')); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; } return $ret; - } - not_nested_a: - - // nested_b - if ('/nested/group/b' === $trimmedPathinfo) { - $ret = array('_route' => 'nested_b'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_nested_b; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_b')); - } - - return $ret; - } - not_nested_b: - - // nested_c - if ('/nested/group/c' === $trimmedPathinfo) { - $ret = array('_route' => 'nested_c'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_nested_c; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_c')); - } - - return $ret; - } - not_nested_c: - } - elseif (0 === strpos($pathinfo, '/slashed/group')) { - // slashed_a - if ('/slashed/group' === $trimmedPathinfo) { - $ret = array('_route' => 'slashed_a'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_slashed_a; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_a')); - } - - return $ret; - } - not_slashed_a: - - // slashed_b - if ('/slashed/group/b' === $trimmedPathinfo) { - $ret = array('_route' => 'slashed_b'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_slashed_b; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_b')); + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/([^/]++)(*:16)' + .'|/nested/([^/]++)(*:39)' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 16 => array(array('_route' => 'a_wildcard'), array('param'), null, null), + 39 => array(array('_route' => 'nested_wildcard'), array('param'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) { + if ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + break; + } + + return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes))); + } + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + + return $ret; } - return $ret; - } - not_slashed_b: - - // slashed_c - if ('/slashed/group/c' === $trimmedPathinfo) { - $ret = array('_route' => 'slashed_c'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_slashed_c; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_c')); + if (39 === $m) { + break; } - - return $ret; + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); } - not_slashed_c: - } - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php index 483b63f96f43a..27804d575dc43 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php @@ -21,189 +21,95 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/trailing/simple')) { - // simple_trailing_slash_no_methods - if ('/trailing/simple/no-methods/' === $pathinfo) { - return array('_route' => 'simple_trailing_slash_no_methods'); - } - - // simple_trailing_slash_GET_method - if ('/trailing/simple/get-method/' === $pathinfo) { - $ret = array('_route' => 'simple_trailing_slash_GET_method'); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_simple_trailing_slash_GET_method; + switch ($pathinfo) { + default: + $routes = array( + '/trailing/simple/no-methods/' => array(array('_route' => 'simple_trailing_slash_no_methods'), null, null, null), + '/trailing/simple/get-method/' => array(array('_route' => 'simple_trailing_slash_GET_method'), null, array('GET' => 0), null), + '/trailing/simple/head-method/' => array(array('_route' => 'simple_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null), + '/trailing/simple/post-method/' => array(array('_route' => 'simple_trailing_slash_POST_method'), null, array('POST' => 0), null), + '/not-trailing/simple/no-methods' => array(array('_route' => 'simple_not_trailing_slash_no_methods'), null, null, null), + '/not-trailing/simple/get-method' => array(array('_route' => 'simple_not_trailing_slash_GET_method'), null, array('GET' => 0), null), + '/not-trailing/simple/head-method' => array(array('_route' => 'simple_not_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null), + '/not-trailing/simple/post-method' => array(array('_route' => 'simple_not_trailing_slash_POST_method'), null, array('POST' => 0), null), + ); + + if (!isset($routes[$pathinfo])) { + break; } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; - return $ret; - } - not_simple_trailing_slash_GET_method: - - // simple_trailing_slash_HEAD_method - if ('/trailing/simple/head-method/' === $pathinfo) { - $ret = array('_route' => 'simple_trailing_slash_HEAD_method'); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_simple_trailing_slash_HEAD_method; - } - - return $ret; - } - not_simple_trailing_slash_HEAD_method: - - // simple_trailing_slash_POST_method - if ('/trailing/simple/post-method/' === $pathinfo) { - $ret = array('_route' => 'simple_trailing_slash_POST_method'); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_simple_trailing_slash_POST_method; + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; } return $ret; - } - not_simple_trailing_slash_POST_method: - } - elseif (0 === strpos($pathinfo, '/trailing/regex')) { - // regex_trailing_slash_no_methods - if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ()); - } - - // regex_trailing_slash_GET_method - if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ()); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_regex_trailing_slash_GET_method; - } - - return $ret; - } - not_regex_trailing_slash_GET_method: - - // regex_trailing_slash_HEAD_method - if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ()); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_regex_trailing_slash_HEAD_method; + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/trailing/regex(?' + .'|/no\\-methods/([^/]++)/(*:47)' + .'|/get\\-method/([^/]++)/(*:76)' + .'|/head\\-method/([^/]++)/(*:106)' + .'|/post\\-method/([^/]++)/(*:137)' + .')' + .'|/not\\-trailing/regex(?' + .'|/no\\-methods/([^/]++)(*:190)' + .'|/get\\-method/([^/]++)(*:219)' + .'|/head\\-method/([^/]++)(*:249)' + .'|/post\\-method/([^/]++)(*:279)' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 47 => array(array('_route' => 'regex_trailing_slash_no_methods'), array('param'), null, null), + 76 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null), + 106 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 137 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + 190 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null), + 219 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null), + 249 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 279 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + + return $ret; } - return $ret; - } - not_regex_trailing_slash_HEAD_method: - - // regex_trailing_slash_POST_method - if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_POST_method')), array ()); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_regex_trailing_slash_POST_method; + if (279 === $m) { + break; } - - return $ret; - } - not_regex_trailing_slash_POST_method: - - } - - elseif (0 === strpos($pathinfo, '/not-trailing/simple')) { - // simple_not_trailing_slash_no_methods - if ('/not-trailing/simple/no-methods' === $pathinfo) { - return array('_route' => 'simple_not_trailing_slash_no_methods'); + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); } - - // simple_not_trailing_slash_GET_method - if ('/not-trailing/simple/get-method' === $pathinfo) { - $ret = array('_route' => 'simple_not_trailing_slash_GET_method'); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_simple_not_trailing_slash_GET_method; - } - - return $ret; - } - not_simple_not_trailing_slash_GET_method: - - // simple_not_trailing_slash_HEAD_method - if ('/not-trailing/simple/head-method' === $pathinfo) { - $ret = array('_route' => 'simple_not_trailing_slash_HEAD_method'); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_simple_not_trailing_slash_HEAD_method; - } - - return $ret; - } - not_simple_not_trailing_slash_HEAD_method: - - // simple_not_trailing_slash_POST_method - if ('/not-trailing/simple/post-method' === $pathinfo) { - $ret = array('_route' => 'simple_not_trailing_slash_POST_method'); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_simple_not_trailing_slash_POST_method; - } - - return $ret; - } - not_simple_not_trailing_slash_POST_method: - - } - - elseif (0 === strpos($pathinfo, '/not-trailing/regex')) { - // regex_not_trailing_slash_no_methods - if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_no_methods')), array ()); - } - - // regex_not_trailing_slash_GET_method - if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_GET_method')), array ()); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_regex_not_trailing_slash_GET_method; - } - - return $ret; - } - not_regex_not_trailing_slash_GET_method: - - // regex_not_trailing_slash_HEAD_method - if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_HEAD_method')), array ()); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_regex_not_trailing_slash_HEAD_method; - } - - return $ret; - } - not_regex_not_trailing_slash_HEAD_method: - - // regex_not_trailing_slash_POST_method - if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_POST_method')), array ()); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_regex_not_trailing_slash_POST_method; - } - - return $ret; - } - not_regex_not_trailing_slash_POST_method: - } - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php index c744f5dacdb33..6046e172321db 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php @@ -21,229 +21,131 @@ public function match($rawPathinfo) $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/trailing/simple')) { - // simple_trailing_slash_no_methods - if ('/trailing/simple/no-methods' === $trimmedPathinfo) { - $ret = array('_route' => 'simple_trailing_slash_no_methods'); - if ('/' === substr($pathinfo, -1)) { + switch ($trimmedPathinfo) { + default: + $routes = array( + '/trailing/simple/no-methods' => array(array('_route' => 'simple_trailing_slash_no_methods'), null, null, null, true), + '/trailing/simple/get-method' => array(array('_route' => 'simple_trailing_slash_GET_method'), null, array('GET' => 0), null, true), + '/trailing/simple/head-method' => array(array('_route' => 'simple_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null, true), + '/trailing/simple/post-method' => array(array('_route' => 'simple_trailing_slash_POST_method'), null, array('POST' => 0), null, true), + '/not-trailing/simple/no-methods' => array(array('_route' => 'simple_not_trailing_slash_no_methods'), null, null, null), + '/not-trailing/simple/get-method' => array(array('_route' => 'simple_not_trailing_slash_GET_method'), null, array('GET' => 0), null), + '/not-trailing/simple/head-method' => array(array('_route' => 'simple_not_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null), + '/not-trailing/simple/post-method' => array(array('_route' => 'simple_not_trailing_slash_POST_method'), null, array('POST' => 0), null), + ); + + if (!isset($routes[$trimmedPathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$trimmedPathinfo]; + + if (empty($routes[$trimmedPathinfo][4]) || '/' === $pathinfo[-1]) { // no-op } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_simple_trailing_slash_no_methods; + $allow['GET'] = 'GET'; + break; } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_no_methods')); + return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route'])); } - return $ret; - } - not_simple_trailing_slash_no_methods: - - // simple_trailing_slash_GET_method - if ('/trailing/simple/get-method' === $trimmedPathinfo) { - $ret = array('_route' => 'simple_trailing_slash_GET_method'); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_simple_trailing_slash_GET_method; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_GET_method')); - } - - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_simple_trailing_slash_GET_method; - } - - return $ret; - } - not_simple_trailing_slash_GET_method: - - // simple_trailing_slash_HEAD_method - if ('/trailing/simple/head-method/' === $pathinfo) { - $ret = array('_route' => 'simple_trailing_slash_HEAD_method'); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_simple_trailing_slash_HEAD_method; - } - - return $ret; - } - not_simple_trailing_slash_HEAD_method: - - // simple_trailing_slash_POST_method - if ('/trailing/simple/post-method/' === $pathinfo) { - $ret = array('_route' => 'simple_trailing_slash_POST_method'); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_simple_trailing_slash_POST_method; - } - - return $ret; - } - not_simple_trailing_slash_POST_method: - - } - - elseif (0 === strpos($pathinfo, '/trailing/regex')) { - // regex_trailing_slash_no_methods - if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P[^/]++)/?$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ()); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_regex_trailing_slash_no_methods; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_no_methods')); - } - - return $ret; - } - not_regex_trailing_slash_no_methods: - - // regex_trailing_slash_GET_method - if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P[^/]++)/?$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ()); - if ('/' === substr($pathinfo, -1)) { - // no-op - } elseif ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_regex_trailing_slash_GET_method; - } else { - return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_GET_method')); - } - - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_regex_trailing_slash_GET_method; - } + if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) { + if ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + break; + } - return $ret; - } - not_regex_trailing_slash_GET_method: - - // regex_trailing_slash_HEAD_method - if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ()); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_regex_trailing_slash_HEAD_method; + return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes))); } - return $ret; - } - not_regex_trailing_slash_HEAD_method: - - // regex_trailing_slash_POST_method - if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_POST_method')), array ()); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_regex_trailing_slash_POST_method; + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; } return $ret; - } - not_regex_trailing_slash_POST_method: - } - elseif (0 === strpos($pathinfo, '/not-trailing/simple')) { - // simple_not_trailing_slash_no_methods - if ('/not-trailing/simple/no-methods' === $pathinfo) { - return array('_route' => 'simple_not_trailing_slash_no_methods'); - } - - // simple_not_trailing_slash_GET_method - if ('/not-trailing/simple/get-method' === $pathinfo) { - $ret = array('_route' => 'simple_not_trailing_slash_GET_method'); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_simple_not_trailing_slash_GET_method; - } - - return $ret; - } - not_simple_not_trailing_slash_GET_method: - - // simple_not_trailing_slash_HEAD_method - if ('/not-trailing/simple/head-method' === $pathinfo) { - $ret = array('_route' => 'simple_not_trailing_slash_HEAD_method'); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_simple_not_trailing_slash_HEAD_method; - } - - return $ret; - } - not_simple_not_trailing_slash_HEAD_method: - - // simple_not_trailing_slash_POST_method - if ('/not-trailing/simple/post-method' === $pathinfo) { - $ret = array('_route' => 'simple_not_trailing_slash_POST_method'); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_simple_not_trailing_slash_POST_method; - } - - return $ret; + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/trailing/regex(?' + .'|/no\\-methods/([^/]++)/?(*:48)' + .'|/get\\-method/([^/]++)/?(*:78)' + .'|/head\\-method/([^/]++)/(*:108)' + .'|/post\\-method/([^/]++)/(*:139)' + .')' + .'|/not\\-trailing/regex(?' + .'|/no\\-methods/([^/]++)(*:192)' + .'|/get\\-method/([^/]++)(*:221)' + .'|/head\\-method/([^/]++)(*:251)' + .'|/post\\-method/([^/]++)(*:281)' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 48 => array(array('_route' => 'regex_trailing_slash_no_methods'), array('param'), null, null, true), + 78 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null, true), + 108 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 139 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + 192 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null), + 221 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null), + 251 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 281 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + if (empty($routes[$m][4]) || '/' === $pathinfo[-1]) { + // no-op + } elseif ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + break; + } else { + return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route'])); + } + + if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) { + if ('GET' !== $canonicalMethod) { + $allow['GET'] = 'GET'; + break; + } + + return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes))); + } + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + + return $ret; + } + + if (281 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); } - not_simple_not_trailing_slash_POST_method: - - } - - elseif (0 === strpos($pathinfo, '/not-trailing/regex')) { - // regex_not_trailing_slash_no_methods - if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P[^/]++)$#sD', $pathinfo, $matches)) { - return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_no_methods')), array ()); - } - - // regex_not_trailing_slash_GET_method - if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_GET_method')), array ()); - if ('GET' !== $canonicalMethod) { - $allow[] = 'GET'; - goto not_regex_not_trailing_slash_GET_method; - } - - return $ret; - } - not_regex_not_trailing_slash_GET_method: - - // regex_not_trailing_slash_HEAD_method - if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_HEAD_method')), array ()); - if ('HEAD' !== $requestMethod) { - $allow[] = 'HEAD'; - goto not_regex_not_trailing_slash_HEAD_method; - } - - return $ret; - } - not_regex_not_trailing_slash_HEAD_method: - - // regex_not_trailing_slash_POST_method - if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { - $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_POST_method')), array ()); - if ('POST' !== $canonicalMethod) { - $allow[] = 'POST'; - goto not_regex_not_trailing_slash_POST_method; - } - - return $ret; - } - not_regex_not_trailing_slash_POST_method: - } - throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php new file mode 100644 index 0000000000000..df7bfb029fe1a --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php @@ -0,0 +1,79 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/(a)(*:11)' + .')$}sD', + 11 => '{^(?' + .'|/(.)(*:26)' + .')$}sDu', + 26 => '{^(?' + .'|/(.)(*:41)' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 11 => array(array('_route' => 'a'), array('a'), null, null), + 26 => array(array('_route' => 'b'), array('a'), null, null), + 41 => array(array('_route' => 'c'), array('a'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + $allow += $requiredMethods; + break; + } + + return $ret; + } + + if (41 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher9.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher9.php new file mode 100644 index 0000000000000..e1af79de23eab --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher9.php @@ -0,0 +1,50 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + $host = strtolower($context->getHost()); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + case '/': + // a + if (preg_match('#^(?P[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) { + return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'a')), array()); + } + // c + if (preg_match('#^(?P[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) { + return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'c')), array()); + } + // b + if ('d.c.b.a' === $host) { + return array('_route' => 'b'); + } + break; + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.xml b/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.xml new file mode 100644 index 0000000000000..b158dadb92734 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.yml b/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.yml new file mode 100644 index 0000000000000..90dce0ea1bfc4 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.yml @@ -0,0 +1,7 @@ +app: + resource: ../controller/routing.yml + +api: + resource: ../controller/routing.yml + name_prefix: api_ + prefix: /api diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml b/src/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml index 015e270fb187b..b01d502738284 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml +++ b/src/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml @@ -1,3 +1,3 @@ someroute: resource: path/to/some.yml - name_prefix: test_ + not_valid_key: test_ diff --git a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php index f84802b35b255..4b2e5b196dc06 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php @@ -86,10 +86,6 @@ public function testDumpWithRoutes() public function testDumpWithTooManyRoutes() { - if (defined('HHVM_VERSION_ID')) { - $this->markTestSkipped('HHVM consumes too much memory on this test.'); - } - $this->routeCollection->add('Test', new Route('/testing/{foo}')); for ($i = 0; $i < 32769; ++$i) { $this->routeCollection->add('route_'.$i, new Route('/route_'.$i)); diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php index 7f1d5765574a4..ad5d6ad40cff9 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php @@ -35,9 +35,6 @@ public function testLoad() $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); } - /** - * @requires PHP 5.4 - */ public function testLoadTraitWithClassConstant() { $this->reader->expects($this->never())->method('getClassAnnotation'); @@ -54,9 +51,6 @@ public function testLoadFileWithoutStartTag() $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/NoStartTagClass.php'); } - /** - * @requires PHP 5.6 - */ public function testLoadVariadic() { $route = new Route(array('path' => '/path/to/{id}')); diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php index 221434b0068aa..e5353d7eba292 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php @@ -361,4 +361,15 @@ public function testImportWithOverriddenController() $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); $loader->load('import_override_defaults.xml'); } + + public function testImportRouteWithNamePrefix() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_name_prefix'))); + $routeCollection = $loader->load('routing.xml'); + + $this->assertNotNull($routeCollection->get('app_blog')); + $this->assertEquals('/blog', $routeCollection->get('app_blog')->getPath()); + $this->assertNotNull($routeCollection->get('api_app_blog')); + $this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath()); + } } diff --git a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php index 1f7fd43897ae3..5fa38f39d05a6 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php @@ -182,4 +182,15 @@ public function testImportWithOverriddenController() $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); $loader->load('import_override_defaults.yml'); } + + public function testImportRouteWithNamePrefix() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_name_prefix'))); + $routeCollection = $loader->load('routing.yml'); + + $this->assertNotNull($routeCollection->get('app_blog')); + $this->assertEquals('/blog', $routeCollection->get('app_blog')->getPath()); + $this->assertNotNull($routeCollection->get('api_app_blog')); + $this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath()); + } } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php deleted file mode 100644 index 823efdb840022..0000000000000 --- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Routing\Tests\Matcher\Dumper; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Routing\Matcher\Dumper\DumperCollection; - -class DumperCollectionTest extends TestCase -{ - public function testGetRoot() - { - $a = new DumperCollection(); - - $b = new DumperCollection(); - $a->add($b); - - $c = new DumperCollection(); - $b->add($c); - - $d = new DumperCollection(); - $c->add($d); - - $this->assertSame($a, $c->getRoot()); - } -} diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php index f29a6d6a30a17..09441e0b9b262 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php @@ -181,7 +181,7 @@ public function getRouteCollections() // prefixes $collection1 = new RouteCollection(); $collection1->add('overridden', new Route('/overridden1')); - $collection1->add('foo1', new Route('/{foo}')); + $collection1->add('foo1', (new Route('/{foo}'))->setMethods('PUT')); $collection1->add('bar1', new Route('/{bar}')); $collection1->addPrefix('/b\'b'); $collection2 = new RouteCollection(); @@ -399,6 +399,7 @@ public function getRouteCollections() $groupOptimisedCollection->add('slashed_b', new Route('/slashed/group/b/')); $groupOptimisedCollection->add('slashed_c', new Route('/slashed/group/c/')); + /* test case 6 & 7 */ $trailingSlashCollection = new RouteCollection(); $trailingSlashCollection->add('simple_trailing_slash_no_methods', new Route('/trailing/simple/no-methods/', array(), array(), array(), '', array(), array())); $trailingSlashCollection->add('simple_trailing_slash_GET_method', new Route('/trailing/simple/get-method/', array(), array(), array(), '', array(), array('GET'))); @@ -418,6 +419,18 @@ public function getRouteCollections() $trailingSlashCollection->add('regex_not_trailing_slash_HEAD_method', new Route('/not-trailing/regex/head-method/{param}', array(), array(), array(), '', array(), array('HEAD'))); $trailingSlashCollection->add('regex_not_trailing_slash_POST_method', new Route('/not-trailing/regex/post-method/{param}', array(), array(), array(), '', array(), array('POST'))); + /* test case 8 */ + $unicodeCollection = new RouteCollection(); + $unicodeCollection->add('a', new Route('/{a}', array(), array('a' => 'a'), array('utf8' => false))); + $unicodeCollection->add('b', new Route('/{a}', array(), array('a' => '.'), array('utf8' => true))); + $unicodeCollection->add('c', new Route('/{a}', array(), array('a' => '.'), array('utf8' => false))); + + /* test case 9 */ + $hostTreeCollection = new RouteCollection(); + $hostTreeCollection->add('a', (new Route('/'))->setHost('{d}.e.c.b.a')); + $hostTreeCollection->add('b', (new Route('/'))->setHost('d.c.b.a')); + $hostTreeCollection->add('c', (new Route('/'))->setHost('{e}.e.c.b.a')); + return array( array(new RouteCollection(), 'url_matcher0.php', array()), array($collection, 'url_matcher1.php', array()), @@ -427,6 +440,8 @@ public function getRouteCollections() array($groupOptimisedCollection, 'url_matcher5.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')), array($trailingSlashCollection, 'url_matcher6.php', array()), array($trailingSlashCollection, 'url_matcher7.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')), + array($unicodeCollection, 'url_matcher8.php', array()), + array($hostTreeCollection, 'url_matcher9.php', array()), ); } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php index 37419e7743640..ea3ff6ff4dc16 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php @@ -18,10 +18,9 @@ public function testGrouping(array $routes, $expected) foreach ($routes as $route) { list($path, $name) = $route; $staticPrefix = (new Route($path))->compile()->getStaticPrefix(); - $collection->addRoute($staticPrefix, $name); + $collection->addRoute($staticPrefix, array($name)); } - $collection->optimizeGroups(); $dumped = $this->dumpCollection($collection); $this->assertEquals($expected, $dumped); } @@ -36,21 +35,22 @@ public function routeProvider() array('/leading/segment/', 'leading_segment'), ), << array( + 'Nested - small group' => array( array( array('/', 'root'), array('/prefix/segment/aa', 'prefix_segment'), array('/prefix/segment/bb', 'leading_segment'), ), << prefix_segment +-> leading_segment EOF ), 'Nested - contains item at intersection' => array( @@ -60,10 +60,10 @@ public function routeProvider() array('/prefix/segment/bb', 'leading_segment'), ), << /prefix/segment prefix_segment --> /prefix/segment/bb leading_segment +-> prefix_segment +-> leading_segment EOF ), 'Simple one level nesting' => array( @@ -74,11 +74,11 @@ public function routeProvider() array('/group/other/', 'other_segment'), ), << /group/segment nested_segment --> /group/thing some_segment --> /group/other other_segment +-> nested_segment +-> some_segment +-> other_segment EOF ), 'Retain matching order with groups' => array( @@ -93,14 +93,14 @@ public function routeProvider() ), << /group/aa aa --> /group/bb bb --> /group/cc cc -/ root +-> aa +-> bb +-> cc +root /group --> /group/dd dd --> /group/ee ee --> /group/ff ff +-> dd +-> ee +-> ff EOF ), 'Retain complex matching order with groups at base' => array( @@ -109,28 +109,30 @@ public function routeProvider() array('/prefixed/group/aa/', 'aa'), array('/prefixed/group/bb/', 'bb'), array('/prefixed/group/cc/', 'cc'), - array('/prefixed/', 'root'), + array('/prefixed/(.*)', 'root'), array('/prefixed/group/dd/', 'dd'), array('/prefixed/group/ee/', 'ee'), + array('/prefixed/', 'parent'), array('/prefixed/group/ff/', 'ff'), array('/aaa/222/', 'second_aaa'), array('/aaa/333/', 'third_aaa'), ), << /aaa/111 first_aaa --> /aaa/222 second_aaa --> /aaa/333 third_aaa +-> first_aaa +-> second_aaa +-> third_aaa /prefixed -> /prefixed/group --> -> /prefixed/group/aa aa --> -> /prefixed/group/bb bb --> -> /prefixed/group/cc cc --> /prefixed root +-> -> aa +-> -> bb +-> -> cc +-> root -> /prefixed/group --> -> /prefixed/group/dd dd --> -> /prefixed/group/ee ee --> -> /prefixed/group/ff ff +-> -> dd +-> -> ee +-> -> ff +-> parent EOF ), @@ -145,13 +147,13 @@ public function routeProvider() ), << /aaa-111 a1 --> /aaa-222 a2 --> /aaa-333 a3 +-> a1 +-> a2 +-> a3 /group- --> /group-aa g1 --> /group-bb g2 --> /group-cc g3 +-> g1 +-> g2 +-> g3 EOF ), ); @@ -161,7 +163,7 @@ private function dumpCollection(StaticPrefixCollection $collection, $prefix = '' { $lines = array(); - foreach ($collection->getItems() as $item) { + foreach ($collection->getRoutes() as $item) { if ($item instanceof StaticPrefixCollection) { $lines[] = $prefix.$item->getPrefix(); $lines[] = $this->dumpCollection($item, $prefix.'-> '); diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index a4ed8b6b24ab9..90dc82031332f 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -475,6 +475,31 @@ public function testNestedCollections() $this->assertEquals(array('_route' => 'buz'), $matcher->match('/prefix/buz')); } + public function testSiblingRoutes() + { + $coll = new RouteCollection(); + $coll->add('a', (new Route('/a{a}'))->setMethods('POST')); + $coll->add('b', (new Route('/a{a}'))->setMethods('PUT')); + $coll->add('c', new Route('/a{a}')); + $coll->add('d', (new Route('/b{a}'))->setCondition('false')); + $coll->add('e', (new Route('/{b}{a}'))->setCondition('false')); + $coll->add('f', (new Route('/{b}{a}'))->setRequirements(array('b' => 'b'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'c', 'a' => 'a'), $matcher->match('/aa')); + $this->assertEquals(array('_route' => 'f', 'b' => 'b', 'a' => 'a'), $matcher->match('/ba')); + } + + public function testUnicodeRoute() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/{a}', array(), array('a' => '.'), array('utf8' => false))); + $coll->add('b', new Route('/{a}', array(), array('a' => '.'), array('utf8' => true))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'b', 'a' => 'é'), $matcher->match('/é')); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext()); diff --git a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php index 83457ff14a7bf..3527e12895683 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php @@ -302,4 +302,19 @@ public function testSetMethods() $this->assertEquals(array('PUT'), $routea->getMethods()); $this->assertEquals(array('PUT'), $routeb->getMethods()); } + + public function testAddNamePrefix() + { + $collection = new RouteCollection(); + $collection->add('foo', $foo = new Route('/foo')); + $collection->add('bar', $bar = new Route('/bar')); + $collection->add('api_foo', $apiFoo = new Route('/api/foo')); + $collection->addNamePrefix('api_'); + + $this->assertEquals($foo, $collection->get('api_foo')); + $this->assertEquals($bar, $collection->get('api_bar')); + $this->assertEquals($apiFoo, $collection->get('api_api_foo')); + $this->assertNull($collection->get('foo')); + $this->assertNull($collection->get('bar')); + } } diff --git a/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php b/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php index dc304e3e7d54e..d9db127da91a8 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php @@ -184,9 +184,8 @@ public function provideCompileData() } /** - * @group legacy * @dataProvider provideCompileImplicitUtf8Data - * @expectedDeprecation Using UTF-8 route %s without setting the "utf8" option is deprecated %s. + * @expectedException \LogicException */ public function testCompileImplicitUtf8Data($name, $arguments, $prefix, $regex, $variables, $tokens, $deprecationType) { diff --git a/src/Symfony/Component/Routing/composer.json b/src/Symfony/Component/Routing/composer.json index 2ad746ec53c24..a558a1d749f3f 100644 --- a/src/Symfony/Component/Routing/composer.json +++ b/src/Symfony/Component/Routing/composer.json @@ -16,21 +16,21 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "require-dev": { - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", "doctrine/annotations": "~1.0", "doctrine/common": "~2.2", "psr/log": "~1.0" }, "conflict": { - "symfony/config": "<2.8", - "symfony/dependency-injection": "<3.3", + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", "symfony/yaml": "<3.4" }, "suggest": { @@ -50,7 +50,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index bd1b7b59eb793..0294b62def564 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -1,6 +1,30 @@ CHANGELOG ========= +4.1.0 +----- + + * The `ContextListener::setLogoutOnUserChange()` method is deprecated and will be removed in 5.0. + * added `UserValueResolver`. + * Using the AdvancedUserInterface is now deprecated. To use the existing + functionality, create a custom user-checker based on the + `Symfony\Component\Security\Core\User\UserChecker`. This functionality will + be removed in Symfony 5.0. + +4.0.0 +----- + + * The `AbstractFormLoginAuthenticator::onAuthenticationSuccess()` was removed. + You should implement this method yourself in your concrete authenticator. + * removed the `AccessDecisionManager::setVoters()` method + * removed the `RoleInterface` + * removed support for voters that don't implement the `VoterInterface` + * added a sixth `string $context` argument to `LogoutUrlGenerator::registerListener()` + * removed HTTP digest authentication + * removed `GuardAuthenticatorInterface` in favor of `AuthenticatorInterface` + * removed `AbstractGuardAuthenticator::supports()` + * added target user to `SwitchUserListener` + 3.4.0 ----- diff --git a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php index c57e575e46662..3a86aecd09aad 100644 --- a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php +++ b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php @@ -40,14 +40,14 @@ class AuthenticationProviderManager implements AuthenticationManagerInterface * * @throws \InvalidArgumentException */ - public function __construct($providers, $eraseCredentials = true) + public function __construct(iterable $providers, bool $eraseCredentials = true) { if (!$providers) { throw new \InvalidArgumentException('You must at least add one authentication provider.'); } $this->providers = $providers; - $this->eraseCredentials = (bool) $eraseCredentials; + $this->eraseCredentials = $eraseCredentials; } public function setEventDispatcher(EventDispatcherInterface $dispatcher) diff --git a/src/Symfony/Component/Security/Core/Authentication/AuthenticationTrustResolver.php b/src/Symfony/Component/Security/Core/Authentication/AuthenticationTrustResolver.php index d191d6d532366..da7c34e58625b 100644 --- a/src/Symfony/Component/Security/Core/Authentication/AuthenticationTrustResolver.php +++ b/src/Symfony/Component/Security/Core/Authentication/AuthenticationTrustResolver.php @@ -23,11 +23,7 @@ class AuthenticationTrustResolver implements AuthenticationTrustResolverInterfac private $anonymousClass; private $rememberMeClass; - /** - * @param string $anonymousClass - * @param string $rememberMeClass - */ - public function __construct($anonymousClass, $rememberMeClass) + public function __construct(string $anonymousClass, string $rememberMeClass) { $this->anonymousClass = $anonymousClass; $this->rememberMeClass = $rememberMeClass; diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php index 8c4ee9a47562c..4472db1ad58cb 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php @@ -34,7 +34,7 @@ class AnonymousAuthenticationProvider implements AuthenticationProviderInterface /** * @param string $secret The secret shared with the AnonymousToken */ - public function __construct($secret) + public function __construct(string $secret) { $this->secret = $secret; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php index b7b7f51d209fe..9b3757ef5750a 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php @@ -31,14 +31,7 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider private $encoderFactory; private $userProvider; - /** - * @param UserProviderInterface $userProvider An UserProviderInterface instance - * @param UserCheckerInterface $userChecker An UserCheckerInterface instance - * @param string $providerKey The provider key - * @param EncoderFactoryInterface $encoderFactory An EncoderFactoryInterface instance - * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not - */ - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true) + public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, EncoderFactoryInterface $encoderFactory, bool $hideUserNotFoundExceptions = true) { parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions); diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php index 6865c1d464047..e8a1baa8f0a9e 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php @@ -35,15 +35,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider private $dnString; private $queryString; - /** - * @param UserProviderInterface $userProvider A UserProvider - * @param UserCheckerInterface $userChecker A UserChecker - * @param string $providerKey The provider key - * @param LdapInterface $ldap A Ldap client - * @param string $dnString A string used to create the bind DN - * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not - */ - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, LdapInterface $ldap, $dnString = '{username}', $hideUserNotFoundExceptions = true) + public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, LdapInterface $ldap, string $dnString = '{username}', bool $hideUserNotFoundExceptions = true) { parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions); diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php index 2ed4d8fc9504c..0f923d92ce991 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php @@ -34,7 +34,7 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn private $userChecker; private $providerKey; - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey) + public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey) { $this->userProvider = $userProvider; $this->userChecker = $userChecker; diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php index f8ba7476ba278..145cb3a0382c7 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php @@ -28,7 +28,7 @@ class RememberMeAuthenticationProvider implements AuthenticationProviderInterfac * @param string $secret A secret * @param string $providerKey A provider secret */ - public function __construct(UserCheckerInterface $userChecker, $secret, $providerKey) + public function __construct(UserCheckerInterface $userChecker, string $secret, string $providerKey) { $this->userChecker = $userChecker; $this->secret = $secret; diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php index ffbc72c055a0f..d3f5ad62913e0 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php @@ -25,7 +25,7 @@ class SimpleAuthenticationProvider implements AuthenticationProviderInterface private $userProvider; private $providerKey; - public function __construct(SimpleAuthenticatorInterface $simpleAuthenticator, UserProviderInterface $userProvider, $providerKey) + public function __construct(SimpleAuthenticatorInterface $simpleAuthenticator, UserProviderInterface $userProvider, string $providerKey) { $this->simpleAuthenticator = $simpleAuthenticator; $this->userProvider = $userProvider; diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php index 9244a2eacf1d8..f57503c7691b3 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php @@ -33,13 +33,9 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter private $providerKey; /** - * @param UserCheckerInterface $userChecker An UserCheckerInterface interface - * @param string $providerKey A provider key - * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not - * * @throws \InvalidArgumentException */ - public function __construct(UserCheckerInterface $userChecker, $providerKey, $hideUserNotFoundExceptions = true) + public function __construct(UserCheckerInterface $userChecker, string $providerKey, bool $hideUserNotFoundExceptions = true) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); diff --git a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php index c6b76c2e6b038..996ad285fdab0 100644 --- a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php @@ -24,16 +24,7 @@ final class PersistentToken implements PersistentTokenInterface private $tokenValue; private $lastUsed; - /** - * @param string $class - * @param string $username - * @param string $series - * @param string $tokenValue - * @param \DateTime $lastUsed - * - * @throws \InvalidArgumentException - */ - public function __construct($class, $username, $series, $tokenValue, \DateTime $lastUsed) + public function __construct(string $class, string $username, string $series, string $tokenValue, \DateTime $lastUsed) { if (empty($class)) { throw new \InvalidArgumentException('$class must not be empty.'); diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php index ca55943ebbdf4..7a0522fd10f60 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Security\Core\Authentication\Token; -use Symfony\Component\Security\Core\Role\RoleInterface; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\AdvancedUserInterface; @@ -31,7 +30,7 @@ abstract class AbstractToken implements TokenInterface private $attributes = array(); /** - * @param (RoleInterface|string)[] $roles An array of roles + * @param (Role|string)[] $roles An array of roles * * @throws \InvalidArgumentException */ @@ -40,8 +39,8 @@ public function __construct(array $roles = array()) foreach ($roles as $role) { if (is_string($role)) { $role = new Role($role); - } elseif (!$role instanceof RoleInterface) { - throw new \InvalidArgumentException(sprintf('$roles must be an array of strings, Role instances or RoleInterface instances, but got %s.', gettype($role))); + } elseif (!$role instanceof Role) { + throw new \InvalidArgumentException(sprintf('$roles must be an array of strings, or Role instances, but got %s.', gettype($role))); } $this->roles[] = $role; @@ -262,6 +261,7 @@ private function hasUserChanged(UserInterface $user) } if ($this->user instanceof AdvancedUserInterface && $user instanceof AdvancedUserInterface) { + @trigger_error(sprintf('Checking for the AdvancedUserInterface in %s has been deprecated in 4.1 and will be removed in 5.0. Implement the %s to check if the user has been changed,', __METHOD__, EquatableInterface::class), E_USER_DEPRECATED); if ($this->user->isAccountNonExpired() !== $user->isAccountNonExpired()) { return true; } @@ -278,6 +278,8 @@ private function hasUserChanged(UserInterface $user) return true; } } elseif ($this->user instanceof AdvancedUserInterface xor $user instanceof AdvancedUserInterface) { + @trigger_error(sprintf('Checking for the AdvancedUserInterface in %s has been deprecated in 4.1 and will be removed in 5.0. Implement the %s to check if the user has been changed,', __METHOD__, EquatableInterface::class), E_USER_DEPRECATED); + return true; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php index 3c93664efcc70..fdeba46f97e6d 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php @@ -27,7 +27,7 @@ class AnonymousToken extends AbstractToken * @param string|object $user The user can be a UserInterface instance, or an object implementing a __toString method or the username as a regular string * @param Role[] $roles An array of roles */ - public function __construct($secret, $user, array $roles = array()) + public function __construct(string $secret, $user, array $roles = array()) { parent::__construct($roles); diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php index 84fdb4b00c086..e2b49614dff4f 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php @@ -24,12 +24,12 @@ class PreAuthenticatedToken extends AbstractToken private $providerKey; /** - * @param string|object $user The user can be a UserInterface instance, or an object implementing a __toString method or the username as a regular string - * @param mixed $credentials The user credentials - * @param string $providerKey The provider key - * @param (RoleInterface|string)[] $roles An array of roles + * @param string|object $user The user can be a UserInterface instance, or an object implementing a __toString method or the username as a regular string + * @param mixed $credentials The user credentials + * @param string $providerKey The provider key + * @param (Role|string)[] $roles An array of roles */ - public function __construct($user, $credentials, $providerKey, array $roles = array()) + public function __construct($user, $credentials, string $providerKey, array $roles = array()) { parent::__construct($roles); diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php index 4130a437da4cc..6579668f50714 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php @@ -30,7 +30,7 @@ class RememberMeToken extends AbstractToken * * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, $providerKey, $secret) + public function __construct(UserInterface $user, string $providerKey, string $secret) { parent::__construct($user->getRoles()); diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php b/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php index 4e1dd7b2fc46b..bb5711ee89107 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Security\Core\Authentication\Token; -use Symfony\Component\Security\Core\Role\RoleInterface; +use Symfony\Component\Security\Core\Role\Role; /** * TokenInterface is the interface for the user authentication information. @@ -33,7 +33,7 @@ public function __toString(); /** * Returns the user roles. * - * @return RoleInterface[] An array of RoleInterface instances + * @return Role[] An array of Role instances */ public function getRoles(); diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/UsernamePasswordToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/UsernamePasswordToken.php index 45f333acdb035..1270cc2b4ce8f 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/UsernamePasswordToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/UsernamePasswordToken.php @@ -22,14 +22,14 @@ class UsernamePasswordToken extends AbstractToken private $providerKey; /** - * @param string|object $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method - * @param mixed $credentials This usually is the password of the user - * @param string $providerKey The provider key - * @param (RoleInterface|string)[] $roles An array of roles + * @param string|object $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method + * @param mixed $credentials This usually is the password of the user + * @param string $providerKey The provider key + * @param (Role|string)[] $roles An array of roles * * @throws \InvalidArgumentException */ - public function __construct($user, $credentials, $providerKey, array $roles = array()) + public function __construct($user, $credentials, string $providerKey, array $roles = array()) { parent::__construct($roles); diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php index 23fb408496870..3259bdd9160d0 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php @@ -13,7 +13,6 @@ use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\LogicException; /** * AccessDecisionManager is the base class for all access decision managers @@ -33,14 +32,14 @@ class AccessDecisionManager implements AccessDecisionManagerInterface private $allowIfEqualGrantedDeniedDecisions; /** - * @param iterable|VoterInterface[] $voters An iterator of VoterInterface instances + * @param iterable|VoterInterface[] $voters An array or an iterator of VoterInterface instances * @param string $strategy The vote strategy * @param bool $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not * @param bool $allowIfEqualGrantedDeniedDecisions Whether to grant access if result are equals * * @throws \InvalidArgumentException */ - public function __construct($voters = array(), $strategy = self::STRATEGY_AFFIRMATIVE, $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true) + public function __construct(iterable $voters = array(), string $strategy = self::STRATEGY_AFFIRMATIVE, bool $allowIfAllAbstainDecisions = false, bool $allowIfEqualGrantedDeniedDecisions = true) { $strategyMethod = 'decide'.ucfirst($strategy); if (!is_callable(array($this, $strategyMethod))) { @@ -49,22 +48,8 @@ public function __construct($voters = array(), $strategy = self::STRATEGY_AFFIRM $this->voters = $voters; $this->strategy = $strategyMethod; - $this->allowIfAllAbstainDecisions = (bool) $allowIfAllAbstainDecisions; - $this->allowIfEqualGrantedDeniedDecisions = (bool) $allowIfEqualGrantedDeniedDecisions; - } - - /** - * Configures the voters. - * - * @param VoterInterface[] $voters An array of VoterInterface instances - * - * @deprecated since version 3.3, to be removed in 4.0. Pass the voters to the constructor instead. - */ - public function setVoters(array $voters) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass the voters to the constructor instead.', __METHOD__), E_USER_DEPRECATED); - - $this->voters = $voters; + $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; + $this->allowIfEqualGrantedDeniedDecisions = $allowIfEqualGrantedDeniedDecisions; } /** @@ -85,7 +70,7 @@ private function decideAffirmative(TokenInterface $token, array $attributes, $ob { $deny = 0; foreach ($this->voters as $voter) { - $result = $this->vote($voter, $token, $object, $attributes); + $result = $voter->vote($token, $object, $attributes); switch ($result) { case VoterInterface::ACCESS_GRANTED: return true; @@ -126,7 +111,7 @@ private function decideConsensus(TokenInterface $token, array $attributes, $obje $grant = 0; $deny = 0; foreach ($this->voters as $voter) { - $result = $this->vote($voter, $token, $object, $attributes); + $result = $voter->vote($token, $object, $attributes); switch ($result) { case VoterInterface::ACCESS_GRANTED: @@ -167,7 +152,7 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje $grant = 0; foreach ($this->voters as $voter) { foreach ($attributes as $attribute) { - $result = $this->vote($voter, $token, $object, array($attribute)); + $result = $voter->vote($token, $object, array($attribute)); switch ($result) { case VoterInterface::ACCESS_GRANTED: @@ -191,27 +176,4 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje return $this->allowIfAllAbstainDecisions; } - - /** - * TokenInterface vote proxy method. - * - * Acts as a BC layer when the VoterInterface is not implemented on the voter. - * - * @deprecated as of 3.4 and will be removed in 4.0. Call the voter directly as the instance will always be a VoterInterface - */ - private function vote($voter, TokenInterface $token, $subject, $attributes) - { - if ($voter instanceof VoterInterface) { - return $voter->vote($token, $subject, $attributes); - } - - if (method_exists($voter, 'vote')) { - @trigger_error(sprintf('Calling vote() on an voter without %1$s is deprecated as of 3.4 and will be removed in 4.0. Implement the %1$s on your voter.', VoterInterface::class), E_USER_DEPRECATED); - - // making the assumption that the signature matches - return $voter->vote($token, $subject, $attributes); - } - - throw new LogicException(sprintf('%s should implement the %s interface when used as voter.', get_class($voter), VoterInterface::class)); - } } diff --git a/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php b/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php index 98934ff141a9d..67d5f833aff85 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php +++ b/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php @@ -30,13 +30,7 @@ class AuthorizationChecker implements AuthorizationCheckerInterface private $authenticationManager; private $alwaysAuthenticate; - /** - * @param TokenStorageInterface $tokenStorage - * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManager instance - * @param AccessDecisionManagerInterface $accessDecisionManager An AccessDecisionManager instance - * @param bool $alwaysAuthenticate - */ - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, $alwaysAuthenticate = false) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, bool $alwaysAuthenticate = false) { $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; diff --git a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php deleted file mode 100644 index aaf04a4fb00cc..0000000000000 --- a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authorization; - -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - -class_exists(TraceableAccessDecisionManager::class); - -if (false) { - /** - * This is a placeholder for the old class, that got renamed; this is not a BC break since the class is internal, this - * placeholder is here just to help backward compatibility with older SecurityBundle versions. - * - * @deprecated The DebugAccessDecisionManager class has been renamed and is deprecated since version 3.3 and will be removed in 4.0. Use the TraceableAccessDecisionManager class instead. - * - * @internal - */ - class DebugAccessDecisionManager implements AccessDecisionManagerInterface - { - /** - * {@inheritdoc} - */ - public function decide(TokenInterface $token, array $attributes, $object = null) - { - } - } -} diff --git a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php index 0778ba78d4869..5cf02c6bf0b45 100644 --- a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php +++ b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Authorization; +use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; if (!class_exists(BaseExpressionLanguage::class)) { @@ -28,7 +29,7 @@ class ExpressionLanguage extends BaseExpressionLanguage /** * {@inheritdoc} */ - public function __construct($cache = null, array $providers = array()) + public function __construct(CacheItemPoolInterface $cache = null, array $providers = array()) { // prepend the default provider to let users override it easily array_unshift($providers, new ExpressionLanguageProvider()); diff --git a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php index dc77ab2df6877..8430c861a96fa 100644 --- a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php @@ -60,23 +60,6 @@ public function decide(TokenInterface $token, array $attributes, $object = null) return $result; } - /** - * {@inheritdoc} - * - * @deprecated since version 3.3, to be removed in 4.0. Pass voters to the decorated AccessDecisionManager instead. - */ - public function setVoters(array $voters) - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass voters to the decorated AccessDecisionManager instead.', __METHOD__), E_USER_DEPRECATED); - - if (!method_exists($this->manager, 'setVoters')) { - return; - } - - $this->voters = $voters; - $this->manager->setVoters($voters); - } - /** * @return string */ diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleHierarchyVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleHierarchyVoter.php index c8f9b7ec8e8a9..06dfb4228ea89 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleHierarchyVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleHierarchyVoter.php @@ -24,7 +24,7 @@ class RoleHierarchyVoter extends RoleVoter { private $roleHierarchy; - public function __construct(RoleHierarchyInterface $roleHierarchy, $prefix = 'ROLE_') + public function __construct(RoleHierarchyInterface $roleHierarchy, string $prefix = 'ROLE_') { $this->roleHierarchy = $roleHierarchy; diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php index 8ba2f3a47f975..3742b9912fa4e 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Core\Authorization\Voter; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Role\RoleInterface; +use Symfony\Component\Security\Core\Role\Role; /** * RoleVoter votes if any attribute starts with a given prefix. @@ -23,10 +23,7 @@ class RoleVoter implements VoterInterface { private $prefix; - /** - * @param string $prefix The role prefix - */ - public function __construct($prefix = 'ROLE_') + public function __construct(string $prefix = 'ROLE_') { $this->prefix = $prefix; } @@ -40,7 +37,7 @@ public function vote(TokenInterface $token, $subject, array $attributes) $roles = $this->extractRoles($token); foreach ($attributes as $attribute) { - if ($attribute instanceof RoleInterface) { + if ($attribute instanceof Role) { $attribute = $attribute->getRole(); } diff --git a/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php index 0362ccc4a277e..2aa011c7cbab6 100644 --- a/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/Argon2iPasswordEncoder.php @@ -22,7 +22,7 @@ class Argon2iPasswordEncoder extends BasePasswordEncoder implements SelfSaltingE { public static function isSupported() { - if (\PHP_VERSION_ID >= 70200 && \defined('PASSWORD_ARGON2I')) { + if (\defined('PASSWORD_ARGON2I')) { return true; } diff --git a/src/Symfony/Component/Security/Core/Encoder/BCryptPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/BCryptPasswordEncoder.php index d85afccd805b0..67e0ce7a07913 100644 --- a/src/Symfony/Component/Security/Core/Encoder/BCryptPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/BCryptPasswordEncoder.php @@ -29,9 +29,8 @@ class BCryptPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEn * @throws \RuntimeException When no BCrypt encoder is available * @throws \InvalidArgumentException if cost is out of range */ - public function __construct($cost) + public function __construct(int $cost) { - $cost = (int) $cost; if ($cost < 4 || $cost > 31) { throw new \InvalidArgumentException('Cost must be in the range of 4-31.'); } diff --git a/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php index e1e430d172888..b960580f203aa 100644 --- a/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php @@ -29,7 +29,7 @@ class MessageDigestPasswordEncoder extends BasePasswordEncoder * @param bool $encodeHashAsBase64 Whether to base64 encode the password hash * @param int $iterations The number of iterations to use to stretch the password hash */ - public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 5000) + public function __construct(string $algorithm = 'sha512', bool $encodeHashAsBase64 = true, int $iterations = 5000) { $this->algorithm = $algorithm; $this->encodeHashAsBase64 = $encodeHashAsBase64; diff --git a/src/Symfony/Component/Security/Core/Encoder/Pbkdf2PasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/Pbkdf2PasswordEncoder.php index ae83ec35872cc..186ee698bf8bd 100644 --- a/src/Symfony/Component/Security/Core/Encoder/Pbkdf2PasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/Pbkdf2PasswordEncoder.php @@ -39,7 +39,7 @@ class Pbkdf2PasswordEncoder extends BasePasswordEncoder * @param int $iterations The number of iterations to use to stretch the password hash * @param int $length Length of derived key to create */ - public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 1000, $length = 40) + public function __construct(string $algorithm = 'sha512', bool $encodeHashAsBase64 = true, int $iterations = 1000, int $length = 40) { $this->algorithm = $algorithm; $this->encodeHashAsBase64 = $encodeHashAsBase64; diff --git a/src/Symfony/Component/Security/Core/Encoder/PlaintextPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/PlaintextPasswordEncoder.php index bda6269a52012..a9347f7257fa9 100644 --- a/src/Symfony/Component/Security/Core/Encoder/PlaintextPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/PlaintextPasswordEncoder.php @@ -25,7 +25,7 @@ class PlaintextPasswordEncoder extends BasePasswordEncoder /** * @param bool $ignorePasswordCase Compare password case-insensitive */ - public function __construct($ignorePasswordCase = false) + public function __construct(bool $ignorePasswordCase = false) { $this->ignorePasswordCase = $ignorePasswordCase; } diff --git a/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php b/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php index a16044ff000a7..c4eebcb7a40a6 100644 --- a/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php +++ b/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php @@ -16,12 +16,12 @@ * * @author Fabien Potencier */ -class AccessDeniedException extends \RuntimeException +class AccessDeniedException extends RuntimeException { private $attributes = array(); private $subject; - public function __construct($message = 'Access Denied.', \Exception $previous = null) + public function __construct(string $message = 'Access Denied.', \Exception $previous = null) { parent::__construct($message, 403, $previous); } diff --git a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php index 8beb87bdb81bc..3abcd862cc7a3 100644 --- a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php +++ b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php @@ -19,7 +19,7 @@ * @author Fabien Potencier * @author Alexander */ -class AuthenticationException extends \RuntimeException implements \Serializable +class AuthenticationException extends RuntimeException implements \Serializable { private $token; diff --git a/src/Symfony/Component/Security/Core/Exception/CustomUserMessageAuthenticationException.php b/src/Symfony/Component/Security/Core/Exception/CustomUserMessageAuthenticationException.php index 9f5071f4307db..2b9a00ebe30ce 100644 --- a/src/Symfony/Component/Security/Core/Exception/CustomUserMessageAuthenticationException.php +++ b/src/Symfony/Component/Security/Core/Exception/CustomUserMessageAuthenticationException.php @@ -26,7 +26,7 @@ class CustomUserMessageAuthenticationException extends AuthenticationException private $messageData = array(); - public function __construct($message = '', array $messageData = array(), $code = 0, \Exception $previous = null) + public function __construct(string $message = '', array $messageData = array(), int $code = 0, \Exception $previous = null) { parent::__construct($message, $code, $previous); diff --git a/src/Symfony/Component/Security/Core/Exception/LogoutException.php b/src/Symfony/Component/Security/Core/Exception/LogoutException.php index 2bb954fa7875d..cd9e5b2268962 100644 --- a/src/Symfony/Component/Security/Core/Exception/LogoutException.php +++ b/src/Symfony/Component/Security/Core/Exception/LogoutException.php @@ -16,9 +16,9 @@ * * @author Jeremy Mikola */ -class LogoutException extends \RuntimeException +class LogoutException extends RuntimeException { - public function __construct($message = 'Logout Exception', \Exception $previous = null) + public function __construct(string $message = 'Logout Exception', \Exception $previous = null) { parent::__construct($message, 403, $previous); } diff --git a/src/Symfony/Component/Security/Core/Exception/NonceExpiredException.php b/src/Symfony/Component/Security/Core/Exception/NonceExpiredException.php deleted file mode 100644 index 9e748725da8b9..0000000000000 --- a/src/Symfony/Component/Security/Core/Exception/NonceExpiredException.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Exception; - -@trigger_error(sprintf('The %s class and the whole HTTP digest authentication system is deprecated since Symfony 3.4 and will be removed in 4.0.', NonceExpiredException::class), E_USER_DEPRECATED); - -/** - * NonceExpiredException is thrown when an authentication is rejected because - * the digest nonce has expired. - * - * @author Fabien Potencier - * @author Alexander - * - * @deprecated since 3.4, to be removed in 4.0 - */ -class NonceExpiredException extends AuthenticationException -{ - /** - * {@inheritdoc} - */ - public function getMessageKey() - { - return 'Digest nonce has expired.'; - } -} diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf index fd18ee6ad9faf..49381ba347f6f 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. رمز الموقع غير صحيح. - - Digest nonce has expired. - انتهت صلاحية(digest nonce). - No authentication provider found to support the authentication token. لا يوجد معرف للدخول يدعم الرمز المستخدم للدخول. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf index a974ed0f024c8..d9d5425cefb9c 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.az.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Yanlış CSRF nişanı. - - Digest nonce has expired. - Dərləmə istifadə müddəti bitib. - No authentication provider found to support the authentication token. Doğrulama nişanını dəstəkləyəcək provayder tapılmadı. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.bg.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.bg.xlf index 06692ea66a843..28c1360eb946e 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.bg.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.bg.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Невалиден CSRF токен. - - Digest nonce has expired. - Digest nonce е изтекъл. - No authentication provider found to support the authentication token. Не е открит провайдър, който да поддържа този токен за автентикация. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf index 7ece2603ae477..b009c6205c362 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Token CSRF no vàlid. - - Digest nonce has expired. - El vector d'inicialització (digest nonce) ha expirat. - No authentication provider found to support the authentication token. No s'ha trobat un proveïdor d'autenticació que suporti el token d'autenticació. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.cs.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.cs.xlf index bd146c68049cb..b455779cb6f20 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.cs.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.cs.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Neplatný CSRF token. - - Digest nonce has expired. - Platnost inicializačního vektoru (digest nonce) vypršela. - No authentication provider found to support the authentication token. Poskytovatel pro ověřovací token nebyl nalezen. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf index 2ac41502d2c7f..102c8f1179521 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.da.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Ugyldigt CSRF token. - - Digest nonce has expired. - Digest nonce er udløbet. - No authentication provider found to support the authentication token. Ingen godkendelsesudbyder er fundet til understøttelsen af godkendelsestoken. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.de.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.de.xlf index e5946ed4aa42d..093d92d2d1fa9 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.de.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.de.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Ungültiges CSRF-Token. - - Digest nonce has expired. - Digest nonce ist abgelaufen. - No authentication provider found to support the authentication token. Es wurde kein Authentifizierungs-Provider gefunden, der das Authentifizierungs-Token unterstützt. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf index 07eabe7ed29e2..02393d0805252 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.el.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Μη έγκυρο CSRF token. - - Digest nonce has expired. - Το digest nonce έχει λήξει. - No authentication provider found to support the authentication token. Δε βρέθηκε κάποιος πάροχος πιστοποίησης που να υποστηρίζει το token πιστοποίησης. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf index 3640698ce9fb3..3c89e44f9380e 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Invalid CSRF token. - - Digest nonce has expired. - Digest nonce has expired. - No authentication provider found to support the authentication token. No authentication provider found to support the authentication token. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.es.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.es.xlf index 00cefbb2dad67..369f11b9b41d4 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.es.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.es.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Token CSRF no válido. - - Digest nonce has expired. - El vector de inicialización (digest nonce) ha expirado. - No authentication provider found to support the authentication token. No se encontró un proveedor de autenticación que soporte el token de autenticación. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf index 0b7629078063c..1b3246feb3d5a 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.fa.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. توکن CSRF معتبر نیست. - - Digest nonce has expired. - Digest nonce منقضی شده است. - No authentication provider found to support the authentication token. هیچ ارایه کننده تعیین اعتباری برای ساپورت توکن تعیین اعتبار پیدا نشد. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.fr.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.fr.xlf index 5a77c6e9ff795..d67dcaefc5029 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.fr.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.fr.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Jeton CSRF invalide. - - Digest nonce has expired. - Le digest nonce a expiré. - No authentication provider found to support the authentication token. Aucun fournisseur d'authentification n'a été trouvé pour supporter le jeton d'authentification. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf index ed6491f7ef97a..ddc838e66af8b 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.gl.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Token CSRF non válido. - - Digest nonce has expired. - O vector de inicialización (digest nonce) expirou. - No authentication provider found to support the authentication token. Non se atopou un provedor de autenticación que soporte o token de autenticación. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf index 3640698ce9fb3..3c89e44f9380e 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.he.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Invalid CSRF token. - - Digest nonce has expired. - Digest nonce has expired. - No authentication provider found to support the authentication token. No authentication provider found to support the authentication token. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.hr.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.hr.xlf index 147b6e311a22f..411a48572a097 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.hr.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.hr.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Neispravan CSRF token. - - Digest nonce has expired. - Digest nonce je isteko. - No authentication provider found to support the authentication token. Nije pronađen autentifikacijski provider koji bi podržao autentifikacijski token. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf index 724397038cb66..f3a163904d367 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.hu.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Érvénytelen CSRF token. - - Digest nonce has expired. - A kivonat bélyege (nonce) lejárt. - No authentication provider found to support the authentication token. Nem található a hitelesítési tokent támogató hitelesítési szolgáltatás. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.id.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.id.xlf index ab1153b8a27ff..6289481d03265 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.id.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.id.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Token CSRF salah. - - Digest nonce has expired. - Digest nonce telah berakhir. - No authentication provider found to support the authentication token. Tidak ditemukan penyedia otentikasi untuk mendukung token otentikasi. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf index 75d81cc8d9312..f2cb0fa48fab5 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.it.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. CSRF token non valido. - - Digest nonce has expired. - Il numero di autenticazione è scaduto. - No authentication provider found to support the authentication token. Non è stato trovato un valido fornitore di autenticazione per supportare il token. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ja.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ja.xlf index 6a6b062d946c3..2dad8dee6a927 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ja.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ja.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. CSRF トークンが無効です。 - - Digest nonce has expired. - Digest の nonce 値が期限切れです。 - No authentication provider found to support the authentication token. 認証トークンをサポートする認証プロバイダーが見つかりません。 diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.lb.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.lb.xlf index 3dc76d5486883..0a7096caea526 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.lb.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.lb.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Ongëltegen CSRF-Token. - - Digest nonce has expired. - Den eemolege Schlëssel ass ofgelaf. - No authentication provider found to support the authentication token. Et gouf keen Authentifizéierungs-Provider fonnt deen den Authentifizéierungs-Token ënnerstëtzt. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf index 63e1ed662ee37..b7909033a6e67 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.lt.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Neteisingas CSRF raktas. - - Digest nonce has expired. - Prieigos kodas yra pasibaigęs. - No authentication provider found to support the authentication token. Nerastas autentifikacijos tiekėjas, kuris palaikytų autentifikacijos raktą. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf index 33c48c617461c..0ad9125e711e9 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.lv.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Nederīgs CSRF talons. - - Digest nonce has expired. - Vienreiz lietojamās atslēgas darbības laiks ir beidzies. - No authentication provider found to support the authentication token. Nav atrasts, autentifikācijas talonu atbalstošs, autentifikācijas sniedzējs. @@ -68,4 +64,4 @@ - \ No newline at end of file + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf index 3635916971476..c5ab83efc5906 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Ugyldig CSRF token. - - Digest nonce has expired. - Digest nonce er utløpt. - No authentication provider found to support the authentication token. Ingen autentiserings tilbyder funnet som støtter gitt autentiserings token. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf index 8969e9ef8ca69..5160143ab7380 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.nl.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. CSRF-code is ongeldig. - - Digest nonce has expired. - Serverauthenticatiesleutel (digest nonce) is verlopen. - No authentication provider found to support the authentication token. Geen authenticatieprovider gevonden die de authenticatietoken ondersteunt. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf index 3635916971476..c5ab83efc5906 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Ugyldig CSRF token. - - Digest nonce has expired. - Digest nonce er utløpt. - No authentication provider found to support the authentication token. Ingen autentiserings tilbyder funnet som støtter gitt autentiserings token. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.pl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.pl.xlf index 8d563d21206a9..9940d2940003d 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.pl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.pl.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Nieprawidłowy token CSRF. - - Digest nonce has expired. - Kod dostępu wygasł. - No authentication provider found to support the authentication token. Nie znaleziono mechanizmu uwierzytelniania zdolnego do obsługi przesłanego tokenu. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.pt_BR.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.pt_BR.xlf index 61685d9f052ea..5981976f167ea 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.pt_BR.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.pt_BR.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Token CSRF inválido. - - Digest nonce has expired. - Digest nonce expirado. - No authentication provider found to support the authentication token. Nenhum provedor de autenticação encontrado para suportar o token de autenticação. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.pt_PT.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.pt_PT.xlf index f2af13ea3d082..b1a4af5154faa 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.pt_PT.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.pt_PT.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Token CSRF inválido. - - Digest nonce has expired. - Digest nonce expirado. - No authentication provider found to support the authentication token. Nenhum fornecedor de autenticação encontrado para suportar o token de autenticação. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf index 440f11036770d..f35a2bb815878 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Tokenul CSRF este invalid. - - Digest nonce has expired. - Tokenul temporar a expirat. - No authentication provider found to support the authentication token. Nu a fost găsit nici un agent de autentificare pentru tokenul specificat. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ru.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ru.xlf index 1964f95e09a64..3f2690b2d3d7c 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ru.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ru.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Недействительный токен CSRF. - - Digest nonce has expired. - Время действия одноразового ключа дайджеста истекло. - No authentication provider found to support the authentication token. Не найден провайдер аутентификации, поддерживающий токен аутентификации. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sk.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sk.xlf index e6552a6a0914e..1447b4ef5a3c8 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sk.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sk.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Neplatný CSRF token. - - Digest nonce has expired. - Platnosť inicializačného vektoru (digest nonce) skončila. - No authentication provider found to support the authentication token. Poskytovateľ pre overovací token nebol nájdený. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf index ee70c9aaa4af0..bc171812f8de3 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sl.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Neveljaven CSRF žeton. - - Digest nonce has expired. - Začasni žeton je potekel. - No authentication provider found to support the authentication token. Ponudnika avtentikacije za podporo prijavnega žetona ni bilo mogoče najti. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf index 35e4ddf29b28c..f677254cce202 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Cyrl.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Невалидан CSRF токен. - - Digest nonce has expired. - Време криптографског кључа је истекло. - No authentication provider found to support the authentication token. Аутентификациони провајдер за подршку токена није пронађен. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf index ddc48076a2a6e..a38c75a9f810f 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sr_Latn.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Nevalidan CSRF token. - - Digest nonce has expired. - Vreme kriptografskog ključa je isteklo. - No authentication provider found to support the authentication token. Autentifikacioni provajder za podršku tokena nije pronađen. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.sv.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.sv.xlf index b5f62092365fa..ec3616f58620f 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.sv.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.sv.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Ogiltig CSRF-token. - - Digest nonce has expired. - Förfallen digest nonce. - No authentication provider found to support the authentication token. Ingen leverantör för autentisering hittades för angiven autentiseringstoken. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf index a8cb8d5ce7e3b..84ae66769c611 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.th.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. CSRF token ไม่ถูกต้อง - - Digest nonce has expired. - Digest nonce หมดอายุ - No authentication provider found to support the authentication token. ไม่พบ authentication provider ที่รองรับสำหรับ authentication token diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.tr.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.tr.xlf index 68c44213d18c3..1ffa76e4d4457 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.tr.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.tr.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Geçersiz CSRF fişi. - - Digest nonce has expired. - Derleme zaman aşımına uğradı. - No authentication provider found to support the authentication token. Yetkilendirme fişini destekleyecek yetkilendirme sağlayıcısı bulunamadı. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ua.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ua.xlf index 79721212068db..f60a9c18eb711 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ua.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ua.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Невірний токен CSRF. - - Digest nonce has expired. - Закінчився термін дії одноразового ключа дайджесту. - No authentication provider found to support the authentication token. Не знайдено провайдера автентифікації, що підтримує токен автентифікаціії. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.vi.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.vi.xlf index b85a43995fc0a..87e20252183f0 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.vi.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.vi.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. Mã CSRF không hợp lệ. - - Digest nonce has expired. - Mã dùng một lần đã hết hạn. - No authentication provider found to support the authentication token. Không tìm thấy nhà cung cấp dịch vụ xác thực nào cho mã xác thực mà bạn sử dụng. diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.zh_CN.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.zh_CN.xlf index 2d6affecec2cc..460c0ac68bf48 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.zh_CN.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.zh_CN.xlf @@ -30,10 +30,6 @@ Invalid CSRF token. 无效的 CSRF token 。 - - Digest nonce has expired. - 摘要随机串(digest nonce)已过期。 - No authentication provider found to support the authentication token. 没有找到支持此 token 的身份验证服务提供方。 diff --git a/src/Symfony/Component/Security/Core/Role/Role.php b/src/Symfony/Component/Security/Core/Role/Role.php index b456f6c3062e6..208fe2f6b7e06 100644 --- a/src/Symfony/Component/Security/Core/Role/Role.php +++ b/src/Symfony/Component/Security/Core/Role/Role.php @@ -16,20 +16,19 @@ * * @author Fabien Potencier */ -class Role implements RoleInterface +class Role { private $role; - /** - * @param string $role The role name - */ - public function __construct($role) + public function __construct(string $role) { - $this->role = (string) $role; + $this->role = $role; } /** - * {@inheritdoc} + * Returns a string representation of the role. + * + * @return string */ public function getRole() { diff --git a/src/Symfony/Component/Security/Core/Role/RoleHierarchyInterface.php b/src/Symfony/Component/Security/Core/Role/RoleHierarchyInterface.php index c994009cb4b40..1a86db9901603 100644 --- a/src/Symfony/Component/Security/Core/Role/RoleHierarchyInterface.php +++ b/src/Symfony/Component/Security/Core/Role/RoleHierarchyInterface.php @@ -24,9 +24,9 @@ interface RoleHierarchyInterface * Reachable roles are the roles directly assigned but also all roles that * are transitively reachable from them in the role hierarchy. * - * @param RoleInterface[] $roles An array of directly assigned roles + * @param Role[] $roles An array of directly assigned roles * - * @return RoleInterface[] An array of all reachable roles + * @return Role[] An array of all reachable roles */ public function getReachableRoles(array $roles); } diff --git a/src/Symfony/Component/Security/Core/Role/RoleInterface.php b/src/Symfony/Component/Security/Core/Role/RoleInterface.php deleted file mode 100644 index a0621baa6b4be..0000000000000 --- a/src/Symfony/Component/Security/Core/Role/RoleInterface.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Role; - -/** - * RoleInterface represents a role granted to a user. - * - * A role must either have a string representation or it needs to be explicitly - * supported by at least one AccessDecisionManager. - * - * @author Fabien Potencier - * - * @deprecated The RoleInterface is deprecated since version 3.3 and will be removed in 4.0. Extend the Symfony\Component\Security\Core\Role\Role class instead. - */ -interface RoleInterface -{ - /** - * Returns the role. - * - * This method returns a string representation whenever possible. - * - * When the role cannot be represented with sufficient precision by a - * string, it should return null. - * - * @return string|null A string representation of the role, or null - */ - public function getRole(); -} diff --git a/src/Symfony/Component/Security/Core/Role/SwitchUserRole.php b/src/Symfony/Component/Security/Core/Role/SwitchUserRole.php index f68f40750d03b..d26c7067a2d90 100644 --- a/src/Symfony/Component/Security/Core/Role/SwitchUserRole.php +++ b/src/Symfony/Component/Security/Core/Role/SwitchUserRole.php @@ -27,7 +27,7 @@ class SwitchUserRole extends Role * @param string $role The role as a string * @param TokenInterface $source The original token */ - public function __construct($role, TokenInterface $source) + public function __construct(string $role, TokenInterface $source) { parent::__construct($role); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php index 4cdf98267600a..0d165a4cbd0fe 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php @@ -59,7 +59,6 @@ public function getCredentials() } } -/** @noinspection PhpUndefinedClassInspection */ class AbstractTokenTest extends TestCase { public function testGetUsername() @@ -185,10 +184,8 @@ public function testSetUser($user) public function getUsers() { $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); - $advancedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\AdvancedUserInterface')->getMock(); return array( - array($advancedUser), array($user), array(new TestUser('foo')), array('foo'), @@ -212,53 +209,59 @@ public function testSetUserSetsAuthenticatedToFalseWhenUserChanges($firstUser, $ } public function getUserChanges() + { + $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + + return array( + array('foo', 'bar'), + array('foo', new TestUser('bar')), + array('foo', $user), + array($user, 'foo'), + array($user, new TestUser('foo')), + array(new TestUser('foo'), new TestUser('bar')), + array(new TestUser('foo'), 'bar'), + array(new TestUser('foo'), $user), + ); + } + + /** + * @group legacy + * + * @dataProvider getUserChangesAdvancedUser + */ + public function testSetUserSetsAuthenticatedToFalseWhenUserChangesdvancedUser($firstUser, $secondUser) + { + $token = $this->getToken(); + $token->setAuthenticated(true); + $this->assertTrue($token->isAuthenticated()); + + $token->setUser($firstUser); + $this->assertTrue($token->isAuthenticated()); + + $token->setUser($secondUser); + $this->assertFalse($token->isAuthenticated()); + } + + public function getUserChangesAdvancedUser() { $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); $advancedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\AdvancedUserInterface')->getMock(); return array( - array( - 'foo', 'bar', - ), - array( - 'foo', new TestUser('bar'), - ), - array( - 'foo', $user, - ), - array( - 'foo', $advancedUser, - ), - array( - $user, 'foo', - ), - array( - $advancedUser, 'foo', - ), - array( - $user, new TestUser('foo'), - ), - array( - $advancedUser, new TestUser('foo'), - ), - array( - new TestUser('foo'), new TestUser('bar'), - ), - array( - new TestUser('foo'), 'bar', - ), - array( - new TestUser('foo'), $user, - ), - array( - new TestUser('foo'), $advancedUser, - ), - array( - $user, $advancedUser, - ), - array( - $advancedUser, $user, - ), + array('foo', 'bar'), + array('foo', new TestUser('bar')), + array('foo', $user), + array('foo', $advancedUser), + array($user, 'foo'), + array($advancedUser, 'foo'), + array($user, new TestUser('foo')), + array($advancedUser, new TestUser('foo')), + array(new TestUser('foo'), new TestUser('bar')), + array(new TestUser('foo'), 'bar'), + array(new TestUser('foo'), $user), + array(new TestUser('foo'), $advancedUser), + array($user, $advancedUser), + array($advancedUser, $user), ); } diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php index 1b233cd9daa43..fefb81099756f 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php @@ -29,18 +29,6 @@ public function testConstructor() $this->assertTrue($token->isAuthenticated()); } - /** - * @expectedException \InvalidArgumentException - */ - public function testConstructorSecretCannotBeNull() - { - new RememberMeToken( - $this->getUser(), - null, - null - ); - } - /** * @expectedException \InvalidArgumentException */ @@ -57,7 +45,7 @@ protected function getUser($roles = array('ROLE_FOO')) { $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); $user - ->expects($this->once()) + ->expects($this->any()) ->method('getRoles') ->will($this->returnValue($roles)) ; diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php index 61d85274fc383..192fe87e2ad93 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -12,11 +12,8 @@ namespace Symfony\Component\Security\Core\Tests\Authorization; use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; -use Symfony\Component\Security\Core\Exception\LogicException; -use Symfony\Component\Security\Core\Tests\Authorization\Stub\VoterWithoutInterface; class AccessDecisionManagerTest extends TestCase { @@ -141,34 +138,4 @@ protected function getVoter($vote) return $voter; } - - public function testVotingWrongTypeNoVoteMethod() - { - $exception = LogicException::class; - $message = sprintf('stdClass should implement the %s interface when used as voter.', VoterInterface::class); - - if (method_exists($this, 'expectException')) { - $this->expectException($exception); - $this->expectExceptionMessage($message); - } else { - $this->setExpectedException($exception, $message); - } - - $adm = new AccessDecisionManager(array(new \stdClass())); - $token = $this->getMockBuilder(TokenInterface::class)->getMock(); - - $adm->decide($token, array('TEST')); - } - - /** - * @group legacy - * @expectedDeprecation Calling vote() on an voter without Symfony\Component\Security\Core\Authorization\Voter\VoterInterface is deprecated as of 3.4 and will be removed in 4.0. Implement the Symfony\Component\Security\Core\Authorization\Voter\VoterInterface on your voter. - */ - public function testVotingWrongTypeWithVote() - { - $adm = new AccessDecisionManager(array(new VoterWithoutInterface())); - $token = $this->getMockBuilder(TokenInterface::class)->getMock(); - - $adm->decide($token, array('TEST')); - } } diff --git a/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php b/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php index 8ec34843eaae0..6cf82d83586f9 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Tests\User; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserChecker; class UserCheckerTest extends TestCase @@ -24,6 +25,16 @@ public function testCheckPostAuthNotAdvancedUserInterface() } public function testCheckPostAuthPass() + { + $checker = new UserChecker(); + $this->assertNull($checker->checkPostAuth(new User('John', 'password'))); + } + + /** + * @group legacy + * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPostAuth with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality. + */ + public function testCheckPostAuthPassAdvancedUser() { $checker = new UserChecker(); @@ -39,21 +50,29 @@ public function testCheckPostAuthPass() public function testCheckPostAuthCredentialsExpired() { $checker = new UserChecker(); - - $account = $this->getMockBuilder('Symfony\Component\Security\Core\User\AdvancedUserInterface')->getMock(); - $account->expects($this->once())->method('isCredentialsNonExpired')->will($this->returnValue(false)); - - $checker->checkPostAuth($account); + $checker->checkPostAuth(new User('John', 'password', array(), true, true, false, true)); } - public function testCheckPreAuthNotAdvancedUserInterface() + /** + * @group legacy + * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPostAuth with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality. + * @expectedException \Symfony\Component\Security\Core\Exception\CredentialsExpiredException + */ + public function testCheckPostAuthCredentialsExpiredAdvancedUser() { $checker = new UserChecker(); - $this->assertNull($checker->checkPreAuth($this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock())); + $account = $this->getMockBuilder('Symfony\Component\Security\Core\User\AdvancedUserInterface')->getMock(); + $account->expects($this->once())->method('isCredentialsNonExpired')->will($this->returnValue(false)); + + $checker->checkPostAuth($account); } - public function testCheckPreAuthPass() + /** + * @group legacy + * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality. + */ + public function testCheckPreAuthPassAdvancedUser() { $checker = new UserChecker(); @@ -69,6 +88,17 @@ public function testCheckPreAuthPass() * @expectedException \Symfony\Component\Security\Core\Exception\LockedException */ public function testCheckPreAuthAccountLocked() + { + $checker = new UserChecker(); + $checker->checkPreAuth(new User('John', 'password', array(), true, true, false, false)); + } + + /** + * @group legacy + * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality. + * @expectedException \Symfony\Component\Security\Core\Exception\LockedException + */ + public function testCheckPreAuthAccountLockedAdvancedUser() { $checker = new UserChecker(); @@ -82,6 +112,17 @@ public function testCheckPreAuthAccountLocked() * @expectedException \Symfony\Component\Security\Core\Exception\DisabledException */ public function testCheckPreAuthDisabled() + { + $checker = new UserChecker(); + $checker->checkPreAuth(new User('John', 'password', array(), false, true, false, true)); + } + + /** + * @group legacy + * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality. + * @expectedException \Symfony\Component\Security\Core\Exception\DisabledException + */ + public function testCheckPreAuthDisabledAdvancedUser() { $checker = new UserChecker(); @@ -96,6 +137,17 @@ public function testCheckPreAuthDisabled() * @expectedException \Symfony\Component\Security\Core\Exception\AccountExpiredException */ public function testCheckPreAuthAccountExpired() + { + $checker = new UserChecker(); + $checker->checkPreAuth(new User('John', 'password', array(), true, false, true, true)); + } + + /** + * @group legacy + * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality. + * @expectedException \Symfony\Component\Security\Core\Exception\AccountExpiredException + */ + public function testCheckPreAuthAccountExpiredAdvancedUser() { $checker = new UserChecker(); diff --git a/src/Symfony/Component/Security/Core/Tests/User/UserTest.php b/src/Symfony/Component/Security/Core/Tests/User/UserTest.php index fa9f33107d90c..f052d85a1e7b8 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/UserTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/UserTest.php @@ -12,7 +12,9 @@ namespace Symfony\Component\Security\Core\Tests\User; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\User; +use Symfony\Component\Security\Core\User\UserInterface; class UserTest extends TestCase { @@ -99,4 +101,37 @@ public function testToString() $user = new User('fabien', 'superpass'); $this->assertEquals('fabien', (string) $user); } + + /** + * @dataProvider isEqualToData + * + * @param bool $expectation + * @param EquatableInterface|UserInterface $a + * @param EquatableInterface|UserInterface $b + */ + public function testIsEqualTo($expectation, $a, $b) + { + $this->assertSame($expectation, $a->isEqualTo($b)); + $this->assertSame($expectation, $b->isEqualTo($a)); + } + + public static function isEqualToData() + { + return array( + array(true, new User('username', 'password'), new User('username', 'password')), + array(true, new User('username', 'password', array('ROLE')), new User('username', 'password')), + array(true, new User('username', 'password', array('ROLE')), new User('username', 'password', array('NO ROLE'))), + array(false, new User('diff', 'diff'), new User('username', 'password')), + array(false, new User('diff', 'diff', array(), false), new User('username', 'password')), + array(false, new User('diff', 'diff', array(), false, false), new User('username', 'password')), + array(false, new User('diff', 'diff', array(), false, false, false), new User('username', 'password')), + array(false, new User('diff', 'diff', array(), false, false, false, false), new User('username', 'password')), + ); + } + + public function testIsEqualToWithDifferentUser() + { + $user = new User('username', 'password'); + $this->assertFalse($user->isEqualTo($this->getMockBuilder(UserInterface::class)->getMock())); + } } diff --git a/src/Symfony/Component/Security/Core/User/AdvancedUserInterface.php b/src/Symfony/Component/Security/Core/User/AdvancedUserInterface.php index 087c3c6e2695e..083833bc1fd1e 100644 --- a/src/Symfony/Component/Security/Core/User/AdvancedUserInterface.php +++ b/src/Symfony/Component/Security/Core/User/AdvancedUserInterface.php @@ -32,6 +32,7 @@ * * @see UserInterface * @see AccountStatusException + * @deprecated since version 4.1, will be removed in 5.0. * * @author Fabien Potencier */ diff --git a/src/Symfony/Component/Security/Core/User/ChainUserProvider.php b/src/Symfony/Component/Security/Core/User/ChainUserProvider.php index e00aa303c5d14..1faa723990f5a 100644 --- a/src/Symfony/Component/Security/Core/User/ChainUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/ChainUserProvider.php @@ -29,7 +29,7 @@ class ChainUserProvider implements UserProviderInterface /** * @param iterable|UserProviderInterface[] $providers */ - public function __construct($providers) + public function __construct(iterable $providers) { $this->providers = $providers; } diff --git a/src/Symfony/Component/Security/Core/User/EquatableInterface.php b/src/Symfony/Component/Security/Core/User/EquatableInterface.php index 4878637454cf4..704081014c5e2 100644 --- a/src/Symfony/Component/Security/Core/User/EquatableInterface.php +++ b/src/Symfony/Component/Security/Core/User/EquatableInterface.php @@ -26,9 +26,6 @@ interface EquatableInterface * However, you do not need to compare every attribute, but only those that * are relevant for assessing whether re-authentication is required. * - * Also implementation should consider that $user instance may implement - * the extended user interface `AdvancedUserInterface`. - * * @return bool */ public function isEqualTo(UserInterface $user); diff --git a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php index c585371253d7d..fcc3bcdb4236d 100644 --- a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php @@ -35,17 +35,7 @@ class LdapUserProvider implements UserProviderInterface private $defaultSearch; private $passwordAttribute; - /** - * @param LdapInterface $ldap - * @param string $baseDn - * @param string $searchDn - * @param string $searchPassword - * @param array $defaultRoles - * @param string $uidKey - * @param string $filter - * @param string $passwordAttribute - */ - public function __construct(LdapInterface $ldap, $baseDn, $searchDn = null, $searchPassword = null, array $defaultRoles = array(), $uidKey = 'sAMAccountName', $filter = '({uid_key}={username})', $passwordAttribute = null) + public function __construct(LdapInterface $ldap, string $baseDn, string $searchDn = null, string $searchPassword = null, array $defaultRoles = array(), string $uidKey = 'sAMAccountName', string $filter = '({uid_key}={username})', string $passwordAttribute = null) { if (null === $uidKey) { $uidKey = 'sAMAccountName'; diff --git a/src/Symfony/Component/Security/Core/User/User.php b/src/Symfony/Component/Security/Core/User/User.php index bc81f7ffa795a..4d612798f213c 100644 --- a/src/Symfony/Component/Security/Core/User/User.php +++ b/src/Symfony/Component/Security/Core/User/User.php @@ -18,7 +18,7 @@ * * @author Fabien Potencier */ -final class User implements AdvancedUserInterface +final class User implements UserInterface, EquatableInterface, AdvancedUserInterface { private $username; private $password; @@ -28,7 +28,7 @@ final class User implements AdvancedUserInterface private $accountNonLocked; private $roles; - public function __construct($username, $password, array $roles = array(), $enabled = true, $userNonExpired = true, $credentialsNonExpired = true, $userNonLocked = true) + public function __construct(?string $username, ?string $password, array $roles = array(), bool $enabled = true, bool $userNonExpired = true, bool $credentialsNonExpired = true, bool $userNonLocked = true) { if ('' === $username || null === $username) { throw new \InvalidArgumentException('The username cannot be empty.'); @@ -117,4 +117,44 @@ public function isEnabled() public function eraseCredentials() { } + + /** + * {@inheritdoc} + */ + public function isEqualTo(UserInterface $user) + { + if (!$user instanceof self) { + return false; + } + + if ($this->getPassword() !== $user->getPassword()) { + return false; + } + + if ($this->getSalt() !== $user->getSalt()) { + return false; + } + + if ($this->getUsername() !== $user->getUsername()) { + return false; + } + + if ($this->isAccountNonExpired() !== $user->isAccountNonExpired()) { + return false; + } + + if ($this->isAccountNonLocked() !== $user->isAccountNonLocked()) { + return false; + } + + if ($this->isCredentialsNonExpired() !== $user->isCredentialsNonExpired()) { + return false; + } + + if ($this->isEnabled() !== $user->isEnabled()) { + return false; + } + + return true; + } } diff --git a/src/Symfony/Component/Security/Core/User/UserChecker.php b/src/Symfony/Component/Security/Core/User/UserChecker.php index ac577a37ba0ca..308b6d96d52c7 100644 --- a/src/Symfony/Component/Security/Core/User/UserChecker.php +++ b/src/Symfony/Component/Security/Core/User/UserChecker.php @@ -28,10 +28,14 @@ class UserChecker implements UserCheckerInterface */ public function checkPreAuth(UserInterface $user) { - if (!$user instanceof AdvancedUserInterface) { + if (!$user instanceof AdvancedUserInterface && !$user instanceof User) { return; } + if ($user instanceof AdvancedUserInterface && !$user instanceof User) { + @trigger_error(sprintf('Calling %s with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality.', __METHOD__), E_USER_DEPRECATED); + } + if (!$user->isAccountNonLocked()) { $ex = new LockedException('User account is locked.'); $ex->setUser($user); @@ -56,10 +60,14 @@ public function checkPreAuth(UserInterface $user) */ public function checkPostAuth(UserInterface $user) { - if (!$user instanceof AdvancedUserInterface) { + if (!$user instanceof AdvancedUserInterface && !$user instanceof User) { return; } + if ($user instanceof AdvancedUserInterface && !$user instanceof User) { + @trigger_error(sprintf('Calling %s with an AdvancedUserInterface is deprecated as of 4.1 and will be removed in 5.0. Create a custom user checker if you wish to keep this functionality.', __METHOD__), E_USER_DEPRECATED); + } + if (!$user->isCredentialsNonExpired()) { $ex = new CredentialsExpiredException('User credentials have expired.'); $ex->setUser($user); diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php index 747884282dcaa..0a359d079da19 100644 --- a/src/Symfony/Component/Security/Core/User/UserInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserInterface.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Security\Core\User; -use Symfony\Component\Security\Core\Role\Role; - /** * Represents the interface that all user classes must implement. * @@ -27,7 +25,6 @@ * loaded by different objects that implement UserProviderInterface * * @see UserProviderInterface - * @see AdvancedUserInterface * * @author Fabien Potencier */ diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index b70ef54004ff6..0e92fa7b2d619 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -16,16 +16,15 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-php56": "~1.0" + "php": "^7.1.3" }, "require-dev": { "psr/container": "^1.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/ldap": "~3.1|~4.0", - "symfony/validator": "^3.2.5|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/ldap": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", "psr/log": "~1.0" }, "suggest": { @@ -45,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Security/Csrf/CsrfToken.php b/src/Symfony/Component/Security/Csrf/CsrfToken.php index 208fac3dba437..c959cc867d2a8 100644 --- a/src/Symfony/Component/Security/Csrf/CsrfToken.php +++ b/src/Symfony/Component/Security/Csrf/CsrfToken.php @@ -21,14 +21,10 @@ class CsrfToken private $id; private $value; - /** - * @param string $id The token ID - * @param string $value The actual token value - */ - public function __construct($id, $value) + public function __construct(string $id, ?string $value) { - $this->id = (string) $id; - $this->value = (string) $value; + $this->id = $id; + $this->value = $value ?? ''; } /** diff --git a/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php b/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php index f1e9122219478..59638518f5c42 100644 --- a/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php +++ b/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php @@ -25,7 +25,7 @@ class UriSafeTokenGenerator implements TokenGeneratorInterface * * @param int $entropy The amount of entropy collected for each token (in bits) */ - public function __construct($entropy = 256) + public function __construct(int $entropy = 256) { $this->entropy = $entropy; } diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php index d4866fd955917..d79e98d212c5d 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php @@ -33,7 +33,7 @@ class NativeSessionTokenStorage implements TokenStorageInterface * * @param string $namespace The namespace under which the token is stored in the session */ - public function __construct($namespace = self::SESSION_NAMESPACE) + public function __construct(string $namespace = self::SESSION_NAMESPACE) { $this->namespace = $namespace; } diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php index 7b00e3231b45a..a13f367b0ec30 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php @@ -35,7 +35,7 @@ class SessionTokenStorage implements TokenStorageInterface * @param SessionInterface $session The user session from which the session ID is returned * @param string $namespace The namespace under which the token is stored in the session */ - public function __construct(SessionInterface $session, $namespace = self::SESSION_NAMESPACE) + public function __construct(SessionInterface $session, string $namespace = self::SESSION_NAMESPACE) { $this->session = $session; $this->namespace = $namespace; diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 5b27fee3a4591..f13de8fefb2d1 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -16,16 +16,14 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-php56": "~1.0", - "symfony/polyfill-php70": "~1.0", - "symfony/security-core": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/security-core": "~3.4|~4.0" }, "require-dev": { - "symfony/http-foundation": "^2.8.31|~3.3.13|~3.4|~4.0" + "symfony/http-foundation": "~3.4|~4.0" }, "conflict": { - "symfony/http-foundation": "<2.8.31|~3.3,<3.3.13" + "symfony/http-foundation": "<3.4" }, "suggest": { "symfony/http-foundation": "For using the class SessionTokenStorage." @@ -39,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php index 5196abcc15430..cdfb613593928 100644 --- a/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php +++ b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Security\Guard; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; @@ -22,16 +21,6 @@ */ abstract class AbstractGuardAuthenticator implements AuthenticatorInterface { - /** - * {@inheritdoc} - */ - public function supports(Request $request) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Implement the "%s::supports()" method in class "%s" instead.', __METHOD__, AuthenticatorInterface::class, get_class($this)), E_USER_DEPRECATED); - - return true; - } - /** * Shortcut to create a PostAuthenticationGuardToken for you, if you don't really * care about which authenticated token you're using. diff --git a/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php b/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php index 7bd4550dc5363..df89871bef153 100644 --- a/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php @@ -15,7 +15,6 @@ use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\Util\TargetPathTrait; @@ -52,38 +51,6 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio return new RedirectResponse($url); } - /** - * Override to change what happens after successful authentication. - * - * @param Request $request - * @param TokenInterface $token - * @param string $providerKey - * - * @return RedirectResponse - */ - public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) - { - @trigger_error(sprintf('The AbstractFormLoginAuthenticator::onAuthenticationSuccess() implementation was deprecated in Symfony 3.1 and will be removed in Symfony 4.0. You should implement this method yourself in %s and remove getDefaultSuccessRedirectUrl().', get_class($this)), E_USER_DEPRECATED); - - if (!method_exists($this, 'getDefaultSuccessRedirectUrl')) { - throw new \Exception(sprintf('You must implement onAuthenticationSuccess() or getDefaultSuccessRedirectUrl() in %s.', get_class($this))); - } - - $targetPath = null; - - // if the user hit a secure page and start() was called, this was - // the URL they were on, and probably where you want to redirect to - if ($request->getSession() instanceof SessionInterface) { - $targetPath = $this->getTargetPath($request->getSession(), $providerKey); - } - - if (!$targetPath) { - $targetPath = $this->getDefaultSuccessRedirectUrl(); - } - - return new RedirectResponse($targetPath); - } - public function supportsRememberMe() { return true; diff --git a/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php index f68b8d6f43b08..10577d74cdffc 100644 --- a/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php @@ -12,6 +12,13 @@ namespace Symfony\Component\Security\Guard; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Guard\Token\GuardTokenInterface; +use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; /** * The interface for all "guard" authenticators. @@ -23,7 +30,7 @@ * @author Ryan Weaver * @author Amaury Leroux de Lens */ -interface AuthenticatorInterface extends GuardAuthenticatorInterface +interface AuthenticatorInterface extends AuthenticationEntryPointInterface { /** * Does the authenticator support the given Request? @@ -60,4 +67,104 @@ public function supports(Request $request); * @throws \UnexpectedValueException If null is returned */ public function getCredentials(Request $request); + + /** + * Return a UserInterface object based on the credentials. + * + * The *credentials* are the return value from getCredentials() + * + * You may throw an AuthenticationException if you wish. If you return + * null, then a UsernameNotFoundException is thrown for you. + * + * @param mixed $credentials + * @param UserProviderInterface $userProvider + * + * @throws AuthenticationException + * + * @return UserInterface|null + */ + public function getUser($credentials, UserProviderInterface $userProvider); + + /** + * Returns true if the credentials are valid. + * + * If any value other than true is returned, authentication will + * fail. You may also throw an AuthenticationException if you wish + * to cause authentication to fail. + * + * The *credentials* are the return value from getCredentials() + * + * @param mixed $credentials + * @param UserInterface $user + * + * @return bool + * + * @throws AuthenticationException + */ + public function checkCredentials($credentials, UserInterface $user); + + /** + * Create an authenticated token for the given user. + * + * If you don't care about which token class is used or don't really + * understand what a "token" is, you can skip this method by extending + * the AbstractGuardAuthenticator class from your authenticator. + * + * @see AbstractGuardAuthenticator + * + * @param UserInterface $user + * @param string $providerKey The provider (i.e. firewall) key + * + * @return GuardTokenInterface + */ + public function createAuthenticatedToken(UserInterface $user, $providerKey); + + /** + * Called when authentication executed, but failed (e.g. wrong username password). + * + * This should return the Response sent back to the user, like a + * RedirectResponse to the login page or a 403 response. + * + * If you return null, the request will continue, but the user will + * not be authenticated. This is probably not what you want to do. + * + * @param Request $request + * @param AuthenticationException $exception + * + * @return Response|null + */ + public function onAuthenticationFailure(Request $request, AuthenticationException $exception); + + /** + * Called when authentication executed and was successful! + * + * This should return the Response sent back to the user, like a + * RedirectResponse to the last page they visited. + * + * If you return null, the current request will continue, and the user + * will be authenticated. This makes sense, for example, with an API. + * + * @param Request $request + * @param TokenInterface $token + * @param string $providerKey The provider (i.e. firewall) key + * + * @return Response|null + */ + public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey); + + /** + * Does this method support remember me cookies? + * + * Remember me cookie will be set if *all* of the following are met: + * A) This method returns true + * B) The remember_me key under your firewall is configured + * C) The "remember me" functionality is activated. This is usually + * done by having a _remember_me checkbox in your form, but + * can be configured by the "always_remember_me" and "remember_me_parameter" + * parameters under the "remember_me" firewall key + * D) The onAuthenticationSuccess method returns a Response object + * + * @return bool + */ + public function supportsRememberMe(); } diff --git a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php index ec8375faff34f..80ae75caa2b85 100644 --- a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php +++ b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php @@ -16,10 +16,9 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; -use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\Security\Guard\AuthenticatorInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -48,7 +47,7 @@ class GuardAuthenticationListener implements ListenerInterface * @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider * @param LoggerInterface $logger A LoggerInterface instance */ - public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null) + public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, $guardAuthenticators, LoggerInterface $logger = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -93,7 +92,7 @@ public function handle(GetResponseEvent $event) } } - private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorInterface $guardAuthenticator, GetResponseEvent $event) + private function executeGuardAuthenticator($uniqueGuardKey, AuthenticatorInterface $guardAuthenticator, GetResponseEvent $event) { $request = $event->getRequest(); try { @@ -102,33 +101,14 @@ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorIn } // abort the execution of the authenticator if it doesn't support the request - if ($guardAuthenticator instanceof AuthenticatorInterface) { - if (!$guardAuthenticator->supports($request)) { - return; - } - // as there was a support for given request, - // authenticator is expected to give not-null credentials. - $credentialsCanBeNull = false; - } else { - // deprecated since version 3.4, to be removed in 4.0 - $credentialsCanBeNull = true; + if (!$guardAuthenticator->supports($request)) { + return; } // allow the authenticator to fetch authentication info from the request $credentials = $guardAuthenticator->getCredentials($request); if (null === $credentials) { - // deprecated since version 3.4, to be removed in 4.0 - if ($credentialsCanBeNull) { - return; - } - - if ($guardAuthenticator instanceof AbstractGuardAuthenticator) { - @trigger_error(sprintf('Returning null from "%1$s::getCredentials()" is deprecated since Symfony 3.4 and will throw an \UnexpectedValueException in 4.0. Return false from "%1$s::supports()" instead.', get_class($guardAuthenticator)), E_USER_DEPRECATED); - - return; - } - throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.', get_class($guardAuthenticator))); } @@ -194,7 +174,7 @@ public function setRememberMeServices(RememberMeServicesInterface $rememberMeSer * Checks to see if remember me is supported in the authenticator and * on the firewall. If it is, the RememberMeServicesInterface is notified. */ - private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null) + private function triggerRememberMe(AuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null) { if (null === $this->rememberMeServices) { if (null !== $this->logger) { diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php index 63b958a4b7b49..2a116e375d8a2 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php @@ -59,15 +59,8 @@ public function authenticateWithToken(TokenInterface $token, Request $request) /** * Returns the "on success" response for the given GuardAuthenticator. - * - * @param TokenInterface $token - * @param Request $request - * @param AuthenticatorInterface $guardAuthenticator - * @param string $providerKey The provider (i.e. firewall) key - * - * @return null|Response */ - public function handleAuthenticationSuccess(TokenInterface $token, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey) + public function handleAuthenticationSuccess(TokenInterface $token, Request $request, AuthenticatorInterface $guardAuthenticator, string $providerKey): ?Response { $response = $guardAuthenticator->onAuthenticationSuccess($request, $token, $providerKey); @@ -86,15 +79,8 @@ public function handleAuthenticationSuccess(TokenInterface $token, Request $requ /** * Convenience method for authenticating the user and returning the * Response *if any* for success. - * - * @param UserInterface $user - * @param Request $request - * @param AuthenticatorInterface $authenticator - * @param string $providerKey The provider (i.e. firewall) key - * - * @return Response|null */ - public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, GuardAuthenticatorInterface $authenticator, $providerKey) + public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, AuthenticatorInterface $authenticator, string $providerKey): ?Response { // create an authenticated token for the User $token = $authenticator->createAuthenticatedToken($user, $providerKey); @@ -108,15 +94,8 @@ public function authenticateUserAndHandleSuccess(UserInterface $user, Request $r /** * Handles an authentication failure and returns the Response for the * GuardAuthenticator. - * - * @param AuthenticationException $authenticationException - * @param Request $request - * @param AuthenticatorInterface $guardAuthenticator - * @param string $providerKey The key of the firewall - * - * @return null|Response */ - public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey) + public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, AuthenticatorInterface $guardAuthenticator, string $providerKey): ?Response { $token = $this->tokenStorage->getToken(); if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) { diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php deleted file mode 100644 index 0d11573c03b11..0000000000000 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php +++ /dev/null @@ -1,163 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Guard; - -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; -use Symfony\Component\Security\Guard\Token\GuardTokenInterface; -use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; - -/** - * The interface for all "guard" authenticators. - * - * The methods on this interface are called throughout the guard authentication - * process to give you the power to control most parts of the process from - * one location. - * - * @author Ryan Weaver - * - * @deprecated since version 3.4, to be removed in 4.0. Use AuthenticatorInterface instead - */ -interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface -{ - /** - * Get the authentication credentials from the request and return them - * as any type (e.g. an associate array). If you return null, authentication - * will be skipped. - * - * Whatever value you return here will be passed to getUser() and checkCredentials() - * - * For example, for a form login, you might: - * - * if ($request->request->has('_username')) { - * return array( - * 'username' => $request->request->get('_username'), - * 'password' => $request->request->get('_password'), - * ); - * } else { - * return; - * } - * - * Or for an API token that's on a header, you might use: - * - * return array('api_key' => $request->headers->get('X-API-TOKEN')); - * - * @param Request $request - * - * @return mixed|null - */ - public function getCredentials(Request $request); - - /** - * Return a UserInterface object based on the credentials. - * - * The *credentials* are the return value from getCredentials() - * - * You may throw an AuthenticationException if you wish. If you return - * null, then a UsernameNotFoundException is thrown for you. - * - * @param mixed $credentials - * @param UserProviderInterface $userProvider - * - * @throws AuthenticationException - * - * @return UserInterface|null - */ - public function getUser($credentials, UserProviderInterface $userProvider); - - /** - * Returns true if the credentials are valid. - * - * If any value other than true is returned, authentication will - * fail. You may also throw an AuthenticationException if you wish - * to cause authentication to fail. - * - * The *credentials* are the return value from getCredentials() - * - * @param mixed $credentials - * @param UserInterface $user - * - * @return bool - * - * @throws AuthenticationException - */ - public function checkCredentials($credentials, UserInterface $user); - - /** - * Create an authenticated token for the given user. - * - * If you don't care about which token class is used or don't really - * understand what a "token" is, you can skip this method by extending - * the AbstractGuardAuthenticator class from your authenticator. - * - * @see AbstractGuardAuthenticator - * - * @param UserInterface $user - * @param string $providerKey The provider (i.e. firewall) key - * - * @return GuardTokenInterface - */ - public function createAuthenticatedToken(UserInterface $user, $providerKey); - - /** - * Called when authentication executed, but failed (e.g. wrong username password). - * - * This should return the Response sent back to the user, like a - * RedirectResponse to the login page or a 403 response. - * - * If you return null, the request will continue, but the user will - * not be authenticated. This is probably not what you want to do. - * - * @param Request $request - * @param AuthenticationException $exception - * - * @return Response|null - */ - public function onAuthenticationFailure(Request $request, AuthenticationException $exception); - - /** - * Called when authentication executed and was successful! - * - * This should return the Response sent back to the user, like a - * RedirectResponse to the last page they visited. - * - * If you return null, the current request will continue, and the user - * will be authenticated. This makes sense, for example, with an API. - * - * @param Request $request - * @param TokenInterface $token - * @param string $providerKey The provider (i.e. firewall) key - * - * @return Response|null - */ - public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey); - - /** - * Does this method support remember me cookies? - * - * Remember me cookie will be set if *all* of the following are met: - * A) This method returns true - * B) The remember_me key under your firewall is configured - * C) The "remember me" functionality is activated. This is usually - * done by having a _remember_me checkbox in your form, but - * can be configured by the "always_remember_me" and "remember_me_parameter" - * parameters under the "remember_me" firewall key - * D) The onAuthenticationSuccess method returns a Response object - * - * @return bool - */ - public function supportsRememberMe(); -} diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 2f2678035fbcf..9ca26a74c2343 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -45,7 +45,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface * @param string $providerKey The provider (i.e. firewall) key * @param UserCheckerInterface $userChecker */ - public function __construct($guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker) + public function __construct($guardAuthenticators, UserProviderInterface $userProvider, string $providerKey, UserCheckerInterface $userChecker) { $this->guardAuthenticators = $guardAuthenticators; $this->userProvider = $userProvider; diff --git a/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php index 77d7194280c1b..e9759cd4f2ec0 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -51,59 +52,6 @@ public function testAuthenticationFailureWithSession() $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); } - /** - * @group legacy - */ - public function testAuthenticationSuccessWithoutSession() - { - $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') - ->disableOriginalConstructor() - ->getMock(); - - $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithoutSession, $token, 'providerkey'); - - $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); - $this->assertEquals(self::DEFAULT_SUCCESS_URL, $redirectResponse->getTargetUrl()); - } - - /** - * @group legacy - */ - public function testAuthenticationSuccessWithSessionButEmpty() - { - $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') - ->disableOriginalConstructor() - ->getMock(); - $this->requestWithSession->getSession() - ->expects($this->once()) - ->method('get') - ->will($this->returnValue(null)); - - $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey'); - - $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); - $this->assertEquals(self::DEFAULT_SUCCESS_URL, $redirectResponse->getTargetUrl()); - } - - /** - * @group legacy - */ - public function testAuthenticationSuccessWithSessionAndTarget() - { - $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') - ->disableOriginalConstructor() - ->getMock(); - $this->requestWithSession->getSession() - ->expects($this->once()) - ->method('get') - ->will($this->returnValue(self::CUSTOM_SUCCESS_URL)); - - $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey'); - - $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); - $this->assertEquals(self::CUSTOM_SUCCESS_URL, $redirectResponse->getTargetUrl()); - } - public function testRememberMe() { $doSupport = $this->authenticator->supportsRememberMe(); @@ -156,6 +104,15 @@ class TestFormLoginAuthenticator extends AbstractFormLoginAuthenticator private $loginUrl; private $defaultSuccessRedirectUrl; + public function supports(Request $request) + { + return true; + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) + { + } + /** * @param mixed $defaultSuccessRedirectUrl * diff --git a/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php index 0d5f2f2fe3fe9..e4a47fd8f0eb9 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php @@ -15,10 +15,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener; -use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -208,131 +206,6 @@ public function testHandleCatchesAuthenticationException() $listener->handle($this->event); } - /** - * @group legacy - */ - public function testLegacyInterfaceNullCredentials() - { - $authenticatorA = $this->getMockBuilder(GuardAuthenticatorInterface::class)->getMock(); - $providerKey = 'my_firewall3'; - - $authenticatorA - ->expects($this->once()) - ->method('getCredentials') - ->will($this->returnValue(null)); - - // this is not called - $this->authenticationManager - ->expects($this->never()) - ->method('authenticate'); - - $this->guardAuthenticatorHandler - ->expects($this->never()) - ->method('handleAuthenticationSuccess'); - - $listener = new GuardAuthenticationListener( - $this->guardAuthenticatorHandler, - $this->authenticationManager, - $providerKey, - array($authenticatorA), - $this->logger - ); - - $listener->handle($this->event); - } - - /** - * @group legacy - */ - public function testLegacyInterfaceKeepsWorking() - { - $authenticator = $this->getMockBuilder(GuardAuthenticatorInterface::class)->getMock(); - $authenticateToken = $this->getMockBuilder(TokenInterface::class)->getMock(); - $providerKey = 'my_firewall'; - - $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base'); - - $authenticator - ->expects($this->once()) - ->method('getCredentials') - ->with($this->equalTo($this->request)) - ->will($this->returnValue($credentials)); - - // a clone of the token that should be created internally - $uniqueGuardKey = 'my_firewall_0'; - $nonAuthedToken = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey); - - $this->authenticationManager - ->expects($this->once()) - ->method('authenticate') - ->with($this->equalTo($nonAuthedToken)) - ->will($this->returnValue($authenticateToken)); - - $this->guardAuthenticatorHandler - ->expects($this->once()) - ->method('authenticateWithToken') - ->with($authenticateToken, $this->request); - - $this->guardAuthenticatorHandler - ->expects($this->once()) - ->method('handleAuthenticationSuccess') - ->with($authenticateToken, $this->request, $authenticator, $providerKey); - - $listener = new GuardAuthenticationListener( - $this->guardAuthenticatorHandler, - $this->authenticationManager, - $providerKey, - array($authenticator), - $this->logger - ); - - $listener->setRememberMeServices($this->rememberMeServices); - // should never be called - our handleAuthenticationSuccess() does not return a Response - $this->rememberMeServices - ->expects($this->never()) - ->method('loginSuccess'); - - $listener->handle($this->event); - } - - /** - * @group legacy - */ - public function testReturnNullToSkipAuth() - { - $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $providerKey = 'my_firewall3'; - - $authenticatorA - ->expects($this->once()) - ->method('getCredentials') - ->will($this->returnValue(null)); - $authenticatorB - ->expects($this->once()) - ->method('getCredentials') - ->will($this->returnValue(null)); - - // this is not called - $this->authenticationManager - ->expects($this->never()) - ->method('authenticate'); - - $this->guardAuthenticatorHandler - ->expects($this->never()) - ->method('handleAuthenticationSuccess'); - - $listener = new GuardAuthenticationListener( - $this->guardAuthenticatorHandler, - $this->authenticationManager, - $providerKey, - array($authenticatorA, $authenticatorB), - $this->logger - ); - - $listener->handle($this->event); - } - public function testSupportsReturnFalseSkipAuth() { $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); @@ -389,37 +262,6 @@ public function testReturnNullFromGetCredentials() $listener->handle($this->event); } - /** - * @group legacy - * @expectedDeprecation Returning null from "%s::getCredentials()" is deprecated since Symfony 3.4 and will throw an \UnexpectedValueException in 4.0. Return false from "%s::supports()" instead. - */ - public function testReturnNullFromGetCredentialsTriggersForAbstractGuardAuthenticatorInstances() - { - $authenticator = $this->getMockBuilder(AbstractGuardAuthenticator::class)->getMock(); - $providerKey = 'my_firewall4'; - - $authenticator - ->expects($this->once()) - ->method('supports') - ->will($this->returnValue(true)); - - // this will raise exception - $authenticator - ->expects($this->once()) - ->method('getCredentials') - ->will($this->returnValue(null)); - - $listener = new GuardAuthenticationListener( - $this->guardAuthenticatorHandler, - $this->authenticationManager, - $providerKey, - array($authenticator), - $this->logger - ); - - $listener->handle($this->event); - } - protected function setUp() { $this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager') diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index 9d8301fce8011..71b53f62f36b2 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -86,68 +86,6 @@ public function testAuthenticate() $this->assertSame($authedToken, $actualAuthedToken); } - /** - * @group legacy - */ - public function testLegacyAuthenticate() - { - $providerKey = 'my_cool_firewall'; - - $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticatorC = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticators = array($authenticatorA, $authenticatorB, $authenticatorC); - - // called 2 times - for authenticator A and B (stops on B because of match) - $this->preAuthenticationToken->expects($this->exactly(2)) - ->method('getGuardProviderKey') - // it will return the "1" index, which will match authenticatorB - ->will($this->returnValue('my_cool_firewall_1')); - - $enteredCredentials = array( - 'username' => '_weaverryan_test_user', - 'password' => 'guard_auth_ftw', - ); - $this->preAuthenticationToken->expects($this->atLeastOnce()) - ->method('getCredentials') - ->will($this->returnValue($enteredCredentials)); - - // authenticators A and C are never called - $authenticatorA->expects($this->never()) - ->method('getUser'); - $authenticatorC->expects($this->never()) - ->method('getUser'); - - $mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); - $authenticatorB->expects($this->once()) - ->method('getUser') - ->with($enteredCredentials, $this->userProvider) - ->will($this->returnValue($mockedUser)); - // checkCredentials is called - $authenticatorB->expects($this->once()) - ->method('checkCredentials') - ->with($enteredCredentials, $mockedUser) - // authentication works! - ->will($this->returnValue(true)); - $authedToken = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); - $authenticatorB->expects($this->once()) - ->method('createAuthenticatedToken') - ->with($mockedUser, $providerKey) - ->will($this->returnValue($authedToken)); - - // user checker should be called - $this->userChecker->expects($this->once()) - ->method('checkPreAuth') - ->with($mockedUser); - $this->userChecker->expects($this->once()) - ->method('checkPostAuth') - ->with($mockedUser); - - $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker); - $actualAuthedToken = $provider->authenticate($this->preAuthenticationToken); - $this->assertSame($authedToken, $actualAuthedToken); - } - /** * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException */ @@ -155,7 +93,7 @@ public function testCheckCredentialsReturningNonTrueFailsAuthentication() { $providerKey = 'my_uncool_firewall'; - $authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); // make sure the authenticator is used $this->preAuthenticationToken->expects($this->any()) diff --git a/src/Symfony/Component/Security/Guard/Token/PostAuthenticationGuardToken.php b/src/Symfony/Component/Security/Guard/Token/PostAuthenticationGuardToken.php index f566b71242353..ad479ac24e5af 100644 --- a/src/Symfony/Component/Security/Guard/Token/PostAuthenticationGuardToken.php +++ b/src/Symfony/Component/Security/Guard/Token/PostAuthenticationGuardToken.php @@ -34,7 +34,7 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn * * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, $providerKey, array $roles) + public function __construct(UserInterface $user, string $providerKey, array $roles) { parent::__construct($roles); diff --git a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php index e1b39417d78e2..9f1c046e4cc1e 100644 --- a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php +++ b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php @@ -31,7 +31,7 @@ class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInt * @param mixed $credentials * @param string $guardProviderKey Unique key that bind this token to a specific AuthenticatorInterface */ - public function __construct($credentials, $guardProviderKey) + public function __construct($credentials, string $guardProviderKey) { $this->credentials = $credentials; $this->guardProviderKey = $guardProviderKey; diff --git a/src/Symfony/Component/Security/Guard/composer.json b/src/Symfony/Component/Security/Guard/composer.json index a4c0f06d569c2..e1faa18d81f47 100644 --- a/src/Symfony/Component/Security/Guard/composer.json +++ b/src/Symfony/Component/Security/Guard/composer.json @@ -16,9 +16,9 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/security-core": "~2.8|~3.0|~4.0", - "symfony/security-http": "~3.1|~4.0" + "php": "^7.1.3", + "symfony/security-core": "~3.4|~4.0", + "symfony/security-http": "~3.4|~4.0" }, "require-dev": { "psr/log": "~1.0" @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php index 32ea2b1c304bd..ba886373e969c 100644 --- a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php +++ b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php @@ -71,11 +71,9 @@ public function getLastUsername() } /** - * @return Request - * * @throws \LogicException */ - private function getRequest() + private function getRequest(): Request { $request = $this->requestStack->getCurrentRequest(); diff --git a/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php b/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php index 369e2d14c7893..64c6d32a91cd0 100644 --- a/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php +++ b/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php @@ -26,7 +26,7 @@ class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler * @param array $options Options for processing a successful authentication attempt * @param string $providerKey The provider key */ - public function __construct(AuthenticationSuccessHandlerInterface $handler, array $options, $providerKey) + public function __construct(AuthenticationSuccessHandlerInterface $handler, array $options, string $providerKey) { $this->handler = $handler; if (method_exists($handler, 'setOptions')) { diff --git a/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php b/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php new file mode 100644 index 0000000000000..221d8d8eada5c --- /dev/null +++ b/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * Supports the argument type of {@see UserInterface}. + * + * @author Iltar van der Berg + */ +final class UserValueResolver implements ArgumentValueResolverInterface +{ + private $tokenStorage; + + public function __construct(TokenStorageInterface $tokenStorage) + { + $this->tokenStorage = $tokenStorage; + } + + public function supports(Request $request, ArgumentMetadata $argument) + { + // only security user implementations are supported + if (UserInterface::class !== $argument->getType()) { + return false; + } + + $token = $this->tokenStorage->getToken(); + if (!$token instanceof TokenInterface) { + return false; + } + + $user = $token->getUser(); + + // in case it's not an object we cannot do anything with it; E.g. "anon." + return $user instanceof UserInterface; + } + + public function resolve(Request $request, ArgumentMetadata $argument) + { + yield $this->tokenStorage->getToken()->getUser(); + } +} diff --git a/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php index 2dc3d1196d154..4e60428df749a 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php @@ -24,7 +24,7 @@ class BasicAuthenticationEntryPoint implements AuthenticationEntryPointInterface { private $realmName; - public function __construct($realmName) + public function __construct(string $realmName) { $this->realmName = $realmName; } diff --git a/src/Symfony/Component/Security/Http/EntryPoint/DigestAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/DigestAuthenticationEntryPoint.php deleted file mode 100644 index 7501a4ef80293..0000000000000 --- a/src/Symfony/Component/Security/Http/EntryPoint/DigestAuthenticationEntryPoint.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Http\EntryPoint; - -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\NonceExpiredException; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Request; -use Psr\Log\LoggerInterface; - -/** - * DigestAuthenticationEntryPoint starts an HTTP Digest authentication. - * - * @author Fabien Potencier - * - * @deprecated since 3.4, to be removed in 4.0 - */ -class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterface -{ - private $secret; - private $realmName; - private $nonceValiditySeconds; - private $logger; - - public function __construct($realmName, $secret, $nonceValiditySeconds = 300, LoggerInterface $logger = null) - { - @trigger_error(sprintf('The %s class and the whole HTTP digest authentication system is deprecated since Symfony 3.4 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - - $this->realmName = $realmName; - $this->secret = $secret; - $this->nonceValiditySeconds = $nonceValiditySeconds; - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function start(Request $request, AuthenticationException $authException = null) - { - $expiryTime = microtime(true) + $this->nonceValiditySeconds * 1000; - $signatureValue = md5($expiryTime.':'.$this->secret); - $nonceValue = $expiryTime.':'.$signatureValue; - $nonceValueBase64 = base64_encode($nonceValue); - - $authenticateHeader = sprintf('Digest realm="%s", qop="auth", nonce="%s"', $this->realmName, $nonceValueBase64); - - if ($authException instanceof NonceExpiredException) { - $authenticateHeader .= ', stale="true"'; - } - - if (null !== $this->logger) { - $this->logger->debug('WWW-Authenticate header sent.', array('header' => $authenticateHeader)); - } - - $response = new Response(); - $response->headers->set('WWW-Authenticate', $authenticateHeader); - $response->setStatusCode(401); - - return $response; - } - - /** - * @return string - */ - public function getSecret() - { - return $this->secret; - } - - /** - * @return string - */ - public function getRealmName() - { - return $this->realmName; - } -} diff --git a/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php index fbef67ea542a4..3ebdeffec8998 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php @@ -34,12 +34,12 @@ class FormAuthenticationEntryPoint implements AuthenticationEntryPointInterface * @param string $loginPath The path to the login form * @param bool $useForward Whether to forward or redirect to the login form */ - public function __construct(HttpKernelInterface $kernel, HttpUtils $httpUtils, $loginPath, $useForward = false) + public function __construct(HttpKernelInterface $kernel, HttpUtils $httpUtils, string $loginPath, bool $useForward = false) { $this->httpKernel = $kernel; $this->httpUtils = $httpUtils; $this->loginPath = $loginPath; - $this->useForward = (bool) $useForward; + $this->useForward = $useForward; } /** diff --git a/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php index d1a0a2880652f..bad771872ec5c 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php @@ -27,7 +27,7 @@ class RetryAuthenticationEntryPoint implements AuthenticationEntryPointInterface private $httpPort; private $httpsPort; - public function __construct($httpPort = 80, $httpsPort = 443) + public function __construct(int $httpPort = 80, int $httpsPort = 443) { $this->httpPort = $httpPort; $this->httpsPort = $httpsPort; diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php index 71a9311a93e3f..7002810ed76af 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php @@ -64,21 +64,9 @@ abstract class AbstractAuthenticationListener implements ListenerInterface private $rememberMeServices; /** - * @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance - * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance - * @param SessionAuthenticationStrategyInterface $sessionStrategy - * @param HttpUtils $httpUtils An HttpUtils instance - * @param string $providerKey - * @param AuthenticationSuccessHandlerInterface $successHandler - * @param AuthenticationFailureHandlerInterface $failureHandler - * @param array $options An array of options for the processing of a - * successful, or failed authentication attempt - * @param LoggerInterface|null $logger A LoggerInterface instance - * @param EventDispatcherInterface|null $dispatcher An EventDispatcherInterface instance - * * @throws \InvalidArgumentException */ - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php index 0065fe8237c3e..1057868ddb337 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php @@ -38,7 +38,7 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface private $providerKey; private $dispatcher; - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; diff --git a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php index 3f4de13df54b9..106b99f840258 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php @@ -31,7 +31,7 @@ class AnonymousAuthenticationListener implements ListenerInterface private $authenticationManager; private $logger; - public function __construct(TokenStorageInterface $tokenStorage, $secret, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null) + public function __construct(TokenStorageInterface $tokenStorage, string $secret, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null) { $this->tokenStorage = $tokenStorage; $this->secret = $secret; diff --git a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php index 1ddc41643448e..f8a01ad405ab0 100644 --- a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php @@ -33,7 +33,7 @@ class BasicAuthenticationListener implements ListenerInterface private $logger; private $ignoreFailure; - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 5b74523d631b0..f748dd24d8e9d 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -43,17 +43,12 @@ class ContextListener implements ListenerInterface private $dispatcher; private $registered; private $trustResolver; - private $logoutOnUserChange = false; /** - * @param TokenStorageInterface $tokenStorage - * @param iterable|UserProviderInterface[] $userProviders - * @param string $contextKey - * @param LoggerInterface|null $logger - * @param EventDispatcherInterface|null $dispatcher - * @param AuthenticationTrustResolverInterface|null $trustResolver + * @param TokenStorageInterface $tokenStorage + * @param iterable|UserProviderInterface[] $userProviders */ - public function __construct(TokenStorageInterface $tokenStorage, $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null) + public function __construct(TokenStorageInterface $tokenStorage, iterable $userProviders, string $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null) { if (empty($contextKey)) { throw new \InvalidArgumentException('$contextKey must not be empty.'); @@ -71,10 +66,12 @@ public function __construct(TokenStorageInterface $tokenStorage, $userProviders, * Enables deauthentication during refreshUser when the user has changed. * * @param bool $logoutOnUserChange + * + * @deprecated since Symfony 4.1, to be removed in 5.0 */ public function setLogoutOnUserChange($logoutOnUserChange) { - $this->logoutOnUserChange = (bool) $logoutOnUserChange; + @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1 and will be removed in 5.0.', __METHOD__), E_USER_DEPRECATED); } /** @@ -177,15 +174,11 @@ protected function refreshUser(TokenInterface $token) // tokens can be deauthenticated if the user has been changed. if (!$token->isAuthenticated()) { - if ($this->logoutOnUserChange) { - if (null !== $this->logger) { - $this->logger->debug('Token was deauthenticated after trying to refresh it.', array('username' => $refreshedUser->getUsername(), 'provider' => get_class($provider))); - } - - return null; + if (null !== $this->logger) { + $this->logger->debug('Token was deauthenticated after trying to refresh it.', array('username' => $refreshedUser->getUsername(), 'provider' => get_class($provider))); } - @trigger_error('Refreshing a deauthenticated user is deprecated as of 3.4 and will trigger a logout in 4.0.', E_USER_DEPRECATED); + return null; } if (null !== $this->logger) { diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php deleted file mode 100644 index 92f96c6034e13..0000000000000 --- a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php +++ /dev/null @@ -1,226 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Http\Firewall; - -use Symfony\Component\Security\Core\User\UserProviderInterface; -use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; -use Psr\Log\LoggerInterface; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; -use Symfony\Component\Security\Core\Exception\NonceExpiredException; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\Exception\AuthenticationException; - -/** - * DigestAuthenticationListener implements Digest HTTP authentication. - * - * @author Fabien Potencier - * - * @deprecated since 3.4, to be removed in 4.0 - */ -class DigestAuthenticationListener implements ListenerInterface -{ - private $tokenStorage; - private $provider; - private $providerKey; - private $authenticationEntryPoint; - private $logger; - - public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null) - { - @trigger_error(sprintf('The %s class and the whole HTTP digest authentication system is deprecated since Symfony 3.4 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - - if (empty($providerKey)) { - throw new \InvalidArgumentException('$providerKey must not be empty.'); - } - - $this->tokenStorage = $tokenStorage; - $this->provider = $provider; - $this->providerKey = $providerKey; - $this->authenticationEntryPoint = $authenticationEntryPoint; - $this->logger = $logger; - } - - /** - * Handles digest authentication. - * - * @throws AuthenticationServiceException - */ - public function handle(GetResponseEvent $event) - { - $request = $event->getRequest(); - - if (!$header = $request->server->get('PHP_AUTH_DIGEST')) { - return; - } - - $digestAuth = new DigestData($header); - - if (null !== $token = $this->tokenStorage->getToken()) { - if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $digestAuth->getUsername()) { - return; - } - } - - if (null !== $this->logger) { - $this->logger->debug('Digest Authorization header received from user agent.', array('header' => $header)); - } - - try { - $digestAuth->validateAndDecode($this->authenticationEntryPoint->getSecret(), $this->authenticationEntryPoint->getRealmName()); - } catch (BadCredentialsException $e) { - $this->fail($event, $request, $e); - - return; - } - - try { - $user = $this->provider->loadUserByUsername($digestAuth->getUsername()); - - if (null === $user) { - throw new AuthenticationServiceException('Digest User provider returned null, which is an interface contract violation'); - } - - $serverDigestMd5 = $digestAuth->calculateServerDigest($user->getPassword(), $request->getMethod()); - } catch (UsernameNotFoundException $e) { - $this->fail($event, $request, new BadCredentialsException(sprintf('Username %s not found.', $digestAuth->getUsername()))); - - return; - } - - if (!hash_equals($serverDigestMd5, $digestAuth->getResponse())) { - if (null !== $this->logger) { - $this->logger->debug('Unexpected response from the DigestAuth received; is the header returning a clear text passwords?', array('expected' => $serverDigestMd5, 'received' => $digestAuth->getResponse())); - } - - $this->fail($event, $request, new BadCredentialsException('Incorrect response')); - - return; - } - - if ($digestAuth->isNonceExpired()) { - $this->fail($event, $request, new NonceExpiredException('Nonce has expired/timed out.')); - - return; - } - - if (null !== $this->logger) { - $this->logger->info('Digest authentication successful.', array('username' => $digestAuth->getUsername(), 'received' => $digestAuth->getResponse())); - } - - $this->tokenStorage->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey)); - } - - private function fail(GetResponseEvent $event, Request $request, AuthenticationException $authException) - { - $token = $this->tokenStorage->getToken(); - if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) { - $this->tokenStorage->setToken(null); - } - - if (null !== $this->logger) { - $this->logger->info('Digest authentication failed.', array('exception' => $authException)); - } - - $event->setResponse($this->authenticationEntryPoint->start($request, $authException)); - } -} - -/** - * @deprecated since 3.4, to be removed in 4.0. - */ -class DigestData -{ - private $elements = array(); - private $header; - private $nonceExpiryTime; - - public function __construct($header) - { - @trigger_error(sprintf('The %s class and the whole HTTP digest authentication system is deprecated since Symfony 3.4 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); - - $this->header = $header; - preg_match_all('/(\w+)=("((?:[^"\\\\]|\\\\.)+)"|([^\s,$]+))/', $header, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - if (isset($match[1]) && isset($match[3])) { - $this->elements[$match[1]] = isset($match[4]) ? $match[4] : $match[3]; - } - } - } - - public function getResponse() - { - return $this->elements['response']; - } - - public function getUsername() - { - return strtr($this->elements['username'], array('\\"' => '"', '\\\\' => '\\')); - } - - public function validateAndDecode($entryPointKey, $expectedRealm) - { - if ($keys = array_diff(array('username', 'realm', 'nonce', 'uri', 'response'), array_keys($this->elements))) { - throw new BadCredentialsException(sprintf('Missing mandatory digest value; received header "%s" (%s)', $this->header, implode(', ', $keys))); - } - - if ('auth' === $this->elements['qop'] && !isset($this->elements['nc'], $this->elements['cnonce'])) { - throw new BadCredentialsException(sprintf('Missing mandatory digest value; received header "%s"', $this->header)); - } - - if ($expectedRealm !== $this->elements['realm']) { - throw new BadCredentialsException(sprintf('Response realm name "%s" does not match system realm name of "%s".', $this->elements['realm'], $expectedRealm)); - } - - if (false === $nonceAsPlainText = base64_decode($this->elements['nonce'])) { - throw new BadCredentialsException(sprintf('Nonce is not encoded in Base64; received nonce "%s".', $this->elements['nonce'])); - } - - $nonceTokens = explode(':', $nonceAsPlainText); - - if (2 !== count($nonceTokens)) { - throw new BadCredentialsException(sprintf('Nonce should have yielded two tokens but was "%s".', $nonceAsPlainText)); - } - - $this->nonceExpiryTime = $nonceTokens[0]; - - if (md5($this->nonceExpiryTime.':'.$entryPointKey) !== $nonceTokens[1]) { - throw new BadCredentialsException(sprintf('Nonce token compromised "%s".', $nonceAsPlainText)); - } - } - - public function calculateServerDigest($password, $httpMethod) - { - $a2Md5 = md5(strtoupper($httpMethod).':'.$this->elements['uri']); - $a1Md5 = md5($this->elements['username'].':'.$this->elements['realm'].':'.$password); - - $digest = $a1Md5.':'.$this->elements['nonce']; - if (!isset($this->elements['qop'])) { - } elseif ('auth' === $this->elements['qop']) { - $digest .= ':'.$this->elements['nc'].':'.$this->elements['cnonce'].':'.$this->elements['qop']; - } else { - throw new \InvalidArgumentException(sprintf('This method does not support a qop: "%s".', $this->elements['qop'])); - } - $digest .= ':'.$a2Md5; - - return md5($digest); - } - - public function isNonceExpired() - { - return $this->nonceExpiryTime < microtime(true); - } -} diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index 355f20bd689c0..76ae56bd68684 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -52,7 +52,7 @@ class ExceptionListener private $httpUtils; private $stateless; - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null, $stateless = false) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, string $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint = null, string $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null, bool $stateless = false) { $this->tokenStorage = $tokenStorage; $this->accessDeniedHandler = $accessDeniedHandler; @@ -98,7 +98,7 @@ public function onKernelException(GetResponseForExceptionEvent $event) } while (null !== $exception = $exception->getPrevious()); } - private function handleAuthenticationException(GetResponseForExceptionEvent $event, AuthenticationException $exception) + private function handleAuthenticationException(GetResponseForExceptionEvent $event, AuthenticationException $exception): void { if (null !== $this->logger) { $this->logger->info('An AuthenticationException was thrown; redirecting to authentication entry point.', array('exception' => $exception)); @@ -161,19 +161,14 @@ private function handleAccessDeniedException(GetResponseForExceptionEvent $event } } - private function handleLogoutException(LogoutException $exception) + private function handleLogoutException(LogoutException $exception): void { if (null !== $this->logger) { $this->logger->info('A LogoutException was thrown.', array('exception' => $exception)); } } - /** - * @return Response - * - * @throws AuthenticationException - */ - private function startAuthentication(Request $request, AuthenticationException $authException) + private function startAuthentication(Request $request, AuthenticationException $authException): Response { if (null === $this->authenticationEntryPoint) { throw $authException; diff --git a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php index 13cb8755dfacd..ee69a793b6757 100644 --- a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php @@ -38,16 +38,7 @@ class RememberMeListener implements ListenerInterface private $catchExceptions = true; private $sessionStrategy; - /** - * @param TokenStorageInterface $tokenStorage - * @param RememberMeServicesInterface $rememberMeServices - * @param AuthenticationManagerInterface $authenticationManager - * @param LoggerInterface|null $logger - * @param EventDispatcherInterface|null $dispatcher - * @param bool $catchExceptions - * @param SessionAuthenticationStrategyInterface|null $sessionStrategy - */ - public function __construct(TokenStorageInterface $tokenStorage, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, $catchExceptions = true, SessionAuthenticationStrategyInterface $sessionStrategy = null) + public function __construct(TokenStorageInterface $tokenStorage, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, bool $catchExceptions = true, SessionAuthenticationStrategyInterface $sessionStrategy = null) { $this->tokenStorage = $tokenStorage; $this->rememberMeServices = $rememberMeServices; diff --git a/src/Symfony/Component/Security/Http/Firewall/RemoteUserAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/RemoteUserAuthenticationListener.php index c42badff26a7d..69dcd4d6f16d3 100644 --- a/src/Symfony/Component/Security/Http/Firewall/RemoteUserAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/RemoteUserAuthenticationListener.php @@ -28,7 +28,7 @@ class RemoteUserAuthenticationListener extends AbstractPreAuthenticatedListener { private $userKey; - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'REMOTE_USER', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, string $userKey = 'REMOTE_USER', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { parent::__construct($tokenStorage, $authenticationManager, $providerKey, $logger, $dispatcher); diff --git a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php index 9d843667a67e9..122eea7ee00f0 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php @@ -38,23 +38,9 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener private $csrfTokenManager; /** - * @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance - * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance - * @param SessionAuthenticationStrategyInterface $sessionStrategy - * @param HttpUtils $httpUtils An HttpUtils instance - * @param string $providerKey - * @param AuthenticationSuccessHandlerInterface $successHandler - * @param AuthenticationFailureHandlerInterface $failureHandler - * @param array $options An array of options for the processing of a - * successful, or failed authentication attempt - * @param LoggerInterface|null $logger A LoggerInterface instance - * @param EventDispatcherInterface|null $dispatcher An EventDispatcherInterface instance - * @param CsrfTokenManagerInterface|null $csrfTokenManager A CsrfTokenManagerInterface instance - * @param SimpleFormAuthenticatorInterface|null $simpleAuthenticator A SimpleFormAuthenticatorInterface instance - * * @throws \InvalidArgumentException In case no simple authenticator is provided */ - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfTokenManagerInterface $csrfTokenManager = null, SimpleFormAuthenticatorInterface $simpleAuthenticator = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfTokenManagerInterface $csrfTokenManager = null, SimpleFormAuthenticatorInterface $simpleAuthenticator = null) { if (!$simpleAuthenticator) { throw new \InvalidArgumentException('Missing simple authenticator'); diff --git a/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php index dd90712408615..ec607bb4574be 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php @@ -39,15 +39,7 @@ class SimplePreAuthenticationListener implements ListenerInterface private $logger; private $dispatcher; - /** - * @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance - * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance - * @param string $providerKey - * @param SimplePreAuthenticatorInterface $simpleAuthenticator A SimplePreAuthenticatorInterface instance - * @param LoggerInterface|null $logger A LoggerInterface instance - * @param EventDispatcherInterface|null $dispatcher An EventDispatcherInterface instance - */ - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, SimplePreAuthenticatorInterface $simpleAuthenticator, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, SimplePreAuthenticatorInterface $simpleAuthenticator, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index 0a0f33441398d..c8cca7ea10f0b 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -51,7 +51,7 @@ class SwitchUserListener implements ListenerInterface private $dispatcher; private $stateless; - public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, UserCheckerInterface $userChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null, $stateless = false) + public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, UserCheckerInterface $userChecker, string $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, string $usernameParameter = '_switch_user', string $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null, bool $stateless = false) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -126,7 +126,9 @@ private function attemptSwitchUser(Request $request, $username) throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername())); } - if (false === $this->accessDecisionManager->decide($token, array($this->role))) { + $user = $this->provider->loadUserByUsername($username); + + if (false === $this->accessDecisionManager->decide($token, array($this->role), $user)) { $exception = new AccessDeniedException(); $exception->setAttributes($this->role); @@ -137,7 +139,6 @@ private function attemptSwitchUser(Request $request, $username) $this->logger->info('Attempting to switch to user.', array('username' => $username)); } - $user = $this->provider->loadUserByUsername($username); $this->userChecker->checkPostAuth($user); $roles = $user->getRoles(); diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index 0a03c04cdcd44..c4ee1bffaf317 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -39,7 +39,7 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL { private $csrfTokenManager; - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfTokenManagerInterface $csrfTokenManager = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfTokenManagerInterface $csrfTokenManager = null) { parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array( 'username_parameter' => '_username', diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php index 955288c23c375..5a977b855ff2e 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php @@ -53,7 +53,7 @@ class UsernamePasswordJsonAuthenticationListener implements ListenerInterface private $eventDispatcher; private $propertyAccessor; - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null, PropertyAccessorInterface $propertyAccessor = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null, PropertyAccessorInterface $propertyAccessor = null) { $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; diff --git a/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php index 326c9af3ba962..f4957bbf65f11 100644 --- a/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php @@ -28,7 +28,7 @@ class X509AuthenticationListener extends AbstractPreAuthenticatedListener private $userKey; private $credentialKey; - public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, string $userKey = 'SSL_CLIENT_S_DN_Email', string $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { parent::__construct($tokenStorage, $authenticationManager, $providerKey, $logger, $dispatcher); diff --git a/src/Symfony/Component/Security/Http/HttpUtils.php b/src/Symfony/Component/Security/Http/HttpUtils.php index a55421340d495..b5762c464d4c4 100644 --- a/src/Symfony/Component/Security/Http/HttpUtils.php +++ b/src/Symfony/Component/Security/Http/HttpUtils.php @@ -77,9 +77,13 @@ public function createRedirectResponse(Request $request, $path, $status = 302) public function createRequest(Request $request, $path) { $newRequest = Request::create($this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all()); - if ($request->hasSession()) { - $newRequest->setSession($request->getSession()); + + static $setSession; + + if (null === $setSession) { + $setSession = \Closure::bind(function ($newRequest, $request) { $newRequest->session = $request->session; }, null, Request::class); } + $setSession($newRequest, $request); if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { $newRequest->attributes->set(Security::AUTHENTICATION_ERROR, $request->attributes->get(Security::AUTHENTICATION_ERROR)); diff --git a/src/Symfony/Component/Security/Http/Logout/DefaultLogoutSuccessHandler.php b/src/Symfony/Component/Security/Http/Logout/DefaultLogoutSuccessHandler.php index 48626b0690356..9f5c959cd23c8 100644 --- a/src/Symfony/Component/Security/Http/Logout/DefaultLogoutSuccessHandler.php +++ b/src/Symfony/Component/Security/Http/Logout/DefaultLogoutSuccessHandler.php @@ -25,14 +25,9 @@ class DefaultLogoutSuccessHandler implements LogoutSuccessHandlerInterface protected $httpUtils; protected $targetUrl; - /** - * @param HttpUtils $httpUtils - * @param string $targetUrl - */ - public function __construct(HttpUtils $httpUtils, $targetUrl = '/') + public function __construct(HttpUtils $httpUtils, string $targetUrl = '/') { $this->httpUtils = $httpUtils; - $this->targetUrl = $targetUrl; } diff --git a/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php index 503ceb7f37709..040fe52775aa0 100644 --- a/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php +++ b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php @@ -48,21 +48,8 @@ public function __construct(RequestStack $requestStack = null, UrlGeneratorInter * @param CsrfTokenManagerInterface|null $csrfTokenManager A CsrfTokenManagerInterface instance * @param string|null $context The listener context */ - public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenManagerInterface $csrfTokenManager = null/*, string $context = null*/) + public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenManagerInterface $csrfTokenManager = null, string $context = null) { - if (func_num_args() >= 6) { - $context = func_get_arg(5); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a sixth `string $context = null` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED); - } - } - - $context = null; - } - $this->listeners[$key] = array($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager, $context); } diff --git a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index afff015ed47b9..64ea8352ac3bd 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -44,15 +44,9 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface private $userProviders; /** - * @param array $userProviders - * @param string $secret - * @param string $providerKey - * @param array $options - * @param LoggerInterface $logger - * * @throws \InvalidArgumentException */ - public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null) + public function __construct(array $userProviders, string $secret, string $providerKey, array $options = array(), LoggerInterface $logger = null) { if (empty($secret)) { throw new \InvalidArgumentException('$secret must not be empty.'); @@ -94,12 +88,10 @@ public function getSecret() * Implementation of RememberMeServicesInterface. Detects whether a remember-me * cookie was set, decodes it, and hands it to subclasses for further processing. * - * @return TokenInterface|null - * * @throws CookieTheftException * @throws \RuntimeException */ - final public function autoLogin(Request $request) + final public function autoLogin(Request $request): ?TokenInterface { if (null === $cookie = $request->cookies->get($this->options['name'])) { return null; @@ -150,6 +142,8 @@ final public function autoLogin(Request $request) throw $e; } + + return null; } /** diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php index dd258a086f1f0..ed055f20949fc 100644 --- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php +++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php @@ -32,7 +32,7 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte private $strategy; - public function __construct($strategy) + public function __construct(string $strategy) { $this->strategy = $strategy; } diff --git a/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php b/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php new file mode 100644 index 0000000000000..62f4c1262120c --- /dev/null +++ b/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Tests\Controller; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Http\Controller\UserValueResolver; + +class UserValueResolverTest extends TestCase +{ + public function testResolveNoToken() + { + $tokenStorage = new TokenStorage(); + $resolver = new UserValueResolver($tokenStorage); + $metadata = new ArgumentMetadata('foo', UserInterface::class, false, false, null); + + $this->assertFalse($resolver->supports(Request::create('/'), $metadata)); + } + + public function testResolveNoUser() + { + $mock = $this->getMockBuilder(UserInterface::class)->getMock(); + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + $resolver = new UserValueResolver($tokenStorage); + $metadata = new ArgumentMetadata('foo', get_class($mock), false, false, null); + + $this->assertFalse($resolver->supports(Request::create('/'), $metadata)); + } + + public function testResolveWrongType() + { + $tokenStorage = new TokenStorage(); + $resolver = new UserValueResolver($tokenStorage); + $metadata = new ArgumentMetadata('foo', null, false, false, null); + + $this->assertFalse($resolver->supports(Request::create('/'), $metadata)); + } + + public function testResolve() + { + $user = $this->getMockBuilder(UserInterface::class)->getMock(); + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); + $token->expects($this->any())->method('getUser')->willReturn($user); + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + $resolver = new UserValueResolver($tokenStorage); + $metadata = new ArgumentMetadata('foo', UserInterface::class, false, false, null); + + $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); + $this->assertSame(array($user), iterator_to_array($resolver->resolve(Request::create('/'), $metadata))); + } + + public function testIntegration() + { + $user = $this->getMockBuilder(UserInterface::class)->getMock(); + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); + $token->expects($this->any())->method('getUser')->willReturn($user); + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + $argumentResolver = new ArgumentResolver(null, array(new UserValueResolver($tokenStorage))); + $this->assertSame(array($user), $argumentResolver->getArguments(Request::create('/'), function (UserInterface $user) {})); + } + + public function testIntegrationNoUser() + { + $token = $this->getMockBuilder(TokenInterface::class)->getMock(); + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + $argumentResolver = new ArgumentResolver(null, array(new UserValueResolver($tokenStorage), new DefaultValueResolver())); + $this->assertSame(array(null), $argumentResolver->getArguments(Request::create('/'), function (UserInterface $user = null) {})); + } +} + +abstract class DummyUser implements UserInterface +{ +} + +abstract class DummySubUser extends DummyUser +{ +} diff --git a/src/Symfony/Component/Security/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php b/src/Symfony/Component/Security/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php deleted file mode 100644 index 035a5baae99cf..0000000000000 --- a/src/Symfony/Component/Security/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Http\Tests\EntryPoint; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\NonceExpiredException; - -/** - * @group legacy - */ -class DigestAuthenticationEntryPointTest extends TestCase -{ - public function testStart() - { - $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); - - $authenticationException = new AuthenticationException('TheAuthenticationExceptionMessage'); - - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); - $response = $entryPoint->start($request, $authenticationException); - - $this->assertEquals(401, $response->getStatusCode()); - $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}"$/', $response->headers->get('WWW-Authenticate')); - } - - public function testStartWithNoException() - { - $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); - - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); - $response = $entryPoint->start($request); - - $this->assertEquals(401, $response->getStatusCode()); - $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}"$/', $response->headers->get('WWW-Authenticate')); - } - - public function testStartWithNonceExpiredException() - { - $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); - - $nonceExpiredException = new NonceExpiredException('TheNonceExpiredExceptionMessage'); - - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); - $response = $entryPoint->start($request, $nonceExpiredException); - - $this->assertEquals(401, $response->getStatusCode()); - $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}", stale="true"$/', $response->headers->get('WWW-Authenticate')); - } -} diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index d05f2da832a4c..16b7fb2bf847b 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -251,24 +251,11 @@ public function testHandleRemovesTokenIfNoPreviousSessionWasFound() $listener->handle($event); } - /** - * @group legacy - * @expectedDeprecation Refreshing a deauthenticated user is deprecated as of 3.4 and will trigger a logout in 4.0. - */ - public function testIfTokenIsDeauthenticatedTriggersDeprecations() - { - $tokenStorage = new TokenStorage(); - $refreshedUser = new User('foobar', 'baz'); - $this->handleEventWithPreviousSession($tokenStorage, array(new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser))); - - $this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser()); - } - public function testIfTokenIsDeauthenticated() { $tokenStorage = new TokenStorage(); $refreshedUser = new User('foobar', 'baz'); - $this->handleEventWithPreviousSession($tokenStorage, array(new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)), null, true); + $this->handleEventWithPreviousSession($tokenStorage, array(new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser))); $this->assertNull($tokenStorage->getToken()); } @@ -344,7 +331,7 @@ protected function runSessionOnKernelResponse($newToken, $original = null) return $session; } - private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, $userProviders, UserInterface $user = null, $logoutOnUserChange = false) + private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, $userProviders, UserInterface $user = null) { $user = $user ?: new User('foo', 'bar'); $session = new Session(new MockArraySessionStorage()); @@ -355,7 +342,6 @@ private function handleEventWithPreviousSession(TokenStorageInterface $tokenStor $request->cookies->set('MOCKSESSID', true); $listener = new ContextListener($tokenStorage, $userProviders, 'context_key'); - $listener->setLogoutOnUserChange($logoutOnUserChange); $listener->handle(new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST)); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/DigestAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/DigestAuthenticationListenerTest.php deleted file mode 100644 index b6f7efa8b5aa3..0000000000000 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/DigestAuthenticationListenerTest.php +++ /dev/null @@ -1,83 +0,0 @@ -calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, 'GET', $uri); - - $digestData = - 'username="'.$username.'", realm="'.$realm.'", nonce="'.$nonce.'", '. - 'uri="'.$uri.'", cnonce="'.$cnonce.'", nc='.$nc.', qop="'.$qop.'", '. - 'response="'.$serverDigest.'"' - ; - - $request = new Request(array(), array(), array(), array(), array(), array('PHP_AUTH_DIGEST' => $digestData)); - - $entryPoint = new DigestAuthenticationEntryPoint($realm, $secret); - - $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); - $user->method('getPassword')->willReturn($password); - - $providerKey = 'TheProviderKey'; - - $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); - $tokenStorage - ->expects($this->once()) - ->method('getToken') - ->will($this->returnValue(null)) - ; - $tokenStorage - ->expects($this->once()) - ->method('setToken') - ->with($this->equalTo(new UsernamePasswordToken($user, $password, $providerKey))) - ; - - $userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock(); - $userProvider->method('loadUserByUsername')->willReturn($user); - - $listener = new DigestAuthenticationListener($tokenStorage, $userProvider, $providerKey, $entryPoint); - - $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')->disableOriginalConstructor()->getMock(); - $event - ->expects($this->any()) - ->method('getRequest') - ->will($this->returnValue($request)) - ; - - $listener->handle($event); - } - - private function calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, $method, $uri) - { - $response = md5( - md5($username.':'.$realm.':'.$password).':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.md5($method.':'.$uri) - ); - - return sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"', - $username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response - ); - } -} diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/DigestDataTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/DigestDataTest.php deleted file mode 100644 index 185055efceb68..0000000000000 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/DigestDataTest.php +++ /dev/null @@ -1,188 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Http\Tests\Firewall; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Http\Firewall\DigestData; - -/** - * @group legacy - */ -class DigestDataTest extends TestCase -{ - public function testGetResponse() - { - $digestAuth = new DigestData( - 'username="user", realm="Welcome, robot!", '. - 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $this->assertEquals('b52938fc9e6d7c01be7702ece9031b42', $digestAuth->getResponse()); - } - - public function testGetUsername() - { - $digestAuth = new DigestData( - 'username="user", realm="Welcome, robot!", '. - 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $this->assertEquals('user', $digestAuth->getUsername()); - } - - public function testGetUsernameWithQuote() - { - $digestAuth = new DigestData( - 'username="\"user\"", realm="Welcome, robot!", '. - 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $this->assertEquals('"user"', $digestAuth->getUsername()); - } - - public function testGetUsernameWithQuoteAndEscape() - { - $digestAuth = new DigestData( - 'username="\"u\\\\\"ser\"", realm="Welcome, robot!", '. - 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $this->assertEquals('"u\\"ser"', $digestAuth->getUsername()); - } - - public function testGetUsernameWithSingleQuote() - { - $digestAuth = new DigestData( - 'username="\"u\'ser\"", realm="Welcome, robot!", '. - 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $this->assertEquals('"u\'ser"', $digestAuth->getUsername()); - } - - public function testGetUsernameWithSingleQuoteAndEscape() - { - $digestAuth = new DigestData( - 'username="\"u\\\'ser\"", realm="Welcome, robot!", '. - 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $this->assertEquals('"u\\\'ser"', $digestAuth->getUsername()); - } - - public function testGetUsernameWithEscape() - { - $digestAuth = new DigestData( - 'username="\"u\\ser\"", realm="Welcome, robot!", '. - 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $this->assertEquals('"u\\ser"', $digestAuth->getUsername()); - } - - /** - * @group time-sensitive - */ - public function testValidateAndDecode() - { - $time = microtime(true); - $key = 'ThisIsAKey'; - $nonce = base64_encode($time.':'.md5($time.':'.$key)); - - $digestAuth = new DigestData( - 'username="user", realm="Welcome, robot!", nonce="'.$nonce.'", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $digestAuth->validateAndDecode($key, 'Welcome, robot!'); - - sleep(1); - - $this->assertTrue($digestAuth->isNonceExpired()); - } - - public function testCalculateServerDigest() - { - $this->calculateServerDigest('user', 'Welcome, robot!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5'); - } - - public function testCalculateServerDigestWithQuote() - { - $this->calculateServerDigest('\"user\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5'); - } - - public function testCalculateServerDigestWithQuoteAndEscape() - { - $this->calculateServerDigest('\"u\\\\\"ser\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5'); - } - - public function testCalculateServerDigestEscape() - { - $this->calculateServerDigest('\"u\\ser\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5'); - $this->calculateServerDigest('\"u\\ser\\\\\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5'); - } - - public function testIsNonceExpired() - { - $time = microtime(true) + 10; - $key = 'ThisIsAKey'; - $nonce = base64_encode($time.':'.md5($time.':'.$key)); - - $digestAuth = new DigestData( - 'username="user", realm="Welcome, robot!", nonce="'.$nonce.'", '. - 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '. - 'response="b52938fc9e6d7c01be7702ece9031b42"' - ); - - $digestAuth->validateAndDecode($key, 'Welcome, robot!'); - - $this->assertFalse($digestAuth->isNonceExpired()); - } - - protected function setUp() - { - class_exists('Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener', true); - } - - private function calculateServerDigest($username, $realm, $password, $key, $nc, $cnonce, $qop, $method, $uri) - { - $time = microtime(true); - $nonce = base64_encode($time.':'.md5($time.':'.$key)); - - $response = md5( - md5($username.':'.$realm.':'.$password).':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.md5($method.':'.$uri) - ); - - $digest = sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"', - $username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response - ); - - $digestAuth = new DigestData($digest); - - $this->assertEquals($digestAuth->getResponse(), $digestAuth->calculateServerDigest($password, $method)); - } -} diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index 0e61ee208ee2c..bdab1f24d58eb 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -182,7 +182,7 @@ public function testSwitchUser() $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) + ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'), $user) ->will($this->returnValue(true)); $this->userProvider->expects($this->once()) @@ -212,7 +212,7 @@ public function testSwitchUserKeepsOtherQueryStringParameters() )); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) + ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'), $user) ->will($this->returnValue(true)); $this->userProvider->expects($this->once()) @@ -240,7 +240,7 @@ public function testSwitchUserWithReplacedToken() $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->any()) - ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) + ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'), $user) ->will($this->returnValue(true)); $this->userProvider->expects($this->any()) @@ -276,7 +276,7 @@ public function testSwitchUserStateless() $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) + ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'), $user) ->will($this->returnValue(true)); $this->userProvider->expects($this->once()) diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 4e1b7ea9fa6b3..7bf03b6e874d4 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -16,18 +16,16 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/security-core": "~3.2|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "~3.3|~4.0", - "symfony/polyfill-php56": "~1.0", - "symfony/polyfill-php70": "~1.0", - "symfony/property-access": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/security-core": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/property-access": "~3.4|~4.0" }, "require-dev": { - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/security-csrf": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/security-csrf": "~3.4|~4.0", "psr/log": "~1.0" }, "suggest": { @@ -43,7 +41,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index cdaeebaf39767..fb7fd2e3046ae 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -16,13 +16,11 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "^2.8.31|~3.3.13|~3.4|~4.0", - "symfony/http-kernel": "~3.3|~4.0", - "symfony/polyfill-php56": "~1.0", - "symfony/polyfill-php70": "~1.0", - "symfony/property-access": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/property-access": "~3.4|~4.0" }, "replace": { "symfony/security-core": "self.version", @@ -32,12 +30,12 @@ }, "require-dev": { "psr/container": "^1.0", - "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/finder": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/validator": "^3.2.5|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/ldap": "~3.1|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/ldap": "~3.4|~4.0", "psr/log": "~1.0" }, "suggest": { @@ -57,7 +55,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Serializer/Annotation/DiscriminatorMap.php b/src/Symfony/Component/Serializer/Annotation/DiscriminatorMap.php new file mode 100644 index 0000000000000..61b952610c4bc --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/DiscriminatorMap.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Annotation; + +use Symfony\Component\Serializer\Exception\InvalidArgumentException; + +/** + * Annotation class for @DiscriminatorMap(). + * + * @Annotation + * @Target({"CLASS"}) + * + * @author Samuel Roze + */ +class DiscriminatorMap +{ + /** + * @var string + */ + private $typeProperty; + + /** + * @var array + */ + private $mapping; + + /** + * @param array $data + * + * @throws InvalidArgumentException + */ + public function __construct(array $data) + { + if (empty($data['typeProperty'])) { + throw new InvalidArgumentException(sprintf('Parameter "typeProperty" of annotation "%s" cannot be empty.', get_class($this))); + } + + if (empty($data['mapping'])) { + throw new InvalidArgumentException(sprintf('Parameter "mapping" of annotation "%s" cannot be empty.', get_class($this))); + } + + $this->typeProperty = $data['typeProperty']; + $this->mapping = $data['mapping']; + } + + public function getTypeProperty(): string + { + return $this->typeProperty; + } + + public function getMapping(): array + { + return $this->mapping; + } +} diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 450640d160227..c680a19b16574 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -1,6 +1,26 @@ CHANGELOG ========= +4.1.0 +----- + +* added `MissingConstructorArgumentsException` new exception for deserialization failure + of objects that needs data insertion in constructor +* added an optional `default_constructor_arguments` option of context to specify a default data in + case the object is not initializable by its constructor because of data missing +* added optional `bool $escapeFormulas = false` argument to `CsvEncoder::__construct` + +4.0.0 +----- + + * removed the `SerializerAwareEncoder` and `SerializerAwareNormalizer` classes, + use the `SerializerAwareTrait` instead + * removed the `Serializer::$normalizerCache` and `Serializer::$denormalizerCache` + properties + * added an optional `string $format = null` argument to `AbstractNormalizer::instantiateObject` + * added an optional `array $context = array()` to `Serializer::supportsNormalization`, `Serializer::supportsDenormalization`, + `Serializer::supportsEncoding` and `Serializer::supportsDecoding` + 3.4.0 ----- diff --git a/src/Symfony/Component/Serializer/DependencyInjection/SerializerPass.php b/src/Symfony/Component/Serializer/DependencyInjection/SerializerPass.php index e6202b8bf9d32..b43d54c291baf 100644 --- a/src/Symfony/Component/Serializer/DependencyInjection/SerializerPass.php +++ b/src/Symfony/Component/Serializer/DependencyInjection/SerializerPass.php @@ -31,7 +31,7 @@ class SerializerPass implements CompilerPassInterface private $normalizerTag; private $encoderTag; - public function __construct($serializerService = 'serializer', $normalizerTag = 'serializer.normalizer', $encoderTag = 'serializer.encoder') + public function __construct(string $serializerService = 'serializer', string $normalizerTag = 'serializer.normalizer', string $encoderTag = 'serializer.encoder') { $this->serializerService = $serializerService; $this->normalizerTag = $normalizerTag; diff --git a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php index 7c967cfd36fdb..545841fffd48b 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php @@ -22,7 +22,7 @@ * * @final since version 3.3. */ -class ChainDecoder implements DecoderInterface /*, ContextAwareDecoderInterface*/ +class ChainDecoder implements ContextAwareDecoderInterface { protected $decoders = array(); protected $decoderByFormat = array(); @@ -43,10 +43,8 @@ final public function decode($data, $format, array $context = array()) /** * {@inheritdoc} */ - public function supportsDecoding($format/*, array $context = array()*/) + public function supportsDecoding($format, array $context = array()) { - $context = func_num_args() > 1 ? func_get_arg(1) : array(); - try { $this->getDecoder($format, $context); } catch (RuntimeException $e) { @@ -59,14 +57,9 @@ public function supportsDecoding($format/*, array $context = array()*/) /** * Gets the decoder supporting the format. * - * @param string $format - * @param array $context - * - * @return DecoderInterface - * * @throws RuntimeException if no decoder is found */ - private function getDecoder($format, array $context) + private function getDecoder(string $format, array $context): DecoderInterface { if (isset($this->decoderByFormat[$format]) && isset($this->decoders[$this->decoderByFormat[$format]]) diff --git a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php index cfd8855bb8150..2737f6eba695f 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php @@ -22,7 +22,7 @@ * * @final since version 3.3. */ -class ChainEncoder implements EncoderInterface /*, ContextAwareEncoderInterface*/ +class ChainEncoder implements ContextAwareEncoderInterface { protected $encoders = array(); protected $encoderByFormat = array(); @@ -43,10 +43,8 @@ final public function encode($data, $format, array $context = array()) /** * {@inheritdoc} */ - public function supportsEncoding($format/*, array $context = array()*/) + public function supportsEncoding($format, array $context = array()) { - $context = func_num_args() > 1 ? func_get_arg(1) : array(); - try { $this->getEncoder($format, $context); } catch (RuntimeException $e) { @@ -64,9 +62,8 @@ public function supportsEncoding($format/*, array $context = array()*/) * * @return bool */ - public function needsNormalization($format/*, array $context = array()*/) + public function needsNormalization($format, array $context = array()) { - $context = func_num_args() > 1 ? func_get_arg(1) : array(); $encoder = $this->getEncoder($format, $context); if (!$encoder instanceof NormalizationAwareInterface) { @@ -83,14 +80,9 @@ public function needsNormalization($format/*, array $context = array()*/) /** * Gets the encoder supporting the format. * - * @param string $format - * @param array $context - * - * @return EncoderInterface - * * @throws RuntimeException if no encoder is found */ - private function getEncoder($format, array $context) + private function getEncoder(string $format, array $context): EncoderInterface { if (isset($this->encoderByFormat[$format]) && isset($this->encoders[$this->encoderByFormat[$format]]) diff --git a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php index cce2c14af1f55..7ecd086d2d636 100644 --- a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php @@ -27,24 +27,22 @@ class CsvEncoder implements EncoderInterface, DecoderInterface const ESCAPE_CHAR_KEY = 'csv_escape_char'; const KEY_SEPARATOR_KEY = 'csv_key_separator'; const HEADERS_KEY = 'csv_headers'; + const ESCAPE_FORMULAS_KEY = 'csv_escape_formulas'; private $delimiter; private $enclosure; private $escapeChar; private $keySeparator; + private $escapeFormulas; + private $formulasStartCharacters = array('=', '-', '+', '@'); - /** - * @param string $delimiter - * @param string $enclosure - * @param string $escapeChar - * @param string $keySeparator - */ - public function __construct($delimiter = ',', $enclosure = '"', $escapeChar = '\\', $keySeparator = '.') + public function __construct(string $delimiter = ',', string $enclosure = '"', string $escapeChar = '\\', string $keySeparator = '.', bool $escapeFormulas = false) { $this->delimiter = $delimiter; $this->enclosure = $enclosure; $this->escapeChar = $escapeChar; $this->keySeparator = $keySeparator; + $this->escapeFormulas = $escapeFormulas; } /** @@ -71,11 +69,11 @@ public function encode($data, $format, array $context = array()) } } - list($delimiter, $enclosure, $escapeChar, $keySeparator, $headers) = $this->getCsvOptions($context); + list($delimiter, $enclosure, $escapeChar, $keySeparator, $headers, $escapeFormulas) = $this->getCsvOptions($context); foreach ($data as &$value) { $flattened = array(); - $this->flatten($value, $flattened, $keySeparator); + $this->flatten($value, $flattened, $keySeparator, '', $escapeFormulas); $value = $flattened; } unset($value); @@ -177,19 +175,18 @@ public function supportsDecoding($format) /** * Flattens an array and generates keys including the path. - * - * @param array $array - * @param array $result - * @param string $keySeparator - * @param string $parentKey */ - private function flatten(array $array, array &$result, $keySeparator, $parentKey = '') + private function flatten(array $array, array &$result, string $keySeparator, string $parentKey = '', bool $escapeFormulas = false) { foreach ($array as $key => $value) { if (is_array($value)) { - $this->flatten($value, $result, $keySeparator, $parentKey.$key.$keySeparator); + $this->flatten($value, $result, $keySeparator, $parentKey.$key.$keySeparator, $escapeFormulas); } else { - $result[$parentKey.$key] = $value; + if ($escapeFormulas && \in_array(substr($value, 0, 1), $this->formulasStartCharacters, true)) { + $result[$parentKey.$key] = "\t".$value; + } else { + $result[$parentKey.$key] = $value; + } } } } @@ -201,12 +198,13 @@ private function getCsvOptions(array $context) $escapeChar = isset($context[self::ESCAPE_CHAR_KEY]) ? $context[self::ESCAPE_CHAR_KEY] : $this->escapeChar; $keySeparator = isset($context[self::KEY_SEPARATOR_KEY]) ? $context[self::KEY_SEPARATOR_KEY] : $this->keySeparator; $headers = isset($context[self::HEADERS_KEY]) ? $context[self::HEADERS_KEY] : array(); + $escapeFormulas = isset($context[self::ESCAPE_FORMULAS_KEY]) ? $context[self::ESCAPE_FORMULAS_KEY] : $this->escapeFormulas; if (!is_array($headers)) { throw new InvalidArgumentException(sprintf('The "%s" context variable must be an array or null, given "%s".', self::HEADERS_KEY, gettype($headers))); } - return array($delimiter, $enclosure, $escapeChar, $keySeparator, $headers); + return array($delimiter, $enclosure, $escapeChar, $keySeparator, $headers, $escapeFormulas); } /** diff --git a/src/Symfony/Component/Serializer/Encoder/JsonDecode.php b/src/Symfony/Component/Serializer/Encoder/JsonDecode.php index bee1f7d4449f4..5b0a432f39202 100644 --- a/src/Symfony/Component/Serializer/Encoder/JsonDecode.php +++ b/src/Symfony/Component/Serializer/Encoder/JsonDecode.php @@ -31,10 +31,10 @@ class JsonDecode implements DecoderInterface * @param bool $associative True to return the result associative array, false for a nested stdClass hierarchy * @param int $depth Specifies the recursion depth */ - public function __construct($associative = false, $depth = 512) + public function __construct(bool $associative = false, int $depth = 512) { $this->associative = $associative; - $this->recursionDepth = (int) $depth; + $this->recursionDepth = $depth; } /** @@ -56,7 +56,7 @@ public function __construct($associative = false, $depth = 512) * If not specified, this method will use the default set in JsonDecode::__construct * * json_decode_options: integer - * Specifies additional options as per documentation for json_decode. Only supported with PHP 5.4.0 and higher + * Specifies additional options as per documentation for json_decode. * * @return mixed * diff --git a/src/Symfony/Component/Serializer/Encoder/JsonEncode.php b/src/Symfony/Component/Serializer/Encoder/JsonEncode.php index ef084eeab2b9c..5cc30b75026f4 100644 --- a/src/Symfony/Component/Serializer/Encoder/JsonEncode.php +++ b/src/Symfony/Component/Serializer/Encoder/JsonEncode.php @@ -22,7 +22,7 @@ class JsonEncode implements EncoderInterface { private $options; - public function __construct($bitmask = 0) + public function __construct(int $bitmask = 0) { $this->options = $bitmask; } diff --git a/src/Symfony/Component/Serializer/Encoder/SerializerAwareEncoder.php b/src/Symfony/Component/Serializer/Encoder/SerializerAwareEncoder.php deleted file mode 100644 index 873af922ef204..0000000000000 --- a/src/Symfony/Component/Serializer/Encoder/SerializerAwareEncoder.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Serializer\Encoder; - -use Symfony\Component\Serializer\SerializerAwareInterface; -use Symfony\Component\Serializer\SerializerAwareTrait; - -/** - * SerializerAware Encoder implementation. - * - * @author Jordi Boggiano - * - * @deprecated since version 3.2, to be removed in 4.0. Use the SerializerAwareTrait instead. - */ -abstract class SerializerAwareEncoder implements SerializerAwareInterface -{ - use SerializerAwareTrait; -} diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index 951b38aa553d8..b15b1946d7a5e 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Serializer\Encoder; use Symfony\Component\Serializer\Exception\NotEncodableValueException; +use Symfony\Component\Serializer\SerializerAwareInterface; +use Symfony\Component\Serializer\SerializerAwareTrait; /** * Encodes XML data. @@ -21,8 +23,10 @@ * @author Fabian Vogler * @author Kévin Dunglas */ -class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, DecoderInterface, NormalizationAwareInterface +class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwareInterface, SerializerAwareInterface { + use SerializerAwareTrait; + const FORMAT = 'xml'; /** @@ -40,7 +44,7 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec * @param string $rootNodeName * @param int|null $loadOptions A bit field of LIBXML_* constants */ - public function __construct($rootNodeName = 'response', $loadOptions = null) + public function __construct(string $rootNodeName = 'response', int $loadOptions = null) { $this->rootNodeName = $rootNodeName; $this->loadOptions = null !== $loadOptions ? $loadOptions : LIBXML_NONET | LIBXML_NOBLANKS; @@ -176,15 +180,9 @@ public function getRootNodeName() return $this->rootNodeName; } - /** - * @param \DOMNode $node - * @param string $val - * - * @return bool - */ - final protected function appendXMLString(\DOMNode $node, $val) + final protected function appendXMLString(\DOMNode $node, string $val): bool { - if (strlen($val) > 0) { + if ('' !== $val) { $frag = $this->dom->createDocumentFragment(); $frag->appendXML($val); $node->appendChild($frag); @@ -195,13 +193,7 @@ final protected function appendXMLString(\DOMNode $node, $val) return false; } - /** - * @param \DOMNode $node - * @param string $val - * - * @return bool - */ - final protected function appendText(\DOMNode $node, $val) + final protected function appendText(\DOMNode $node, string $val): bool { $nodeText = $this->dom->createTextNode($val); $node->appendChild($nodeText); @@ -209,13 +201,7 @@ final protected function appendText(\DOMNode $node, $val) return true; } - /** - * @param \DOMNode $node - * @param string $val - * - * @return bool - */ - final protected function appendCData(\DOMNode $node, $val) + final protected function appendCData(\DOMNode $node, string $val): bool { $nodeText = $this->dom->createCDATASection($val); $node->appendChild($nodeText); @@ -226,10 +212,8 @@ final protected function appendCData(\DOMNode $node, $val) /** * @param \DOMNode $node * @param \DOMDocumentFragment $fragment - * - * @return bool */ - final protected function appendDocumentFragment(\DOMNode $node, $fragment) + final protected function appendDocumentFragment(\DOMNode $node, $fragment): bool { if ($fragment instanceof \DOMDocumentFragment) { $node->appendChild($fragment); @@ -242,12 +226,8 @@ final protected function appendDocumentFragment(\DOMNode $node, $fragment) /** * Checks the name is a valid xml element name. - * - * @param string $name - * - * @return bool */ - final protected function isElementNameValid($name) + final protected function isElementNameValid(string $name): bool { return $name && false === strpos($name, ' ') && @@ -290,10 +270,8 @@ private function parseXml(\DOMNode $node, array $context = array()) /** * Parse the input DOMNode attributes into an array. - * - * @return array */ - private function parseXmlAttributes(\DOMNode $node, array $context = array()) + private function parseXmlAttributes(\DOMNode $node, array $context = array()): array { if (!$node->hasAttributes()) { return array(); @@ -370,13 +348,10 @@ private function parseXmlValue(\DOMNode $node, array $context = array()) * * @param \DOMNode $parentNode * @param array|object $data - * @param string|null $xmlRootNodeName - * - * @return bool * * @throws NotEncodableValueException */ - private function buildXml(\DOMNode $parentNode, $data, $xmlRootNodeName = null) + private function buildXml(\DOMNode $parentNode, $data, string $xmlRootNodeName = null): bool { $append = true; @@ -439,12 +414,8 @@ private function buildXml(\DOMNode $parentNode, $data, $xmlRootNodeName = null) * * @param \DOMNode $parentNode * @param array|object $data - * @param string $nodeName - * @param string $key - * - * @return bool */ - private function appendNode(\DOMNode $parentNode, $data, $nodeName, $key = null) + private function appendNode(\DOMNode $parentNode, $data, string $nodeName, string $key = null): bool { $node = $this->dom->createElement($nodeName); if (null !== $key) { @@ -461,12 +432,8 @@ private function appendNode(\DOMNode $parentNode, $data, $nodeName, $key = null) /** * Checks if a value contains any characters which would require CDATA wrapping. - * - * @param string $val - * - * @return bool */ - private function needsCdataWrapping($val) + private function needsCdataWrapping(string $val): bool { return 0 < preg_match('/[<>&]/', $val); } @@ -477,11 +444,9 @@ private function needsCdataWrapping($val) * @param \DOMNode $node * @param mixed $val * - * @return bool - * * @throws NotEncodableValueException */ - private function selectNodeType(\DOMNode $node, $val) + private function selectNodeType(\DOMNode $node, $val): bool { if (is_array($val)) { return $this->buildXml($node, $val); @@ -510,10 +475,8 @@ private function selectNodeType(\DOMNode $node, $val) /** * Get real XML root node name, taking serializer options into account. - * - * @return string */ - private function resolveXmlRootName(array $context = array()) + private function resolveXmlRootName(array $context = array()): string { return isset($context['xml_root_node_name']) ? $context['xml_root_node_name'] @@ -522,12 +485,8 @@ private function resolveXmlRootName(array $context = array()) /** * Get XML option for type casting attributes Defaults to true. - * - * @param array $context - * - * @return bool */ - private function resolveXmlTypeCastAttributes(array $context = array()) + private function resolveXmlTypeCastAttributes(array $context = array()): bool { return isset($context['xml_type_cast_attributes']) ? (bool) $context['xml_type_cast_attributes'] @@ -536,12 +495,8 @@ private function resolveXmlTypeCastAttributes(array $context = array()) /** * Create a DOM document, taking serializer options into account. - * - * @param array $context Options that the encoder has access to - * - * @return \DOMDocument */ - private function createDomDocument(array $context) + private function createDomDocument(array $context): \DOMDocument { $document = new \DOMDocument(); diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Stub/VoterWithoutInterface.php b/src/Symfony/Component/Serializer/Exception/MissingConstructorArgumentsException.php similarity index 55% rename from src/Symfony/Component/Security/Core/Tests/Authorization/Stub/VoterWithoutInterface.php rename to src/Symfony/Component/Serializer/Exception/MissingConstructorArgumentsException.php index 09c284d3c67fc..b9b768b53f5e7 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/Stub/VoterWithoutInterface.php +++ b/src/Symfony/Component/Serializer/Exception/MissingConstructorArgumentsException.php @@ -9,14 +9,13 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Tests\Authorization\Stub; +namespace Symfony\Component\Serializer\Exception; /** - * @author Iltar van der Berg + * IncompleteInputDataException. + * + * @author Maxime VEBER */ -class VoterWithoutInterface +class MissingConstructorArgumentsException extends RuntimeException { - public function vote() - { - } } diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php index 5888234a4c285..64e3812465459 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -41,12 +41,7 @@ class AttributeMetadata implements AttributeMetadataInterface */ public $maxDepth; - /** - * Constructs a metadata for the given attribute. - * - * @param string $name - */ - public function __construct($name) + public function __construct(string $name) { $this->name = $name; } diff --git a/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorFromClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorFromClassMetadata.php new file mode 100644 index 0000000000000..7196651624369 --- /dev/null +++ b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorFromClassMetadata.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Mapping; + +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; + +/** + * @author Samuel Roze + */ +class ClassDiscriminatorFromClassMetadata implements ClassDiscriminatorResolverInterface +{ + /** + * @var ClassMetadataFactoryInterface + */ + private $classMetadataFactory; + private $mappingForMappedObjectCache = array(); + + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory) + { + $this->classMetadataFactory = $classMetadataFactory; + } + + /** + * {@inheritdoc} + */ + public function getMappingForClass(string $class): ?ClassDiscriminatorMapping + { + if ($this->classMetadataFactory->hasMetadataFor($class)) { + return $this->classMetadataFactory->getMetadataFor($class)->getClassDiscriminatorMapping(); + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping + { + if ($this->classMetadataFactory->hasMetadataFor($object)) { + $metadata = $this->classMetadataFactory->getMetadataFor($object); + + if (null !== $metadata->getClassDiscriminatorMapping()) { + return $metadata->getClassDiscriminatorMapping(); + } + } + + $cacheKey = is_object($object) ? get_class($object) : $object; + if (!array_key_exists($cacheKey, $this->mappingForMappedObjectCache)) { + $this->mappingForMappedObjectCache[$cacheKey] = $this->resolveMappingForMappedObject($object); + } + + return $this->mappingForMappedObjectCache[$cacheKey]; + } + + /** + * {@inheritdoc} + */ + public function getTypeForMappedObject($object): ?string + { + if (null === $mapping = $this->getMappingForMappedObject($object)) { + return null; + } + + return $mapping->getMappedObjectType($object); + } + + private function resolveMappingForMappedObject($object) + { + $reflectionClass = new \ReflectionClass($object); + if ($parentClass = $reflectionClass->getParentClass()) { + return $this->getMappingForMappedObject($parentClass->getName()); + } + + foreach ($reflectionClass->getInterfaceNames() as $interfaceName) { + if (null !== ($interfaceMapping = $this->getMappingForMappedObject($interfaceName))) { + return $interfaceMapping; + } + } + + return null; + } +} diff --git a/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorMapping.php b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorMapping.php new file mode 100644 index 0000000000000..5d33a001fd3de --- /dev/null +++ b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorMapping.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Mapping; + +/** + * @author Samuel Roze + */ +class ClassDiscriminatorMapping +{ + private $typeProperty; + private $typesMapping; + + public function __construct(string $typeProperty, array $typesMapping = array()) + { + $this->typeProperty = $typeProperty; + $this->typesMapping = $typesMapping; + } + + public function getTypeProperty(): string + { + return $this->typeProperty; + } + + public function getClassForType(string $type): ?string + { + if (isset($this->typesMapping[$type])) { + return $this->typesMapping[$type]; + } + + return null; + } + + /** + * @param object|string $object + * + * @return string|null + */ + public function getMappedObjectType($object): ?string + { + foreach ($this->typesMapping as $type => $typeClass) { + if (is_a($object, $typeClass)) { + return $type; + } + } + + return null; + } + + public function getTypesMapping(): array + { + return $this->typesMapping; + } +} diff --git a/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorResolverInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorResolverInterface.php new file mode 100644 index 0000000000000..073947bde5f23 --- /dev/null +++ b/src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorResolverInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Mapping; + +/** + * Knows how to get the class discriminator mapping for classes and objects. + * + * @author Samuel Roze + */ +interface ClassDiscriminatorResolverInterface +{ + /** + * @param string $class + * + * @return ClassDiscriminatorMapping|null + */ + public function getMappingForClass(string $class): ?ClassDiscriminatorMapping; + + /** + * @param object|string $object + * + * @return ClassDiscriminatorMapping|null + */ + public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping; + + /** + * @param object|string $object + * + * @return string|null + */ + public function getTypeForMappedObject($object): ?string; +} diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php index 7858f74dbbe29..e1d474504c25b 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php @@ -39,14 +39,25 @@ class ClassMetadata implements ClassMetadataInterface */ private $reflClass; + /** + * @var ClassDiscriminatorMapping|null + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getClassDiscriminatorMapping()} instead. + */ + public $classDiscriminatorMapping; + /** * Constructs a metadata for the given class. * - * @param string $class + * @param string $class + * @param ClassDiscriminatorMapping|null $classDiscriminatorMapping */ - public function __construct($class) + public function __construct(string $class, ClassDiscriminatorMapping $classDiscriminatorMapping = null) { $this->name = $class; + $this->classDiscriminatorMapping = $classDiscriminatorMapping; } /** @@ -99,6 +110,22 @@ public function getReflectionClass() return $this->reflClass; } + /** + * {@inheritdoc} + */ + public function getClassDiscriminatorMapping() + { + return $this->classDiscriminatorMapping; + } + + /** + * {@inheritdoc} + */ + public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null) + { + $this->classDiscriminatorMapping = $mapping; + } + /** * Returns the names of the properties that should be serialized. * @@ -109,6 +136,7 @@ public function __sleep() return array( 'name', 'attributesMetadata', + 'classDiscriminatorMapping', ); } } diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php index 3811e56548a0c..ddcffe97c9b3f 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php @@ -54,4 +54,14 @@ public function merge(ClassMetadataInterface $classMetadata); * @return \ReflectionClass */ public function getReflectionClass(); + + /** + * @return ClassDiscriminatorMapping|null + */ + public function getClassDiscriminatorMapping(); + + /** + * @param ClassDiscriminatorMapping|null $mapping + */ + public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null); } diff --git a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php index 294dfd9e36759..11000b9c294e4 100644 --- a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php +++ b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Serializer\Mapping\Factory; -use Doctrine\Common\Cache\Cache; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Mapping\ClassMetadata; use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface; @@ -26,17 +25,15 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface use ClassResolverTrait; private $loader; - private $cache; + + /** + * @var array + */ private $loadedClasses; - public function __construct(LoaderInterface $loader, Cache $cache = null) + public function __construct(LoaderInterface $loader) { $this->loader = $loader; - $this->cache = $cache; - - if (null !== $cache) { - @trigger_error(sprintf('Passing a Doctrine Cache instance as 2nd parameter of the "%s" constructor is deprecated since Symfony 3.1. This parameter will be removed in Symfony 4.0. Use the "%s" class instead.', __CLASS__, CacheClassMetadataFactory::class), E_USER_DEPRECATED); - } } /** @@ -50,10 +47,6 @@ public function getMetadataFor($value) return $this->loadedClasses[$class]; } - if ($this->cache && ($this->loadedClasses[$class] = $this->cache->fetch($class))) { - return $this->loadedClasses[$class]; - } - $classMetadata = new ClassMetadata($class); $this->loader->loadClassMetadata($classMetadata); @@ -69,10 +62,6 @@ public function getMetadataFor($value) $classMetadata->merge($this->getMetadataFor($interface->name)); } - if ($this->cache) { - $this->cache->save($class, $classMetadata); - } - return $this->loadedClasses[$class] = $classMetadata; } diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php index 5527b5f71731e..0c195f671dad9 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php @@ -12,10 +12,12 @@ namespace Symfony\Component\Serializer\Mapping\Loader; use Doctrine\Common\Annotations\Reader; +use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; use Symfony\Component\Serializer\Exception\MappingException; use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; /** @@ -43,6 +45,15 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) $attributesMetadata = $classMetadata->getAttributesMetadata(); + foreach ($this->reader->getClassAnnotations($reflectionClass) as $annotation) { + if ($annotation instanceof DiscriminatorMap) { + $classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping( + $annotation->getTypeProperty(), + $annotation->getMapping() + )); + } + } + foreach ($reflectionClass->getProperties() as $property) { if (!isset($attributesMetadata[$property->name])) { $attributesMetadata[$property->name] = new AttributeMetadata($property->name); diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php index d1b0dee93e9c0..80544427dccf0 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php @@ -27,7 +27,7 @@ abstract class FileLoader implements LoaderInterface * * @throws MappingException if the mapping file does not exist or is not readable */ - public function __construct($file) + public function __construct(string $file) { if (!is_file($file)) { throw new MappingException(sprintf('The mapping file %s does not exist', $file)); diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php index 76d064326f168..eec766f91d533 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php @@ -14,6 +14,7 @@ use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Serializer\Exception\MappingException; use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; /** @@ -67,6 +68,18 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) } } + if (isset($xml->{'discriminator-map'})) { + $mapping = array(); + foreach ($xml->{'discriminator-map'}->mapping as $element) { + $mapping[(string) $element->attributes()->type] = (string) $element->attributes()->class; + } + + $classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping( + (string) $xml->{'discriminator-map'}->attributes()->{'type-property'}, + $mapping + )); + } + return true; } diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php index 970241d34767f..aa3b21859b019 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php @@ -13,8 +13,10 @@ use Symfony\Component\Serializer\Exception\MappingException; use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; use Symfony\Component\Yaml\Parser; +use Symfony\Component\Yaml\Yaml; /** * YAML File Loader. @@ -86,6 +88,21 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) } } + if (isset($yaml['discriminator_map'])) { + if (!isset($yaml['discriminator_map']['type_property'])) { + throw new MappingException(sprintf('The "type_property" key must be set for the discriminator map of the class "%s" in "%s".', $classMetadata->getName(), $this->file)); + } + + if (!isset($yaml['discriminator_map']['mapping'])) { + throw new MappingException(sprintf('The "mapping" key must be set for the discriminator map of the class "%s" in "%s".', $classMetadata->getName(), $this->file)); + } + + $classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping( + $yaml['discriminator_map']['type_property'], + $yaml['discriminator_map']['mapping'] + )); + } + return true; } @@ -113,7 +130,7 @@ private function getClassesFromYaml() $this->yamlParser = new Parser(); } - $classes = $this->yamlParser->parseFile($this->file); + $classes = $this->yamlParser->parseFile($this->file, Yaml::PARSE_CONSTANT); if (empty($classes)) { return array(); diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd b/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd index afa8b92191362..14eff8c4dea7f 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd +++ b/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd @@ -8,7 +8,7 @@ @@ -37,10 +37,23 @@ + + + + + + + + + + + + + attributes = $attributes; $this->lowerCamelCase = $lowerCamelCase; diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 3ccf48529f6b4..6442e98a1473f 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Serializer\Normalizer; use Symfony\Component\Serializer\Exception\CircularReferenceException; +use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Exception\RuntimeException; @@ -19,21 +20,24 @@ use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; use Symfony\Component\Serializer\SerializerAwareInterface; +use Symfony\Component\Serializer\SerializerAwareTrait; /** * Normalizer implementation. * * @author Kévin Dunglas */ -abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface +abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface { use ObjectToPopulateTrait; + use SerializerAwareTrait; const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit'; const OBJECT_TO_POPULATE = 'object_to_populate'; const GROUPS = 'groups'; const ATTRIBUTES = 'attributes'; const ALLOW_EXTRA_ATTRIBUTES = 'allow_extra_attributes'; + const DEFAULT_CONSTRUCTOR_ARGUMENTS = 'default_constructor_arguments'; /** * @var int @@ -306,22 +310,10 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec * @return object * * @throws RuntimeException + * @throws MissingConstructorArgumentsException */ - protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes/*, string $format = null*/) + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null) { - if (func_num_args() >= 6) { - $format = func_get_arg(5); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s::%s() will have a 6th `string $format = null` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); - } - } - - $format = null; - } - if (null !== $object = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) { unset($context[static::OBJECT_TO_POPULATE]); @@ -339,7 +331,7 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref $allowed = false === $allowedAttributes || in_array($paramName, $allowedAttributes); $ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context); - if (method_exists($constructorParameter, 'isVariadic') && $constructorParameter->isVariadic()) { + if ($constructorParameter->isVariadic()) { if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { if (!is_array($data[$paramName])) { throw new RuntimeException(sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name)); @@ -364,10 +356,12 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref // Don't run set for a parameter passed to the constructor $params[] = $parameterData; unset($data[$key]); + } elseif (isset($context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key])) { + $params[] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key]; } elseif ($constructorParameter->isDefaultValueAvailable()) { $params[] = $constructorParameter->getDefaultValue(); } else { - throw new RuntimeException( + throw new MissingConstructorArgumentsException( sprintf( 'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.', $class, diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 08fbc5fa3fd88..bd812d289de37 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -18,7 +18,10 @@ use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\Serializer\Exception\RuntimeException; use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -38,11 +41,21 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer private $attributesCache = array(); private $cache = array(); - public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null) + /** + * @var ClassDiscriminatorResolverInterface|null + */ + protected $classDiscriminatorResolver; + + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null) { parent::__construct($classMetadataFactory, $nameConverter); $this->propertyTypeExtractor = $propertyTypeExtractor; + + if (null === $classDiscriminatorResolver && null !== $classMetadataFactory) { + $classDiscriminatorResolver = new ClassDiscriminatorFromClassMetadata($classMetadataFactory); + } + $this->classDiscriminatorResolver = $classDiscriminatorResolver; } /** @@ -101,6 +114,28 @@ public function normalize($object, $format = null, array $context = array()) return $data; } + /** + * {@inheritdoc} + */ + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null) + { + if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) { + if (!isset($data[$mapping->getTypeProperty()])) { + throw new RuntimeException(sprintf('Type property "%s" not found for the abstract object "%s"', $mapping->getTypeProperty(), $class)); + } + + $type = $data[$mapping->getTypeProperty()]; + if (null === ($mappedClass = $mapping->getClassForType($type))) { + throw new RuntimeException(sprintf('The type "%s" has no mapped class for the abstract object "%s"', $type, $class)); + } + + $class = $mappedClass; + $reflectionClass = new \ReflectionClass($class); + } + + return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format); + } + /** * Gets and caches attributes for the given object, format and context. * @@ -137,7 +172,13 @@ protected function getAttributes($object, $format = null, array $context) return $this->attributesCache[$class]; } - return $this->attributesCache[$class] = $this->extractAttributes($object, $format, $context); + $attributes = $this->extractAttributes($object, $format, $context); + + if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) { + array_unshift($attributes, $mapping->getTypeProperty()); + } + + return $this->attributesCache[$class] = $attributes; } /** @@ -168,7 +209,11 @@ abstract protected function getAttributeValue($object, $attribute, $format = nul */ public function supportsDenormalization($data, $type, $format = null) { - return isset($this->cache[$type]) ? $this->cache[$type] : $this->cache[$type] = class_exists($type); + if (!isset($this->cache[$type])) { + $this->cache[$type] = class_exists($type) || (interface_exists($type) && null !== $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type)); + } + + return $this->cache[$type]; } /** @@ -229,18 +274,14 @@ abstract protected function setAttributeValue($object, $attribute, $value, $form /** * Validates the submitted data and denormalizes it. * - * @param string $currentClass - * @param string $attribute - * @param mixed $data - * @param string|null $format - * @param array $context + * @param mixed $data * * @return mixed * * @throws NotNormalizableValueException * @throws LogicException */ - private function validateAndDenormalize($currentClass, $attribute, $data, $format, array $context) + private function validateAndDenormalize(string $currentClass, string $attribute, $data, ?string $format, array $context) { if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($currentClass, $attribute)) { return $data; @@ -302,13 +343,9 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma /** * Sets an attribute and apply the name converter if necessary. * - * @param array $data - * @param string $attribute - * @param mixed $attributeValue - * - * @return array + * @param mixed $attributeValue */ - private function updateData(array $data, $attribute, $attributeValue) + private function updateData(array $data, string $attribute, $attributeValue): array { if ($this->nameConverter) { $attribute = $this->nameConverter->normalize($attribute); @@ -323,13 +360,8 @@ private function updateData(array $data, $attribute, $attributeValue) * Is the max depth reached for the given attribute? * * @param AttributeMetadataInterface[] $attributesMetadata - * @param string $class - * @param string $attribute - * @param array $context - * - * @return bool */ - private function isMaxDepthReached(array $attributesMetadata, $class, $attribute, array &$context) + private function isMaxDepthReached(array $attributesMetadata, string $class, string $attribute, array &$context): bool { if ( !isset($context[static::ENABLE_MAX_DEPTH]) || @@ -358,12 +390,9 @@ private function isMaxDepthReached(array $attributesMetadata, $class, $attribute /** * Gets the cache key to use. * - * @param string|null $format - * @param array $context - * * @return bool|string */ - private function getCacheKey($format, array $context) + private function getCacheKey(?string $format, array $context) { try { return md5($format.serialize($context)); diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index 60ba68be25146..af4771348193c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -24,7 +24,7 @@ * * @final since version 3.3. */ -class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterface +class ArrayDenormalizer implements ContextAwareDenormalizerInterface, SerializerAwareInterface { /** * @var SerializerInterface|DenormalizerInterface @@ -66,10 +66,8 @@ public function denormalize($data, $class, $format = null, array $context = arra /** * {@inheritdoc} */ - public function supportsDenormalization($data, $type, $format = null/*, array $context = array()*/) + public function supportsDenormalization($data, $type, $format = null, array $context = array()) { - $context = \func_num_args() > 3 ? func_get_arg(3) : array(); - return '[]' === substr($type, -2) && $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format, $context); } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index f3c663c6b788d..7ab102ed7a9ec 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -26,10 +26,7 @@ class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterfa private $format; - /** - * @param string $format - */ - public function __construct($format = 'P%yY%mM%dDT%hH%iM%sS') + public function __construct(string $format = 'P%yY%mM%dDT%hH%iM%sS') { $this->format = $format; } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index aedd84a31f7a2..3dd94e07e029b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -34,11 +34,7 @@ class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface \DateTime::class => true, ); - /** - * @param string $format - * @param \DateTimeZone|null $timezone - */ - public function __construct($format = \DateTime::RFC3339, \DateTimeZone $timezone = null) + public function __construct(?string $format = \DateTime::RFC3339, \DateTimeZone $timezone = null) { $this->format = $format; $this->timezone = $timezone; @@ -88,12 +84,7 @@ public function denormalize($data, $class, $format = null, array $context = arra } if (null !== $dateTimeFormat) { - if (null === $timezone && PHP_VERSION_ID < 70000) { - // https://bugs.php.net/bug.php?id=68669 - $object = \DateTime::class === $class ? \DateTime::createFromFormat($dateTimeFormat, $data) : \DateTimeImmutable::createFromFormat($dateTimeFormat, $data); - } else { - $object = \DateTime::class === $class ? \DateTime::createFromFormat($dateTimeFormat, $data, $timezone) : \DateTimeImmutable::createFromFormat($dateTimeFormat, $data, $timezone); - } + $object = \DateTime::class === $class ? \DateTime::createFromFormat($dateTimeFormat, $data, $timezone) : \DateTimeImmutable::createFromFormat($dateTimeFormat, $data, $timezone); if (false !== $object) { return $object; diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index b5dbee7d51f34..3307fbec1a718 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -55,12 +55,8 @@ public function supportsDenormalization($data, $type, $format = null) /** * Checks if the given class has any get{Property} method. - * - * @param string $class - * - * @return bool */ - private function supports($class) + private function supports(string $class): bool { $class = new \ReflectionClass($class); $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); @@ -75,10 +71,8 @@ private function supports($class) /** * Checks if a method's name is get.* or is.*, and can be called without parameters. - * - * @return bool whether the method is a getter or boolean getter */ - private function isGetMethod(\ReflectionMethod $method) + private function isGetMethod(\ReflectionMethod $method): bool { $methodLength = strlen($method->name); diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index a92eb176d9c6f..294fd3b322491 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -16,6 +16,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Serializer\Exception\RuntimeException; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -28,13 +29,13 @@ class ObjectNormalizer extends AbstractObjectNormalizer { protected $propertyAccessor; - public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null) + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null) { if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) { throw new RuntimeException('The ObjectNormalizer class requires the "PropertyAccess" component. Install "symfony/property-access" to use it.'); } - parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor); + parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver); $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); } @@ -100,6 +101,14 @@ protected function extractAttributes($object, $format = null, array $context = a */ protected function getAttributeValue($object, $attribute, $format = null, array $context = array()) { + if (null !== $this->classDiscriminatorResolver) { + $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object); + + if (null !== $mapping && $attribute == $mapping->getTypeProperty()) { + return $this->classDiscriminatorResolver->getTypeForMappedObject($object); + } + } + return $this->propertyAccessor->getValue($object, $attribute); } diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 52dc4f4a52dcc..fe0d3521e890e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -50,12 +50,8 @@ public function supportsDenormalization($data, $type, $format = null) /** * Checks if the given class has any non-static property. - * - * @param string $class - * - * @return bool */ - private function supports($class) + private function supports(string $class): bool { $class = new \ReflectionClass($class); @@ -157,13 +153,10 @@ protected function setAttributeValue($object, $attribute, $value, $format = null /** * @param string|object $classOrObject - * @param string $attribute - * - * @return \ReflectionProperty * * @throws \ReflectionException */ - private function getReflectionProperty($classOrObject, $attribute) + private function getReflectionProperty($classOrObject, string $attribute): \ReflectionProperty { $reflectionClass = new \ReflectionClass($classOrObject); while (true) { diff --git a/src/Symfony/Component/Serializer/Normalizer/SerializerAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/SerializerAwareNormalizer.php deleted file mode 100644 index 0480d9ffba98b..0000000000000 --- a/src/Symfony/Component/Serializer/Normalizer/SerializerAwareNormalizer.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Serializer\Normalizer; - -use Symfony\Component\Serializer\SerializerAwareTrait; -use Symfony\Component\Serializer\SerializerAwareInterface; - -/** - * SerializerAware Normalizer implementation. - * - * @author Jordi Boggiano - * - * @deprecated since version 3.1, to be removed in 4.0. Use the SerializerAwareTrait instead. - */ -abstract class SerializerAwareNormalizer implements SerializerAwareInterface -{ - use SerializerAwareTrait; -} diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 1b244177c78a0..92975a35bbc85 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -13,10 +13,14 @@ use Symfony\Component\Serializer\Encoder\ChainDecoder; use Symfony\Component\Serializer\Encoder\ChainEncoder; +use Symfony\Component\Serializer\Encoder\ContextAwareDecoderInterface; +use Symfony\Component\Serializer\Encoder\ContextAwareEncoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Exception\NotEncodableValueException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; +use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -38,7 +42,7 @@ * @author Lukas Kahwe Smith * @author Kévin Dunglas */ -class Serializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface, EncoderInterface, DecoderInterface +class Serializer implements SerializerInterface, ContextAwareNormalizerInterface, ContextAwareDenormalizerInterface, ContextAwareEncoderInterface, ContextAwareDecoderInterface { /** * @var Encoder\ChainEncoder @@ -55,20 +59,6 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz */ protected $normalizers = array(); - /** - * @var array - * - * @deprecated since 3.1 will be removed in 4.0 - */ - protected $normalizerCache = array(); - - /** - * @var array - * - * @deprecated since 3.1 will be removed in 4.0 - */ - protected $denormalizerCache = array(); - public function __construct(array $normalizers = array(), array $encoders = array()) { foreach ($normalizers as $normalizer) { @@ -188,42 +178,16 @@ public function denormalize($data, $type, $format = null, array $context = array /** * {@inheritdoc} */ - public function supportsNormalization($data, $format = null/*, array $context = array()*/) + public function supportsNormalization($data, $format = null, array $context = array()) { - if (func_num_args() > 2) { - $context = func_get_arg(2); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED); - } - } - - $context = array(); - } - return null !== $this->getNormalizer($data, $format, $context); } /** * {@inheritdoc} */ - public function supportsDenormalization($data, $type, $format = null/*, array $context = array()*/) + public function supportsDenormalization($data, $type, $format = null, array $context = array()) { - if (func_num_args() > 3) { - $context = func_get_arg(3); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a fourth `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED); - } - } - - $context = array(); - } - return null !== $this->getDenormalizer($data, $type, $format, $context); } @@ -283,42 +247,16 @@ final public function decode($data, $format, array $context = array()) /** * {@inheritdoc} */ - public function supportsEncoding($format/*, array $context = array()*/) + public function supportsEncoding($format, array $context = array()) { - if (func_num_args() > 1) { - $context = func_get_arg(1); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a second `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED); - } - } - - $context = array(); - } - return $this->encoder->supportsEncoding($format, $context); } /** * {@inheritdoc} */ - public function supportsDecoding($format/*, array $context = array()*/) + public function supportsDecoding($format, array $context = array()) { - if (func_num_args() > 1) { - $context = func_get_arg(1); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a second `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED); - } - } - - $context = array(); - } - return $this->decoder->supportsDecoding($format, $context); } } diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/DiscriminatorMapTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/DiscriminatorMapTest.php new file mode 100644 index 0000000000000..df2f111fa34c3 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/DiscriminatorMapTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Annotation\DiscriminatorMap; + +/** + * @author Samuel Roze + */ +class DiscriminatorMapTest extends TestCase +{ + public function testGetTypePropertyAndMapping() + { + $annotation = new DiscriminatorMap(array('typeProperty' => 'type', 'mapping' => array( + 'foo' => 'FooClass', + 'bar' => 'BarClass', + ))); + + $this->assertEquals('type', $annotation->getTypeProperty()); + $this->assertEquals(array( + 'foo' => 'FooClass', + 'bar' => 'BarClass', + ), $annotation->getMapping()); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testExceptionWithoutTypeProperty() + { + new DiscriminatorMap(array('mapping' => array('foo' => 'FooClass'))); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testExceptionWithEmptyTypeProperty() + { + new DiscriminatorMap(array('typeProperty' => '', 'mapping' => array('foo' => 'FooClass'))); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testExceptionWithoutMappingProperty() + { + new DiscriminatorMap(array('typeProperty' => 'type')); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testExceptionWitEmptyMappingProperty() + { + new DiscriminatorMap(array('typeProperty' => 'type', 'mapping' => array())); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php index a5e5c256f34ad..de5d505a7e85b 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php @@ -173,6 +173,109 @@ public function testEncodeCustomHeaders() $this->assertEquals($csv, $this->encoder->encode($value, 'csv', $context)); } + public function testEncodeFormulas() + { + $this->encoder = new CsvEncoder(',', '"', '\\', '.', true); + + $this->assertSame(<<<'CSV' +0 +" =2+3" + +CSV + , $this->encoder->encode(array('=2+3'), 'csv')); + + $this->assertSame(<<<'CSV' +0 +" -2+3" + +CSV + , $this->encoder->encode(array('-2+3'), 'csv')); + + $this->assertSame(<<<'CSV' +0 +" +2+3" + +CSV + , $this->encoder->encode(array('+2+3'), 'csv')); + + $this->assertSame(<<<'CSV' +0 +" @MyDataColumn" + +CSV + , $this->encoder->encode(array('@MyDataColumn'), 'csv')); + } + + public function testDoNotEncodeFormulas() + { + $this->assertSame(<<<'CSV' +0 +=2+3 + +CSV + , $this->encoder->encode(array('=2+3'), 'csv')); + + $this->assertSame(<<<'CSV' +0 +-2+3 + +CSV + , $this->encoder->encode(array('-2+3'), 'csv')); + + $this->assertSame(<<<'CSV' +0 ++2+3 + +CSV + , $this->encoder->encode(array('+2+3'), 'csv')); + + $this->assertSame(<<<'CSV' +0 +@MyDataColumn + +CSV + , $this->encoder->encode(array('@MyDataColumn'), 'csv')); + } + + public function testEncodeFormulasWithSettingsPassedInContext() + { + $this->assertSame(<<<'CSV' +0 +" =2+3" + +CSV + , $this->encoder->encode(array('=2+3'), 'csv', array( + CsvEncoder::ESCAPE_FORMULAS_KEY => true, + ))); + + $this->assertSame(<<<'CSV' +0 +" -2+3" + +CSV + , $this->encoder->encode(array('-2+3'), 'csv', array( + CsvEncoder::ESCAPE_FORMULAS_KEY => true, + ))); + + $this->assertSame(<<<'CSV' +0 +" +2+3" + +CSV + , $this->encoder->encode(array('+2+3'), 'csv', array( + CsvEncoder::ESCAPE_FORMULAS_KEY => true, + ))); + + $this->assertSame(<<<'CSV' +0 +" @MyDataColumn" + +CSV + , $this->encoder->encode(array('@MyDataColumn'), 'csv', array( + CsvEncoder::ESCAPE_FORMULAS_KEY => true, + ))); + } + public function testSupportsDecoding() { $this->assertTrue($this->encoder->supportsDecoding('csv')); diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php index 774064d43a853..f4208f1256820 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php @@ -56,7 +56,6 @@ public function decodeProvider() } /** - * @requires function json_last_error_msg * @dataProvider decodeProviderException * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException */ diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php index ed33233fb47ea..f4cbf76afc16b 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php @@ -17,7 +17,7 @@ class JsonEncodeTest extends TestCase { - private $encoder; + private $encode; protected function setUp() { @@ -50,7 +50,6 @@ public function encodeProvider() } /** - * @requires function json_last_error_msg * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException */ public function testEncodeWithError() diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummy.php new file mode 100644 index 0000000000000..b25f7ff0c1a45 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummy.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Annotation\DiscriminatorMap; + +/** + * @DiscriminatorMap(typeProperty="type", mapping={ + * "first"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild", + * "second"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild" + * }) + */ +abstract class AbstractDummy +{ + public $foo; + + public function __construct($foo = null) + { + $this->foo = $foo; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummyFirstChild.php b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummyFirstChild.php new file mode 100644 index 0000000000000..645c307c35735 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummyFirstChild.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +class AbstractDummyFirstChild extends AbstractDummy +{ + public $bar; + + public function __construct($foo = null, $bar = null) + { + parent::__construct($foo); + + $this->bar = $bar; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummySecondChild.php b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummySecondChild.php new file mode 100644 index 0000000000000..5a41b9441ad8b --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummySecondChild.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +class AbstractDummySecondChild extends AbstractDummy +{ + public $baz; + + public function __construct($foo = null, $baz = null) + { + parent::__construct($foo); + + $this->baz = $baz; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageInterface.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageInterface.php new file mode 100644 index 0000000000000..f0b4c4d128c38 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Annotation\DiscriminatorMap; + +/** + * @DiscriminatorMap(typeProperty="type", mapping={ + * "first"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild", + * "second"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild" + * }) + * + * @author Samuel Roze + */ +interface DummyMessageInterface +{ +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberOne.php similarity index 58% rename from src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php rename to src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberOne.php index c85a589578ec5..381f7f8a6c70b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberOne.php @@ -9,14 +9,12 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity; +namespace Symfony\Component\Serializer\Tests\Fixtures; /** - * Car. - * - * @author Kévin Dunglas + * @author Samuel Roze */ -class Car +class DummyMessageNumberOne implements DummyMessageInterface { - public $id; + public $one; } diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml index 9ba51cbfdf6d4..d6f5ce3795ae1 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml @@ -20,4 +20,13 @@ + + + + + + + + + diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml index c4038704a50de..a967faf2a6d49 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml @@ -10,3 +10,11 @@ max_depth: 2 bar: max_depth: 3 +'Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy': + discriminator_map: + type_property: type + mapping: + first: 'Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild' + second: 'Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild' + attributes: + foo: ~ diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/ClassDiscriminatorMappingTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/ClassDiscriminatorMappingTest.php new file mode 100644 index 0000000000000..390a5b281e99d --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Mapping/ClassDiscriminatorMappingTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Mapping; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild; + +/** + * @author Samuel Roze + */ +class ClassDiscriminatorMappingTest extends TestCase +{ + public function testGetClass() + { + $mapping = new ClassDiscriminatorMapping('type', array( + 'first' => AbstractDummyFirstChild::class, + )); + + $this->assertEquals(AbstractDummyFirstChild::class, $mapping->getClassForType('first')); + $this->assertEquals(null, $mapping->getClassForType('second')); + } + + public function testMappedObjectType() + { + $mapping = new ClassDiscriminatorMapping('type', array( + 'first' => AbstractDummyFirstChild::class, + )); + + $this->assertEquals('first', $mapping->getMappedObjectType(new AbstractDummyFirstChild())); + $this->assertEquals(null, $mapping->getMappedObjectType(new AbstractDummySecondChild())); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php index 903cc04d1f49f..15aa621c298cd 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php @@ -45,35 +45,4 @@ public function testHasMetadataFor() $this->assertTrue($factory->hasMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummyInterface')); $this->assertFalse($factory->hasMetadataFor('Dunglas\Entity')); } - - /** - * @group legacy - */ - public function testCacheExists() - { - $cache = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); - $cache - ->expects($this->once()) - ->method('fetch') - ->will($this->returnValue('foo')) - ; - - $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $cache); - $this->assertEquals('foo', $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy')); - } - - /** - * @group legacy - */ - public function testCacheNotExists() - { - $cache = $this->getMockBuilder('Doctrine\Common\Cache\Cache')->getMock(); - $cache->method('fetch')->will($this->returnValue(false)); - $cache->method('save'); - - $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $cache); - $metadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); - - $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true, true), $metadata); - } } diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php index b2e5c69211227..b6566d333166c 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php @@ -13,8 +13,13 @@ use Doctrine\Common\Annotations\AnnotationReader; use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\ClassMetadata; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild; use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory; /** @@ -52,6 +57,22 @@ public function testLoadGroups() $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata); } + public function testLoadDiscriminatorMap() + { + $classMetadata = new ClassMetadata(AbstractDummy::class); + $this->loader->loadClassMetadata($classMetadata); + + $expected = new ClassMetadata(AbstractDummy::class, new ClassDiscriminatorMapping('type', array( + 'first' => AbstractDummyFirstChild::class, + 'second' => AbstractDummySecondChild::class, + ))); + + $expected->addAttributeMetadata(new AttributeMetadata('foo')); + $expected->getReflectionClass(); + + $this->assertEquals($expected, $classMetadata); + } + public function testLoadMaxDepth() { $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy'); diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php index 974d42ee55926..db2d7fda81aa7 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php @@ -12,8 +12,13 @@ namespace Symfony\Component\Serializer\Tests\Mapping\Loader; use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild; use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory; /** @@ -62,4 +67,19 @@ public function testMaxDepth() $this->assertEquals(2, $attributesMetadata['foo']->getMaxDepth()); $this->assertEquals(3, $attributesMetadata['bar']->getMaxDepth()); } + + public function testLoadDiscriminatorMap() + { + $classMetadata = new ClassMetadata(AbstractDummy::class); + $this->loader->loadClassMetadata($classMetadata); + + $expected = new ClassMetadata(AbstractDummy::class, new ClassDiscriminatorMapping('type', array( + 'first' => AbstractDummyFirstChild::class, + 'second' => AbstractDummySecondChild::class, + ))); + + $expected->addAttributeMetadata(new AttributeMetadata('foo')); + + $this->assertEquals($expected, $classMetadata); + } } diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php index 918af73b14d8f..7a0ecdd446d05 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php @@ -12,8 +12,13 @@ namespace Symfony\Component\Serializer\Tests\Mapping\Loader; use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild; use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory; /** @@ -77,4 +82,19 @@ public function testMaxDepth() $this->assertEquals(2, $attributesMetadata['foo']->getMaxDepth()); $this->assertEquals(3, $attributesMetadata['bar']->getMaxDepth()); } + + public function testLoadDiscriminatorMap() + { + $classMetadata = new ClassMetadata(AbstractDummy::class); + $this->loader->loadClassMetadata($classMetadata); + + $expected = new ClassMetadata(AbstractDummy::class, new ClassDiscriminatorMapping('type', array( + 'first' => AbstractDummyFirstChild::class, + 'second' => AbstractDummySecondChild::class, + ))); + + $expected->addAttributeMetadata(new AttributeMetadata('foo')); + + $this->assertEquals($expected, $classMetadata); + } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 3ca418d55ed6b..a1092138395d0 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -91,7 +91,7 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null return in_array($attribute, array('foo', 'baz')); } - public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null) + public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null) { return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format); } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php index 4bc94435992f8..e6bd915aebd43 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php @@ -156,7 +156,7 @@ public function testValidData($uri) public function validUriProvider() { - $data = array( + return array( array('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC'), array('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC'), array('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC '), @@ -167,14 +167,8 @@ public function validUriProvider() array('data:application/ld+json;base64,eyJAaWQiOiAiL2ZvbyJ9'), array('data:application/vnd.ms-word.document.macroenabled.12;base64,UEsDBBQABgAIAAAAIQBnzQ+udAEAADoFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0lMtOwzAQRfdI/EPkLUrcskAINe0CyhIqUT7AtSepIX7Idl9/zzhpI1TRBFG6iZTM3HvPOJmMJltVJWtwXhqdk2E2IAloboTUZU7e58/pPUl8YFqwymjIyQ48mYyvr0bznQWfoFr7nCxDsA+Uer4ExXxmLGisFMYpFvDWldQy/slKoLeDwR3lRgfQIQ3Rg4xHT1CwVRWS6RYfNyQoJ8lj0xejcsKsrSRnAcs0VumPOgeV7xCutTiiS/dkGSrrHr+U1t+cTviwUB4lSBVHqwuoecXjdFJAMmMuvDCFDXRjnKDC8JVCUdY9XGRUPo2SrJUoxp2ZaraoAKtM6gPhyTQfdhX4X2QdnYcpCsmhDY5e1hkO3uM3oaqs8e2PhxBQcAmAvXMvwgYWbxej+GbeC1Jg7jy+uv/HaK17IQLuJjTX4dkctU1XJHbOnLEed939YezDUkZ1igNbcEF2f3VtIlqfPR/EfRcgfsim9Z9v/AUAAP//AwBQSwMEFAAGAAgAAAAhAMfCJ7z/AAAA3wIAAAsACAJfcmVscy8ucmVscyCiBAIooAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsks1KAzEQgO+C7xDm3s22iog024sIvYmsDzAm093o5odkqu3bG0XUhWUR7HH+Pr5JZr05uEG8Uso2eAXLqgZBXgdjfafgsb1bXIPIjN7gEDwpOFKGTXN+tn6gAbkM5d7GLArFZwU9c7yRMuueHOYqRPKlsgvJIZcwdTKifsGO5Kqur2T6zYBmxBRboyBtzQWI9hjpf2zpiNEgo9Qh0SKmMp3Yll1Ei6kjVmCCvi/p/NlRFTLIaaHLvwuF3c5qug1678jzlBcdmLwhM6+EMc4ZLU9pNO74kXkLyUjzlZ6zWZ32w7jfuyePdph4l+9a9Ryp+xCSo7Ns3gEAAP//AwBQSwMEFAAGAAgAAAAhABOqPof2AAAAMQMAABwACAF3b3JkL19yZWxzL2RvY3VtZW50LnhtbC5yZWxzIKIEASigAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArJLLasMwEEX3hf6DmH0tO31QQuRsSiHb1v0ARR4/qCwJzfThv69oSOvQYLrw8l4x955Bs9l+Dla8Y6TeOwVFloNAZ3zdu1bBS/V4dQ+CWLtaW+9QwYgE2/LyYvOEVnMaoq4PJFKKIwUdc1hLSabDQVPmA7r00vg4aE4ytjJo86pblKs8v5NxmgHlSabY1Qrirr4GUY0B/5Ptm6Y3+ODN24COz1TID9w/I3NajlKsji2ygomZpUSQ50FulgRpvONK7y3+YvxYcxC3S0Jwmp0AfMuDWcwxFEsyEI8WJ59x0HP1q0Xr/1zD0TkiyJNDL78AAAD//wMAUEsDBBQABgAIAAAAIQAz6gHKYAIAAAIHAAARAAAAd29yZC9kb2N1bWVudC54bWykld1u2jAUx+8n7R2Q70sSCpRGQKVBh9A0qRrb9WQcJ7GIfSzbQLs32nPsxXacL7JNQ7QgFPt8+Of/sWNn+vAsi96BGytAzUjUD0mPKwaJUNmMfPv68WZCetZRldACFJ+RF27Jw/z9u+kxToDtJVeuhwhl46NmM5I7p+MgsCznktq+FMyAhdT1GcgA0lQwHhzBJMEgjMKypw0wbi3Ot6DqQC2pcRIuo0nKmu4gDCdoC9Uy/lUEmisMpmAkdWiaDEeY3V7fIFNTJ7aiEO7Fs8Yt5jAje6PimnHT6vBjYhQQH2TRJMO53Epo3TQjzCUiqyHLeslLeYHhBQoGZXOhT+v2VhoG8wZytuBOsUcdDa/b9KWhR2xOwEvkJ9UgWVTKzxOj8IId8Yh2xCUS/pyzUdJ9+Y5vW5rO4kaj1wEGfwN0dt3mrAzs9YkmrqOt1a5l+avkFax6k7ul2evEbHKq8QRKFq8zBYZuC1SEW9bDVe/515rM8YrbQvLiW43uYaypoetkRsLB8hb/Q1J6HX923ntX/9Ab43WafMHEcHQXLUaD1rXkKd0Xzkduw9FitChnMf7h5p9+/TwINQ183z9L9xZg5y+ojaPGIUb4+T1PUYmKv6/gA2U7EnRzH1XSZgYlSvuw5cw9mf+r22Dcex+j0eQ+LJXpbPMDo3gioug+LOfNsT+eDEuyT/hMPdIBHtxoGFXViyx3J3MLzoE82QVPO9Gc04TjFXgXTryZAriOme1dadbTMSgseq2mjFc5pRu/RCsjfNGFUPxJOIYqb8dN9VXhZbfa0OD08Zr/BgAA//8DAFBLAwQUAAYACAAAACEAJyDgAjMGAACMGgAAFQAAAHdvcmQvdGhlbWUvdGhlbWUxLnhtbOxZTYvbRhi+F/ofhO6OZVvyxxJvsGU7abObhOwmJcexNJYmO9KYmdHumhAoybFQKE1LDw301kJpG0igl/TUn7JtSptC/kJHI8uesccsaTawhNhgzcfzvvPM+848I1kXLx0n2DqElCGSdu3aBce2YBqQEKVR1761P6q0bYtxkIYAkxR27Rlk9qXtDz+4CLZ4DBNoCfuUbYGuHXM+3apWWSCaAbtApjAVfRNCE8BFlUbVkIIj4TfB1brjNKsJQKltpSARbvfj338Qzq5PJiiA9nbpfYjFT8pZ3hBgupf7hnOTfkYhyCQ2PKjlFzZjPqbWIcBdWwwUkqN9eMxtCwPGRUfXduTHrm5frC6MMN9gq9iN5GduNzcID+rSjkbjhaHrem6zt/AvAZiv44atYXPYXPiTABAEYqYFFxXr9Tv9gTfHKqCiaPA9aA0aNQ2v+G+s4Xte/tXwElQU3TX8aOQvY6iAiqJniEmr7rsaXoKKYnMN33J6A7el4SUoxig9WEM7XrPhl7NdQCYEXzHCO547atXn8CWqqqyuwj7lm9ZaAu4SOhIAmVzAUWrx2RROQCBwPsBoTJG1g6JYLLwpSAkTzU7dGTkN8Zt/XVmSEQFbECjWRVPA1ppyPhYLKJryrv2x8GorkFfPf3r1/Kl18uDZyYNfTx4+PHnwi8HqCkgj1erl91/8+/hT65+n37189JUZz1T8nz9/9sdvX5qBXAW++PrJX8+evPjm879/fGSA9ygYq/B9lEBmXYNH1k2SiIkZBoBj+noW+zFAqkUvjRhIQW5jQA95rKGvzQAGBlwf6hG8TYVMmICXs7sa4b2YZhwZgFfjRAPuEoL7hBrndDUfS41ClkbmwWmm4m4CcGga21/J7zCbivWOTC79GGo0b2CRchDBFHIr7yMHEBrM7iCkxXUXBZQwMuHWHWT1ATKGZB+NtdW0NLqCEpGXmYmgyLcWm93bVp9gk/sBPNSRYlcAbHIJsRbGyyDjIDEyBglWkTuAxyaSezMaaAFnXGQ6gphYwxAyZrK5Tmca3atCXsxp38WzREdSjg5MyB1AiIockAM/BsnUyBmlsYr9iB2IJQqsG4QbSRB9h+R1kQeQbkz3bQS1dJ++t28JZTUvkLwno6YtAYm+H2d4AqB0Xl3R8wSlp4r7iqx7b1fWhZC++PaxWXfPpaD3KDLuqFUZ34RbFW+f0BCdf+0egCy9AcV2MUDfS/d76X7npXvTfj57wV5qtLyJL2/VpZtk4337BGG8x2cY7jCp7kxMLxyJRlmRRovHhGksivPhNFxEgSxblPBPEI/3YjAVw9TkCBGbu46YNSVMnA+y2eg778BZskvCorVWK59MhQHgy3ZxvpTt4jTiRWuztXwEW7iXtUg+KpcEctvXIaEMppNoGEi0ysZTSMiZnQmLjoFFO3e/kYW8zLMi9p8F8n81PLdgJNYbwDDM81TYl9k980xvCqY+7bphep2c69lkWiOhLDedhLIMYxDC1eYzznVnmVKNXh6KdRqt9tvIdS4iK9qAU71mHYk91/CEmwBMu/ZE3BmKYjIV/liumwBHadcO+DzQ/0dZppTxAWBxAZNdxfwTxCG1MErEWlfTgNMlt1q9lc/xnJLrOOcvcvKiJhlOJjDgG1qWVdFXODH2viE4r5BMkN6LwyNrjDN6E4hAea1aHsAQMb6IZoiosriXUVyRq/lW1P4xW25RgKcxmJ8oqpgXcFle0FHmIZmuzkqvzyczjvIkvfGpe7pR3qGI5oYDJD81zfrx9g55hdVS9zVWhXSval2n1LpNp8SbHwgKteVgGrWcsYHaslWndoY3BMpwi6W56Yw469NgddXmB0R5Xylra68myPiuWPkDcbuaYc4kVXgsnhH88k/lQglka6kux9zKKOra9xyv5/p1z684bW9YcRuuU2l7vUal53mN2tCrOYN+/b4ICo+TmleMPRLPM3g2f/Ui29devyTlbfaFgCRVIt+rVKWxfP1Sq2uvX4r3LtZ+3m9bSETmXrM+6jQ6/Wal0+iNKu6g3650/Ga/Mmj6rcFo4Hvtzui+bR1KsNtr+G5z2K40a75fcZtOTr/dqbTcer3ntnrtodu7P4+1mHl5LcMreW3/BwAA//8DAFBLAwQKAAAAAAAAACEAahPYDYwlAACMJQAAFwAAAGRvY1Byb3BzL3RodW1ibmFpbC5qcGVn/9j/4AAQSkZJRgABAQAASABIAAD/4QCARXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAWmgAwAEAAAAAQAAAgAAAAAA/+0AOFBob3Rvc2hvcCAzLjAAOEJJTQQEAAAAAAAAOEJJTQQlAAAAAAAQ1B2M2Y8AsgTpgAmY7PhCfv/AABEIAgABaQMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/3QAEAC7/2gAMAwEAAhEDEQA/AP7Yfgx8GPg9N8HvhRLL8KPhrLLL8NfAskkkngTws8kkj+F9LZ3d200s7uxLMzHczEk5JNAHpX/ClPg3/wBEl+GX/hBeFf8A5W0AH/ClPg3/ANEl+GX/AIQXhX/5W0AH/ClPg3/0SX4Zf+EF4V/+VtAB/wAKU+Df/RJfhl/4QXhX/wCVtAB/wpT4N/8ARJfhl/4QXhX/AOVtAB/wpT4N/wDRJfhl/wCEF4V/+VtAB/wpT4N/9El+GX/hBeFf/lbQAf8AClPg3/0SX4Zf+EF4V/8AlbQAf8KU+Df/AESX4Zf+EF4V/wDlbQAf8KU+Df8A0SX4Zf8AhBeFf/lbQAf8KU+Df/RJfhl/4QXhX/5W0AH/AApT4N/9El+GX/hBeFf/AJW0AH/ClPg3/wBEl+GX/hBeFf8A5W0AH/ClPg3/ANEl+GX/AIQXhX/5W0AH/ClPg3/0SX4Zf+EF4V/+VtAB/wAKU+Df/RJfhl/4QXhX/wCVtAB/wpT4N/8ARJfhl/4QXhX/AOVtAB/wpT4N/wDRJfhl/wCEF4V/+VtAB/wpT4N/9El+GX/hBeFf/lbQAf8AClPg3/0SX4Zf+EF4V/8AlbQAf8KU+Df/AESX4Zf+EF4V/wDlbQAf8KU+Df8A0SX4Zf8AhBeFf/lbQAf8KU+Df/RJfhl/4QXhX/5W0AH/AApT4N/9El+GX/hBeFf/AJW0AH/ClPg3/wBEl+GX/hBeFf8A5W0AH/ClPg3/ANEl+GX/AIQXhX/5W0AH/ClPg3/0SX4Zf+EF4V/+VtAB/wAKU+Df/RJfhl/4QXhX/wCVtAB/wpT4N/8ARJfhl/4QXhX/AOVtAB/wpT4N/wDRJfhl/wCEF4V/+VtAB/wpT4N/9El+GX/hBeFf/lbQAf8AClPg3/0SX4Zf+EF4V/8AlbQAf8KU+Df/AESX4Zf+EF4V/wDlbQAf8KU+Df8A0SX4Zf8AhBeFf/lbQAf8KU+Df/RJfhl/4QXhX/5W0AH/AApT4N/9El+GX/hBeFf/AJW0AH/ClPg3/wBEl+GX/hBeFf8A5W0AH/ClPg3/ANEl+GX/AIQXhX/5W0AH/ClPg3/0SX4Zf+EF4V/+VtAB/wAKU+Df/RJfhl/4QXhX/wCVtAB/wpT4N/8ARJfhl/4QXhX/AOVtAB/wpT4N/wDRJfhl/wCEF4V/+VtAB/wpT4N/9El+GX/hBeFf/lbQAf8AClPg3/0SX4Zf+EF4V/8AlbQB/Nd/wrT4c/8ARP8AwT/4Sug//INAH//Q/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9H+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0v7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/T/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9T+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAfP3j79pv4T/DT4n6P8IvFmr3Vj4w1jwRqvxN2GC3TSNH+G3hs6v/AMJj8QPEmsXV5a2Xh3wZ4JTSB/wlPiPVnttK0+81vwpokdxc+IPFfh/SdQAMPQ/2v/gXr2heH9WtvFlrZ6n4g8X6B4Gi8GatfaFpnjPS/Eev+N2+HyafrmjXWsxx6bdaV4lg1DTtasZbxtUstX0rUvCS6fP45gXwu4B6v8L/AIufDX40+GR4x+FfjDSPG3hk3ENqNW0eSZoPNu9I0rxFp7GO5ht7hbfV/DevaD4m0O7aL7LrnhjXtC8SaRPe6JrOmX10AejUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/1f7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQB4L8Vv2YvgX8b9UbWvij4Ct/FWqP4Yv/BbXsut+JtKlPhTV9K8WaFrWgf8SPWdMT+zNd0Lx14t0TXrbAXW9H1y70zVftliILeIA4/Rv2Jv2Y9AvvDuqaX8Mkg1Pwr4a8O+D9Dv5PF3ju6u7Xw74T+M9h+0J4e06aW68TzG/GnfGDTLTxglzqH2q7mKS6FczzeGbm50eUA9j+Gfwk+Hvwd0e80D4c+H/wDhHdJv5PDUl3a/2rrerec/hD4eeCvhT4dbz9c1LU7lP7O8A/DvwdoOI5lF3/Y/9qX32nWdQ1PUb0A9HoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA/mXoA/9b+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1/7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/Q/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9H+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0v7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/T/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9T+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1f7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/W/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9f+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0P7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/R/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9L+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0/7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/U/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9X+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1v7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/X/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9D+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0f7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/S/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9P+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1P7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/V/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9b+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1/7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/Q/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9H+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0v7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/T/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9T+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1f7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/W/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9f+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0P7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/R/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9L+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0/7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/U/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9X+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1v7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/X/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9D+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0f7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/S/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9P+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1P7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/V/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9b+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1/7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/Q/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9H+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/0v7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/T/ur+Cn/JGvhJ/wBky8B/+orpVAHptABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/MvQB/9T+6v4Kf8ka+En/AGTLwH/6iulUAem0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH8y9AH/1f7q/gp/yRr4Sf8AZMvAf/qK6VQB6bQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAfzL0Af/W/th+DHxn+D0Pwe+FEUvxX+GsUsXw18CxyRyeO/CySRyJ4X0tXR0bUgyOjAqysNysCDgg0Aelf8Lr+Df/AEVr4Zf+F74V/wDllQAf8Lr+Df8A0Vr4Zf8Ahe+Ff/llQAf8Lr+Df/RWvhl/4XvhX/5ZUAH/AAuv4N/9Fa+GX/he+Ff/AJZUAH/C6/g3/wBFa+GX/he+Ff8A5ZUAH/C6/g3/ANFa+GX/AIXvhX/5ZUAH/C6/g3/0Vr4Zf+F74V/+WVAB/wALr+Df/RWvhl/4XvhX/wCWVAB/wuv4N/8ARWvhl/4XvhX/AOWVAB/wuv4N/wDRWvhl/wCF74V/+WVAB/wuv4N/9Fa+GX/he+Ff/llQAf8AC6/g3/0Vr4Zf+F74V/8AllQAf8Lr+Df/AEVr4Zf+F74V/wDllQAf8Lr+Df8A0Vr4Zf8Ahe+Ff/llQAf8Lr+Df/RWvhl/4XvhX/5ZUAH/AAuv4N/9Fa+GX/he+Ff/AJZUAH/C6/g3/wBFa+GX/he+Ff8A5ZUAH/C6/g3/ANFa+GX/AIXvhX/5ZUAH/C6/g3/0Vr4Zf+F74V/+WVAB/wALr+Df/RWvhl/4XvhX/wCWVAB/wuv4N/8ARWvhl/4XvhX/AOWVAB/wuv4N/wDRWvhl/wCF74V/+WVAB/wuv4N/9Fa+GX/he+Ff/llQAf8AC6/g3/0Vr4Zf+F74V/8AllQAf8Lr+Df/AEVr4Zf+F74V/wDllQAf8Lr+Df8A0Vr4Zf8Ahe+Ff/llQAf8Lr+Df/RWvhl/4XvhX/5ZUAH/AAuv4N/9Fa+GX/he+Ff/AJZUAH/C6/g3/wBFa+GX/he+Ff8A5ZUAH/C6/g3/ANFa+GX/AIXvhX/5ZUAH/C6/g3/0Vr4Zf+F74V/+WVAB/wALr+Df/RWvhl/4XvhX/wCWVAB/wuv4N/8ARWvhl/4XvhX/AOWVAB/wuv4N/wDRWvhl/wCF74V/+WVAB/wuv4N/9Fa+GX/he+Ff/llQAf8AC6/g3/0Vr4Zf+F74V/8AllQAf8Lr+Df/AEVr4Zf+F74V/wDllQAf8Lr+Df8A0Vr4Zf8Ahe+Ff/llQAf8Lr+Df/RWvhl/4XvhX/5ZUAH/AAuv4N/9Fa+GX/he+Ff/AJZUAH/C6/g3/wBFa+GX/he+Ff8A5ZUAH/C6/g3/ANFa+GX/AIXvhX/5ZUAH/C6/g3/0Vr4Zf+F74V/+WVAB/wALr+Df/RWvhl/4XvhX/wCWVAH813/Cy/hz/wBFA8E/+FVoP/ydQB//2QAAUEsDBBQABgAIAAAAIQDSXhXfgQMAAEwJAAARAAAAd29yZC9zZXR0aW5ncy54bWy0Vltv2zYUfh/Q/2DouYolxXJdrU4RO/GaIl6D2H3ZGyVRNhFehEPKmjvsv++IFCNvDQp3Rf1i8nznzu8c+937PwUfHShopuQ8iC+iYERloUomd/Pg83YVzoKRNkSWhCtJ58GR6uD91atf3rWZpsagmh6hC6kzUcyDvTF1Nh7rYk8F0ReqphLBSoEgBq+wGwsCT00dFkrUxLCccWaO4ySKpkHvRs2DBmTWuwgFK0BpVZnOJFNVxQraf3kLOCeuM7lRRSOoNDbiGCjHHJTUe1Zr7038X28I7r2Tw7eKOAju9do4OqPcVkH5bHFOep1BDaqgWuMDCe4TZHIIPPnK0XPsC4zdl2hdoXkc2dNp5un3OUj+40Dzcypx0D3LgYDjSV+GKLK7nVRAco6sxHJGmFFwhbT8opQYtVlNocC3QU5PomDcASWtSMPNluQbo2pUORDM4U00c/D+WO+ptIT4A6nu8UmSOrzYEyCFobCpSYFtXSppQHGvV6rflVkirQG77iw0OdAHoAdG2wdWmAaoc2S5P5w2bo7QkSQCi/nXbKxViURvswbY+f3uDGxSsc/9xUAK5x5YSbddEzfmyOkKa9qwL/Ralh8bbRh6tA35gQy+lQC2GyN/wmffHmu6oqTrkf5JwewDrTir1wxAwZ0skR4/LRirKgoYgBFD18g6Bqq1ff5ASYnr9gfjjk9phMu71P7wqJTxqlF0GaXLdOky7dBzkPRNvEyTl5DbOJ29tdM0fo4qsm7xPYA/dRQaCWexJCIHRkbrbjWOO40cnhZMejynOOv0FNk0uQfD0AFaEM5XOHoesAmIrGS6vqGVPfM1gd3gt9eAF6W4Bj4+++pWBIXfQDW1Q1sgtaOGV4knboGIjElzz4SX6ybfeCuJ2+kEamT56QC2T0N72szgE9sRuyeWKla3gnD12FOJw6ajAV2TunZsynfxPOBstzdxRwCDtxJ/Qe0l3yU9llgscZi9kKKrDLX7wyBLvOxE79LLLgfZxMsmgyz1snSQTb1s2slwiVLgTD4hsf2xk1eKc9XS8sOAfyXyW7pg+OKbo8iH5fraYZxpnLQa97BR4LFfLRZPslIVd0hWPLl3i5LbWZIsHJza/W22yKMnbO0jrRZE07LHvGnqTP9adZ9Zsgiv45sknEzTRThLlrfhYpVcx8vrt9N0mfzdz4H/G3T1DwAAAP//AwBQSwMEFAAGAAgAAAAhAPC8NQHcAQAA8QUAABIAAAB3b3JkL2ZvbnRUYWJsZS54bWy8k9tq4zAQhu8LfQej+8ay4vRg6pQ0bWBh6cXSfQBFkW2xOhhJiTdvvyPZcQMhbJallUHI/4x+jT40j0+/lUx23DphdImyCUYJ18xshK5L9PN9dXOPEuep3lBpNC/Rnjv0NL++euyKymjvEtivXaFYiRrv2yJNHWu4om5iWq4hWBmrqIdfW6eK2l/b9oYZ1VIv1kIKv08JxrdosLGXuJiqEoy/GLZVXPu4P7VcgqPRrhGtO7h1l7h1xm5aaxh3Du6sZO+nqNCjTZafGCnBrHGm8hO4zFBRtILtGY4rJT8MZv9mQEYDxYpvtTaWriXAh0oSMEPzgX7SFZoqCCypFGsrYqCl2jieQWxHZYkwwSs8gzl8OZ6GGaUhkTXUOh5M+kTcyxVVQu4PKt160+ut8Kw5yDtqRaipDzlRQ2Dr1rhErxgGWa1Qr2QlykFYLEeFhKPiyAZlOio4KCz69BkPcReLPmMOnJn2AE5AvAvFXfLGu+SHUVSfAULwLYCYAY4AZvr5QMji9QjIEpS7+/xw/Q8gD38H0mO8HMgCypJnMDwDhnx4GfF1fD6G43cxYJh+BYahQZLvom782TYJzfFFbbIIFZPjVxHahOC75xMc8fL/2SbDws3/AAAA//8DAFBLAwQUAAYACAAAACEA4IvKVR8BAAARAgAAFAAAAHdvcmQvd2ViU2V0dGluZ3MueG1slNFRS8MwEAfwd8HvUPK+pRs6tKwbgkz2MgbVD5Cl1zWY5EIua7dv71nnRHyZbzku9+P+3Hx5dDbrIJJBX4rJOBcZeI218ftSvL2uRg8io6R8rSx6KMUJSCwXtzfzvuhhV0FK/JMyVjwVTpeiTSkUUpJuwSkaYwDPzQajU4nLuJdOxfdDGGl0QSWzM9akk5zm+UycmXiNgk1jNDyjPjjwaZiXESyL6Kk1gb61/hqtx1iHiBqIOI+zX55Txl+Yyd0fyBkdkbBJYw5z3migeHySDy9nf4D7/wHTC+B0sd57jGpn+QS8ScaYWPANlLXYbzcv8rOocYOpUh08UcUpLKyMhaETzBEsbSGuvW6zvuiULcXjTHBT/jrk4gMAAP//AwBQSwMEFAAGAAgAAAAhAJ/qVV97AQAAFQMAABEACAFkb2NQcm9wcy9jb3JlLnhtbCCiBAEooAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJySUU/CMBCA3038D0vfRzdQgssYiRqeJJoI0fhW2xtUtrZpDwb/3m6DIZHExLfe7ruvt7umk11ZBFuwTmo1JnEvIgEoroVUyzFZzKfhiAQOmRKs0ArGZA+OTLLrq5SbhGsLL1YbsCjBBd6kXMLNmKwQTUKp4ysomet5Qvlkrm3J0Id2SQ3ja7YE2o+iIS0BmWDIaC0MTWckB6XgndJsbNEIBKdQQAkKHY17MT2xCLZ0FwuazA+ylLg3cBE9Jjt652QHVlXVqwYN6vuP6fvs6bX51VCqelYcSJYKnqDEArKUno7+5DafX8Cx/dwF/swtMNQ2W6AspGMIGxsICGaSW+10jsFznksOTd2Rrbewhn2lrXDeeBZ5TIDjVhr0u23vO/vg6YI5nPll5xLE/f7vq3+X1BYLW1m/nyxuiC5MD8to2wUR+CEm7ciPmbfBw+N8SrJ+FA/DOAr7N/NolNzeJVH0UXd8Vn8SlocG/m08CtqhnT/k7BsAAP//AwBQSwMEFAAGAAgAAAAhAIGW/TkyCwAAZHIAAA8AAAB3b3JkL3N0eWxlcy54bWy8ndty27oVhu8703fg6Kq9cHyMnXi2s8d24tpTO9s7cppriIQk1CCh8uBDn74gSEmQF0FxAau+siVqfQDx4wewQFL67feXVEZPPC+Eys5G+x/2RhHPYpWIbHY2+vlwtfNpFBUlyxImVcbPRq+8GP3+5a9/+e35tChfJS8iDciK0zQ+G83LcnG6u1vEc56y4oNa8EwfnKo8ZaV+mc92U5Y/VoudWKULVoqJkKJ83T3Y2zsetZh8CEVNpyLmX1VcpTwrTfxuzqUmqqyYi0WxpD0PoT2rPFnkKuZFoU86lQ0vZSJbYfaPACgVca4KNS0/6JNpa2RQOnx/z/yXyjXgIw5wsAKk8enNLFM5m0jd+romkYaNvujmT1T8lU9ZJcuifpnf5+3L9pX5c6WysoieT1kRC/GgS9aQVGje9XlWiJE+wllRnheCdR6c1/90HomL0nr7QiRitFuXWPxXH3xi8mx0cLR857KuwcZ7kmWz5XvTfOfqh12TsxHPdn6O67cmmns2YvnO+LwO3G1PrPlrne5i9ar51Ju20V1Dd5Rx01/1UT69VfEjT8alPnA22quL0m/+vLnPhcp1nzwbff7cvjnmqbgWScIz64PZXCT815xnPwuerN//88r0q/aNWFWZ/v/w057RSxbJt5eYL+peqo9mrG6973WArD9diXXhJvw/S9h+22Zd8XPOaqtG+28RpvooxEEdUVhn282s3py7+RSqoMP3KujovQr6+F4FHb9XQSfvVdCn9yrIYP6fBYks4S+NEWExgLqN43AjmuMwG5rj8BKa47AKmuNwAprj6OhojqMfozmOborglCp29UKrsx86ens/d/sc4cfdPiX4cbfPAH7c7QO+H3f7+O7H3T6c+3G3j95+3O2DNZ7bLLWiG22zrAx22VSpMlMlj0r+Ek5jmWaZ/IWGV096PCc5SQJMM7K1E3EwLWbm9fYeYkzqP5+XdcoVqWk0FbMq12lvaMV59sSlTkAjliSaRwjMeVnljhbx6dM5n/KcZzGn7Nh0UCkyHmVVOiHomws2I2PxLCFuviWRZFBYdWhWlfPaJIKgU6cszlV41RQjGx9uRRHeVjUkuqik5ESs7zRdzLDCcwODCU8NDCY8MzCY8MTA0oyqiVoaUUu1NKIGa2lE7db0T6p2a2lE7dbSiNqtpYW324MopRni7VXH/vC9u0up6h3n4HqMxSxjegEQPt20e6bRPcvZLGeLeVTvH3dj7XPGlnOhktfogWJOW5Go1vWmi1zqsxZZFd6gGzQqc614RPZa8YgMtuKFW+xOL5PrBdo1TT4zriZlp2kNaZBpx0xWzYI23G2sDO9hawNcibwgs0E3lqAHf6+Xs7WcFCPfupbhFVuzwm31dlQirV6LJKilVPEjzTB8/brguU7LHoNJV0pK9cwTOuK4zFXT12zLHxhJBln+W7qYs0KYXGkDMXyqX16rju7YIviE7iUTGY1u33ZSJmREt4K4fri7jR7Uok4z64ahAV6oslQpGbPdCfzbLz75O00Fz3USnL0Sne050faQgV0KgkmmIamEiKSXmSITJHOo4f2Tv04UyxMa2n3Om9tDSk5EHLN00Sw6CLylx8VnPf4QrIYM718sF/W+EJWpHkhg1rZhUU3+zePwoe67ikh2hv6oSrP/aJa6JpoOF75M2MCFLxGMmnp6qPsvwclu4MJPdgNHdbKXkhWFcF5C9eZRne6SR32+4clfy1NS5dNK0jXgEkjWgksgWRMqWaVZQXnGhkd4woZHfb6EXcbwCLbkDO8fuUjIxDAwKiUMjEoGA6PSwMBIBQi/Q8eChd+mY8HC79VpYERLAAtG1c9Ip3+iqzwWjKqfGRhVPzMwqn5mYFT97PBrxKdTvQimm2IsJFWfs5B0E01W8nShcpa/EiG/ST5jBBukDe0+V9P6uQGVNTdxEyDrPWpJuNhucFQi/+ITsqrVLMp6EeyIMimVItpbW084JnLz3rVtYeaZi+AqmM32W/7EKVbjFozoMkADC5fNgoVPUxYsfJqyYOHTlAULn6YsWPg0ZcHC71++lyzmcyUTnjuM2FeRaLxgcXttCVyjHrRXfytm8zIaz1eXqGzM8d7WyOUu00bY9gK7Borjg56wO56IKl1WFD4BdHw4PNgYeiN4+aBWT/B6+bsR+XFgJCzzeHvkOrXbiDwZGAnL/DQw0oxSG5F9g/hXlj92doSTvv6z2phwdL6Tvl60Cu4stq8jrSK7uuBJXy/asEp0Hsf1JS6ozjDPuOOHmccdj3GRm4Kxk5sy2FduRJ/BfvAnUS9HMYOmKW91y8/b4g7NlDpo5PyzUs3Fpo2rpMOfRLzRq/2s4FEn53D41daNUcbdjoOHGzdi8LjjRgwegNyIQSORMxw1JLkpg8cmN2LwIOVGoEcrOCPgRisYjxutYLzPaAUpPqNVwCrAjRi8HHAj0EaFCLRRA1YKbgTKqCDcy6iQgjYqRKCNChFoo8IFGM6oMB5nVBjvY1RI8TEqpKCNChFoo0IE2qgQgTYqRKCN6rm2d4Z7GRVS0EaFCLRRIQJtVLNeDDAqjMcZFcb7GBVSfIwKKWijQgTaqBCBNipEoI0KEWijQgTKqCDcy6iQgjYqRKCNChFoozbPx/obFcbjjArjfYwKKT5GhRS0USECbVSIQBsVItBGhQi0USECZVQQ7mVUSEEbFSLQRoUItFHNpYMAo8J4nFFhvI9RIcXHqJCCNipEoI0KEWijQgTaqBCBNipEoIwKwr2MCiloo0IE2qgQ0dc/2+vqrmdD9vG7ns7HTIZfumor9cP+/gEbdTgctayVmzX8AZoLpR6jzqdlD02+MQwiJlIos0XtuBfE5poLpKir9X9c9j+WZtMDvymsfYDHXOgH8KOhkWBP5aivy9uRIMk76uvpdiRYdR71jb52JJgGj/oGXePL5Z1UejoCwX3DjBW87wjvG62tcNjEfWO0FQhbuG9ktgJhA/eNx1bgx6genN9GfxzYTserm6IBoa87WoQTN6GvW0KtlsMxNMZQ0dyEoeq5CUNldBNQejoxeGHdKLTCbpSf1NBmWKn9jeomYKWGBC+pAcZfaojylhqi/KSGAyNWakjASu0/OLsJXlIDjL/UEOUtNUT5SQ2nMqzUkICVGhKwUgdOyE6Mv9QQ5S01RPlJDRd3WKkhASs1JGClhgQvqQHGX2qI8pYaovykBlkyWmpIwEoNCVipIcFLaoDxlxqivKWGqD6pzS7KhtQoha1w3CLMCsRNyFYgbnC2Aj2yJSvaM1uyCJ7ZEtRqqTkuW7JFcxOGqucmDJXRTUDp6cTghXWj0Aq7UX5S47KlLqn9jeomYKXGZUtOqXHZUq/UuGypV2pctuSWGpctdUmNy5a6pPYfnN0EL6lx2VKv1LhsqVdqXLbklhqXLXVJjcuWuqTGZUtdUgdOyE6Mv9S4bKlXaly25JYaly11SY3LlrqkxmVLXVLjsiWn1LhsqVdqXLbUKzUuW3JLjcuWuqTGZUtdUuOypS6pcdmSU2pcttQrNS5b6pXakS3tPm/8aljNNr93pz9cvi54/cXx1gMzSfPFue1FQPPBm2T16151cF2TqP3Fs/ZtU+H2gmFTogmERcVzXVbcfuWXo6h7JYU+b5Yn+nAJinR8s6+pwvrkl59uG3N9EbT53MYFz94al3Vj99TWiMGq3vZpFHNV8XPbBbfVUddoIpsfw9P/3GSJBjy3v7DW1DV5YQ1KH7/kUt6x5tNq4f6o5NOyObq/Zx6ffXN80nxhoTM+N4OEE7C7WZnmZfvDd44Wb37CoL167Wj18yquMi61G3hHm5v7KUKbe13B5X/Fl/8BAAD//wMAUEsDBBQABgAIAAAAIQDtJ+K6ZQEAALUCAAAQAAgBZG9jUHJvcHMvYXBwLnhtbCCiBAEooAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJxSwUrFMBC8C/5D6d2XPkER2RcRRTyoCK/qOSTbNpgmIVnF9/durNaqN3Pand1MZobA2dvoqldM2Qa/qderpq7Q62Cs7zf1Q3t1cFJXmZQ3ygWPm3qHuT6T+3twn0LERBZzxRQ+b+qBKJ4KkfWAo8orHnuedCGNirhNvQhdZzVeBv0yoidx2DTHAt8IvUFzEGfCemI8faX/kpqgi7782O4i80locYxOEcq7ctOtTKARxIxCG0i51o4oG4bnBu5Vj1muQUwFPIVkctmZCrgYVFKaOD95BGLRwXmMzmpFnKu8tTqFHDqqbpW2nkIeqkIAYrkF7GGL+iVZ2pUnli3cWD8JmQoWllSfVBw+1c0dbLVyeMHuZadcRhDfQGF5zg+xDZfF9ef8J7iw9GRp2Eal8Ze5BQ5bRtGw1Pm1GYBrDj+5ws53fY/ma+fvoMT1OP1CuT5aNXw+wvnC2OL8PeQ7AAAA//8DAFBLAQItABQABgAIAAAAIQBnzQ+udAEAADoFAAATAAAAAAAAAAAAAAAAAAAAAABbQ29udGVudF9UeXBlc10ueG1sUEsBAi0AFAAGAAgAAAAhAMfCJ7z/AAAA3wIAAAsAAAAAAAAAAAAAAAAArQMAAF9yZWxzLy5yZWxzUEsBAi0AFAAGAAgAAAAhABOqPof2AAAAMQMAABwAAAAAAAAAAAAAAAAA3QYAAHdvcmQvX3JlbHMvZG9jdW1lbnQueG1sLnJlbHNQSwECLQAUAAYACAAAACEAM+oBymACAAACBwAAEQAAAAAAAAAAAAAAAAAVCQAAd29yZC9kb2N1bWVudC54bWxQSwECLQAUAAYACAAAACEAJyDgAjMGAACMGgAAFQAAAAAAAAAAAAAAAACkCwAAd29yZC90aGVtZS90aGVtZTEueG1sUEsBAi0ACgAAAAAAAAAhAGoT2A2MJQAAjCUAABcAAAAAAAAAAAAAAAAAChIAAGRvY1Byb3BzL3RodW1ibmFpbC5qcGVnUEsBAi0AFAAGAAgAAAAhANJeFd+BAwAATAkAABEAAAAAAAAAAAAAAAAAyzcAAHdvcmQvc2V0dGluZ3MueG1sUEsBAi0AFAAGAAgAAAAhAPC8NQHcAQAA8QUAABIAAAAAAAAAAAAAAAAAezsAAHdvcmQvZm9udFRhYmxlLnhtbFBLAQItABQABgAIAAAAIQDgi8pVHwEAABECAAAUAAAAAAAAAAAAAAAAAIc9AAB3b3JkL3dlYlNldHRpbmdzLnhtbFBLAQItABQABgAIAAAAIQCf6lVfewEAABUDAAARAAAAAAAAAAAAAAAAANg+AABkb2NQcm9wcy9jb3JlLnhtbFBLAQItABQABgAIAAAAIQCBlv05MgsAAGRyAAAPAAAAAAAAAAAAAAAAAIpBAAB3b3JkL3N0eWxlcy54bWxQSwECLQAUAAYACAAAACEA7SfiumUBAAC1AgAAEAAAAAAAAAAAAAAAAADpTAAAZG9jUHJvcHMvYXBwLnhtbFBLBQYAAAAADAAMAAYDAACETwAAAAA='), array('data:a!b#c&d-e^f_g+h.i/a!b#c&d-e^f_g+h.i;base64,foobar'), + array('data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQh'), ); - - if (!defined('HHVM_VERSION')) { - // See https://github.com/facebook/hhvm/issues/6354 - $data[] = array('data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQh'); - } - - return $data; } private function getContent(\SplFileObject $file) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php index 6358cd7a908e9..16b04fb5d6088 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php @@ -146,9 +146,6 @@ public function testConstructorDenormalizeWithOptionalDefaultArgument() $this->assertEquals('test', $obj->getBar()); } - /** - * @requires PHP 5.6 - */ public function testConstructorDenormalizeWithVariadicArgument() { $obj = $this->normalizer->denormalize( @@ -157,9 +154,6 @@ public function testConstructorDenormalizeWithVariadicArgument() $this->assertEquals(array(1, 2, 3), $obj->getFoo()); } - /** - * @requires PHP 5.6 - */ public function testConstructorDenormalizeWithMissingVariadicArgument() { $obj = $this->normalizer->denormalize( diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index f2d389a0df07e..68f062cd5dcc6 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -203,6 +203,38 @@ public function testConstructorWithUnknownObjectTypeHintDenormalize() $normalizer->denormalize($data, DummyWithConstructorInexistingObject::class); } + /** + * @expectedException \Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException + * @expectedExceptionMessage Cannot create an instance of Symfony\Component\Serializer\Tests\Normalizer\DummyValueObject from serialized data because its constructor requires parameter "bar" to be present. + */ + public function testConstructorWithMissingData() + { + $data = array( + 'foo' => 10, + ); + + $normalizer = new ObjectNormalizer(); + + $normalizer->denormalize($data, DummyValueObject::class); + } + + public function testFillWithEmptyDataWhenMissingData() + { + $data = array( + 'foo' => 10, + ); + + $normalizer = new ObjectNormalizer(); + + $result = $normalizer->denormalize($data, DummyValueObject::class, 'json', array( + 'default_constructor_arguments' => array( + DummyValueObject::class => array('foo' => '', 'bar' => ''), + ), + )); + + $this->assertEquals(new DummyValueObject(10, ''), $result); + } + public function testGroupsNormalize() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); @@ -1025,6 +1057,17 @@ public function __construct($id, Unknown $unknown) { } } +class DummyValueObject +{ + private $foo; + private $bar; + + public function __construct($foo, $bar) + { + $this->foo = $foo; + $this->bar = $bar; + } +} class JsonNumber { diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index f7f8594cb12bc..7550b3c9b7ba1 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -12,6 +12,10 @@ namespace Symfony\Component\Serializer\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; +use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -23,6 +27,11 @@ use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild; +use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild; +use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface; +use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne; use Symfony\Component\Serializer\Tests\Fixtures\TraversableDummy; use Symfony\Component\Serializer\Tests\Fixtures\NormalizableTraversableDummy; use Symfony\Component\Serializer\Tests\Normalizer\TestNormalizer; @@ -346,6 +355,107 @@ public function testDeserializeObjectConstructorWithObjectTypeHint() $this->assertEquals(new Foo(new Bar('baz')), $serializer->deserialize($jsonData, Foo::class, 'json')); } + + public function testDeserializeAndSerializeAbstractObjectsWithTheClassMetadataDiscriminatorResolver() + { + $example = new AbstractDummyFirstChild('foo-value', 'bar-value'); + + $loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock(); + $loaderMock->method('hasMetadataFor')->will($this->returnValueMap(array( + array( + AbstractDummy::class, + true, + ), + ))); + + $loaderMock->method('getMetadataFor')->will($this->returnValueMap(array( + array( + AbstractDummy::class, + new ClassMetadata( + AbstractDummy::class, + new ClassDiscriminatorMapping('type', array( + 'first' => AbstractDummyFirstChild::class, + 'second' => AbstractDummySecondChild::class, + )) + ), + ), + ))); + + $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock); + $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); + + $jsonData = '{"type":"first","bar":"bar-value","foo":"foo-value"}'; + + $deserialized = $serializer->deserialize($jsonData, AbstractDummy::class, 'json'); + $this->assertEquals($example, $deserialized); + + $serialized = $serializer->serialize($deserialized, 'json'); + $this->assertEquals($jsonData, $serialized); + } + + public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadataDiscriminatorResolver() + { + $example = new DummyMessageNumberOne(); + $example->one = 1; + + $jsonData = '{"message-type":"one","one":1}'; + + $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface()); + $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); + + $deserialized = $serializer->deserialize($jsonData, DummyMessageInterface::class, 'json'); + $this->assertEquals($example, $deserialized); + + $serialized = $serializer->serialize($deserialized, 'json'); + $this->assertEquals($jsonData, $serialized); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\RuntimeException + * @expectedExceptionMessage The type "second" has no mapped class for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface" + */ + public function testExceptionWhenTypeIsNotKnownInDiscriminator() + { + $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface()); + $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); + $serializer->deserialize('{"message-type":"second","one":1}', DummyMessageInterface::class, 'json'); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\RuntimeException + * @expectedExceptionMessage Type property "message-type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface" + */ + public function testExceptionWhenTypeIsNotInTheBodyToDeserialiaze() + { + $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface()); + $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); + $serializer->deserialize('{"one":1}', DummyMessageInterface::class, 'json'); + } + + private function metadataFactoryMockForDummyInterface() + { + $factoryMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock(); + $factoryMock->method('hasMetadataFor')->will($this->returnValueMap(array( + array( + DummyMessageInterface::class, + true, + ), + ))); + + $factoryMock->method('getMetadataFor')->will($this->returnValueMap(array( + array( + DummyMessageInterface::class, + new ClassMetadata( + DummyMessageInterface::class, + new ClassDiscriminatorMapping('message-type', array( + 'one' => DummyMessageNumberOne::class, + )) + ), + ), + ))); + + return $factoryMock; + } } class Model diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index e25b4b0a7e1c1..4de54606d928d 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -16,25 +16,25 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "require-dev": { "symfony/yaml": "~3.4|~4.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/property-access": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/cache": "~3.1|~4.0", - "symfony/property-info": "~3.1|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/property-access": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/cache": "~3.4|~4.0", + "symfony/property-info": "~3.4|~4.0", "doctrine/annotations": "~1.0", - "symfony/dependency-injection": "~3.2|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", "doctrine/cache": "~1.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0" }, "conflict": { "phpdocumentor/type-resolver": "<0.2.1", - "symfony/dependency-injection": "<3.2", - "symfony/property-access": ">=3.0,<3.0.4|>=2.8,<2.8.4", - "symfony/property-info": "<3.1", + "symfony/dependency-injection": "<3.4", + "symfony/property-access": "<3.4", + "symfony/property-info": "<3.4", "symfony/yaml": "<3.4" }, "suggest": { @@ -56,7 +56,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Stopwatch/Section.php b/src/Symfony/Component/Stopwatch/Section.php index 0860dbe7fcdfd..50640b0b27740 100644 --- a/src/Symfony/Component/Stopwatch/Section.php +++ b/src/Symfony/Component/Stopwatch/Section.php @@ -47,9 +47,9 @@ class Section * @param float|null $origin Set the origin of the events in this section, use null to set their origin to their start time * @param bool $morePrecision If true, time is stored as float to keep the original microsecond precision */ - public function __construct($origin = null, $morePrecision = false) + public function __construct(float $origin = null, bool $morePrecision = false) { - $this->origin = is_numeric($origin) ? $origin : null; + $this->origin = $origin; $this->morePrecision = $morePrecision; } diff --git a/src/Symfony/Component/Stopwatch/Stopwatch.php b/src/Symfony/Component/Stopwatch/Stopwatch.php index a1d5d959c8e1a..fe1645fc544e1 100644 --- a/src/Symfony/Component/Stopwatch/Stopwatch.php +++ b/src/Symfony/Component/Stopwatch/Stopwatch.php @@ -36,7 +36,7 @@ class Stopwatch /** * @param bool $morePrecision If true, time is stored as float to keep the original microsecond precision */ - public function __construct($morePrecision = false) + public function __construct(bool $morePrecision = false) { $this->morePrecision = $morePrecision; $this->reset(); diff --git a/src/Symfony/Component/Stopwatch/StopwatchEvent.php b/src/Symfony/Component/Stopwatch/StopwatchEvent.php index d8f9d1361d586..410f935a269eb 100644 --- a/src/Symfony/Component/Stopwatch/StopwatchEvent.php +++ b/src/Symfony/Component/Stopwatch/StopwatchEvent.php @@ -50,7 +50,7 @@ class StopwatchEvent * * @throws \InvalidArgumentException When the raw time is not valid */ - public function __construct($origin, $category = null, $morePrecision = false) + public function __construct(float $origin, string $category = null, bool $morePrecision = false) { $this->origin = $this->formatTime($origin); $this->category = is_string($category) ? $category : 'default'; diff --git a/src/Symfony/Component/Stopwatch/StopwatchPeriod.php b/src/Symfony/Component/Stopwatch/StopwatchPeriod.php index 5626aa5333042..213cf59e12e89 100644 --- a/src/Symfony/Component/Stopwatch/StopwatchPeriod.php +++ b/src/Symfony/Component/Stopwatch/StopwatchPeriod.php @@ -27,7 +27,7 @@ class StopwatchPeriod * @param int|float $end The relative time of the end of the period (in milliseconds) * @param bool $morePrecision If true, time is stored as float to keep the original microsecond precision */ - public function __construct($start, $end, $morePrecision = false) + public function __construct($start, $end, bool $morePrecision = false) { $this->start = $morePrecision ? (float) $start : (int) $start; $this->end = $morePrecision ? (float) $end : (int) $end; diff --git a/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php b/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php index f2ee039797753..c1f64f9bfd821 100644 --- a/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php +++ b/src/Symfony/Component/Stopwatch/Tests/StopwatchEventTest.php @@ -154,14 +154,6 @@ public function testStartTime() $this->assertEquals(0, $event->getStartTime(), null, self::DELTA); } - /** - * @expectedException \InvalidArgumentException - */ - public function testInvalidOriginThrowsAnException() - { - new StopwatchEvent('abc'); - } - public function testHumanRepresentation() { $event = new StopwatchEvent(microtime(true) * 1000); diff --git a/src/Symfony/Component/Stopwatch/composer.json b/src/Symfony/Component/Stopwatch/composer.json index 8c3397878725f..cb1f823cc49f7 100644 --- a/src/Symfony/Component/Stopwatch/composer.json +++ b/src/Symfony/Component/Stopwatch/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" }, @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Templating/Loader/CacheLoader.php b/src/Symfony/Component/Templating/Loader/CacheLoader.php index b17461c5ee8fb..51f3024082a20 100644 --- a/src/Symfony/Component/Templating/Loader/CacheLoader.php +++ b/src/Symfony/Component/Templating/Loader/CacheLoader.php @@ -33,7 +33,7 @@ class CacheLoader extends Loader * @param LoaderInterface $loader A Loader instance * @param string $dir The directory where to store the cache files */ - public function __construct(LoaderInterface $loader, $dir) + public function __construct(LoaderInterface $loader, string $dir) { $this->loader = $loader; $this->dir = $dir; diff --git a/src/Symfony/Component/Templating/Storage/Storage.php b/src/Symfony/Component/Templating/Storage/Storage.php index 87245b3d425f3..8c817ba5b0187 100644 --- a/src/Symfony/Component/Templating/Storage/Storage.php +++ b/src/Symfony/Component/Templating/Storage/Storage.php @@ -23,7 +23,7 @@ abstract class Storage /** * @param string $template The template name */ - public function __construct($template) + public function __construct(string $template) { $this->template = $template; } diff --git a/src/Symfony/Component/Templating/TemplateReference.php b/src/Symfony/Component/Templating/TemplateReference.php index 3477f088ff245..311e817600543 100644 --- a/src/Symfony/Component/Templating/TemplateReference.php +++ b/src/Symfony/Component/Templating/TemplateReference.php @@ -20,7 +20,7 @@ class TemplateReference implements TemplateReferenceInterface { protected $parameters; - public function __construct($name = null, $engine = null) + public function __construct(string $name = null, string $engine = null) { $this->parameters = array( 'name' => $name, diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json index 08d06fc4951da..c594d6811f0f4 100644 --- a/src/Symfony/Component/Templating/composer.json +++ b/src/Symfony/Component/Templating/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "require-dev": { "psr/log": "~1.0" @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Translation/CHANGELOG.md b/src/Symfony/Component/Translation/CHANGELOG.md index 105a67e266504..daab7fd3fc0eb 100644 --- a/src/Symfony/Component/Translation/CHANGELOG.md +++ b/src/Symfony/Component/Translation/CHANGELOG.md @@ -1,6 +1,19 @@ CHANGELOG ========= +4.1.0 +----- + + * The `FileDumper::setBackup()` method is deprecated and will be removed in 5.0. + * The `TranslationWriter::disableBackup()` method is deprecated and will be removed in 5.0. + +4.0.0 +----- + + * removed the backup feature of the `FileDumper` class + * removed `TranslationWriter::writeTranslations()` method + * removed support for passing `MessageSelector` instances to the constructor of the `Translator` class + 3.4.0 ----- diff --git a/src/Symfony/Component/Translation/Command/XliffLintCommand.php b/src/Symfony/Component/Translation/Command/XliffLintCommand.php index fead5edcb18bb..042ab1eab8b32 100644 --- a/src/Symfony/Component/Translation/Command/XliffLintCommand.php +++ b/src/Symfony/Component/Translation/Command/XliffLintCommand.php @@ -33,7 +33,7 @@ class XliffLintCommand extends Command private $directoryIteratorProvider; private $isReadableProvider; - public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) + public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null) { parent::__construct($name); diff --git a/src/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php b/src/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php index 1ca79cf00a5bd..e620226e83267 100644 --- a/src/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php +++ b/src/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php @@ -23,7 +23,7 @@ class TranslationDumperPass implements CompilerPassInterface private $writerServiceId; private $dumperTag; - public function __construct($writerServiceId = 'translation.writer', $dumperTag = 'translation.dumper') + public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper') { $this->writerServiceId = $writerServiceId; $this->dumperTag = $dumperTag; diff --git a/src/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php b/src/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php index 06105187952c4..18c67f68ec2a8 100644 --- a/src/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php +++ b/src/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php @@ -24,7 +24,7 @@ class TranslationExtractorPass implements CompilerPassInterface private $extractorServiceId; private $extractorTag; - public function __construct($extractorServiceId = 'translation.extractor', $extractorTag = 'translation.extractor') + public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor') { $this->extractorServiceId = $extractorServiceId; $this->extractorTag = $extractorTag; diff --git a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php index db2a2a1ecc46b..1f7839dbff35e 100644 --- a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php +++ b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php @@ -24,12 +24,8 @@ class TranslatorPass implements CompilerPassInterface private $debugCommandServiceId; private $updateCommandServiceId; - public function __construct($translatorServiceId = 'translator.default', $readerServiceId = 'translation.loader', $loaderTag = 'translation.loader', $debugCommandServiceId = 'console.command.translation_debug', $updateCommandServiceId = 'console.command.translation_update') + public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update') { - if ('translation.loader' === $readerServiceId && 2 > func_num_args()) { - @trigger_error('The default value for $readerServiceId will change in 4.0 to "translation.reader".', E_USER_DEPRECATED); - } - $this->translatorServiceId = $translatorServiceId; $this->readerServiceId = $readerServiceId; $this->loaderTag = $loaderTag; @@ -62,18 +58,6 @@ public function process(ContainerBuilder $container) } } - // Duplicated code to support "translation.reader", to be removed in 4.0 - if ('translation.reader' !== $this->readerServiceId) { - if ($container->hasDefinition('translation.reader')) { - $definition = $container->getDefinition('translation.reader'); - foreach ($loaders as $id => $formats) { - foreach ($formats as $format) { - $definition->addMethodCall('addLoader', array($format, $loaderRefs[$id])); - } - } - } - } - $container ->findDefinition($this->translatorServiceId) ->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs)) diff --git a/src/Symfony/Component/Translation/Dumper/FileDumper.php b/src/Symfony/Component/Translation/Dumper/FileDumper.php index 7296d8ccd9392..846b302147301 100644 --- a/src/Symfony/Component/Translation/Dumper/FileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/FileDumper.php @@ -17,7 +17,6 @@ /** * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s). - * Performs backup of already existing files. * * Options: * - path (mandatory): the directory where the files should be saved @@ -33,13 +32,6 @@ abstract class FileDumper implements DumperInterface */ protected $relativePathTemplate = '%domain%.%locale%.%extension%'; - /** - * Make file backup before the dump. - * - * @var bool - */ - private $backup = true; - /** * Sets the template for the relative paths to files. * @@ -54,10 +46,16 @@ public function setRelativePathTemplate($relativePathTemplate) * Sets backup flag. * * @param bool + * + * @deprecated since Symfony 4.1, to be removed in 5.0 */ public function setBackup($backup) { - $this->backup = $backup; + @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1 and will be removed in 5.0.', __METHOD__), E_USER_DEPRECATED); + + if (false !== $backup) { + throw new \LogicException('The backup feature is no longer supported.'); + } } /** @@ -71,14 +69,8 @@ public function dump(MessageCatalogue $messages, $options = array()) // save a file for each domain foreach ($messages->getDomains() as $domain) { - // backup $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale()); - if (file_exists($fullpath)) { - if ($this->backup) { - @trigger_error('Creating a backup while dumping a message catalogue is deprecated since Symfony 3.1 and will be removed in 4.0. Use TranslationWriter::disableBackup() to disable the backup.', E_USER_DEPRECATED); - copy($fullpath, $fullpath.'~'); - } - } else { + if (!file_exists($fullpath)) { $directory = dirname($fullpath); if (!file_exists($directory) && !@mkdir($directory, 0777, true)) { throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory)); @@ -109,13 +101,8 @@ abstract protected function getExtension(); /** * Gets the relative file path using the template. - * - * @param string $domain The domain - * @param string $locale The locale - * - * @return string The relative file path */ - private function getRelativePath($domain, $locale) + private function getRelativePath(string $domain, string $locale): string { return strtr($this->relativePathTemplate, array( '%domain%' => $domain, diff --git a/src/Symfony/Component/Translation/Dumper/JsonFileDumper.php b/src/Symfony/Component/Translation/Dumper/JsonFileDumper.php index 08b538e1fec83..32bdaf5181199 100644 --- a/src/Symfony/Component/Translation/Dumper/JsonFileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/JsonFileDumper.php @@ -28,7 +28,7 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti if (isset($options['json_encoding'])) { $flags = $options['json_encoding']; } else { - $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; + $flags = JSON_PRETTY_PRINT; } return json_encode($messages->all($domain), $flags); diff --git a/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php b/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php index ecf00fa64ba92..db9e4b3645a39 100644 --- a/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php @@ -25,7 +25,7 @@ class YamlFileDumper extends FileDumper { private $extension; - public function __construct(/**string */$extension = 'yml') + public function __construct(string $extension = 'yml') { $this->extension = $extension; } diff --git a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php index b9c524e820c03..40b36451dab52 100644 --- a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php +++ b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php @@ -43,12 +43,7 @@ protected function extractFiles($resource) return $files; } - /** - * @param string $file - * - * @return \SplFileInfo - */ - private function toSplFileInfo($file) + private function toSplFileInfo(string $file): \SplFileInfo { return ($file instanceof \SplFileInfo) ? $file : new \SplFileInfo($file); } diff --git a/src/Symfony/Component/Translation/Extractor/PhpExtractor.php b/src/Symfony/Component/Translation/Extractor/PhpExtractor.php index ab641ba96fb42..f364bdecd3fcb 100644 --- a/src/Symfony/Component/Translation/Extractor/PhpExtractor.php +++ b/src/Symfony/Component/Translation/Extractor/PhpExtractor.php @@ -83,10 +83,8 @@ public function extract($resource, MessageCatalogue $catalog) foreach ($files as $file) { $this->parseTokens(token_get_all(file_get_contents($file)), $catalog); - if (\PHP_VERSION_ID >= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - gc_mem_caches(); - } + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + gc_mem_caches(); } } diff --git a/src/Symfony/Component/Translation/Loader/IcuDatFileLoader.php b/src/Symfony/Component/Translation/Loader/IcuDatFileLoader.php index 71ba90a39d9cc..822bc362072a8 100644 --- a/src/Symfony/Component/Translation/Loader/IcuDatFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/IcuDatFileLoader.php @@ -39,7 +39,6 @@ public function load($resource, $locale, $domain = 'messages') try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception $e) { - // HHVM compatibility: constructor throws on invalid resource $rb = null; } diff --git a/src/Symfony/Component/Translation/Loader/IcuResFileLoader.php b/src/Symfony/Component/Translation/Loader/IcuResFileLoader.php index 6b5b5e125ff6f..927cae16a9f5b 100644 --- a/src/Symfony/Component/Translation/Loader/IcuResFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/IcuResFileLoader.php @@ -39,7 +39,6 @@ public function load($resource, $locale, $domain = 'messages') try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception $e) { - // HHVM compatibility: constructor throws on invalid resource $rb = null; } diff --git a/src/Symfony/Component/Translation/Loader/MoFileLoader.php b/src/Symfony/Component/Translation/Loader/MoFileLoader.php index aad2bba220437..918bf94a0a5dd 100644 --- a/src/Symfony/Component/Translation/Loader/MoFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/MoFileLoader.php @@ -134,11 +134,8 @@ protected function loadResource($resource) * Reads an unsigned long from stream respecting endianness. * * @param resource $stream - * @param bool $isBigEndian - * - * @return int */ - private function readLong($stream, $isBigEndian) + private function readLong($stream, bool $isBigEndian): int { $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4)); $result = current($result); diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php index 8e4e5a787bc8c..d4eaedb3a9c55 100644 --- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php @@ -75,7 +75,7 @@ private function extract($resource, MessageCatalogue $catalogue, $domain) * @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata * @param string $domain The domain */ - private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, $domain) + private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) { $xml = simplexml_import_dom($dom); $encoding = strtoupper($dom->encoding); @@ -115,12 +115,7 @@ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, $ } } - /** - * @param \DOMDocument $dom - * @param MessageCatalogue $catalogue - * @param string $domain - */ - private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, $domain) + private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) { $xml = simplexml_import_dom($dom); $encoding = strtoupper($dom->encoding); @@ -163,13 +158,8 @@ private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, $ /** * Convert a UTF8 string to the specified encoding. - * - * @param string $content String to decode - * @param string $encoding Target encoding - * - * @return string */ - private function utf8ToCharset($content, $encoding = null) + private function utf8ToCharset(string $content, string $encoding = null): string { if ('UTF-8' !== $encoding && !empty($encoding)) { return mb_convert_encoding($content, $encoding, 'UTF-8'); @@ -181,13 +171,9 @@ private function utf8ToCharset($content, $encoding = null) /** * Validates and parses the given file into a DOMDocument. * - * @param string $file - * @param \DOMDocument $dom - * @param string $schema source of the schema - * * @throws InvalidResourceException */ - private function validateSchema($file, \DOMDocument $dom, $schema) + private function validateSchema(string $file, \DOMDocument $dom, string $schema) { $internalErrors = libxml_use_internal_errors(true); @@ -224,13 +210,8 @@ private function getSchema($xliffVersion) /** * Internally changes the URI of a dependent xsd to be loaded locally. - * - * @param string $schemaSource Current content of schema file - * @param string $xmlUri External URI of XML to convert to local - * - * @return string */ - private function fixXmlLocation($schemaSource, $xmlUri) + private function fixXmlLocation(string $schemaSource, string $xmlUri): string { $newPath = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd'; $parts = explode('/', $newPath); @@ -254,12 +235,8 @@ private function fixXmlLocation($schemaSource, $xmlUri) /** * Returns the XML errors of the internal XML parser. - * - * @param bool $internalErrors - * - * @return array An array of errors */ - private function getXmlErrors($internalErrors) + private function getXmlErrors(bool $internalErrors): array { $errors = array(); foreach (libxml_get_errors() as $error) { @@ -283,13 +260,9 @@ private function getXmlErrors($internalErrors) * Gets xliff file version based on the root "version" attribute. * Defaults to 1.2 for backwards compatibility. * - * @param \DOMDocument $dom - * * @throws InvalidArgumentException - * - * @return string */ - private function getVersionNumber(\DOMDocument $dom) + private function getVersionNumber(\DOMDocument $dom): string { /** @var \DOMNode $xliff */ foreach ($dom->getElementsByTagName('xliff') as $xliff) { @@ -312,13 +285,7 @@ private function getVersionNumber(\DOMDocument $dom) return '1.2'; } - /** - * @param \SimpleXMLElement|null $noteElement - * @param string|null $encoding - * - * @return array - */ - private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $encoding = null) + private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array { $notes = array(); diff --git a/src/Symfony/Component/Translation/Loader/YamlFileLoader.php b/src/Symfony/Component/Translation/Loader/YamlFileLoader.php index 07914f263155c..ef84c32f171e1 100644 --- a/src/Symfony/Component/Translation/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/YamlFileLoader.php @@ -15,6 +15,7 @@ use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Yaml\Parser as YamlParser; use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Yaml; /** * YamlFileLoader loads translations from Yaml files. @@ -38,18 +39,10 @@ protected function loadResource($resource) $this->yamlParser = new YamlParser(); } - $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($resource, &$prevErrorHandler) { - $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$resource.'"$0', $message) : $message; - - return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false; - }); - try { - $messages = $this->yamlParser->parseFile($resource); + $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e); - } finally { - restore_error_handler(); } return $messages; diff --git a/src/Symfony/Component/Translation/MessageCatalogue.php b/src/Symfony/Component/Translation/MessageCatalogue.php index df917bbba9e34..c36ea30ec04fd 100644 --- a/src/Symfony/Component/Translation/MessageCatalogue.php +++ b/src/Symfony/Component/Translation/MessageCatalogue.php @@ -30,7 +30,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf * @param string $locale The locale * @param array $messages An array of messages classified by domain */ - public function __construct($locale, array $messages = array()) + public function __construct(?string $locale, array $messages = array()) { $this->locale = $locale; $this->messages = $messages; diff --git a/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslationPassTest.php b/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslationPassTest.php index c0274738d0f0d..4082d169c43b5 100644 --- a/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslationPassTest.php +++ b/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslationPassTest.php @@ -54,50 +54,4 @@ public function testValidCollector() $expected = array('translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))); $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0)); } - - /** - * @group legacy - * @expectedDeprecation The default value for $readerServiceId will change in 4.0 to "translation.reader". - * - * A test that verifies the deprecated "translation.loader" gets the LoaderInterfaces added. - * - * This test should be removed in 4.0. - */ - public function testValidCollectorWithDeprecatedTranslationLoader() - { - $loader = (new Definition()) - ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')); - - $legacyReader = new Definition(); - $reader = new Definition(); - - $translator = (new Definition()) - ->setArguments(array(null, null, null, null)); - - $container = new ContainerBuilder(); - $container->setDefinition('translator.default', $translator); - $container->setDefinition('translation.loader', $legacyReader); - $container->setDefinition('translation.reader', $reader); - $container->setDefinition('translation.xliff_loader', $loader); - - $pass = new TranslatorPass(); - $pass->process($container); - - $expectedReader = (new Definition()) - ->addMethodCall('addLoader', array('xliff', new Reference('translation.xliff_loader'))) - ->addMethodCall('addLoader', array('xlf', new Reference('translation.xliff_loader'))) - ; - $this->assertEquals($expectedReader, $legacyReader); - $this->assertEquals($expectedReader, $reader); - - $expectedLoader = (new Definition()) - ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')) - ; - $this->assertEquals($expectedLoader, $loader); - - $this->assertSame(array('translation.xliff_loader' => array('xliff', 'xlf')), $translator->getArgument(3)); - - $expected = array('translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))); - $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0)); - } } diff --git a/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php index 79205195393ec..25b8561077a63 100644 --- a/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php +++ b/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php @@ -32,29 +32,6 @@ public function testDump() @unlink($tempDir.'/messages.en.concrete'); } - /** - * @group legacy - */ - public function testDumpBackupsFileIfExisting() - { - $tempDir = sys_get_temp_dir(); - $file = $tempDir.'/messages.en.concrete'; - $backupFile = $file.'~'; - - @touch($file); - - $catalogue = new MessageCatalogue('en'); - $catalogue->add(array('foo' => 'bar')); - - $dumper = new ConcreteFileDumper(); - $dumper->dump($catalogue, array('path' => $tempDir)); - - $this->assertFileExists($backupFile); - - @unlink($file); - @unlink($backupFile); - } - public function testDumpCreatesNestedDirectoriesAndFile() { $tempDir = sys_get_temp_dir(); diff --git a/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php b/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php index 8837553d56808..ab66af13e7d96 100644 --- a/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php +++ b/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php @@ -18,22 +18,6 @@ class TranslationWriterTest extends TestCase { - /** - * @group legacy - * @expectedDeprecation Method Symfony\Component\Translation\Writer\TranslationWriter::writeTranslations() is deprecated since Symfony 3.4 and will be removed in 4.0. Use write() instead. - */ - public function testWriteTranslations() - { - $dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock(); - $dumper - ->expects($this->once()) - ->method('dump'); - - $writer = new TranslationWriter(); - $writer->addDumper('test', $dumper); - $writer->writeTranslations(new MessageCatalogue('en'), 'test'); - } - public function testWrite() { $dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock(); @@ -43,9 +27,12 @@ public function testWrite() $writer = new TranslationWriter(); $writer->addDumper('test', $dumper); - $writer->write(new MessageCatalogue(array()), 'test'); + $writer->write(new MessageCatalogue('en'), 'test'); } + /** + * @group legacy + */ public function testDisableBackup() { $nonBackupDumper = new NonBackupDumper(); diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 411eec7167ff9..2899db0101e1d 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -74,21 +74,13 @@ class Translator implements TranslatorInterface, TranslatorBagInterface private $configCacheFactory; /** - * @param string $locale The locale - * @param MessageFormatterInterface|null $formatter The message formatter - * @param string|null $cacheDir The directory to use for the cache - * @param bool $debug Use cache in debug mode ? - * * @throws InvalidArgumentException If a locale contains invalid characters */ - public function __construct($locale, $formatter = null, $cacheDir = null, $debug = false) + public function __construct(?string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false) { $this->setLocale($locale); - if ($formatter instanceof MessageSelector) { - $formatter = new MessageFormatter($formatter); - @trigger_error(sprintf('Passing a "%s" instance into the "%s" as a second argument is deprecated since Symfony 3.4 and will be removed in 4.0. Inject a "%s" implementation instead.', MessageSelector::class, __METHOD__, MessageFormatterInterface::class), E_USER_DEPRECATED); - } elseif (null === $formatter) { + if (null === $formatter) { $formatter = new MessageFormatter(); } @@ -283,10 +275,7 @@ protected function initializeCatalogue($locale) $this->loadFallbackCatalogues($locale); } - /** - * @param string $locale - */ - private function initializeCacheCatalogue($locale) + private function initializeCacheCatalogue(string $locale): void { if (isset($this->catalogues[$locale])) { /* Catalogue already initialized. */ @@ -309,7 +298,7 @@ function (ConfigCacheInterface $cache) use ($locale) { $this->catalogues[$locale] = include $cache->getPath(); } - private function dumpCatalogue($locale, ConfigCacheInterface $cache) + private function dumpCatalogue($locale, ConfigCacheInterface $cache): void { $this->initializeCatalogue($locale); $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]); @@ -334,7 +323,7 @@ private function dumpCatalogue($locale, ConfigCacheInterface $cache) $cache->write($content, $this->catalogues[$locale]->getResources()); } - private function getFallbackContent(MessageCatalogue $catalogue) + private function getFallbackContent(MessageCatalogue $catalogue): string { $fallbackContent = ''; $current = ''; @@ -369,7 +358,7 @@ private function getCatalogueCachePath($locale) return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php'; } - private function doLoadCatalogue($locale) + private function doLoadCatalogue($locale): void { $this->catalogues[$locale] = new MessageCatalogue($locale); @@ -383,7 +372,7 @@ private function doLoadCatalogue($locale) } } - private function loadFallbackCatalogues($locale) + private function loadFallbackCatalogues($locale): void { $current = $this->catalogues[$locale]; @@ -436,10 +425,8 @@ protected function assertValidLocale($locale) /** * Provides the ConfigCache factory implementation, falling back to a * default implementation if necessary. - * - * @return ConfigCacheFactoryInterface $configCacheFactory */ - private function getConfigCacheFactory() + private function getConfigCacheFactory(): ConfigCacheFactoryInterface { if (!$this->configCacheFactory) { $this->configCacheFactory = new ConfigCacheFactory($this->debug); diff --git a/src/Symfony/Component/Translation/Writer/TranslationWriter.php b/src/Symfony/Component/Translation/Writer/TranslationWriter.php index 5ff5cd855dfa4..3975147a091d9 100644 --- a/src/Symfony/Component/Translation/Writer/TranslationWriter.php +++ b/src/Symfony/Component/Translation/Writer/TranslationWriter.php @@ -38,9 +38,13 @@ public function addDumper($format, DumperInterface $dumper) /** * Disables dumper backup. + * + * @deprecated since Symfony 4.1, to be removed in 5.0 */ public function disableBackup() { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1 and will be removed in 5.0.', __METHOD__), E_USER_DEPRECATED); + foreach ($this->dumpers as $dumper) { if (method_exists($dumper, 'setBackup')) { $dumper->setBackup(false); @@ -83,21 +87,4 @@ public function write(MessageCatalogue $catalogue, $format, $options = array()) // save $dumper->dump($catalogue, $options); } - - /** - * Writes translation from the catalogue according to the selected format. - * - * @param MessageCatalogue $catalogue The message catalogue to write - * @param string $format The format to use to dump the messages - * @param array $options Options that are passed to the dumper - * - * @throws InvalidArgumentException - * - * @deprecated since 3.4 will be removed in 4.0. Use write instead. - */ - public function writeTranslations(MessageCatalogue $catalogue, $format, $options = array()) - { - @trigger_error(sprintf('Method %s() is deprecated since Symfony 3.4 and will be removed in 4.0. Use write() instead.', __METHOD__), E_USER_DEPRECATED); - $this->write($catalogue, $format, $options); - } } diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index 3a1e1589f5460..8c80c01c6cede 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -16,19 +16,19 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/config": "~2.8|~3.0|~4.0", + "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/intl": "^2.8.18|^3.2.5|~4.0", + "symfony/intl": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0", "symfony/finder": "~2.8|~3.0|~4.0", "psr/log": "~1.0" }, "conflict": { - "symfony/config": "<2.8", + "symfony/config": "<3.4", "symfony/dependency-injection": "<3.4", "symfony/yaml": "<3.4" }, @@ -46,7 +46,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 740e23b09051e..6bcc64a12b0d3 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -1,6 +1,21 @@ CHANGELOG ========= +4.1.0 +----- + + * Deprecated the `checkDNS` and `dnsMessage` options of the `Url` constraint. They will be removed in 5.0. + * added a `values` option to the `Expression` constraint + +4.0.0 +----- + + * Setting the `strict` option of the `Choice` constraint to anything but `true` + is not supported anymore. + * removed the `DateTimeValidator::PATTERN` constant + * removed the `AbstractConstraintValidatorTest` class + * removed support for setting the `checkDNS` option of the `Url` constraint to `true` + 3.4.0 ----- diff --git a/src/Symfony/Component/Validator/ConstraintViolation.php b/src/Symfony/Component/Validator/ConstraintViolation.php index 97c1f1c1aac60..b3462e7d1c6b6 100644 --- a/src/Symfony/Component/Validator/ConstraintViolation.php +++ b/src/Symfony/Component/Validator/ConstraintViolation.php @@ -49,7 +49,7 @@ class ConstraintViolation implements ConstraintViolationInterface * caused the violation * @param mixed $cause The cause of the violation */ - public function __construct($message, $messageTemplate, array $parameters, $root, $propertyPath, $invalidValue, $plural = null, $code = null, Constraint $constraint = null, $cause = null) + public function __construct(?string $message, ?string $messageTemplate, array $parameters, $root, ?string $propertyPath, $invalidValue, int $plural = null, $code = null, Constraint $constraint = null, $cause = null) { $this->message = $message; $this->messageTemplate = $messageTemplate; diff --git a/src/Symfony/Component/Validator/Constraints/Choice.php b/src/Symfony/Component/Validator/Constraints/Choice.php index 4b93c70e4a5f4..8c4c23ad0ed65 100644 --- a/src/Symfony/Component/Validator/Constraints/Choice.php +++ b/src/Symfony/Component/Validator/Constraints/Choice.php @@ -34,7 +34,7 @@ class Choice extends Constraint public $choices; public $callback; public $multiple = false; - public $strict = false; + public $strict = true; public $min; public $max; public $message = 'The value you selected is not a valid choice.'; diff --git a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php index 43fecb1137fdb..2dc5b182ff80e 100644 --- a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php @@ -59,12 +59,12 @@ public function validate($value, Constraint $constraint) } if (true !== $constraint->strict) { - @trigger_error('Not setting the strict option of the Choice constraint to true is deprecated since Symfony 3.4 and will throw an exception in 4.0.', E_USER_DEPRECATED); + throw new \RuntimeException('The "strict" option of the Choice constraint should not be used.'); } if ($constraint->multiple) { foreach ($value as $_value) { - if (!in_array($_value, $choices, $constraint->strict)) { + if (!in_array($_value, $choices, true)) { $this->context->buildViolation($constraint->multipleMessage) ->setParameter('{{ value }}', $this->formatValue($_value)) ->setCode(Choice::NO_SUCH_CHOICE_ERROR) @@ -96,7 +96,7 @@ public function validate($value, Constraint $constraint) return; } - } elseif (!in_array($value, $choices, $constraint->strict)) { + } elseif (!in_array($value, $choices, true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Choice::NO_SUCH_CHOICE_ERROR) diff --git a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php index af956ee06b583..63c6a7b903008 100644 --- a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php @@ -20,11 +20,6 @@ */ class DateTimeValidator extends DateValidator { - /** - * @deprecated since version 3.1, to be removed in 4.0. - */ - const PATTERN = '/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/'; - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Constraints/Email.php b/src/Symfony/Component/Validator/Constraints/Email.php index a9d9ab15391fa..43e1422981d9d 100644 --- a/src/Symfony/Component/Validator/Constraints/Email.php +++ b/src/Symfony/Component/Validator/Constraints/Email.php @@ -21,6 +21,10 @@ */ class Email extends Constraint { + public const VALIDATION_MODE_HTML5 = 'html5'; + public const VALIDATION_MODE_STRICT = 'strict'; + public const VALIDATION_MODE_LOOSE = 'loose'; + const INVALID_FORMAT_ERROR = 'bd79c0ab-ddba-46cc-a703-a7a4b08de310'; const MX_CHECK_FAILED_ERROR = 'bf447c1c-0266-4e10-9c6c-573df282e413'; const HOST_CHECK_FAILED_ERROR = '7da53a8b-56f3-4288-bb3e-ee9ede4ef9a1'; @@ -31,8 +35,37 @@ class Email extends Constraint self::HOST_CHECK_FAILED_ERROR => 'HOST_CHECK_FAILED_ERROR', ); + /** + * @var string[] + * + * @internal + */ + public static $validationModes = array( + self::VALIDATION_MODE_HTML5, + self::VALIDATION_MODE_STRICT, + self::VALIDATION_MODE_LOOSE, + ); + public $message = 'This value is not a valid email address.'; public $checkMX = false; public $checkHost = false; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0. Set mode to "strict" instead. + */ public $strict; + public $mode; + + public function __construct($options = null) + { + if (is_array($options) && array_key_exists('strict', $options)) { + @trigger_error(sprintf('The "strict" property is deprecated since Symfony 4.1 and will be removed in 5.0. Use "mode"=>"%s" instead.', self::VALIDATION_MODE_STRICT), E_USER_DEPRECATED); + } + + if (is_array($options) && array_key_exists('mode', $options) && !in_array($options['mode'], self::$validationModes, true)) { + throw new \InvalidArgumentException('The "mode" parameter value is not valid.'); + } + + parent::__construct($options); + } } diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index b9537cab04843..79016cb09d5c9 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -23,14 +23,42 @@ */ class EmailValidator extends ConstraintValidator { - private $isStrict; + /** + * @internal + */ + const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/'; + + /** + * @internal + */ + const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/'; + + private static $emailPatterns = array( + Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE, + Email::VALIDATION_MODE_HTML5 => self::PATTERN_HTML5, + ); /** - * @param bool $strict + * @var string */ - public function __construct($strict = false) + private $defaultMode; + + /** + * @param string $defaultMode + */ + public function __construct($defaultMode = Email::VALIDATION_MODE_LOOSE) { - $this->isStrict = $strict; + if (is_bool($defaultMode)) { + @trigger_error(sprintf('Calling `new %s(%s)` is deprecated since Symfony 4.1 and will be removed in 5.0, use `new %s("%s")` instead.', self::class, $defaultMode ? 'true' : 'false', self::class, $defaultMode ? Email::VALIDATION_MODE_STRICT : Email::VALIDATION_MODE_LOOSE), E_USER_DEPRECATED); + + $defaultMode = $defaultMode ? Email::VALIDATION_MODE_STRICT : Email::VALIDATION_MODE_LOOSE; + } + + if (!in_array($defaultMode, Email::$validationModes, true)) { + throw new \InvalidArgumentException('The "defaultMode" parameter value is not valid.'); + } + + $this->defaultMode = $defaultMode; } /** @@ -52,11 +80,25 @@ public function validate($value, Constraint $constraint) $value = (string) $value; - if (null === $constraint->strict) { - $constraint->strict = $this->isStrict; + if (null !== $constraint->strict) { + @trigger_error(sprintf('The %s::$strict property is deprecated since Symfony 4.1 and will be removed in 5.0. Use %s::mode="%s" instead.', Email::class, Email::class, Email::VALIDATION_MODE_STRICT), E_USER_DEPRECATED); + + if ($constraint->strict) { + $constraint->mode = Email::VALIDATION_MODE_STRICT; + } else { + $constraint->mode = Email::VALIDATION_MODE_LOOSE; + } + } + + if (null === $constraint->mode) { + $constraint->mode = $this->defaultMode; + } + + if (!in_array($constraint->mode, Email::$validationModes, true)) { + throw new \InvalidArgumentException(sprintf('The %s::$mode parameter value is not valid.', get_class($constraint))); } - if ($constraint->strict) { + if (Email::VALIDATION_MODE_STRICT === $constraint->mode) { if (!class_exists('\Egulias\EmailValidator\EmailValidator')) { throw new RuntimeException('Strict email validation requires egulias/email-validator ~1.2|~2.0'); } @@ -78,7 +120,7 @@ public function validate($value, Constraint $constraint) return; } - } elseif (!preg_match('/^.+\@\S+\.\S+$/', $value)) { + } elseif (!preg_match(self::$emailPatterns[$constraint->mode], $value)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Email::INVALID_FORMAT_ERROR) @@ -111,24 +153,16 @@ public function validate($value, Constraint $constraint) /** * Check DNS Records for MX type. - * - * @param string $host Host - * - * @return bool */ - private function checkMX($host) + private function checkMX(string $host): bool { return '' !== $host && checkdnsrr($host, 'MX'); } /** * Check if one of MX, A or AAAA DNS RR exists. - * - * @param string $host Host - * - * @return bool */ - private function checkHost($host) + private function checkHost(string $host): bool { return '' !== $host && ($this->checkMX($host) || (checkdnsrr($host, 'A') || checkdnsrr($host, 'AAAA'))); } diff --git a/src/Symfony/Component/Validator/Constraints/Expression.php b/src/Symfony/Component/Validator/Constraints/Expression.php index 3329bd2494028..b8c3cb71df691 100644 --- a/src/Symfony/Component/Validator/Constraints/Expression.php +++ b/src/Symfony/Component/Validator/Constraints/Expression.php @@ -30,6 +30,7 @@ class Expression extends Constraint public $message = 'This value is not valid.'; public $expression; + public $values = array(); /** * {@inheritdoc} diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index ce77d9ba843a4..5f40cfd3f60c2 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -39,7 +39,7 @@ public function validate($value, Constraint $constraint) throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Expression'); } - $variables = array(); + $variables = $constraint->values; $variables['value'] = $value; $variables['this'] = $this->context->getObject(); diff --git a/src/Symfony/Component/Validator/Constraints/File.php b/src/Symfony/Component/Validator/Constraints/File.php index 5cc3a7313f7a5..ff2627b38e809 100644 --- a/src/Symfony/Component/Validator/Constraints/File.php +++ b/src/Symfony/Component/Validator/Constraints/File.php @@ -59,6 +59,9 @@ class File extends Constraint protected $maxSize; + /** + * {@inheritdoc} + */ public function __construct($options = null) { parent::__construct($options); diff --git a/src/Symfony/Component/Validator/Constraints/Locale.php b/src/Symfony/Component/Validator/Constraints/Locale.php index 5aa7070402e2a..0ee4bf65b8548 100644 --- a/src/Symfony/Component/Validator/Constraints/Locale.php +++ b/src/Symfony/Component/Validator/Constraints/Locale.php @@ -28,4 +28,5 @@ class Locale extends Constraint ); public $message = 'This value is not a valid locale.'; + public $canonicalize = false; } diff --git a/src/Symfony/Component/Validator/Constraints/LocaleValidator.php b/src/Symfony/Component/Validator/Constraints/LocaleValidator.php index 93eab8cb7e757..f3a3516a1151f 100644 --- a/src/Symfony/Component/Validator/Constraints/LocaleValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LocaleValidator.php @@ -41,10 +41,13 @@ public function validate($value, Constraint $constraint) } $value = (string) $value; - $locales = Intl::getLocaleBundle()->getLocaleNames(); - $aliases = Intl::getLocaleBundle()->getAliases(); + if ($constraint->canonicalize) { + $value = \Locale::canonicalize($value); + } + $localeBundle = Intl::getLocaleBundle(); + $locales = $localeBundle->getLocaleNames(); - if (!isset($locales[$value]) && !in_array($value, $aliases)) { + if (!isset($locales[$value]) && !in_array($value, $localeBundle->getAliases(), true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Locale::NO_SUCH_LOCALE_ERROR) diff --git a/src/Symfony/Component/Validator/Constraints/Url.php b/src/Symfony/Component/Validator/Constraints/Url.php index 988dd19136da6..3306c1b405561 100644 --- a/src/Symfony/Component/Validator/Constraints/Url.php +++ b/src/Symfony/Component/Validator/Constraints/Url.php @@ -21,18 +21,69 @@ */ class Url extends Constraint { + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_ANY = 'ANY'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_NONE = false; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_A = 'A'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_A6 = 'A6'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_AAAA = 'AAAA'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_CNAME = 'CNAME'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_MX = 'MX'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_NAPTR = 'NAPTR'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_NS = 'NS'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_PTR = 'PTR'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_SOA = 'SOA'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_SRV = 'SRV'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ const CHECK_DNS_TYPE_TXT = 'TXT'; const INVALID_URL_ERROR = '57c2f299-1154-4870-89bb-ef3b1f5ad229'; @@ -42,7 +93,29 @@ class Url extends Constraint ); public $message = 'This value is not a valid URL.'; + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ public $dnsMessage = 'The host could not be resolved.'; public $protocols = array('http', 'https'); + + /** + * @deprecated since Symfony 4.1, to be removed in 5.0 + */ public $checkDNS = self::CHECK_DNS_TYPE_NONE; + + public function __construct($options = null) + { + if (is_array($options)) { + if (array_key_exists('checkDNS', $options)) { + @trigger_error(sprintf('The "checkDNS" option in "%s" is deprecated since Symfony 4.1 and will be removed in 5.0. Its false-positive rate is too high to be relied upon.', self::class), E_USER_DEPRECATED); + } + if (array_key_exists('dnsMessage', $options)) { + @trigger_error(sprintf('The "dnsMessage" option in "%s" is deprecated since Symfony 4.1 and will be removed in 5.0.', self::class), E_USER_DEPRECATED); + } + } + + parent::__construct($options); + } } diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 222597ed10f41..932aa14436fbc 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -73,12 +73,6 @@ public function validate($value, Constraint $constraint) } if ($constraint->checkDNS) { - // backwards compatibility - if (true === $constraint->checkDNS) { - $constraint->checkDNS = Url::CHECK_DNS_TYPE_ANY; - @trigger_error(sprintf('Use of the boolean TRUE for the "checkDNS" option in %s is deprecated. Use Url::CHECK_DNS_TYPE_ANY instead.', Url::class), E_USER_DEPRECATED); - } - if (!in_array($constraint->checkDNS, array( Url::CHECK_DNS_TYPE_ANY, Url::CHECK_DNS_TYPE_A, diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index 9c50a4f0f685b..1c1aa7216cb60 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -140,7 +140,7 @@ class ExecutionContext implements ExecutionContextInterface * @internal Called by {@link ExecutionContextFactory}. Should not be used * in user code. */ - public function __construct(ValidatorInterface $validator, $root, TranslatorInterface $translator, $translationDomain = null) + public function __construct(ValidatorInterface $validator, $root, TranslatorInterface $translator, string $translationDomain = null) { $this->validator = $validator; $this->root = $root; diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php index 84c5a9c8cb8b8..a8369a4955403 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php @@ -34,7 +34,7 @@ class ExecutionContextFactory implements ExecutionContextFactoryInterface * use for translating * violation messages */ - public function __construct(TranslatorInterface $translator, $translationDomain = null) + public function __construct(TranslatorInterface $translator, string $translationDomain = null) { $this->translator = $translator; $this->translationDomain = $translationDomain; diff --git a/src/Symfony/Component/Validator/DependencyInjection/AddConstraintValidatorsPass.php b/src/Symfony/Component/Validator/DependencyInjection/AddConstraintValidatorsPass.php index 207a1ad49d487..f10385c8c8f8e 100644 --- a/src/Symfony/Component/Validator/DependencyInjection/AddConstraintValidatorsPass.php +++ b/src/Symfony/Component/Validator/DependencyInjection/AddConstraintValidatorsPass.php @@ -25,7 +25,7 @@ class AddConstraintValidatorsPass implements CompilerPassInterface private $validatorFactoryServiceId; private $constraintValidatorTag; - public function __construct($validatorFactoryServiceId = 'validator.validator_factory', $constraintValidatorTag = 'validator.constraint_validator') + public function __construct(string $validatorFactoryServiceId = 'validator.validator_factory', string $constraintValidatorTag = 'validator.constraint_validator') { $this->validatorFactoryServiceId = $validatorFactoryServiceId; $this->constraintValidatorTag = $constraintValidatorTag; diff --git a/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php b/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php index 52be28534bae2..aa7dc0c74ba9a 100644 --- a/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php +++ b/src/Symfony/Component/Validator/DependencyInjection/AddValidatorInitializersPass.php @@ -24,7 +24,7 @@ class AddValidatorInitializersPass implements CompilerPassInterface private $builderService; private $initializerTag; - public function __construct($builderService = 'validator.builder', $initializerTag = 'validator.initializer') + public function __construct(string $builderService = 'validator.builder', string $initializerTag = 'validator.initializer') { $this->builderService = $builderService; $this->initializerTag = $initializerTag; diff --git a/src/Symfony/Component/Validator/Exception/InvalidOptionsException.php b/src/Symfony/Component/Validator/Exception/InvalidOptionsException.php index ce87c42ef4988..79d064f5377ab 100644 --- a/src/Symfony/Component/Validator/Exception/InvalidOptionsException.php +++ b/src/Symfony/Component/Validator/Exception/InvalidOptionsException.php @@ -15,7 +15,7 @@ class InvalidOptionsException extends ValidatorException { private $options; - public function __construct($message, array $options) + public function __construct(string $message, array $options) { parent::__construct($message); diff --git a/src/Symfony/Component/Validator/Exception/MissingOptionsException.php b/src/Symfony/Component/Validator/Exception/MissingOptionsException.php index 07c5d9ecda23e..61de56755defc 100644 --- a/src/Symfony/Component/Validator/Exception/MissingOptionsException.php +++ b/src/Symfony/Component/Validator/Exception/MissingOptionsException.php @@ -15,7 +15,7 @@ class MissingOptionsException extends ValidatorException { private $options; - public function __construct($message, array $options) + public function __construct(string $message, array $options) { parent::__construct($message); diff --git a/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php b/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php index 49d8cc2082234..3240aab6034c3 100644 --- a/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php +++ b/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php @@ -13,7 +13,7 @@ class UnexpectedTypeException extends ValidatorException { - public function __construct($value, $expectedType) + public function __construct($value, string $expectedType) { parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); } diff --git a/src/Symfony/Component/Validator/Mapping/Cache/Psr6Cache.php b/src/Symfony/Component/Validator/Mapping/Cache/Psr6Cache.php index 73c9513c168af..c0266e65a5a0b 100644 --- a/src/Symfony/Component/Validator/Mapping/Cache/Psr6Cache.php +++ b/src/Symfony/Component/Validator/Mapping/Cache/Psr6Cache.php @@ -63,12 +63,8 @@ public function write(ClassMetadata $metadata) /** * Replaces backslashes by dots in a class name. - * - * @param string $class - * - * @return string */ - private function escapeClassName($class) + private function escapeClassName(string $class): string { return str_replace('\\', '.', $class); } diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index 39fb926ef684c..caba1fcb20ae3 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -109,12 +109,7 @@ class ClassMetadata extends GenericMetadata implements ClassMetadataInterface */ private $reflClass; - /** - * Constructs a metadata for the given class. - * - * @param string $class - */ - public function __construct($class) + public function __construct(string $class) { $this->name = $class; // class name without namespace diff --git a/src/Symfony/Component/Validator/Mapping/GetterMetadata.php b/src/Symfony/Component/Validator/Mapping/GetterMetadata.php index 5a72bd84b7442..bfa3bce4d5ba7 100644 --- a/src/Symfony/Component/Validator/Mapping/GetterMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/GetterMetadata.php @@ -39,7 +39,7 @@ class GetterMetadata extends MemberMetadata * * @throws ValidatorException */ - public function __construct($class, $property, $method = null) + public function __construct(string $class, string $property, string $method = null) { if (null === $method) { $getMethod = 'get'.ucfirst($property); diff --git a/src/Symfony/Component/Validator/Mapping/Loader/FileLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/FileLoader.php index b8f9490379aee..3aff5c92b31cb 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/FileLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/FileLoader.php @@ -32,7 +32,7 @@ abstract class FileLoader extends AbstractLoader * * @throws MappingException If the file does not exist or is not readable */ - public function __construct($file) + public function __construct(string $file) { if (!is_file($file)) { throw new MappingException(sprintf('The mapping file "%s" does not exist', $file)); diff --git a/src/Symfony/Component/Validator/Mapping/Loader/StaticMethodLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/StaticMethodLoader.php index 95fc578eb4b5c..01b176cc3d489 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/StaticMethodLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/StaticMethodLoader.php @@ -28,7 +28,7 @@ class StaticMethodLoader implements LoaderInterface * * @param string $methodName The name of the static method to call */ - public function __construct($methodName = 'loadValidatorMetadata') + public function __construct(string $methodName = 'loadValidatorMetadata') { $this->methodName = $methodName; } diff --git a/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php index a213b292acaae..e5e84c38c35dd 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php @@ -115,18 +115,10 @@ protected function parseNodes(array $nodes) */ private function parseFile($path) { - $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($path, &$prevErrorHandler) { - $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$path.'"$0', $message) : $message; - - return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false; - }); - try { $classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e); - } finally { - restore_error_handler(); } // empty file diff --git a/src/Symfony/Component/Validator/Mapping/MemberMetadata.php b/src/Symfony/Component/Validator/Mapping/MemberMetadata.php index d71a13be71f03..6d303e1ee8cba 100644 --- a/src/Symfony/Component/Validator/Mapping/MemberMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/MemberMetadata.php @@ -59,7 +59,7 @@ abstract class MemberMetadata extends GenericMetadata implements PropertyMetadat * @param string $name The name of the member * @param string $property The property the member belongs to */ - public function __construct($class, $name, $property) + public function __construct(string $class, string $name, string $property) { $this->class = $class; $this->name = $name; diff --git a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php index 7fc19dd2f7020..f15ca30884d74 100644 --- a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php @@ -34,7 +34,7 @@ class PropertyMetadata extends MemberMetadata * * @throws ValidatorException */ - public function __construct($class, $name) + public function __construct(string $class, string $name) { if (!property_exists($class, $name)) { throw new ValidatorException(sprintf('Property "%s" does not exist in class "%s"', $name, $class)); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php deleted file mode 100644 index 52af8991fe0fc..0000000000000 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Constraints; - -use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; - -/** - * @deprecated Since Symfony 3.2, use ConstraintValidatorTestCase instead. - */ -abstract class AbstractConstraintValidatorTest extends ConstraintValidatorTestCase -{ -} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php index 01483161c9fc4..38607bb9a8891 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php @@ -45,7 +45,6 @@ public function testExpectArrayIfMultipleIsTrue() $constraint = new Choice(array( 'choices' => array('foo', 'bar'), 'multiple' => true, - 'strict' => true, )); $this->validator->validate('asdf', $constraint); @@ -58,7 +57,6 @@ public function testNullIsValid() new Choice( array( 'choices' => array('foo', 'bar'), - 'strict' => true, ) ) ); @@ -71,7 +69,7 @@ public function testNullIsValid() */ public function testChoicesOrCallbackExpected() { - $this->validator->validate('foobar', new Choice(array('strict' => true))); + $this->validator->validate('foobar', new Choice()); } /** @@ -79,12 +77,12 @@ public function testChoicesOrCallbackExpected() */ public function testValidCallbackExpected() { - $this->validator->validate('foobar', new Choice(array('callback' => 'abcd', 'strict' => true))); + $this->validator->validate('foobar', new Choice(array('callback' => 'abcd'))); } public function testValidChoiceArray() { - $constraint = new Choice(array('choices' => array('foo', 'bar'), 'strict' => true)); + $constraint = new Choice(array('choices' => array('foo', 'bar'))); $this->validator->validate('bar', $constraint); @@ -93,7 +91,7 @@ public function testValidChoiceArray() public function testValidChoiceCallbackFunction() { - $constraint = new Choice(array('callback' => __NAMESPACE__.'\choice_callback', 'strict' => true)); + $constraint = new Choice(array('callback' => __NAMESPACE__.'\choice_callback')); $this->validator->validate('bar', $constraint); @@ -104,7 +102,6 @@ public function testValidChoiceCallbackClosure() { $constraint = new Choice( array( - 'strict' => true, 'callback' => function () { return array('foo', 'bar'); }, @@ -118,7 +115,7 @@ public function testValidChoiceCallbackClosure() public function testValidChoiceCallbackStaticMethod() { - $constraint = new Choice(array('callback' => array(__CLASS__, 'staticCallback'), 'strict' => true)); + $constraint = new Choice(array('callback' => array(__CLASS__, 'staticCallback'))); $this->validator->validate('bar', $constraint); @@ -130,7 +127,7 @@ public function testValidChoiceCallbackContextMethod() // search $this for "staticCallback" $this->setObject($this); - $constraint = new Choice(array('callback' => 'staticCallback', 'strict' => true)); + $constraint = new Choice(array('callback' => 'staticCallback')); $this->validator->validate('bar', $constraint); @@ -142,7 +139,7 @@ public function testValidChoiceCallbackContextObjectMethod() // search $this for "objectMethodCallback" $this->setObject($this); - $constraint = new Choice(array('callback' => 'objectMethodCallback', 'strict' => true)); + $constraint = new Choice(array('callback' => 'objectMethodCallback')); $this->validator->validate('bar', $constraint); @@ -154,7 +151,6 @@ public function testMultipleChoices() $constraint = new Choice(array( 'choices' => array('foo', 'bar', 'baz'), 'multiple' => true, - 'strict' => true, )); $this->validator->validate(array('baz', 'bar'), $constraint); @@ -167,7 +163,6 @@ public function testInvalidChoice() $constraint = new Choice(array( 'choices' => array('foo', 'bar'), 'message' => 'myMessage', - 'strict' => true, )); $this->validator->validate('baz', $constraint); @@ -185,7 +180,6 @@ public function testInvalidChoiceEmptyChoices() // the DB or the model 'choices' => array(), 'message' => 'myMessage', - 'strict' => true, )); $this->validator->validate('baz', $constraint); @@ -202,7 +196,6 @@ public function testInvalidChoiceMultiple() 'choices' => array('foo', 'bar'), 'multipleMessage' => 'myMessage', 'multiple' => true, - 'strict' => true, )); $this->validator->validate(array('foo', 'baz'), $constraint); @@ -221,7 +214,6 @@ public function testTooFewChoices() 'multiple' => true, 'min' => 2, 'minMessage' => 'myMessage', - 'strict' => true, )); $value = array('foo'); @@ -245,7 +237,6 @@ public function testTooManyChoices() 'multiple' => true, 'max' => 2, 'maxMessage' => 'myMessage', - 'strict' => true, )); $value = array('foo', 'bar', 'moo'); @@ -262,27 +253,10 @@ public function testTooManyChoices() ->assertRaised(); } - /** - * @group legacy - */ - public function testNonStrict() - { - $constraint = new Choice(array( - 'choices' => array(1, 2), - 'strict' => false, - )); - - $this->validator->validate('2', $constraint); - $this->validator->validate(2, $constraint); - - $this->assertNoViolation(); - } - public function testStrictAllowsExactValue() { $constraint = new Choice(array( 'choices' => array(1, 2), - 'strict' => true, )); $this->validator->validate(2, $constraint); @@ -294,7 +268,6 @@ public function testStrictDisallowsDifferentType() { $constraint = new Choice(array( 'choices' => array(1, 2), - 'strict' => true, 'message' => 'myMessage', )); @@ -306,28 +279,11 @@ public function testStrictDisallowsDifferentType() ->assertRaised(); } - /** - * @group legacy - */ - public function testNonStrictWithMultipleChoices() - { - $constraint = new Choice(array( - 'choices' => array(1, 2, 3), - 'multiple' => true, - 'strict' => false, - )); - - $this->validator->validate(array('2', 3), $constraint); - - $this->assertNoViolation(); - } - public function testStrictWithMultipleChoices() { $constraint = new Choice(array( 'choices' => array(1, 2, 3), 'multiple' => true, - 'strict' => true, 'multipleMessage' => 'myMessage', )); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailTest.php new file mode 100644 index 0000000000000..7a8cd53faa677 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\Email; + +class EmailTest extends TestCase +{ + /** + * @expectedDeprecation The "strict" property is deprecated since Symfony 4.1 and will be removed in 5.0. Use "mode"=>"strict" instead. + * @group legacy + */ + public function testLegacyConstructorStrict() + { + $subject = new Email(array('strict' => true)); + + $this->assertTrue($subject->strict); + } + + public function testConstructorStrict() + { + $subject = new Email(array('mode' => Email::VALIDATION_MODE_STRICT)); + + $this->assertEquals(Email::VALIDATION_MODE_STRICT, $subject->mode); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "mode" parameter value is not valid. + */ + public function testUnknownModesTriggerException() + { + new Email(array('mode' => 'Unknown Mode')); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index 94857c1784173..5c7c63aa91f66 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -23,7 +23,29 @@ class EmailValidatorTest extends ConstraintValidatorTestCase { protected function createValidator() { - return new EmailValidator(false); + return new EmailValidator(Email::VALIDATION_MODE_LOOSE); + } + + /** + * @expectedDeprecation Calling `new Symfony\Component\Validator\Constraints\EmailValidator(true)` is deprecated since Symfony 4.1 and will be removed in 5.0, use `new Symfony\Component\Validator\Constraints\EmailValidator("strict")` instead. + * @group legacy + */ + public function testLegacyValidatorConstructorStrict() + { + $this->validator = new EmailValidator(true); + $this->validator->initialize($this->context); + $this->validator->validate('example@localhost', new Email()); + + $this->assertNoViolation(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "defaultMode" parameter value is not valid. + */ + public function testUnknownDefaultModeTriggerException() + { + new EmailValidator('Unknown Mode'); } public function testNullIsValid() @@ -64,6 +86,31 @@ public function getValidEmails() array('fabien@symfony.com'), array('example@example.co.uk'), array('fabien_potencier@example.fr'), + array('example@example.co..uk'), + array('{}~!@!@£$%%^&*().!@£$%^&*()'), + array('example@example.co..uk'), + array('example@-example.com'), + array(sprintf('example@%s.com', str_repeat('a', 64))), + ); + } + + /** + * @dataProvider getValidEmailsHtml5 + */ + public function testValidEmailsHtml5($email) + { + $this->validator->validate($email, new Email(array('mode' => Email::VALIDATION_MODE_HTML5))); + + $this->assertNoViolation(); + } + + public function getValidEmailsHtml5() + { + return array( + array('fabien@symfony.com'), + array('example@example.co.uk'), + array('fabien_potencier@example.fr'), + array('{}~!@example.com'), ); } @@ -94,6 +141,95 @@ public function getInvalidEmails() ); } + /** + * @dataProvider getInvalidHtml5Emails + */ + public function testInvalidHtml5Emails($email) + { + $constraint = new Email( + array( + 'message' => 'myMessage', + 'mode' => Email::VALIDATION_MODE_HTML5, + ) + ); + + $this->validator->validate($email, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$email.'"') + ->setCode(Email::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + + public function getInvalidHtml5Emails() + { + return array( + array('example'), + array('example@'), + array('example@localhost'), + array('example@example.co..uk'), + array('foo@example.com bar'), + array('example@example.'), + array('example@.fr'), + array('@example.com'), + array('example@example.com;example@example.com'), + array('example@.'), + array(' example@example.com'), + array('example@ '), + array(' example@example.com '), + array(' example @example .com '), + array('example@-example.com'), + array(sprintf('example@%s.com', str_repeat('a', 64))), + ); + } + + public function testModeStrict() + { + $constraint = new Email(array('mode' => Email::VALIDATION_MODE_STRICT)); + + $this->validator->validate('example@localhost', $constraint); + + $this->assertNoViolation(); + } + + public function testModeHtml5() + { + $constraint = new Email(array('mode' => Email::VALIDATION_MODE_HTML5)); + + $this->validator->validate('example@example..com', $constraint); + + $this->buildViolation('This value is not a valid email address.') + ->setParameter('{{ value }}', '"example@example..com"') + ->setCode(Email::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + + public function testModeLoose() + { + $constraint = new Email(array('mode' => Email::VALIDATION_MODE_LOOSE)); + + $this->validator->validate('example@example..com', $constraint); + + $this->assertNoViolation(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The Symfony\Component\Validator\Constraints\Email::$mode parameter value is not valid. + */ + public function testUnknownModesOnValidateTriggerException() + { + $constraint = new Email(); + $constraint->mode = 'Unknown Mode'; + + $this->validator->validate('example@example..com', $constraint); + } + + /** + * @expectedDeprecation The "strict" property is deprecated since Symfony 4.1 and will be removed in 5.0. Use "mode"=>"strict" instead. + * @expectedDeprecation The Symfony\Component\Validator\Constraints\Email::$strict property is deprecated since Symfony 4.1 and will be removed in 5.0. Use Symfony\Component\Validator\Constraints\Email::mode="strict" instead. + * @group legacy + */ public function testStrict() { $constraint = new Email(array('strict' => true)); @@ -110,7 +246,7 @@ public function testStrictWithInvalidEmails($email) { $constraint = new Email(array( 'message' => 'myMessage', - 'strict' => true, + 'mode' => Email::VALIDATION_MODE_STRICT, )); $this->validator->validate($email, $constraint); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index 07f17d648aa39..aa2c9bdf10014 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -270,4 +270,18 @@ public function testExpressionLanguageUsage() $this->assertTrue($used, 'Failed asserting that custom ExpressionLanguage instance is used.'); } + + public function testPassingCustomValues() + { + $constraint = new Expression(array( + 'expression' => 'value + custom == 2', + 'values' => array( + 'custom' => 1, + ), + )); + + $this->validator->validate(1, $constraint); + + $this->assertNoViolation(); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index 4cf62a6215507..05c2d499a3d0f 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -83,7 +83,8 @@ public function testValidFile() public function testValidUploadedfile() { - $file = new UploadedFile($this->path, 'originalName', null, null, null, true); + file_put_contents($this->path, '1'); + $file = new UploadedFile($this->path, 'originalName', null, null, true); $this->validator->validate($file, new File()); $this->assertNoViolation(); @@ -411,7 +412,7 @@ public function testDisallowEmpty() */ public function testUploadedFileError($error, $message, array $params = array(), $maxSize = null) { - $file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error); + $file = new UploadedFile(tempnam(sys_get_temp_dir(), 'file-validator-test-'), 'originalName', 'mime', $error); $constraint = new File(array( $message => 'myMessage', diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php index 29409e61f52f7..7ebe1cdc8fa24 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php @@ -90,4 +90,45 @@ public function getInvalidLocales() array('foobar'), ); } + + /** + * @dataProvider getUncanonicalizedLocales + */ + public function testInvalidLocalesWithoutCanonicalization(string $locale) + { + $constraint = new Locale(array( + 'message' => 'myMessage', + )); + + $this->validator->validate($locale, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$locale.'"') + ->setCode(Locale::NO_SUCH_LOCALE_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getUncanonicalizedLocales + */ + public function testValidLocalesWithCanonicalization(string $locale) + { + $constraint = new Locale(array( + 'message' => 'myMessage', + 'canonicalize' => true, + )); + + $this->validator->validate($locale, $constraint); + + $this->assertNoViolation(); + } + + public function getUncanonicalizedLocales(): iterable + { + return array( + array('en-US'), + array('es-AR'), + array('fr_FR.utf8'), + ); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index 13cacb0f3ba56..b7c1dc1d776a7 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -200,6 +200,8 @@ public function getValidCustomUrls() /** * @dataProvider getCheckDns * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts + * @group legacy + * @expectedDeprecation The "checkDNS" option in "Symfony\Component\Validator\Constraints\Url" is deprecated since Symfony 4.1 and will be removed in 5.0. Its false-positive rate is too high to be relied upon. */ public function testCheckDns($violation) { @@ -230,6 +232,8 @@ public function getCheckDns() /** * @dataProvider getCheckDnsTypes * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts + * @group legacy + * @expectedDeprecation The "checkDNS" option in "Symfony\Component\Validator\Constraints\Url" is deprecated since Symfony 4.1 and will be removed in 5.0. Its false-positive rate is too high to be relied upon. */ public function testCheckDnsByType($type) { @@ -264,33 +268,32 @@ public function getCheckDnsTypes() } /** + * @expectedException \Symfony\Component\Validator\Exception\InvalidOptionsException + * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts * @group legacy + * @expectedDeprecation The "checkDNS" option in "Symfony\Component\Validator\Constraints\Url" is deprecated since Symfony 4.1 and will be removed in 5.0. Its false-positive rate is too high to be relied upon. + * @expectedDeprecation The "dnsMessage" option in "Symfony\Component\Validator\Constraints\Url" is deprecated since Symfony 4.1 and will be removed in 5.0. */ - public function testCheckDnsWithBoolean() + public function testCheckDnsWithInvalidType() { DnsMock::withMockedHosts(array('example.com' => array(array('type' => 'A')))); $constraint = new Url(array( - 'checkDNS' => true, + 'checkDNS' => 'BOGUS', 'dnsMessage' => 'myMessage', )); $this->validator->validate('http://example.com', $constraint); - - $this->assertNoViolation(); } /** - * @expectedException \Symfony\Component\Validator\Exception\InvalidOptionsException - * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts + * @group legacy + * @expectedDeprecation The "checkDNS" option in "Symfony\Component\Validator\Constraints\Url" is deprecated since Symfony 4.1 and will be removed in 5.0. Its false-positive rate is too high to be relied upon. */ - public function testCheckDnsWithInvalidType() + public function testCheckDnsOptionIsDeprecated() { - DnsMock::withMockedHosts(array('example.com' => array(array('type' => 'A')))); - $constraint = new Url(array( - 'checkDNS' => 'BOGUS', - 'dnsMessage' => 'myMessage', + 'checkDNS' => Url::CHECK_DNS_TYPE_NONE, )); $this->validator->validate('http://example.com', $constraint); diff --git a/src/Symfony/Component/Validator/Validation.php b/src/Symfony/Component/Validator/Validation.php index 950efb6cce267..71edfedb1839a 100644 --- a/src/Symfony/Component/Validator/Validation.php +++ b/src/Symfony/Component/Validator/Validation.php @@ -28,7 +28,7 @@ final class Validation * * @return ValidatorInterface The new validator */ - public static function createValidator() + public static function createValidator(): ValidatorInterface { return self::createValidatorBuilder()->getValidator(); } @@ -38,7 +38,7 @@ public static function createValidator() * * @return ValidatorBuilderInterface The new builder */ - public static function createValidatorBuilder() + public static function createValidatorBuilder(): ValidatorBuilder { return new ValidatorBuilder(); } diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index 52519d7c99f38..7e7ce9df17ede 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -16,29 +16,30 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation": "~2.8|~3.0|~4.0" + "symfony/translation": "~3.4|~4.0" }, "require-dev": { - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "^3.3.5|~4.0", - "symfony/var-dumper": "~3.3|~4.0", - "symfony/intl": "^2.8.18|^3.2.5|~4.0", + "symfony/http-foundation": "~4.1", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0", + "symfony/intl": "~4.1", "symfony/yaml": "~3.4|~4.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/cache": "~3.1|~4.0", - "symfony/property-access": "~2.8|~3.0|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/cache": "~3.4|~4.0", + "symfony/property-access": "~3.4|~4.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0", "egulias/email-validator": "^1.2.8|~2.0" }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/dependency-injection": "<3.3", - "symfony/http-kernel": "<3.3.5", + "symfony/dependency-injection": "<3.4", + "symfony/http-kernel": "<3.4", + "symfony/intl": "<4.1", "symfony/yaml": "<3.4" }, "suggest": { @@ -62,7 +63,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/VarDumper/CHANGELOG.md b/src/Symfony/Component/VarDumper/CHANGELOG.md index 2d44cad2259c0..6ece28fcb6798 100644 --- a/src/Symfony/Component/VarDumper/CHANGELOG.md +++ b/src/Symfony/Component/VarDumper/CHANGELOG.md @@ -1,6 +1,17 @@ CHANGELOG ========= +4.0.0 +----- + + * support for passing `\ReflectionClass` instances to the `Caster::castObject()` + method has been dropped, pass class names as strings instead + * the `Data::getRawData()` method has been removed + * the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$context = null` + argument and moves `$message = ''` argument at 4th position. + * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$context = null` + argument and moves `$message = ''` argument at 4th position. + 3.4.0 ----- diff --git a/src/Symfony/Component/VarDumper/Caster/ArgsStub.php b/src/Symfony/Component/VarDumper/Caster/ArgsStub.php index 6675caa0478b9..0c53a7bfb7e19 100644 --- a/src/Symfony/Component/VarDumper/Caster/ArgsStub.php +++ b/src/Symfony/Component/VarDumper/Caster/ArgsStub.php @@ -22,7 +22,7 @@ class ArgsStub extends EnumStub { private static $parameters = array(); - public function __construct(array $args, $function, $class) + public function __construct(array $args, string $function, ?string $class) { list($variadic, $params) = self::getParameters($function, $class); @@ -68,7 +68,7 @@ private static function getParameters($function, $class) if ($v->isPassedByReference()) { $k = '&'.$k; } - if (method_exists($v, 'isVariadic') && $v->isVariadic()) { + if ($v->isVariadic()) { $variadic .= $k; } else { $params[] = $k; diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php index 2676fb78bf0b3..7f4e02283f580 100644 --- a/src/Symfony/Component/VarDumper/Caster/Caster.php +++ b/src/Symfony/Component/VarDumper/Caster/Caster.php @@ -48,11 +48,6 @@ class Caster */ public static function castObject($obj, $class, $hasDebugInfo = false) { - if ($class instanceof \ReflectionClass) { - @trigger_error(sprintf('Passing a ReflectionClass to %s() is deprecated since Symfony 3.3 and will be unsupported in 4.0. Pass the class name as string instead.', __METHOD__), E_USER_DEPRECATED); - $hasDebugInfo = $class->hasMethod('__debugInfo'); - $class = $class->name; - } if ($hasDebugInfo) { $a = $obj->__debugInfo(); } elseif ($obj instanceof \Closure) { diff --git a/src/Symfony/Component/VarDumper/Caster/ClassStub.php b/src/Symfony/Component/VarDumper/Caster/ClassStub.php index 2e1f41c157921..9eebae082962a 100644 --- a/src/Symfony/Component/VarDumper/Caster/ClassStub.php +++ b/src/Symfony/Component/VarDumper/Caster/ClassStub.php @@ -22,7 +22,7 @@ class ClassStub extends ConstStub * @param string A PHP identifier, e.g. a class, method, interface, etc. name * @param callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier */ - public function __construct($identifier, $callable = null) + public function __construct(string $identifier, $callable = null) { $this->value = $identifier; diff --git a/src/Symfony/Component/VarDumper/Caster/ConstStub.php b/src/Symfony/Component/VarDumper/Caster/ConstStub.php index 26c0010b66a8c..250a250a9f7c4 100644 --- a/src/Symfony/Component/VarDumper/Caster/ConstStub.php +++ b/src/Symfony/Component/VarDumper/Caster/ConstStub.php @@ -20,7 +20,7 @@ */ class ConstStub extends Stub { - public function __construct($name, $value) + public function __construct(string $name, $value) { $this->class = $name; $this->value = $value; diff --git a/src/Symfony/Component/VarDumper/Caster/DateCaster.php b/src/Symfony/Component/VarDumper/Caster/DateCaster.php index 7e8831296c08b..af9ce9605592e 100644 --- a/src/Symfony/Component/VarDumper/Caster/DateCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/DateCaster.php @@ -20,6 +20,8 @@ */ class DateCaster { + private const PERIOD_LIMIT = 3; + public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, $isNested, $filter) { $prefix = Caster::PREFIX_VIRTUAL; @@ -61,12 +63,7 @@ private static function formatInterval(\DateInterval $i) $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); } - if (\PHP_VERSION_ID >= 70100 && isset($i->f)) { - $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; - } else { - $format .= $i->h || $i->i || $i->s ? '%H:%I:%S' : ''; - } - + $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; $format = '%R ' === $format ? '0s' : $format; return $i->format(rtrim($format)); @@ -76,7 +73,7 @@ public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stu { $location = $timeZone->getLocation(); $formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P'); - $title = $location && extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code'], \Locale::getDefault()) : ''; + $title = $location && extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; $z = array(Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)); @@ -85,14 +82,10 @@ public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stu public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNested, $filter) { - if (defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { // see https://bugs.php.net/bug.php?id=71635 - return $a; - } - $dates = array(); if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/bug.php?id=74639 foreach (clone $p as $i => $d) { - if (3 === $i) { + if (self::PERIOD_LIMIT === $i) { $now = new \DateTimeImmutable(); $dates[] = sprintf('%s more', ($end = $p->getEndDate()) ? ceil(($end->format('U.u') - $d->format('U.u')) / ($now->add($p->getDateInterval())->format('U.u') - $now->format('U.u'))) diff --git a/src/Symfony/Component/VarDumper/Caster/EnumStub.php b/src/Symfony/Component/VarDumper/Caster/EnumStub.php index 3cee23eac202b..7a4e98a21b4d1 100644 --- a/src/Symfony/Component/VarDumper/Caster/EnumStub.php +++ b/src/Symfony/Component/VarDumper/Caster/EnumStub.php @@ -22,7 +22,7 @@ class EnumStub extends Stub { public $dumpKeys = true; - public function __construct(array $values, $dumpKeys = true) + public function __construct(array $values, bool $dumpKeys = true) { $this->value = $values; $this->dumpKeys = $dumpKeys; diff --git a/src/Symfony/Component/VarDumper/Caster/FrameStub.php b/src/Symfony/Component/VarDumper/Caster/FrameStub.php index 1e1194dc85b89..878675528f7e7 100644 --- a/src/Symfony/Component/VarDumper/Caster/FrameStub.php +++ b/src/Symfony/Component/VarDumper/Caster/FrameStub.php @@ -21,7 +21,7 @@ class FrameStub extends EnumStub public $keepArgs; public $inTraceStub; - public function __construct(array $frame, $keepArgs = true, $inTraceStub = false) + public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false) { $this->value = $frame; $this->keepArgs = $keepArgs; diff --git a/src/Symfony/Component/VarDumper/Caster/GmpCaster.php b/src/Symfony/Component/VarDumper/Caster/GmpCaster.php new file mode 100644 index 0000000000000..504dc078867a8 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/GmpCaster.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts GMP objects to array representation. + * + * @author Hamza Amrouche + * @author Nicolas Grekas + */ +class GmpCaster +{ + public static function castGmp(\GMP $gmp, array $a, Stub $stub, $isNested, $filter): array + { + $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp)); + + return $a; + } +} diff --git a/src/Symfony/Component/VarDumper/Caster/LinkStub.php b/src/Symfony/Component/VarDumper/Caster/LinkStub.php index 24f360b8f5553..d709e262de2f6 100644 --- a/src/Symfony/Component/VarDumper/Caster/LinkStub.php +++ b/src/Symfony/Component/VarDumper/Caster/LinkStub.php @@ -23,7 +23,7 @@ class LinkStub extends ConstStub private static $vendorRoots; private static $composerRoots; - public function __construct($label, $line = 0, $href = null) + public function __construct($label, int $line = 0, $href = null) { $this->value = $label; diff --git a/src/Symfony/Component/VarDumper/Caster/MongoCaster.php b/src/Symfony/Component/VarDumper/Caster/MongoCaster.php deleted file mode 100644 index 3b8fb338e5bff..0000000000000 --- a/src/Symfony/Component/VarDumper/Caster/MongoCaster.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -@trigger_error('The '.__NAMESPACE__.'\MongoCaster class is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); - -/** - * Casts classes from the MongoDb extension to array representation. - * - * @author Nicolas Grekas - * - * @deprecated since version 3.4, to be removed in 4.0. - */ -class MongoCaster -{ - public static function castCursor(\MongoCursorInterface $cursor, array $a, Stub $stub, $isNested) - { - if ($info = $cursor->info()) { - foreach ($info as $k => $v) { - $a[Caster::PREFIX_VIRTUAL.$k] = $v; - } - } - $a[Caster::PREFIX_VIRTUAL.'dead'] = $cursor->dead(); - - return $a; - } -} diff --git a/src/Symfony/Component/VarDumper/Caster/RedisCaster.php b/src/Symfony/Component/VarDumper/Caster/RedisCaster.php index 07a3a1b091126..3d7063f40a271 100644 --- a/src/Symfony/Component/VarDumper/Caster/RedisCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/RedisCaster.php @@ -30,15 +30,6 @@ public static function castRedis(\Redis $c, array $a, Stub $stub, $isNested) { $prefix = Caster::PREFIX_VIRTUAL; - if (defined('HHVM_VERSION_ID')) { - if (isset($a[Caster::PREFIX_PROTECTED.'serializer'])) { - $ser = $a[Caster::PREFIX_PROTECTED.'serializer']; - $a[Caster::PREFIX_PROTECTED.'serializer'] = isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser; - } - - return $a; - } - if (!$connected = $c->isConnected()) { return $a + array( $prefix.'isConnected' => $connected, diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 4634fb915ea62..b7aee32124940 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -36,7 +36,6 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested, $prefix = Caster::PREFIX_VIRTUAL; $c = new \ReflectionFunction($c); - $stub->class = 'Closure'; // HHVM generates unique class names for closures $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); if (isset($a[$prefix.'parameters'])) { @@ -86,7 +85,7 @@ public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNes $prefix = Caster::PREFIX_VIRTUAL; $a += array( - $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : $c->__toString(), + $prefix.'name' => $c->getName(), $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), ); @@ -173,7 +172,7 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra if (isset($a[$prefix.'returnType'])) { $v = $a[$prefix.'returnType']; - $v = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); + $v = $v->getName(); $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, array(class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '')); } if (isset($a[$prefix.'class'])) { @@ -185,7 +184,7 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra foreach ($c->getParameters() as $v) { $k = '$'.$v->name; - if (method_exists($v, 'isVariadic') && $v->isVariadic()) { + if ($v->isVariadic()) { $k = '...'.$k; } if ($v->isPassedByReference()) { @@ -213,9 +212,6 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra self::addExtra($a, $c); } - // Added by HHVM - unset($a[Caster::PREFIX_DYNAMIC.'static']); - return $a; } @@ -230,9 +226,6 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st { $prefix = Caster::PREFIX_VIRTUAL; - // Added by HHVM - unset($a['info']); - self::addMap($a, $c, array( 'position' => 'getPosition', 'isVariadic' => 'isVariadic', @@ -240,12 +233,8 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st 'allowsNull' => 'allowsNull', )); - if (method_exists($c, 'getType')) { - if ($v = $c->getType()) { - $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); - } - } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $c, $v)) { - $a[$prefix.'typeHint'] = $v[1]; + if ($v = $c->getType()) { + $a[$prefix.'typeHint'] = $v->getName(); } if (isset($a[$prefix.'typeHint'])) { @@ -257,17 +246,13 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st try { $a[$prefix.'default'] = $v = $c->getDefaultValue(); - if (method_exists($c, 'isDefaultValueConstant') && $c->isDefaultValueConstant()) { + if ($c->isDefaultValueConstant()) { $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); } if (null === $v) { unset($a[$prefix.'allowsNull']); } } catch (\ReflectionException $e) { - if (isset($a[$prefix.'typeHint']) && $c->allowsNull() && !class_exists('ReflectionNamedType', false)) { - $a[$prefix.'default'] = null; - unset($a[$prefix.'allowsNull']); - } } return $a; diff --git a/src/Symfony/Component/VarDumper/Caster/TraceStub.php b/src/Symfony/Component/VarDumper/Caster/TraceStub.php index 59548acaee61c..5eea1c876680f 100644 --- a/src/Symfony/Component/VarDumper/Caster/TraceStub.php +++ b/src/Symfony/Component/VarDumper/Caster/TraceStub.php @@ -25,7 +25,7 @@ class TraceStub extends Stub public $sliceLength; public $numberingOffset; - public function __construct(array $trace, $keepArgs = true, $sliceOffset = 0, $sliceLength = null, $numberingOffset = 0) + public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0) { $this->value = $trace; $this->keepArgs = $keepArgs; diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index e3246927001a4..a79fb2cd3fa55 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -104,8 +104,6 @@ abstract class AbstractCloner implements ClonerInterface 'SplPriorityQueue' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'), 'OuterIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'), - 'MongoCursorInterface' => array('Symfony\Component\VarDumper\Caster\MongoCaster', 'castCursor'), - 'Redis' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'), 'RedisArray' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'), @@ -114,6 +112,8 @@ abstract class AbstractCloner implements ClonerInterface 'DateTimeZone' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'), 'DatePeriod' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'), + 'GMP' => array('Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'), + ':curl' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'), ':dba' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'), ':dba persistent' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'), @@ -133,7 +133,6 @@ abstract class AbstractCloner implements ClonerInterface protected $maxItems = 2500; protected $maxString = -1; protected $minDepth = 1; - protected $useExt; private $casters = array(); private $prevErrorHandler; @@ -151,7 +150,6 @@ public function __construct(array $casters = null) $casters = static::$defaultCasters; } $this->addCasters($casters); - $this->useExt = extension_loaded('symfony_debug'); } /** diff --git a/src/Symfony/Component/VarDumper/Cloner/Data.php b/src/Symfony/Component/VarDumper/Cloner/Data.php index aa06b1cfb0fa9..4e17d62693d5d 100644 --- a/src/Symfony/Component/VarDumper/Cloner/Data.php +++ b/src/Symfony/Component/VarDumper/Cloner/Data.php @@ -165,18 +165,6 @@ public function __toString() return sprintf('%s (count=%d)', $this->getType(), count($value)); } - /** - * @return array The raw data structure - * - * @deprecated since version 3.3. Use array or object access instead. - */ - public function getRawData() - { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the array or object access instead.', __METHOD__)); - - return $this->data; - } - /** * Returns a depth limited clone of $this. * diff --git a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php index 3664609c5e648..012743cd611da 100644 --- a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php @@ -17,8 +17,6 @@ class VarCloner extends AbstractCloner { private static $gid; - private static $hashMask = 0; - private static $hashOffset = 0; private static $arrayCache = array(); /** @@ -31,10 +29,10 @@ protected function doClone($var) $refsCounter = 0; // Hard references counter $queue = array(array($var)); // This breadth-first queue is the return value $indexedArrays = array(); // Map of queue indexes that hold numerically indexed arrays - $hardRefs = array(); // Map of original zval hashes to stub objects + $hardRefs = array(); // Map of original zval ids to stub objects $objRefs = array(); // Map of original object handles to their stub object couterpart $resRefs = array(); // Map of original resource handles to their stub object couterpart - $values = array(); // Map of stub objects' hashes to original values + $values = array(); // Map of stub objects' ids to original values $maxItems = $this->maxItems; $maxString = $this->maxString; $minDepth = $this->minDepth; @@ -46,13 +44,9 @@ protected function doClone($var) $stub = null; // Stub capturing the main properties of an original item value // or null if the original value is used directly - if (!self::$hashMask) { - self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable - self::initHashMask(); + if (!$gid = self::$gid) { + $gid = self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable } - $gid = self::$gid; - $hashMask = self::$hashMask; - $hashOffset = self::$hashOffset; $arrayStub = new Stub(); $arrayStub->type = Stub::TYPE_ARRAY; $fromObjCast = false; @@ -89,7 +83,7 @@ protected function doClone($var) if ($zvalIsRef = $vals[$k] === $cookie) { $vals[$k] = &$stub; // Break hard references to make $queue completely unset($stub); // independent from the original structure - if ($v instanceof Stub && isset($hardRefs[\spl_object_hash($v)])) { + if ($v instanceof Stub && isset($hardRefs[\spl_object_id($v)])) { $vals[$k] = $refs[$k] = $v; if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { ++$v->value->refCount; @@ -99,7 +93,7 @@ protected function doClone($var) } $refs[$k] = $vals[$k] = new Stub(); $refs[$k]->value = $v; - $h = \spl_object_hash($refs[$k]); + $h = \spl_object_id($refs[$k]); $hardRefs[$h] = &$refs[$k]; $values[$h] = $v; $vals[$k]->handle = ++$refsCounter; @@ -178,7 +172,7 @@ protected function doClone($var) case \is_object($v): case $v instanceof \__PHP_Incomplete_Class: - if (empty($objRefs[$h = $hashMask ^ \hexdec(\substr(\spl_object_hash($v), $hashOffset, \PHP_INT_SIZE))])) { + if (empty($objRefs[$h = \spl_object_id($v)])) { $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = \get_class($v); @@ -189,8 +183,7 @@ protected function doClone($var) if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { break; } - $h = $hashMask ^ \hexdec(\substr(\spl_object_hash($stub->value), $hashOffset, \PHP_INT_SIZE)); - $stub->handle = $h; + $stub->handle = $h = \spl_object_id($stub->value); } $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { @@ -297,31 +290,4 @@ protected function doClone($var) return $queue; } - - private static function initHashMask() - { - $obj = (object) array(); - self::$hashOffset = 16 - PHP_INT_SIZE; - self::$hashMask = -1; - - if (defined('HHVM_VERSION')) { - self::$hashOffset += 16; - } else { - // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below - $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush'); - foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { - if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && in_array($frame['function'], $obFuncs)) { - $frame['line'] = 0; - break; - } - } - if (!empty($frame['line'])) { - ob_start(); - debug_zval_dump($obj); - self::$hashMask = (int) substr(ob_get_clean(), 17); - } - } - - self::$hashMask ^= hexdec(substr(spl_object_hash($obj), self::$hashOffset, PHP_INT_SIZE)); - } } diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php index e27675ad13c9f..73b5f643a724d 100644 --- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php @@ -42,9 +42,9 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface * @param string $charset The default character encoding to use for non-UTF8 strings * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation */ - public function __construct($output = null, $charset = null, $flags = 0) + public function __construct($output = null, string $charset = null, int $flags = 0) { - $this->flags = (int) $flags; + $this->flags = $flags; $this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'); $this->decimalPoint = localeconv(); $this->decimalPoint = $this->decimalPoint['decimal_point']; diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 30c2531191ff1..2faf54372f714 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -58,7 +58,7 @@ class CliDumper extends AbstractDumper /** * {@inheritdoc} */ - public function __construct($output = null, $charset = null, $flags = 0) + public function __construct($output = null, string $charset = null, int $flags = 0) { parent::__construct($output, $charset, $flags); diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index c1263026b8c63..f0f1f83b6f560 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -56,7 +56,7 @@ class HtmlDumper extends CliDumper /** * {@inheritdoc} */ - public function __construct($output = null, $charset = null, $flags = 0) + public function __construct($output = null, string $charset = null, int $flags = 0) { AbstractDumper::__construct($output, $charset, $flags); $this->dumpId = 'sf-dump-'.mt_rand(); diff --git a/src/Symfony/Component/VarDumper/Resources/functions/dump.php b/src/Symfony/Component/VarDumper/Resources/functions/dump.php index 95d8bb59808dd..58cbfc017db34 100644 --- a/src/Symfony/Component/VarDumper/Resources/functions/dump.php +++ b/src/Symfony/Component/VarDumper/Resources/functions/dump.php @@ -15,9 +15,11 @@ /** * @author Nicolas Grekas */ - function dump($var) + function dump($var, ...$moreVars) { - foreach (func_get_args() as $var) { + VarDumper::dump($var); + + foreach ($moreVars as $var) { VarDumper::dump($var); } diff --git a/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php b/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php index 0fffd55122218..3946b2eb86f7e 100644 --- a/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php +++ b/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php @@ -19,26 +19,14 @@ */ trait VarDumperTestTrait { - public function assertDumpEquals($dump, $data, $filter = 0, $message = '') + public function assertDumpEquals($expected, $data, $filter = 0, $message = '') { - if (is_string($filter)) { - @trigger_error(sprintf('The $message argument of the "%s()" method at 3rd position is deprecated since Symfony 3.4 and will be moved at 4th position in 4.0.', __METHOD__), E_USER_DEPRECATED); - $message = $filter; - $filter = 0; - } - - $this->assertSame(rtrim($dump), $this->getDump($data, null, $filter), $message); + $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } - public function assertDumpMatchesFormat($dump, $data, $filter = 0, $message = '') + public function assertDumpMatchesFormat($expected, $data, $filter = 0, $message = '') { - if (is_string($filter)) { - @trigger_error(sprintf('The $message argument of the "%s()" method at 3rd position is deprecated since Symfony 3.4 and will be moved at 4th position in 4.0.', __METHOD__), E_USER_DEPRECATED); - $message = $filter; - $filter = 0; - } - - $this->assertStringMatchesFormat(rtrim($dump), $this->getDump($data, null, $filter), $message); + $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } protected function getDump($data, $key = null, $filter = 0) @@ -57,4 +45,13 @@ protected function getDump($data, $key = null, $filter = 0) return rtrim($dumper->dump($data, true)); } + + private function prepareExpectation($expected, $filter) + { + if (!is_string($expected)) { + $expected = $this->getDump($expected, null, $filter); + } + + return rtrim($expected); + } } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php index 105d5638ee1bc..1dacce59bc180 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php @@ -151,9 +151,6 @@ public function provideFilter() ); } - /** - * @requires PHP 7.0 - */ public function testAnonymousClass() { $c = eval('return new class extends stdClass { private $foo = "foo"; };'); diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php index 96c167d3e44a3..f6d03792b12a7 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php @@ -29,10 +29,6 @@ class DateCasterTest extends TestCase */ public function testDumpDateTime($time, $timezone, $xDate, $xTimestamp) { - if ((defined('HHVM_VERSION_ID') || PHP_VERSION_ID <= 50509) && preg_match('/[-+]\d{2}:\d{2}/', $timezone)) { - $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); - } - $date = new \DateTime($time, new \DateTimeZone($timezone)); $xDump = <<markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); - } - $stub = new Stub(); $date = new \DateTime($time, new \DateTimeZone($timezone)); $cast = DateCaster::castDateTime($date, array('foo' => 'bar'), $stub, false, 0); @@ -181,35 +173,32 @@ public function testCastInterval($intervalSpec, $ms, $invert, $xInterval, $xSeco public function provideIntervals() { - $i = new \DateInterval('PT0S'); - $ms = ($withMs = \PHP_VERSION_ID >= 70100 && isset($i->f)) ? '.0' : ''; - return array( array('PT0S', 0, 0, '0s', '0s'), - array('PT0S', 0.1, 0, $withMs ? '+ 00:00:00.100' : '0s', '%is'), - array('PT1S', 0, 0, '+ 00:00:01'.$ms, '%is'), - array('PT2M', 0, 0, '+ 00:02:00'.$ms, '%is'), - array('PT3H', 0, 0, '+ 03:00:00'.$ms, '%ss'), + array('PT0S', 0.1, 0, '+ 00:00:00.100', '%is'), + array('PT1S', 0, 0, '+ 00:00:01.0', '%is'), + array('PT2M', 0, 0, '+ 00:02:00.0', '%is'), + array('PT3H', 0, 0, '+ 03:00:00.0', '%ss'), array('P4D', 0, 0, '+ 4d', '%ss'), array('P5M', 0, 0, '+ 5m', null), array('P6Y', 0, 0, '+ 6y', null), - array('P1Y2M3DT4H5M6S', 0, 0, '+ 1y 2m 3d 04:05:06'.$ms, null), - array('PT1M60S', 0, 0, '+ 00:02:00'.$ms, null), - array('PT1H60M', 0, 0, '+ 02:00:00'.$ms, null), + array('P1Y2M3DT4H5M6S', 0, 0, '+ 1y 2m 3d 04:05:06.0', null), + array('PT1M60S', 0, 0, '+ 00:02:00.0', null), + array('PT1H60M', 0, 0, '+ 02:00:00.0', null), array('P1DT24H', 0, 0, '+ 2d', null), array('P1M32D', 0, 0, '+ 1m 32d', null), array('PT0S', 0, 1, '0s', '0s'), - array('PT0S', 0.1, 1, $withMs ? '- 00:00:00.100' : '0s', '%is'), - array('PT1S', 0, 1, '- 00:00:01'.$ms, '%is'), - array('PT2M', 0, 1, '- 00:02:00'.$ms, '%is'), - array('PT3H', 0, 1, '- 03:00:00'.$ms, '%ss'), + array('PT0S', 0.1, 1, '- 00:00:00.100', '%is'), + array('PT1S', 0, 1, '- 00:00:01.0', '%is'), + array('PT2M', 0, 1, '- 00:02:00.0', '%is'), + array('PT3H', 0, 1, '- 03:00:00.0', '%ss'), array('P4D', 0, 1, '- 4d', '%ss'), array('P5M', 0, 1, '- 5m', null), array('P6Y', 0, 1, '- 6y', null), - array('P1Y2M3DT4H5M6S', 0, 1, '- 1y 2m 3d 04:05:06'.$ms, null), - array('PT1M60S', 0, 1, '- 00:02:00'.$ms, null), - array('PT1H60M', 0, 1, '- 02:00:00'.$ms, null), + array('P1Y2M3DT4H5M6S', 0, 1, '- 1y 2m 3d 04:05:06.0', null), + array('PT1M60S', 0, 1, '- 00:02:00.0', null), + array('PT1H60M', 0, 1, '- 02:00:00.0', null), array('P1DT24H', 0, 1, '- 2d', null), array('P1M32D', 0, 1, '- 1m 32d', null), ); @@ -220,10 +209,6 @@ public function provideIntervals() */ public function testDumpTimeZone($timezone, $expected) { - if ((defined('HHVM_VERSION_ID') || PHP_VERSION_ID <= 50509) && !preg_match('/\w+\/\w+/', $timezone)) { - $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); - } - $timezone = new \DateTimeZone($timezone); $xDump = <<markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); - } - $timezone = new \DateTimeZone($timezone); $xDump = <<markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); - } - $timezone = new \DateTimeZone($timezone); $stub = new Stub(); @@ -325,10 +302,6 @@ public function provideTimeZones() */ public function testDumpPeriod($start, $interval, $end, $options, $expected) { - if (defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { - $this->markTestSkipped(); - } - $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), is_int($end) ? $end : new \DateTime($end), $options); $xDump = <<= 70000 && \PHP_VERSION_ID < 70005)) { - $this->markTestSkipped(); - } - $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), is_int($end) ? $end : new \DateTime($end), $options); $stub = new Stub(); @@ -380,9 +349,6 @@ public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDa public function providePeriods() { - $i = new \DateInterval('PT0S'); - $ms = \PHP_VERSION_ID >= 70100 && isset($i->f) ? '.0' : ''; - $periods = array( array('2017-01-01', 'P1D', '2017-01-03', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02'), array('2017-01-01', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01%a2) 2017-01-02'), @@ -399,8 +365,8 @@ public function providePeriods() array('2017-01-01 01:00:00', 'P1D', '2017-01-03 01:00:00', 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) to 2017-01-03 01:00:00.0', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'), array('2017-01-01 01:00:00', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'), - array('2017-01-01', 'P1DT1H', '2017-01-03', 0, "every + 1d 01:00:00$ms, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0", '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'), - array('2017-01-01', 'P1DT1H', 1, 0, "every + 1d 01:00:00$ms, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s", '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'), + array('2017-01-01', 'P1DT1H', '2017-01-03', 0, 'every + 1d 01:00:00.0, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'), + array('2017-01-01', 'P1DT1H', 1, 0, 'every + 1d 01:00:00.0, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'), array('2017-01-01', 'P1D', '2017-01-04', \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) to 2017-01-04 00:00:00.0', '1) 2017-01-02%a2) 2017-01-03'), array('2017-01-01', 'P1D', 2, \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) recurring 2 time/s', '1) 2017-01-02%a2) 2017-01-03'), @@ -416,9 +382,7 @@ public function providePeriods() private function createInterval($intervalSpec, $ms, $invert) { $interval = new \DateInterval($intervalSpec); - if (\PHP_VERSION_ID >= 70100 && isset($interval->f)) { - $interval->f = $ms; - } + $interval->f = $ms; $interval->invert = $invert; return $interval; diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/GmpCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/GmpCasterTest.php new file mode 100644 index 0000000000000..1ddf897461f6f --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Caster/GmpCasterTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\GmpCaster; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +class GmpCasterTest extends TestCase +{ + use VarDumperTestTrait; + + /** + * @requires extension gmp + */ + public function testCastGmp() + { + $gmpString = gmp_init('1234'); + $gmpOctal = gmp_init(010); + $gmp = gmp_init('01101'); + $gmpDump = << %s +] +EODUMP; + $this->assertDumpEquals(sprintf($gmpDump, $gmpString), GmpCaster::castGmp($gmpString, array(), new Stub(), false, 0)); + $this->assertDumpEquals(sprintf($gmpDump, $gmpOctal), GmpCaster::castGmp($gmpOctal, array(), new Stub(), false, 0)); + $this->assertDumpEquals(sprintf($gmpDump, $gmp), GmpCaster::castGmp($gmp, array(), new Stub(), false, 0)); + + $dump = <<assertDumpEquals($dump, $gmp); + } +} diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php index 80acdb66798f9..f724639d40e4f 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php @@ -26,20 +26,11 @@ public function testNotConnected() { $redis = new \Redis(); - if (defined('HHVM_VERSION_ID')) { - $xCast = <<<'EODUMP' -Redis { - #host: "" -%A -} -EODUMP; - } else { - $xCast = <<<'EODUMP' + $xCast = <<<'EODUMP' Redis { isConnected: false } EODUMP; - } $this->assertDumpMatchesFormat($xCast, $redis); } @@ -52,15 +43,7 @@ public function testConnected() self::markTestSkipped($e['message']); } - if (defined('HHVM_VERSION_ID')) { - $xCast = <<<'EODUMP' -Redis { - #host: "127.0.0.1" -%A -} -EODUMP; - } else { - $xCast = <<<'EODUMP' + $xCast = <<<'EODUMP' Redis {%A isConnected: true host: "127.0.0.1" @@ -77,7 +60,6 @@ public function testConnected() } } EODUMP; - } $this->assertDumpMatchesFormat($xCast, $redis); } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index 71eecc1aa896d..3eaf958d8f685 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -116,9 +116,6 @@ public function testReflectionParameter() ); } - /** - * @requires PHP 7.0 - */ public function testReflectionParameterScalar() { $f = eval('return function (int $a) {};'); @@ -136,9 +133,6 @@ public function testReflectionParameterScalar() ); } - /** - * @requires PHP 7.0 - */ public function testReturnType() { $f = eval('return function ():int {};'); @@ -158,9 +152,6 @@ class: "Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest" ); } - /** - * @requires PHP 7.0 - */ public function testGenerator() { if (extension_loaded('xdebug')) { diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php index 88c7f3008e37e..d25f88d1669fb 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php @@ -48,7 +48,6 @@ public function testGet() $intMax = PHP_INT_MAX; $res = (int) $var['res']; - $r = defined('HHVM_VERSION') ? '' : '#%d'; $this->assertStringMatchesFormat( << Closure {{$r} + "closure" => Closure {#%d class: "Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest" - this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {{$r} …} + this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {#%d …} parameters: { \$a: {} &\$b: { @@ -290,10 +289,6 @@ public function testObjectCast() public function testClosedResource() { - if (defined('HHVM_VERSION') && HHVM_VERSION_ID < 30600) { - $this->markTestSkipped(); - } - $var = fopen(__FILE__, 'r'); fclose($var); @@ -384,11 +379,10 @@ public function testThrowingCaster() $dumper->dump($data, $out); $out = stream_get_contents($out, -1, 0); - $r = defined('HHVM_VERSION') ? '' : '#%d'; $this->assertStringMatchesFormat( <<cloneVar($var); $out = $dumper->dump($data, true); - $r = defined('HHVM_VERSION') ? '' : '#%d'; $this->assertStringMatchesFormat( <<getSpecialVars(); unset($var[0]); @@ -491,10 +483,6 @@ public function testGlobalsNoExt() $dumper->setColors(false); $cloner = new VarCloner(); - $refl = new \ReflectionProperty($cloner, 'useExt'); - $refl->setAccessible(true); - $refl->setValue($cloner, false); - $data = $cloner->cloneVar($var); $dumper->dump($data); @@ -509,47 +497,6 @@ public function testGlobalsNoExt() 2 => &1 array:1 [&1] ] -EOTXT - , - $out - ); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testBuggyRefs() - { - if (\PHP_VERSION_ID >= 50600) { - $this->markTestSkipped('PHP 5.6 fixed refs counting'); - } - - $var = $this->getSpecialVars(); - $var = $var[0]; - - $dumper = new CliDumper(); - $dumper->setColors(false); - $cloner = new VarCloner(); - - $data = $cloner->cloneVar($var)->withMaxDepth(3); - $out = ''; - $dumper->dump($data, function ($line, $depth) use (&$out) { - if ($depth >= 0) { - $out .= str_repeat(' ', $depth).$line."\n"; - } - }); - - $this->assertSame( - <<<'EOTXT' -array:1 [ - 0 => array:1 [ - 0 => array:1 [ - 0 => array:1 [ …1] - ] - ] -] - EOTXT , $out diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php index b852f27476d0a..12a7240186910 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php @@ -47,7 +47,6 @@ public function testGet() $dumpId = $dumpId[0]; $res = (int) $var['res']; - $r = defined('HHVM_VERSION') ? '' : '#%d'; $this->assertStringMatchesFormat( <<array:24 [ @@ -75,10 +74,10 @@ public function testGet() +foo: "foo" +"bar": "bar" } - "closure" => Closure {{$r} + "closure" => Closure {#%d class: "Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest" - this: HtmlDumperTest {{$r} &%s;} + this: HtmlDumperTest {#%d &%s;} parameters: { \$a: {} &\$b: { diff --git a/src/Symfony/Component/VarDumper/Tests/Test/VarDumperTestTraitTest.php b/src/Symfony/Component/VarDumper/Tests/Test/VarDumperTestTraitTest.php index 464d67f6cec03..25ce23c5ce794 100644 --- a/src/Symfony/Component/VarDumper/Tests/Test/VarDumperTestTraitTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Test/VarDumperTestTraitTest.php @@ -38,4 +38,9 @@ public function testItComparesLargeData() $this->assertDumpEquals($expected, $data); } + + public function testAllowsNonScalarExpectation() + { + $this->assertDumpEquals(new \ArrayObject(array('bim' => 'bam')), new \ArrayObject(array('bim' => 'bam'))); + } } diff --git a/src/Symfony/Component/VarDumper/composer.json b/src/Symfony/Component/VarDumper/composer.json index d31e0b495abb9..3337070dd33ff 100644 --- a/src/Symfony/Component/VarDumper/composer.json +++ b/src/Symfony/Component/VarDumper/composer.json @@ -16,8 +16,9 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.0" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.5" }, "require-dev": { "ext-iconv": "*", @@ -28,8 +29,7 @@ }, "suggest": { "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "ext-symfony_debug": "" + "ext-intl": "To show region name in time zone dump" }, "autoload": { "files": [ "Resources/functions/dump.php" ], @@ -41,7 +41,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/WebLink/HttpHeaderSerializer.php b/src/Symfony/Component/WebLink/HttpHeaderSerializer.php index 66bfa55ab4a8b..01bb9f154b82d 100644 --- a/src/Symfony/Component/WebLink/HttpHeaderSerializer.php +++ b/src/Symfony/Component/WebLink/HttpHeaderSerializer.php @@ -29,7 +29,7 @@ final class HttpHeaderSerializer * * @return string|null */ - public function serialize($links) + public function serialize(iterable $links) { $elements = array(); foreach ($links as $link) { diff --git a/src/Symfony/Component/WebLink/composer.json b/src/Symfony/Component/WebLink/composer.json index 03d079e467994..1211d16ea3d05 100644 --- a/src/Symfony/Component/WebLink/composer.json +++ b/src/Symfony/Component/WebLink/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "fig/link-util": "^1.0", "psr/link": "^1.0" }, @@ -24,9 +24,9 @@ "symfony/http-kernel": "" }, "require-dev": { - "symfony/event-dispatcher": "^2.8|^3.0|~4.0", - "symfony/http-foundation": "^2.8|^3.0|~4.0", - "symfony/http-kernel": "^2.8|^3.0|~4.0" + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\WebLink\\": "" }, @@ -37,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Workflow/CHANGELOG.md b/src/Symfony/Component/Workflow/CHANGELOG.md index abbf3238011c1..1bdca90c9b479 100644 --- a/src/Symfony/Component/Workflow/CHANGELOG.md +++ b/src/Symfony/Component/Workflow/CHANGELOG.md @@ -1,6 +1,19 @@ CHANGELOG ========= +4.1.0 +----- + + * Deprecate the usage of `add(Workflow $workflow, $supportStrategy)` in `Workflow/Registry`, use `addWorkflow(WorkflowInterface, $supportStrategy)` instead. + * Deprecate the usage of `SupportStrategyInterface`, use `WorkflowSupportStrategyInterface` instead. + * The `Workflow` class now implements `WorkflowInterface`. + * Deprecated the class `ClassInstanceSupportStrategy` in favor of the class `InstanceOfSupportStrategy`. + +4.0.0 +----- + + * Removed class name support in `WorkflowRegistry::add()` as second parameter. + 3.4.0 ----- diff --git a/src/Symfony/Component/Workflow/Definition.php b/src/Symfony/Component/Workflow/Definition.php index e366407638594..98536ddf8fed7 100644 --- a/src/Symfony/Component/Workflow/Definition.php +++ b/src/Symfony/Component/Workflow/Definition.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Workflow; -use Symfony\Component\Workflow\Exception\InvalidArgumentException; use Symfony\Component\Workflow\Exception\LogicException; /** @@ -30,7 +29,7 @@ final class Definition * @param Transition[] $transitions * @param string|null $initialPlace */ - public function __construct(array $places, array $transitions, $initialPlace = null) + public function __construct(array $places, array $transitions, string $initialPlace = null) { foreach ($places as $place) { $this->addPlace($place); @@ -54,7 +53,7 @@ public function getInitialPlace() /** * @return string[] */ - public function getPlaces() + public function getPlaces(): array { return $this->places; } @@ -62,12 +61,12 @@ public function getPlaces() /** * @return Transition[] */ - public function getTransitions() + public function getTransitions(): array { return $this->transitions; } - private function setInitialPlace($place) + private function setInitialPlace(string $place = null) { if (null === $place) { return; @@ -80,12 +79,8 @@ private function setInitialPlace($place) $this->initialPlace = $place; } - private function addPlace($place) + private function addPlace(string $place) { - if (!preg_match('{^[\w_-]+$}', $place)) { - throw new InvalidArgumentException(sprintf('The place "%s" contains invalid characters.', $place)); - } - if (!count($this->places)) { $this->initialPlace = $place; } diff --git a/src/Symfony/Component/Workflow/DefinitionBuilder.php b/src/Symfony/Component/Workflow/DefinitionBuilder.php index 16d66286efb4c..bcc31cf2a7471 100644 --- a/src/Symfony/Component/Workflow/DefinitionBuilder.php +++ b/src/Symfony/Component/Workflow/DefinitionBuilder.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Workflow; -use Symfony\Component\Workflow\Exception\InvalidArgumentException; - /** * Builds a definition. * @@ -77,10 +75,6 @@ public function setInitialPlace($place) */ public function addPlace($place) { - if (!preg_match('{^[\w_-]+$}', $place)) { - throw new InvalidArgumentException(sprintf('The place "%s" contains invalid characters.', $place)); - } - if (!$this->places) { $this->initialPlace = $place; } diff --git a/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php b/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php index 7100095735de3..b4e222d335fcd 100644 --- a/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php +++ b/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php @@ -24,7 +24,7 @@ class ValidateWorkflowsPass implements CompilerPassInterface { private $definitionTag; - public function __construct($definitionTag = 'workflow.definition') + public function __construct(string $definitionTag = 'workflow.definition') { $this->definitionTag = $definitionTag; } diff --git a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php index 3681b6f1391a8..2d883c8264b13 100644 --- a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php @@ -107,7 +107,7 @@ protected function addPlaces(array $places) $code = ''; foreach ($places as $id => $place) { - $code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $id, $this->addAttributes($place['attributes'])); + $code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $this->escape($id), $this->addAttributes($place['attributes'])); } return $code; @@ -121,7 +121,7 @@ protected function addTransitions(array $transitions) $code = ''; foreach ($transitions as $place) { - $code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", $this->dotize($place['name']), $place['name'], $this->addAttributes($place['attributes'])); + $code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", $this->dotize($place['name']), $this->escape($place['name']), $this->addAttributes($place['attributes'])); } return $code; @@ -198,21 +198,29 @@ protected function endDot() */ protected function dotize($id) { - return strtolower(preg_replace('/[^\w]/i', '_', $id)); + return hash('sha1', $id); } - private function addAttributes(array $attributes) + /** + * @internal + */ + protected function escape(string $string): string + { + return addslashes($string); + } + + private function addAttributes(array $attributes): string { $code = array(); foreach ($attributes as $k => $v) { - $code[] = sprintf('%s="%s"', $k, $v); + $code[] = sprintf('%s="%s"', $k, $this->escape($v)); } return $code ? ', '.implode(', ', $code) : ''; } - private function addOptions(array $options) + private function addOptions(array $options): string { $code = array(); diff --git a/src/Symfony/Component/Workflow/Dumper/PlantUmlDumper.php b/src/Symfony/Component/Workflow/Dumper/PlantUmlDumper.php new file mode 100644 index 0000000000000..2acf919ca1397 --- /dev/null +++ b/src/Symfony/Component/Workflow/Dumper/PlantUmlDumper.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\Dumper; + +use InvalidArgumentException; +use Symfony\Component\Workflow\Definition; +use Symfony\Component\Workflow\Marking; + +/** + * PlantUmlDumper dumps a workflow as a PlantUML file. + * + * You can convert the generated puml file with the plantuml.jar utility (http://plantuml.com/): + * + * php bin/console workflow:dump pull_request travis --dump-format=puml | java -jar plantuml.jar -p > workflow.png + * + * @author Sébastien Morel + */ +class PlantUmlDumper implements DumperInterface +{ + private const INITIAL = '<>'; + private const MARKED = '<>'; + + const STATEMACHINE_TRANSITION = 'arrow'; + const WORKFLOW_TRANSITION = 'square'; + const TRANSITION_TYPES = array(self::STATEMACHINE_TRANSITION, self::WORKFLOW_TRANSITION); + const DEFAULT_OPTIONS = array( + 'skinparams' => array( + 'titleBorderRoundCorner' => 15, + 'titleBorderThickness' => 2, + 'state' => array( + 'BackgroundColor'.self::INITIAL => '#87b741', + 'BackgroundColor'.self::MARKED => '#3887C6', + 'BorderColor' => '#3887C6', + 'BorderColor'.self::MARKED => 'Black', + 'FontColor'.self::MARKED => 'White', + ), + 'agent' => array( + 'BackgroundColor' => '#ffffff', + 'BorderColor' => '#3887C6', + ), + ), + ); + + private $transitionType = self::STATEMACHINE_TRANSITION; + + public function __construct(string $transitionType = null) + { + if (!\in_array($transitionType, self::TRANSITION_TYPES, true)) { + throw new InvalidArgumentException("Transition type '$transitionType' does not exist."); + } + $this->transitionType = $transitionType; + } + + public function dump(Definition $definition, Marking $marking = null, array $options = array()): string + { + $options = array_replace_recursive(self::DEFAULT_OPTIONS, $options); + $code = $this->initialize($options); + foreach ($definition->getPlaces() as $place) { + $placeEscaped = $this->escape($place); + $code[] = + "state $placeEscaped". + ($definition->getInitialPlace() === $place ? ' '.self::INITIAL : ''). + ($marking && $marking->has($place) ? ' '.self::MARKED : ''); + } + if ($this->isWorkflowTransitionType()) { + foreach ($definition->getTransitions() as $transition) { + $transitionEscaped = $this->escape($transition->getName()); + $code[] = "agent $transitionEscaped"; + } + } + foreach ($definition->getTransitions() as $transition) { + $transitionEscaped = $this->escape($transition->getName()); + foreach ($transition->getFroms() as $from) { + $fromEscaped = $this->escape($from); + foreach ($transition->getTos() as $to) { + $toEscaped = $this->escape($to); + if ($this->isWorkflowTransitionType()) { + $lines = array( + "$fromEscaped --> $transitionEscaped", + "$transitionEscaped --> $toEscaped", + ); + foreach ($lines as $line) { + if (!in_array($line, $code)) { + $code[] = $line; + } + } + } else { + $code[] = "$fromEscaped --> $toEscaped: $transitionEscaped"; + } + } + } + } + + return $this->startPuml($options).$this->getLines($code).$this->endPuml($options); + } + + private function isWorkflowTransitionType(): bool + { + return self::WORKFLOW_TRANSITION === $this->transitionType; + } + + private function startPuml(array $options): string + { + $start = '@startuml'.PHP_EOL; + $start .= 'allow_mixing'.PHP_EOL; + + return $start; + } + + private function endPuml(array $options): string + { + return PHP_EOL.'@enduml'; + } + + private function getLines(array $code): string + { + return implode(PHP_EOL, $code); + } + + private function initialize(array $options): array + { + $code = array(); + if (isset($options['title'])) { + $code[] = "title {$options['title']}"; + } + if (isset($options['name'])) { + $code[] = "title {$options['name']}"; + } + if (isset($options['skinparams']) && is_array($options['skinparams'])) { + foreach ($options['skinparams'] as $skinparamKey => $skinparamValue) { + if (!$this->isWorkflowTransitionType() && 'agent' === $skinparamKey) { + continue; + } + if (!is_array($skinparamValue)) { + $code[] = "skinparam {$skinparamKey} $skinparamValue"; + continue; + } + $code[] = "skinparam {$skinparamKey} {"; + foreach ($skinparamValue as $key => $value) { + $code[] = " {$key} $value"; + } + $code[] = '}'; + } + } + + return $code; + } + + private function escape(string $string): string + { + // It's not possible to escape property double quote, so let's remove it + return '"'.str_replace('"', '', $string).'"'; + } +} diff --git a/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php b/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php index 9f68e1daf72f3..e73517581aec0 100644 --- a/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php +++ b/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php @@ -71,7 +71,7 @@ protected function addEdges(array $edges) foreach ($edges as $id => $edges) { foreach ($edges as $edge) { - $code .= sprintf(" place_%s -> place_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], 'solid'); + $code .= sprintf(" place_%s -> place_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $this->escape($edge['name']), 'solid'); } } diff --git a/src/Symfony/Component/Workflow/Event/Event.php b/src/Symfony/Component/Workflow/Event/Event.php index a268a373c08c3..19c78d47082d3 100644 --- a/src/Symfony/Component/Workflow/Event/Event.php +++ b/src/Symfony/Component/Workflow/Event/Event.php @@ -32,7 +32,7 @@ class Event extends BaseEvent * @param Transition $transition * @param string $workflowName */ - public function __construct($subject, Marking $marking, Transition $transition, $workflowName = 'unnamed') + public function __construct($subject, Marking $marking, Transition $transition, string $workflowName = 'unnamed') { $this->subject = $subject; $this->marking = $marking; diff --git a/src/Symfony/Component/Workflow/EventListener/GuardListener.php b/src/Symfony/Component/Workflow/EventListener/GuardListener.php index 893f304e47834..a4114e68e1854 100644 --- a/src/Symfony/Component/Workflow/EventListener/GuardListener.php +++ b/src/Symfony/Component/Workflow/EventListener/GuardListener.php @@ -32,7 +32,7 @@ class GuardListener private $roleHierarchy; private $validator; - public function __construct($configuration, ExpressionLanguage $expressionLanguage, TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authenticationChecker, AuthenticationTrustResolverInterface $trustResolver, RoleHierarchyInterface $roleHierarchy = null, ValidatorInterface $validator = null) + public function __construct(array $configuration, ExpressionLanguage $expressionLanguage, TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authenticationChecker, AuthenticationTrustResolverInterface $trustResolver, RoleHierarchyInterface $roleHierarchy = null, ValidatorInterface $validator = null) { $this->configuration = $configuration; $this->expressionLanguage = $expressionLanguage; @@ -55,7 +55,7 @@ public function onTransition(GuardEvent $event, $eventName) } // code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter - private function getVariables(GuardEvent $event) + private function getVariables(GuardEvent $event): array { $token = $this->tokenStorage->getToken(); diff --git a/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php index 6d2d0884893dc..cc3acb48c2f13 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php +++ b/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php @@ -29,11 +29,7 @@ class MultipleStateMarkingStore implements MarkingStoreInterface private $property; private $propertyAccessor; - /** - * @param string $property - * @param PropertyAccessorInterface|null $propertyAccessor - */ - public function __construct($property = 'marking', PropertyAccessorInterface $propertyAccessor = null) + public function __construct(string $property = 'marking', PropertyAccessorInterface $propertyAccessor = null) { $this->property = $property; $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); diff --git a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php index d6afc6aeeb764..1da179db64b37 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php +++ b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php @@ -28,11 +28,7 @@ class SingleStateMarkingStore implements MarkingStoreInterface private $property; private $propertyAccessor; - /** - * @param string $property - * @param PropertyAccessorInterface|null $propertyAccessor - */ - public function __construct($property = 'marking', PropertyAccessorInterface $propertyAccessor = null) + public function __construct(string $property = 'marking', PropertyAccessorInterface $propertyAccessor = null) { $this->property = $property; $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); diff --git a/src/Symfony/Component/Workflow/Registry.php b/src/Symfony/Component/Workflow/Registry.php index 4bc806d200ba0..78757d522811a 100644 --- a/src/Symfony/Component/Workflow/Registry.php +++ b/src/Symfony/Component/Workflow/Registry.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Workflow; use Symfony\Component\Workflow\Exception\InvalidArgumentException; -use Symfony\Component\Workflow\SupportStrategy\ClassInstanceSupportStrategy; use Symfony\Component\Workflow\SupportStrategy\SupportStrategyInterface; +use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface; /** * @author Fabien Potencier @@ -24,17 +24,19 @@ class Registry private $workflows = array(); /** - * @param Workflow $workflow - * @param string|SupportStrategyInterface $supportStrategy + * @param Workflow $workflow + * @param SupportStrategyInterface $supportStrategy + * + * @deprecated since Symfony 4.1, to be removed in 5.0. Use addWorkflow() instead. */ public function add(Workflow $workflow, $supportStrategy) { - if (!$supportStrategy instanceof SupportStrategyInterface) { - @trigger_error('Support of class name string was deprecated after version 3.2 and won\'t work anymore in 4.0.', E_USER_DEPRECATED); - - $supportStrategy = new ClassInstanceSupportStrategy($supportStrategy); - } + @trigger_error(sprintf('%s is deprecated since Symfony 4.1. Use addWorkflow() instead.', __METHOD__), E_USER_DEPRECATED); + $this->workflows[] = array($workflow, $supportStrategy); + } + public function addWorkflow(WorkflowInterface $workflow, WorkflowSupportStrategyInterface $supportStrategy) + { $this->workflows[] = array($workflow, $supportStrategy); } @@ -64,7 +66,7 @@ public function get($subject, $workflowName = null) return $matched; } - private function supports(Workflow $workflow, $supportStrategy, $subject, $workflowName) + private function supports(WorkflowInterface $workflow, $supportStrategy, $subject, $workflowName): bool { if (null !== $workflowName && $workflowName !== $workflow->getName()) { return false; diff --git a/src/Symfony/Component/Workflow/StateMachine.php b/src/Symfony/Component/Workflow/StateMachine.php index 00cfdac7d493a..6adf1f4dcbddb 100644 --- a/src/Symfony/Component/Workflow/StateMachine.php +++ b/src/Symfony/Component/Workflow/StateMachine.php @@ -11,7 +11,7 @@ */ class StateMachine extends Workflow { - public function __construct(Definition $definition, MarkingStoreInterface $markingStore = null, EventDispatcherInterface $dispatcher = null, $name = 'unnamed') + public function __construct(Definition $definition, MarkingStoreInterface $markingStore = null, EventDispatcherInterface $dispatcher = null, string $name = 'unnamed') { parent::__construct($definition, $markingStore ?: new SingleStateMarkingStore(), $dispatcher, $name); } diff --git a/src/Symfony/Component/Workflow/SupportStrategy/ClassInstanceSupportStrategy.php b/src/Symfony/Component/Workflow/SupportStrategy/ClassInstanceSupportStrategy.php index 8828946dbf543..8b206b0f7cb6b 100644 --- a/src/Symfony/Component/Workflow/SupportStrategy/ClassInstanceSupportStrategy.php +++ b/src/Symfony/Component/Workflow/SupportStrategy/ClassInstanceSupportStrategy.php @@ -1,20 +1,30 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Workflow\SupportStrategy; +@trigger_error(sprintf('"%s" is deprecated since Symfony 4.1. Use "%s" instead.', ClassInstanceSupportStrategy::class, InstanceOfSupportStrategy::class), E_USER_DEPRECATED); + use Symfony\Component\Workflow\Workflow; /** * @author Andreas Kleemann + * + * @deprecated since Symfony 4.1, to be removed in 5.0. Use InstanceOfSupportStrategy instead */ final class ClassInstanceSupportStrategy implements SupportStrategyInterface { private $className; - /** - * @param string $className a FQCN - */ - public function __construct($className) + public function __construct(string $className) { $this->className = $className; } diff --git a/src/Symfony/Component/Workflow/SupportStrategy/InstanceOfSupportStrategy.php b/src/Symfony/Component/Workflow/SupportStrategy/InstanceOfSupportStrategy.php new file mode 100644 index 0000000000000..079bf27cbd091 --- /dev/null +++ b/src/Symfony/Component/Workflow/SupportStrategy/InstanceOfSupportStrategy.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\SupportStrategy; + +use Symfony\Component\Workflow\WorkflowInterface; + +/** + * @author Andreas Kleemann + * @author Amrouche Hamza + */ +final class InstanceOfSupportStrategy implements WorkflowSupportStrategyInterface +{ + private $className; + + public function __construct(string $className) + { + $this->className = $className; + } + + /** + * {@inheritdoc} + */ + public function supports(WorkflowInterface $workflow, $subject): bool + { + return $subject instanceof $this->className; + } + + public function getClassName(): string + { + return $this->className; + } +} diff --git a/src/Symfony/Component/Workflow/SupportStrategy/SupportStrategyInterface.php b/src/Symfony/Component/Workflow/SupportStrategy/SupportStrategyInterface.php index 097c6c4d9fe76..68a75cb063dd7 100644 --- a/src/Symfony/Component/Workflow/SupportStrategy/SupportStrategyInterface.php +++ b/src/Symfony/Component/Workflow/SupportStrategy/SupportStrategyInterface.php @@ -15,6 +15,8 @@ /** * @author Andreas Kleemann + * + * @deprecated since Symfony 4.1, to be removed in 5.0. Use WorkflowSupportStrategyInterface instead */ interface SupportStrategyInterface { diff --git a/src/Symfony/Component/Workflow/SupportStrategy/WorkflowSupportStrategyInterface.php b/src/Symfony/Component/Workflow/SupportStrategy/WorkflowSupportStrategyInterface.php new file mode 100644 index 0000000000000..715c317636626 --- /dev/null +++ b/src/Symfony/Component/Workflow/SupportStrategy/WorkflowSupportStrategyInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\SupportStrategy; + +use Symfony\Component\Workflow\WorkflowInterface; + +/** + * @author Amrouche Hamza + */ +interface WorkflowSupportStrategyInterface +{ + public function supports(WorkflowInterface $workflow, $subject): bool; +} diff --git a/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php b/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php index 20eb1c8feeed4..1939fb5713963 100644 --- a/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php +++ b/src/Symfony/Component/Workflow/Tests/DefinitionBuilderTest.php @@ -8,14 +8,6 @@ class DefinitionBuilderTest extends TestCase { - /** - * @expectedException \Symfony\Component\Workflow\Exception\InvalidArgumentException - */ - public function testAddPlaceInvalidName() - { - $builder = new DefinitionBuilder(array('a"', 'b')); - } - public function testSetInitialPlace() { $builder = new DefinitionBuilder(array('a', 'b')); diff --git a/src/Symfony/Component/Workflow/Tests/DefinitionTest.php b/src/Symfony/Component/Workflow/Tests/DefinitionTest.php index 8eb1b6e4cf0fc..92e517df2f573 100644 --- a/src/Symfony/Component/Workflow/Tests/DefinitionTest.php +++ b/src/Symfony/Component/Workflow/Tests/DefinitionTest.php @@ -18,15 +18,6 @@ public function testAddPlaces() $this->assertEquals('a', $definition->getInitialPlace()); } - /** - * @expectedException \Symfony\Component\Workflow\Exception\InvalidArgumentException - */ - public function testAddPlacesInvalidArgument() - { - $places = array('a"', 'e"'); - $definition = new Definition($places, array()); - } - public function testSetInitialPlace() { $places = range('a', 'e'); diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php index 067cb9d41534a..203f33f6dcf35 100644 --- a/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php +++ b/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php @@ -66,33 +66,33 @@ public function createComplexWorkflowDefinitionDumpWithMarking() node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; - place_a [label="a", shape=circle, style="filled"]; - place_b [label="b", shape=circle, color="#FF0000", shape="doublecircle"]; - place_c [label="c", shape=circle]; - place_d [label="d", shape=circle]; - place_e [label="e", shape=circle]; - place_f [label="f", shape=circle]; - place_g [label="g", shape=circle]; - transition_t1 [label="t1", shape=box, shape="box", regular="1"]; - transition_t2 [label="t2", shape=box, shape="box", regular="1"]; - transition_t3 [label="t3", shape=box, shape="box", regular="1"]; - transition_t4 [label="t4", shape=box, shape="box", regular="1"]; - transition_t5 [label="t5", shape=box, shape="box", regular="1"]; - transition_t6 [label="t6", shape=box, shape="box", regular="1"]; - place_a -> transition_t1 [style="solid"]; - transition_t1 -> place_b [style="solid"]; - transition_t1 -> place_c [style="solid"]; - place_b -> transition_t2 [style="solid"]; - place_c -> transition_t2 [style="solid"]; - transition_t2 -> place_d [style="solid"]; - place_d -> transition_t3 [style="solid"]; - transition_t3 -> place_e [style="solid"]; - place_d -> transition_t4 [style="solid"]; - transition_t4 -> place_f [style="solid"]; - place_e -> transition_t5 [style="solid"]; - transition_t5 -> place_g [style="solid"]; - place_f -> transition_t6 [style="solid"]; - transition_t6 -> place_g [style="solid"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 [label="a", shape=circle, style="filled"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="b", shape=circle, color="#FF0000", shape="doublecircle"]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="c", shape=circle]; + place_3c363836cf4e16666669a25da280a1865c2d2874 [label="d", shape=circle]; + place_58e6b3a414a1e090dfc6029add0f3555ccba127f [label="e", shape=circle]; + place_4a0a19218e082a343a1b17e5333409af9d98f0f5 [label="f", shape=circle]; + place_54fd1711209fb1c0781092374132c66e79e2241b [label="g", shape=circle]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f [label="t1", shape=box, shape="box", regular="1"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [label="t2", shape=box, shape="box", regular="1"]; + transition_4358694eeb098c6708ae914a10562ce722bbbc34 [label="t3", shape=box, shape="box", regular="1"]; + transition_a9dfb15be45a5f3128784c80c733f2cdee2f756a [label="t4", shape=box, shape="box", regular="1"]; + transition_bf55e75fa263cbbc2529db49da43cb7f1d370b88 [label="t5", shape=box, shape="box", regular="1"]; + transition_e92a96c0e3a20d87ace74ab7871931a8f9f25943 [label="t6", shape=box, shape="box", regular="1"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 -> transition_e5353879bd69bfddcb465dad176ff52db8319d6f [style="solid"]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [style="solid"]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f -> place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [style="solid"]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 -> transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [style="solid"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d -> place_3c363836cf4e16666669a25da280a1865c2d2874 [style="solid"]; + place_3c363836cf4e16666669a25da280a1865c2d2874 -> transition_4358694eeb098c6708ae914a10562ce722bbbc34 [style="solid"]; + transition_4358694eeb098c6708ae914a10562ce722bbbc34 -> place_58e6b3a414a1e090dfc6029add0f3555ccba127f [style="solid"]; + place_3c363836cf4e16666669a25da280a1865c2d2874 -> transition_a9dfb15be45a5f3128784c80c733f2cdee2f756a [style="solid"]; + transition_a9dfb15be45a5f3128784c80c733f2cdee2f756a -> place_4a0a19218e082a343a1b17e5333409af9d98f0f5 [style="solid"]; + place_58e6b3a414a1e090dfc6029add0f3555ccba127f -> transition_bf55e75fa263cbbc2529db49da43cb7f1d370b88 [style="solid"]; + transition_bf55e75fa263cbbc2529db49da43cb7f1d370b88 -> place_54fd1711209fb1c0781092374132c66e79e2241b [style="solid"]; + place_4a0a19218e082a343a1b17e5333409af9d98f0f5 -> transition_e92a96c0e3a20d87ace74ab7871931a8f9f25943 [style="solid"]; + transition_e92a96c0e3a20d87ace74ab7871931a8f9f25943 -> place_54fd1711209fb1c0781092374132c66e79e2241b [style="solid"]; } '; } @@ -104,15 +104,15 @@ public function createSimpleWorkflowDumpWithMarking() node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; - place_a [label="a", shape=circle, style="filled"]; - place_b [label="b", shape=circle]; - place_c [label="c", shape=circle, color="#FF0000", shape="doublecircle"]; - transition_t1 [label="t1", shape=box, shape="box", regular="1"]; - transition_t2 [label="t2", shape=box, shape="box", regular="1"]; - place_a -> transition_t1 [style="solid"]; - transition_t1 -> place_b [style="solid"]; - place_b -> transition_t2 [style="solid"]; - transition_t2 -> place_c [style="solid"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 [label="a", shape=circle, style="filled"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="b", shape=circle]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="c", shape=circle, color="#FF0000", shape="doublecircle"]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f [label="t1", shape=box, shape="box", regular="1"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [label="t2", shape=box, shape="box", regular="1"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 -> transition_e5353879bd69bfddcb465dad176ff52db8319d6f [style="solid"]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [style="solid"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d -> place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [style="solid"]; } '; } @@ -124,33 +124,33 @@ public function provideComplexWorkflowDumpWithoutMarking() node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; - place_a [label="a", shape=circle, style="filled"]; - place_b [label="b", shape=circle]; - place_c [label="c", shape=circle]; - place_d [label="d", shape=circle]; - place_e [label="e", shape=circle]; - place_f [label="f", shape=circle]; - place_g [label="g", shape=circle]; - transition_t1 [label="t1", shape=box, shape="box", regular="1"]; - transition_t2 [label="t2", shape=box, shape="box", regular="1"]; - transition_t3 [label="t3", shape=box, shape="box", regular="1"]; - transition_t4 [label="t4", shape=box, shape="box", regular="1"]; - transition_t5 [label="t5", shape=box, shape="box", regular="1"]; - transition_t6 [label="t6", shape=box, shape="box", regular="1"]; - place_a -> transition_t1 [style="solid"]; - transition_t1 -> place_b [style="solid"]; - transition_t1 -> place_c [style="solid"]; - place_b -> transition_t2 [style="solid"]; - place_c -> transition_t2 [style="solid"]; - transition_t2 -> place_d [style="solid"]; - place_d -> transition_t3 [style="solid"]; - transition_t3 -> place_e [style="solid"]; - place_d -> transition_t4 [style="solid"]; - transition_t4 -> place_f [style="solid"]; - place_e -> transition_t5 [style="solid"]; - transition_t5 -> place_g [style="solid"]; - place_f -> transition_t6 [style="solid"]; - transition_t6 -> place_g [style="solid"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 [label="a", shape=circle, style="filled"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="b", shape=circle]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="c", shape=circle]; + place_3c363836cf4e16666669a25da280a1865c2d2874 [label="d", shape=circle]; + place_58e6b3a414a1e090dfc6029add0f3555ccba127f [label="e", shape=circle]; + place_4a0a19218e082a343a1b17e5333409af9d98f0f5 [label="f", shape=circle]; + place_54fd1711209fb1c0781092374132c66e79e2241b [label="g", shape=circle]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f [label="t1", shape=box, shape="box", regular="1"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [label="t2", shape=box, shape="box", regular="1"]; + transition_4358694eeb098c6708ae914a10562ce722bbbc34 [label="t3", shape=box, shape="box", regular="1"]; + transition_a9dfb15be45a5f3128784c80c733f2cdee2f756a [label="t4", shape=box, shape="box", regular="1"]; + transition_bf55e75fa263cbbc2529db49da43cb7f1d370b88 [label="t5", shape=box, shape="box", regular="1"]; + transition_e92a96c0e3a20d87ace74ab7871931a8f9f25943 [label="t6", shape=box, shape="box", regular="1"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 -> transition_e5353879bd69bfddcb465dad176ff52db8319d6f [style="solid"]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [style="solid"]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f -> place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [style="solid"]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 -> transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [style="solid"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d -> place_3c363836cf4e16666669a25da280a1865c2d2874 [style="solid"]; + place_3c363836cf4e16666669a25da280a1865c2d2874 -> transition_4358694eeb098c6708ae914a10562ce722bbbc34 [style="solid"]; + transition_4358694eeb098c6708ae914a10562ce722bbbc34 -> place_58e6b3a414a1e090dfc6029add0f3555ccba127f [style="solid"]; + place_3c363836cf4e16666669a25da280a1865c2d2874 -> transition_a9dfb15be45a5f3128784c80c733f2cdee2f756a [style="solid"]; + transition_a9dfb15be45a5f3128784c80c733f2cdee2f756a -> place_4a0a19218e082a343a1b17e5333409af9d98f0f5 [style="solid"]; + place_58e6b3a414a1e090dfc6029add0f3555ccba127f -> transition_bf55e75fa263cbbc2529db49da43cb7f1d370b88 [style="solid"]; + transition_bf55e75fa263cbbc2529db49da43cb7f1d370b88 -> place_54fd1711209fb1c0781092374132c66e79e2241b [style="solid"]; + place_4a0a19218e082a343a1b17e5333409af9d98f0f5 -> transition_e92a96c0e3a20d87ace74ab7871931a8f9f25943 [style="solid"]; + transition_e92a96c0e3a20d87ace74ab7871931a8f9f25943 -> place_54fd1711209fb1c0781092374132c66e79e2241b [style="solid"]; } '; } @@ -162,15 +162,15 @@ public function provideSimpleWorkflowDumpWithoutMarking() node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; - place_a [label="a", shape=circle, style="filled"]; - place_b [label="b", shape=circle]; - place_c [label="c", shape=circle]; - transition_t1 [label="t1", shape=box, shape="box", regular="1"]; - transition_t2 [label="t2", shape=box, shape="box", regular="1"]; - place_a -> transition_t1 [style="solid"]; - transition_t1 -> place_b [style="solid"]; - place_b -> transition_t2 [style="solid"]; - transition_t2 -> place_c [style="solid"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 [label="a", shape=circle, style="filled"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="b", shape=circle]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="c", shape=circle]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f [label="t1", shape=box, shape="box", regular="1"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [label="t2", shape=box, shape="box", regular="1"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 -> transition_e5353879bd69bfddcb465dad176ff52db8319d6f [style="solid"]; + transition_e5353879bd69bfddcb465dad176ff52db8319d6f -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> transition_2a5bd02710e975a7fbb92da876655950fbd5e70d [style="solid"]; + transition_2a5bd02710e975a7fbb92da876655950fbd5e70d -> place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [style="solid"]; } '; } diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/PlantUmlDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/PlantUmlDumperTest.php new file mode 100644 index 0000000000000..8469415255126 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/Dumper/PlantUmlDumperTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Workflow\Dumper\PlantUmlDumper; +use Symfony\Component\Workflow\Marking; +use Symfony\Component\Workflow\Tests\WorkflowBuilderTrait; + +class PlantUmlDumperTest extends TestCase +{ + use WorkflowBuilderTrait; + + /** + * @dataProvider provideWorkflowDefinitionWithoutMarking + */ + public function testDumpWorkflowWithoutMarking($definition, $marking, $expectedFileName, $title) + { + $dumper = new PlantUmlDumper(PlantUmlDumper::WORKFLOW_TRANSITION); + $dump = $dumper->dump($definition, $marking, array('title' => $title)); + // handle windows, and avoid to create more fixtures + $dump = str_replace(PHP_EOL, "\n", $dump.PHP_EOL); + $file = $this->getFixturePath($expectedFileName, PlantUmlDumper::WORKFLOW_TRANSITION); + $this->assertStringEqualsFile($file, $dump); + } + + public function provideWorkflowDefinitionWithoutMarking() + { + yield array($this->createSimpleWorkflowDefinition(), null, 'simple-workflow-nomarking', 'SimpleDiagram'); + yield array($this->createComplexWorkflowDefinition(), null, 'complex-workflow-nomarking', 'ComplexDiagram'); + $marking = new Marking(array('b' => 1)); + yield array($this->createSimpleWorkflowDefinition(), $marking, 'simple-workflow-marking', 'SimpleDiagram'); + $marking = new Marking(array('c' => 1, 'e' => 1)); + yield array($this->createComplexWorkflowDefinition(), $marking, 'complex-workflow-marking', 'ComplexDiagram'); + } + + /** + * @dataProvider provideStateMachineDefinitionWithoutMarking + */ + public function testDumpStateMachineWithoutMarking($definition, $marking, $expectedFileName, $title) + { + $dumper = new PlantUmlDumper(PlantUmlDumper::STATEMACHINE_TRANSITION); + $dump = $dumper->dump($definition, $marking, array('title' => $title)); + // handle windows, and avoid to create more fixtures + $dump = str_replace(PHP_EOL, "\n", $dump.PHP_EOL); + $file = $this->getFixturePath($expectedFileName, PlantUmlDumper::STATEMACHINE_TRANSITION); + $this->assertStringEqualsFile($file, $dump); + } + + public function provideStateMachineDefinitionWithoutMarking() + { + yield array($this->createComplexStateMachineDefinition(), null, 'complex-state-machine-nomarking', 'SimpleDiagram'); + $marking = new Marking(array('c' => 1, 'e' => 1)); + yield array($this->createComplexStateMachineDefinition(), $marking, 'complex-state-machine-marking', 'SimpleDiagram'); + } + + private function getFixturePath($name, $transitionType) + { + return __DIR__.'/../fixtures/puml/'.$transitionType.'/'.$name.'.puml'; + } +} diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php index 7277b1c65b838..29899109a06c4 100644 --- a/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php +++ b/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php @@ -30,14 +30,14 @@ public function testDumpWithoutMarking() node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; - place_a [label="a", shape=circle, style="filled"]; - place_b [label="b", shape=circle]; - place_c [label="c", shape=circle]; - place_d [label="d", shape=circle]; - place_a -> place_b [label="t1" style="solid"]; - place_d -> place_b [label="t1" style="solid"]; - place_b -> place_c [label="t2" style="solid"]; - place_b -> place_d [label="t3" style="solid"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 [label="a", shape=circle, style="filled"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="b", shape=circle]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="c", shape=circle]; + place_3c363836cf4e16666669a25da280a1865c2d2874 [label="d", shape=circle]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="t1" style="solid"]; + place_3c363836cf4e16666669a25da280a1865c2d2874 -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="t1" style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="t2" style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> place_3c363836cf4e16666669a25da280a1865c2d2874 [label="t3" style="solid"]; } EOGRAPH; @@ -56,14 +56,14 @@ public function testDumpWithMarking() node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; - place_a [label="a", shape=circle, style="filled"]; - place_b [label="b", shape=circle, color="#FF0000", shape="doublecircle"]; - place_c [label="c", shape=circle]; - place_d [label="d", shape=circle]; - place_a -> place_b [label="t1" style="solid"]; - place_d -> place_b [label="t1" style="solid"]; - place_b -> place_c [label="t2" style="solid"]; - place_b -> place_d [label="t3" style="solid"]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 [label="a", shape=circle, style="filled"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="b", shape=circle, color="#FF0000", shape="doublecircle"]; + place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="c", shape=circle]; + place_3c363836cf4e16666669a25da280a1865c2d2874 [label="d", shape=circle]; + place_86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="t1" style="solid"]; + place_3c363836cf4e16666669a25da280a1865c2d2874 -> place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 [label="t1" style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> place_84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 [label="t2" style="solid"]; + place_e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 -> place_3c363836cf4e16666669a25da280a1865c2d2874 [label="t3" style="solid"]; } EOGRAPH; diff --git a/src/Symfony/Component/Workflow/Tests/RegistryTest.php b/src/Symfony/Component/Workflow/Tests/RegistryTest.php index 262e7cfe59ddf..c122b5e2ed712 100644 --- a/src/Symfony/Component/Workflow/Tests/RegistryTest.php +++ b/src/Symfony/Component/Workflow/Tests/RegistryTest.php @@ -8,6 +8,7 @@ use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface; use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\SupportStrategy\SupportStrategyInterface; +use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface; use Symfony\Component\Workflow\Workflow; class RegistryTest extends TestCase @@ -18,9 +19,9 @@ protected function setUp() { $this->registry = new Registry(); - $this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), $this->createSupportStrategy(Subject1::class)); - $this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow2'), $this->createSupportStrategy(Subject2::class)); - $this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow3'), $this->createSupportStrategy(Subject2::class)); + $this->registry->addWorkflow(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), $this->createWorkflowSupportStrategy(Subject1::class)); + $this->registry->addWorkflow(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow2'), $this->createWorkflowSupportStrategy(Subject2::class)); + $this->registry->addWorkflow(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow3'), $this->createWorkflowSupportStrategy(Subject2::class)); } protected function tearDown() @@ -28,6 +29,21 @@ protected function tearDown() $this->registry = null; } + /** + * @group legacy + * @expectedDeprecation Symfony\Component\Workflow\Registry::add is deprecated since Symfony 4.1. Use addWorkflow() instead. + */ + public function testAddIsDeprecated() + { + $registry = new Registry(); + + $registry->add($w = new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), $this->createSupportStrategy(Subject1::class)); + + $workflow = $registry->get(new Subject1()); + $this->assertInstanceOf(Workflow::class, $workflow); + $this->assertSame('workflow1', $workflow->getName()); + } + public function testGetWithSuccess() { $workflow = $this->registry->get(new Subject1()); @@ -68,29 +84,23 @@ public function testGetWithNoMatch() /** * @group legacy */ - public function testGetWithSuccessLegacyStrategy() + private function createSupportStrategy($supportedClassName) { - $registry = new Registry(); - - $registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), Subject1::class); - $registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow2'), Subject2::class); - - $workflow = $registry->get(new Subject1()); - $this->assertInstanceOf(Workflow::class, $workflow); - $this->assertSame('workflow1', $workflow->getName()); - - $workflow = $registry->get(new Subject1(), 'workflow1'); - $this->assertInstanceOf(Workflow::class, $workflow); - $this->assertSame('workflow1', $workflow->getName()); + $strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock(); + $strategy->expects($this->any())->method('supports') + ->will($this->returnCallback(function ($workflow, $subject) use ($supportedClassName) { + return $subject instanceof $supportedClassName; + })); - $workflow = $registry->get(new Subject2(), 'workflow2'); - $this->assertInstanceOf(Workflow::class, $workflow); - $this->assertSame('workflow2', $workflow->getName()); + return $strategy; } - private function createSupportStrategy($supportedClassName) + /** + * @group legacy + */ + private function createWorkflowSupportStrategy($supportedClassName) { - $strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock(); + $strategy = $this->getMockBuilder(WorkflowSupportStrategyInterface::class)->getMock(); $strategy->expects($this->any())->method('supports') ->will($this->returnCallback(function ($workflow, $subject) use ($supportedClassName) { return $subject instanceof $supportedClassName; diff --git a/src/Symfony/Component/Workflow/Tests/SupportStrategy/ClassInstanceSupportStrategyTest.php b/src/Symfony/Component/Workflow/Tests/SupportStrategy/ClassInstanceSupportStrategyTest.php index 29d3d150a67de..e79a8a1f3f31b 100644 --- a/src/Symfony/Component/Workflow/Tests/SupportStrategy/ClassInstanceSupportStrategyTest.php +++ b/src/Symfony/Component/Workflow/Tests/SupportStrategy/ClassInstanceSupportStrategyTest.php @@ -6,8 +6,14 @@ use Symfony\Component\Workflow\SupportStrategy\ClassInstanceSupportStrategy; use Symfony\Component\Workflow\Workflow; +/** + * @group legacy + */ class ClassInstanceSupportStrategyTest extends TestCase { + /** + * @expectedDeprecation "Symfony\Component\Workflow\SupportStrategy\ClassInstanceSupportStrategy" is deprecated since Symfony 4.1. Use "Symfony\Component\Workflow\SupportStrategy\InstanceOfSupportStrategy" instead. + */ public function testSupportsIfClassInstance() { $strategy = new ClassInstanceSupportStrategy('Symfony\Component\Workflow\Tests\SupportStrategy\Subject1'); @@ -29,10 +35,3 @@ private function createWorkflow() ->getMock(); } } - -class Subject1 -{ -} -class Subject2 -{ -} diff --git a/src/Symfony/Component/Workflow/Tests/SupportStrategy/InstanceOfSupportStrategyTest.php b/src/Symfony/Component/Workflow/Tests/SupportStrategy/InstanceOfSupportStrategyTest.php new file mode 100644 index 0000000000000..a541da0d285a2 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/SupportStrategy/InstanceOfSupportStrategyTest.php @@ -0,0 +1,38 @@ +assertTrue($strategy->supports($this->createWorkflow(), new Subject1())); + } + + public function testSupportsIfNotClassInstance() + { + $strategy = new InstanceOfSupportStrategy(Subject2::class); + + $this->assertFalse($strategy->supports($this->createWorkflow(), new Subject1())); + } + + private function createWorkflow() + { + return $this->getMockBuilder(Workflow::class) + ->disableOriginalConstructor() + ->getMock(); + } +} + +class Subject1 +{ +} +class Subject2 +{ +} diff --git a/src/Symfony/Component/Workflow/Tests/TransitionTest.php b/src/Symfony/Component/Workflow/Tests/TransitionTest.php index 74bab16f71166..cf2d28162073d 100644 --- a/src/Symfony/Component/Workflow/Tests/TransitionTest.php +++ b/src/Symfony/Component/Workflow/Tests/TransitionTest.php @@ -7,15 +7,6 @@ class TransitionTest extends TestCase { - /** - * @expectedException \Symfony\Component\Workflow\Exception\InvalidArgumentException - * @expectedExceptionMessage The transition "foo.bar" contains invalid characters. - */ - public function testValidateName() - { - $transition = new Transition('foo.bar', 'a', 'b'); - } - public function testConstructor() { $transition = new Transition('name', 'a', 'b'); diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-state-machine-marking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-state-machine-marking.puml new file mode 100644 index 0000000000000..699548ef160c0 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-state-machine-marking.puml @@ -0,0 +1,21 @@ +@startuml +allow_mixing +title SimpleDiagram +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam state { + BackgroundColor<> #87b741 + BackgroundColor<> #3887C6 + BorderColor #3887C6 + BorderColor<> Black + FontColor<> White +} +state "a" <> +state "b" +state "c" <> +state "d" +"a" --> "b": "t1" +"d" --> "b": "t1" +"b" --> "c": "t2" +"b" --> "d": "t3" +@enduml diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-state-machine-nomarking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-state-machine-nomarking.puml new file mode 100644 index 0000000000000..eb91152a46ecf --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-state-machine-nomarking.puml @@ -0,0 +1,21 @@ +@startuml +allow_mixing +title SimpleDiagram +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam state { + BackgroundColor<> #87b741 + BackgroundColor<> #3887C6 + BorderColor #3887C6 + BorderColor<> Black + FontColor<> White +} +state "a" <> +state "b" +state "c" +state "d" +"a" --> "b": "t1" +"d" --> "b": "t1" +"b" --> "c": "t2" +"b" --> "d": "t3" +@enduml diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking.puml new file mode 100644 index 0000000000000..0ae74a7c441d9 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking.puml @@ -0,0 +1,44 @@ +@startuml +allow_mixing +title ComplexDiagram +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam state { + BackgroundColor<> #87b741 + BackgroundColor<> #3887C6 + BorderColor #3887C6 + BorderColor<> Black + FontColor<> White +} +skinparam agent { + BackgroundColor #ffffff + BorderColor #3887C6 +} +state "a" <> +state "b" +state "c" <> +state "d" +state "e" <> +state "f" +state "g" +agent "t1" +agent "t2" +agent "t3" +agent "t4" +agent "t5" +agent "t6" +"a" --> "t1" +"t1" --> "b" +"t1" --> "c" +"b" --> "t2" +"t2" --> "d" +"c" --> "t2" +"d" --> "t3" +"t3" --> "e" +"d" --> "t4" +"t4" --> "f" +"e" --> "t5" +"t5" --> "g" +"f" --> "t6" +"t6" --> "g" +@enduml diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking.puml new file mode 100644 index 0000000000000..db3c8bf208d3e --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking.puml @@ -0,0 +1,44 @@ +@startuml +allow_mixing +title ComplexDiagram +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam state { + BackgroundColor<> #87b741 + BackgroundColor<> #3887C6 + BorderColor #3887C6 + BorderColor<> Black + FontColor<> White +} +skinparam agent { + BackgroundColor #ffffff + BorderColor #3887C6 +} +state "a" <> +state "b" +state "c" +state "d" +state "e" +state "f" +state "g" +agent "t1" +agent "t2" +agent "t3" +agent "t4" +agent "t5" +agent "t6" +"a" --> "t1" +"t1" --> "b" +"t1" --> "c" +"b" --> "t2" +"t2" --> "d" +"c" --> "t2" +"d" --> "t3" +"t3" --> "e" +"d" --> "t4" +"t4" --> "f" +"e" --> "t5" +"t5" --> "g" +"f" --> "t6" +"t6" --> "g" +@enduml diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking.puml new file mode 100644 index 0000000000000..f81c44c5c2ca2 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking.puml @@ -0,0 +1,26 @@ +@startuml +allow_mixing +title SimpleDiagram +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam state { + BackgroundColor<> #87b741 + BackgroundColor<> #3887C6 + BorderColor #3887C6 + BorderColor<> Black + FontColor<> White +} +skinparam agent { + BackgroundColor #ffffff + BorderColor #3887C6 +} +state "a" <> +state "b" <> +state "c" +agent "t1" +agent "t2" +"a" --> "t1" +"t1" --> "b" +"b" --> "t2" +"t2" --> "c" +@enduml diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking.puml new file mode 100644 index 0000000000000..c677c24f89e47 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking.puml @@ -0,0 +1,26 @@ +@startuml +allow_mixing +title SimpleDiagram +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam state { + BackgroundColor<> #87b741 + BackgroundColor<> #3887C6 + BorderColor #3887C6 + BorderColor<> Black + FontColor<> White +} +skinparam agent { + BackgroundColor #ffffff + BorderColor #3887C6 +} +state "a" <> +state "b" +state "c" +agent "t1" +agent "t2" +"a" --> "t1" +"t1" --> "b" +"b" --> "t2" +"t2" --> "c" +@enduml diff --git a/src/Symfony/Component/Workflow/Transition.php b/src/Symfony/Component/Workflow/Transition.php index f673517f32e6c..0516fa181a7f2 100644 --- a/src/Symfony/Component/Workflow/Transition.php +++ b/src/Symfony/Component/Workflow/Transition.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Workflow; -use Symfony\Component\Workflow\Exception\InvalidArgumentException; - /** * @author Fabien Potencier * @author Grégoire Pineau @@ -28,12 +26,8 @@ class Transition * @param string|string[] $froms * @param string|string[] $tos */ - public function __construct($name, $froms, $tos) + public function __construct(string $name, $froms, $tos) { - if (!preg_match('{^[\w_-]+$}', $name)) { - throw new InvalidArgumentException(sprintf('The transition "%s" contains invalid characters.', $name)); - } - $this->name = $name; $this->froms = (array) $froms; $this->tos = (array) $tos; diff --git a/src/Symfony/Component/Workflow/Validator/WorkflowValidator.php b/src/Symfony/Component/Workflow/Validator/WorkflowValidator.php index 27bd06f0d651b..e105224d221dd 100644 --- a/src/Symfony/Component/Workflow/Validator/WorkflowValidator.php +++ b/src/Symfony/Component/Workflow/Validator/WorkflowValidator.php @@ -21,10 +21,7 @@ class WorkflowValidator implements DefinitionValidatorInterface { private $singlePlace; - /** - * @param bool $singlePlace - */ - public function __construct($singlePlace = false) + public function __construct(bool $singlePlace = false) { $this->singlePlace = $singlePlace; } diff --git a/src/Symfony/Component/Workflow/Workflow.php b/src/Symfony/Component/Workflow/Workflow.php index d3d18f03746a7..ac52794c2dc8d 100644 --- a/src/Symfony/Component/Workflow/Workflow.php +++ b/src/Symfony/Component/Workflow/Workflow.php @@ -23,14 +23,14 @@ * @author Grégoire Pineau * @author Tobias Nyholm */ -class Workflow +class Workflow implements WorkflowInterface { private $definition; private $markingStore; private $dispatcher; private $name; - public function __construct(Definition $definition, MarkingStoreInterface $markingStore = null, EventDispatcherInterface $dispatcher = null, $name = 'unnamed') + public function __construct(Definition $definition, MarkingStoreInterface $markingStore = null, EventDispatcherInterface $dispatcher = null, string $name = 'unnamed') { $this->definition = $definition; $this->markingStore = $markingStore ?: new MultipleStateMarkingStore(); @@ -39,13 +39,7 @@ public function __construct(Definition $definition, MarkingStoreInterface $marki } /** - * Returns the object's Marking. - * - * @param object $subject A subject - * - * @return Marking The Marking - * - * @throws LogicException + * {@inheritdoc} */ public function getMarking($subject) { @@ -83,12 +77,7 @@ public function getMarking($subject) } /** - * Returns true if the transition is enabled. - * - * @param object $subject A subject - * @param string $transitionName A transition - * - * @return bool true if the transition is enabled + * {@inheritdoc} */ public function can($subject, $transitionName) { @@ -113,15 +102,7 @@ public function can($subject, $transitionName) } /** - * Fire a transition. - * - * @param object $subject A subject - * @param string $transitionName A transition - * - * @return Marking The new Marking - * - * @throws LogicException If the transition is not applicable - * @throws LogicException If the transition does not exist + * {@inheritdoc} */ public function apply($subject, $transitionName) { @@ -164,11 +145,7 @@ public function apply($subject, $transitionName) } /** - * Returns all enabled transitions. - * - * @param object $subject A subject - * - * @return Transition[] All enabled transitions + * {@inheritdoc} */ public function getEnabledTransitions($subject) { @@ -184,13 +161,16 @@ public function getEnabledTransitions($subject) return $enabled; } + /** + * {@inheritdoc} + */ public function getName() { return $this->name; } /** - * @return Definition + * {@inheritdoc} */ public function getDefinition() { @@ -198,7 +178,7 @@ public function getDefinition() } /** - * @return MarkingStoreInterface + * {@inheritdoc} */ public function getMarkingStore() { diff --git a/src/Symfony/Component/Workflow/WorkflowInterface.php b/src/Symfony/Component/Workflow/WorkflowInterface.php new file mode 100644 index 0000000000000..15ee8a4ec81af --- /dev/null +++ b/src/Symfony/Component/Workflow/WorkflowInterface.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow; + +use Symfony\Component\Workflow\Exception\LogicException; +use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface; + +/** + * @author Amrouche Hamza + */ +interface WorkflowInterface +{ + /** + * Returns the object's Marking. + * + * @param object $subject A subject + * + * @return Marking The Marking + * + * @throws LogicException + */ + public function getMarking($subject); + + /** + * Returns true if the transition is enabled. + * + * @param object $subject A subject + * @param string $transitionName A transition + * + * @return bool true if the transition is enabled + * + * @throws LogicException + */ + public function can($subject, $transitionName); + + /** + * Fire a transition. + * + * @param object $subject A subject + * @param string $transitionName A transition + * + * @return Marking The new Marking + * + * @throws LogicException If the transition is not applicable + * @throws LogicException If the transition does not exist + */ + public function apply($subject, $transitionName); + + /** + * Returns all enabled transitions. + * + * @param object $subject A subject + * + * @return Transition[] All enabled transitions + */ + public function getEnabledTransitions($subject); + + /** + * @return string + */ + public function getName(); + + /** + * @return Definition + */ + public function getDefinition(); + + /** + * @return MarkingStoreInterface + */ + public function getMarkingStore(); +} diff --git a/src/Symfony/Component/Workflow/composer.json b/src/Symfony/Component/Workflow/composer.json index 1ff206983b822..643f762e4a27c 100644 --- a/src/Symfony/Component/Workflow/composer.json +++ b/src/Symfony/Component/Workflow/composer.json @@ -20,16 +20,16 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/property-access": "~2.3|~3.0|~4.0" + "php": "^7.1.3", + "symfony/property-access": "~3.4|~4.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/dependency-injection": "~2.8|~3.0|~4.0", - "symfony/event-dispatcher": "~2.1|~3.0|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/security-core": "~2.8|~3.0|~4.0", - "symfony/validator": "~2.8|~3.4|~4.0" + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/security-core": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Workflow\\": "" } @@ -37,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } } diff --git a/src/Symfony/Component/Yaml/CHANGELOG.md b/src/Symfony/Component/Yaml/CHANGELOG.md index 1b07b239cd6e5..70d392da343ee 100644 --- a/src/Symfony/Component/Yaml/CHANGELOG.md +++ b/src/Symfony/Component/Yaml/CHANGELOG.md @@ -1,6 +1,36 @@ CHANGELOG ========= +4.0.0 +----- + + * The behavior of the non-specific tag `!` is changed and now forces + non-evaluating your values. + * complex mappings will throw a `ParseException` + * support for the comma as a group separator for floats has been dropped, use + the underscore instead + * support for the `!!php/object` tag has been dropped, use the `!php/object` + tag instead + * duplicate mapping keys throw a `ParseException` + * non-string mapping keys throw a `ParseException`, use the `Yaml::PARSE_KEYS_AS_STRINGS` + flag to cast them to strings + * `%` at the beginning of an unquoted string throw a `ParseException` + * mappings with a colon (`:`) that is not followed by a whitespace throw a + `ParseException` + * the `Dumper::setIndentation()` method has been removed + * being able to pass boolean options to the `Yaml::parse()`, `Yaml::dump()`, + `Parser::parse()`, and `Dumper::dump()` methods to configure the behavior of + the parser and dumper is no longer supported, pass bitmask flags instead + * the constructor arguments of the `Parser` class have been removed + * the `Inline` class is internal and no longer part of the BC promise + * removed support for the `!str` tag, use the `!!str` tag instead + * added support for tagged scalars. + + ```yml + Yaml::parse('!foo bar', Yaml::PARSE_CUSTOM_TAGS); + // returns TaggedValue('foo', 'bar'); + ``` + 3.4.0 ----- diff --git a/src/Symfony/Component/Yaml/Command/LintCommand.php b/src/Symfony/Component/Yaml/Command/LintCommand.php index 4f2dc637df03a..04ead2584b195 100644 --- a/src/Symfony/Component/Yaml/Command/LintCommand.php +++ b/src/Symfony/Component/Yaml/Command/LintCommand.php @@ -36,7 +36,7 @@ class LintCommand extends Command private $directoryIteratorProvider; private $isReadableProvider; - public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) + public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null) { parent::__construct($name); diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index 0513044d2fcd3..6681379816c0c 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -27,10 +27,7 @@ class Dumper */ protected $indentation; - /** - * @param int $indentation - */ - public function __construct($indentation = 4) + public function __construct(int $indentation = 4) { if ($indentation < 1) { throw new \InvalidArgumentException('The indentation must be greater than zero.'); @@ -39,20 +36,6 @@ public function __construct($indentation = 4) $this->indentation = $indentation; } - /** - * Sets the indentation. - * - * @param int $num The amount of spaces to use for indentation of nested nodes - * - * @deprecated since version 3.1, to be removed in 4.0. Pass the indentation to the constructor instead. - */ - public function setIndentation($num) - { - @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 3.1 and will be removed in 4.0. Pass the indentation to the constructor instead.', E_USER_DEPRECATED); - - $this->indentation = (int) $num; - } - /** * Dumps a PHP value to YAML. * @@ -63,26 +46,8 @@ public function setIndentation($num) * * @return string The YAML representation of the PHP value */ - public function dump($input, $inline = 0, $indent = 0, $flags = 0) + public function dump($input, int $inline = 0, int $indent = 0, int $flags = 0): string { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 5) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(4)) { - $flags |= Yaml::DUMP_OBJECT; - } - } - $output = ''; $prefix = $indent ? str_repeat(' ', $indent) : ''; $dumpObjectAsInlineMap = true; diff --git a/src/Symfony/Component/Yaml/Escaper.php b/src/Symfony/Component/Yaml/Escaper.php index 94bb3924b618b..66e65bc618c24 100644 --- a/src/Symfony/Component/Yaml/Escaper.php +++ b/src/Symfony/Component/Yaml/Escaper.php @@ -50,7 +50,7 @@ class Escaper * * @return bool True if the value would require double quotes */ - public static function requiresDoubleQuoting($value) + public static function requiresDoubleQuoting(string $value): bool { return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); } @@ -62,7 +62,7 @@ public static function requiresDoubleQuoting($value) * * @return string The quoted, escaped string */ - public static function escapeWithDoubleQuotes($value) + public static function escapeWithDoubleQuotes(string $value): string { return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); } @@ -74,7 +74,7 @@ public static function escapeWithDoubleQuotes($value) * * @return bool True if the value would require single quotes */ - public static function requiresSingleQuoting($value) + public static function requiresSingleQuoting(string $value): bool { // Determines if a PHP value is entirely composed of a value that would // require single quoting in YAML. @@ -94,7 +94,7 @@ public static function requiresSingleQuoting($value) * * @return string The quoted, escaped string */ - public static function escapeWithSingleQuotes($value) + public static function escapeWithSingleQuotes(string $value): string { return sprintf("'%s'", str_replace('\'', '\'\'', $value)); } diff --git a/src/Symfony/Component/Yaml/Exception/ParseException.php b/src/Symfony/Component/Yaml/Exception/ParseException.php index be79960b3e8a7..95efe68fbb8a1 100644 --- a/src/Symfony/Component/Yaml/Exception/ParseException.php +++ b/src/Symfony/Component/Yaml/Exception/ParseException.php @@ -30,7 +30,7 @@ class ParseException extends RuntimeException * @param string|null $parsedFile The file name where the error occurred * @param \Exception|null $previous The previous exception */ - public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) + public function __construct(string $message, int $parsedLine = -1, string $snippet = null, string $parsedFile = null, \Exception $previous = null) { $this->parsedFile = $parsedFile; $this->parsedLine = $parsedLine; diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 5e9f825037149..abab86903b8d3 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -63,40 +63,8 @@ public static function initialize($flags, $parsedLineNumber = null, $parsedFilen * * @throws ParseException */ - public static function parse($value, $flags = 0, $references = array()) + public static function parse(string $value = null, int $flags = 0, array $references = array()) { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3 && !is_array($references)) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if ($references) { - $flags |= Yaml::PARSE_OBJECT; - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= Yaml::PARSE_OBJECT_FOR_MAP; - } - } - - if (func_num_args() >= 5) { - $references = func_get_arg(4); - } else { - $references = array(); - } - } - self::initialize($flags); $value = trim($value); @@ -125,7 +93,7 @@ public static function parse($value, $flags = 0, $references = array()) $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references); } - if (null !== $tag) { + if (null !== $tag && '' !== $tag) { return new TaggedValue($tag, $result); } @@ -151,26 +119,8 @@ public static function parse($value, $flags = 0, $references = array()) * * @throws DumpException When trying to dump PHP resource */ - public static function dump($value, $flags = 0) + public static function dump($value, int $flags = 0): string { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= Yaml::DUMP_OBJECT; - } - } - switch (true) { case is_resource($value): if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { @@ -190,7 +140,13 @@ public static function dump($value, $flags = 0) } if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { - return self::dumpArray($value, $flags & ~Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + $output = array(); + + foreach ($value as $key => $val) { + $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); + } + + return sprintf('{ %s }', implode(', ', $output)); } if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { @@ -248,13 +204,11 @@ public static function dump($value, $flags = 0) /** * Check if given array is hash or just normal indexed array. * - * @internal - * * @param array|\ArrayObject|\stdClass $value The PHP array or array-like object to check * * @return bool true if value is hash array, false otherwise */ - public static function isHash($value) + public static function isHash($value): bool { if ($value instanceof \stdClass || $value instanceof \ArrayObject) { return true; @@ -279,7 +233,7 @@ public static function isHash($value) * * @return string The YAML string representing the PHP array */ - private static function dumpArray($value, $flags) + private static function dumpArray(array $value, int $flags): string { // array if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) { @@ -303,20 +257,11 @@ private static function dumpArray($value, $flags) /** * Parses a YAML scalar. * - * @param string $scalar - * @param int $flags - * @param string[] $delimiters - * @param int &$i - * @param bool $evaluate - * @param array $references - * - * @return string + * @return mixed * * @throws ParseException When malformed inline YAML string is parsed - * - * @internal */ - public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = array(), $legacyOmittedKeySupport = false) + public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array $references = array()) { if (in_array($scalar[$i], array('"', "'"))) { // quoted scalar @@ -338,22 +283,19 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { $output = substr($output, 0, $match[0][1]); } - } elseif (Parser::preg_match('/^(.'.($legacyOmittedKeySupport ? '+' : '*').'?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { + } elseif (Parser::preg_match('/^(.*?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { $output = $match[1]; $i += strlen($output); + $output = trim($output); } else { throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename); } // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) - if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) { + if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0] || '%' === $output[0])) { throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber + 1, $output, self::$parsedFilename); } - if ($output && '%' === $output[0]) { - @trigger_error(self::getDeprecationMessage(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output)), E_USER_DEPRECATED); - } - if ($evaluate) { $output = self::evaluateScalar($output, $flags, $references); } @@ -365,14 +307,9 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i /** * Parses a YAML quoted scalar. * - * @param string $scalar - * @param int &$i - * - * @return string - * * @throws ParseException When malformed inline YAML string is parsed */ - private static function parseQuotedScalar($scalar, &$i) + private static function parseQuotedScalar(string $scalar, int &$i): string { if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); @@ -395,16 +332,9 @@ private static function parseQuotedScalar($scalar, &$i) /** * Parses a YAML sequence. * - * @param string $sequence - * @param int $flags - * @param int &$i - * @param array $references - * - * @return array - * * @throws ParseException When malformed inline YAML string is parsed */ - private static function parseSequence($sequence, $flags, &$i = 0, $references = array()) + private static function parseSequence(string $sequence, int $flags, int &$i = 0, array $references = array()): array { $output = array(); $len = strlen($sequence); @@ -449,7 +379,7 @@ private static function parseSequence($sequence, $flags, &$i = 0, $references = --$i; } - if (null !== $tag) { + if (null !== $tag && '' !== $tag) { $value = new TaggedValue($tag, $value); } @@ -464,16 +394,11 @@ private static function parseSequence($sequence, $flags, &$i = 0, $references = /** * Parses a YAML mapping. * - * @param string $mapping - * @param int $flags - * @param int &$i - * @param array $references - * * @return array|\stdClass * * @throws ParseException When malformed inline YAML string is parsed */ - private static function parseMapping($mapping, $flags, &$i = 0, $references = array()) + private static function parseMapping(string $mapping, int $flags, int &$i = 0, array $references = array()) { $output = array(); $len = strlen($mapping); @@ -496,27 +421,28 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar } // key + $offsetBeforeKeyParsing = $i; $isKeyQuoted = in_array($mapping[$i], array('"', "'"), true); - $key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array(), true); + $key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array()); - if (':' !== $key && false === $i = strpos($mapping, ':', $i)) { - break; + if ($offsetBeforeKeyParsing === $i) { + throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping); } - if (':' === $key) { - @trigger_error(self::getDeprecationMessage('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0.'), E_USER_DEPRECATED); + if (false === $i = strpos($mapping, ':', $i)) { + break; } if (!$isKeyQuoted) { $evaluatedKey = self::evaluateScalar($key, $flags, $references); if ('' !== $key && $evaluatedKey !== $key && !is_string($evaluatedKey) && !is_int($evaluatedKey)) { - @trigger_error(self::getDeprecationMessage('Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.'), E_USER_DEPRECATED); + throw new ParseException('Implicit casting of incompatible mapping keys to strings is not supported. Quote your evaluable mapping keys instead.', self::$parsedLineNumber + 1, $mapping); } } - if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) { - @trigger_error(self::getDeprecationMessage('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since Symfony 3.2 and will throw a ParseException in 4.0.'), E_USER_DEPRECATED); + if (!$isKeyQuoted && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) { + throw new ParseException('Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}").', self::$parsedLineNumber + 1, $mapping); } if ('<<' === $key) { @@ -550,7 +476,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar $output[$key] = $value; } } elseif (isset($output[$key])) { - @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); } break; case '{': @@ -569,7 +495,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar $output[$key] = $value; } } elseif (isset($output[$key])) { - @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); } break; default: @@ -587,7 +513,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar $output[$key] = $value; } } elseif (isset($output[$key])) { - @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); } --$i; } @@ -603,15 +529,11 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar /** * Evaluates scalars and replaces magic values. * - * @param string $scalar - * @param int $flags - * @param array $references - * * @return mixed The evaluated YAML string * * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved */ - private static function evaluateScalar($scalar, $flags, $references = array()) + private static function evaluateScalar(string $scalar, int $flags, array $references = array()) { $scalar = trim($scalar); $scalarLower = strtolower($scalar); @@ -646,40 +568,10 @@ private static function evaluateScalar($scalar, $flags, $references = array()) return false; case '!' === $scalar[0]: switch (true) { - case 0 === strpos($scalar, '!str'): - @trigger_error(self::getDeprecationMessage('Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead.'), E_USER_DEPRECATED); - - return (string) substr($scalar, 5); case 0 === strpos($scalar, '!!str '): return (string) substr($scalar, 6); case 0 === strpos($scalar, '! '): - @trigger_error(self::getDeprecationMessage('Using the non-specific tag "!" is deprecated since Symfony 3.4 as its behavior will change in 4.0. It will force non-evaluating your values in 4.0. Use plain integers or !!float instead.'), E_USER_DEPRECATED); - - return (int) self::parseScalar(substr($scalar, 2), $flags); - case 0 === strpos($scalar, '!php/object:'): - if (self::$objectSupport) { - @trigger_error(self::getDeprecationMessage('The !php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), E_USER_DEPRECATED); - - return unserialize(substr($scalar, 12)); - } - - if (self::$exceptionOnInvalidType) { - throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); - } - - return; - case 0 === strpos($scalar, '!!php/object:'): - if (self::$objectSupport) { - @trigger_error(self::getDeprecationMessage('The !!php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.1 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), E_USER_DEPRECATED); - - return unserialize(substr($scalar, 13)); - } - - if (self::$exceptionOnInvalidType) { - throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); - } - - return; + return substr($scalar, 2); case 0 === strpos($scalar, '!php/object'): if (self::$objectSupport) { return unserialize(self::parseScalar(substr($scalar, 12))); @@ -689,21 +581,6 @@ private static function evaluateScalar($scalar, $flags, $references = array()) throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } - return; - case 0 === strpos($scalar, '!php/const:'): - if (self::$constantSupport) { - @trigger_error(self::getDeprecationMessage('The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead.'), E_USER_DEPRECATED); - - if (defined($const = substr($scalar, 11))) { - return constant($const); - } - - throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); - } - if (self::$exceptionOnInvalidType) { - throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); - } - return; case 0 === strpos($scalar, '!php/const'): if (self::$constantSupport) { @@ -724,7 +601,7 @@ private static function evaluateScalar($scalar, $flags, $references = array()) case 0 === strpos($scalar, '!!binary '): return self::evaluateBinaryScalar(substr($scalar, 9)); default: - @trigger_error(self::getDeprecationMessage(sprintf('Using the unquoted scalar value "%s" is deprecated since Symfony 3.3 and will be considered as a tagged value in 4.0. You must quote it.', $scalar)), E_USER_DEPRECATED); + throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename); } // Optimize for returning strings. @@ -755,13 +632,8 @@ private static function evaluateScalar($scalar, $flags, $references = array()) return -log(0); case '-.inf' === $scalarLower: return log(0); - case Parser::preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar): case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): - if (false !== strpos($scalar, ',')) { - @trigger_error(self::getDeprecationMessage('Using the comma as a group separator for floats is deprecated since Symfony 3.2 and will be removed in 4.0.'), E_USER_DEPRECATED); - } - - return (float) str_replace(array(',', '_'), '', $scalar); + return (float) str_replace('_', '', $scalar); case Parser::preg_match(self::getTimestampRegex(), $scalar): if (Yaml::PARSE_DATETIME & $flags) { // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. @@ -780,53 +652,39 @@ private static function evaluateScalar($scalar, $flags, $references = array()) return (string) $scalar; } - /** - * @param string $value - * @param int &$i - * @param int $flags - * - * @return null|string - */ - private static function parseTag($value, &$i, $flags) + private static function parseTag(string $value, int &$i, int $flags): ?string { if ('!' !== $value[$i]) { - return; + return null; } - $tagLength = strcspn($value, " \t\n", $i + 1); + $tagLength = strcspn($value, " \t\n[]{},", $i + 1); $tag = substr($value, $i + 1, $tagLength); $nextOffset = $i + $tagLength + 1; $nextOffset += strspn($value, ' ', $nextOffset); - // Is followed by a scalar - if ((!isset($value[$nextOffset]) || !in_array($value[$nextOffset], array('[', '{'), true)) && 'tagged' !== $tag) { - // Manage non-whitelisted scalars in {@link self::evaluateScalar()} - return; + // Is followed by a scalar and is a built-in tag + if ($tag && (!isset($value[$nextOffset]) || !in_array($value[$nextOffset], array('[', '{'), true)) && ('!' === $tag[0] || 'str' === $tag || 'php/const' === $tag || 'php/object' === $tag)) { + // Manage in {@link self::evaluateScalar()} + return null; } + $i = $nextOffset; + // Built-in tags if ($tag && '!' === $tag[0]) { throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } - if (Yaml::PARSE_CUSTOM_TAGS & $flags) { - $i = $nextOffset; - + if ('' === $tag || Yaml::PARSE_CUSTOM_TAGS & $flags) { return $tag; } throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } - /** - * @param string $scalar - * - * @return string - * - * @internal - */ - public static function evaluateBinaryScalar($scalar) + public static function evaluateBinaryScalar(string $scalar): string { $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); @@ -841,7 +699,7 @@ public static function evaluateBinaryScalar($scalar) return base64_decode($parsedBinaryData, true); } - private static function isBinaryString($value) + private static function isBinaryString(string $value) { return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value); } @@ -853,7 +711,7 @@ private static function isBinaryString($value) * * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 */ - private static function getTimestampRegex() + private static function getTimestampRegex(): string { return << 0) { - @trigger_error(sprintf('The constructor arguments $offset, $totalNumberOfLines, $skippedLineNumbers of %s are deprecated and will be removed in 4.0', self::class), E_USER_DEPRECATED); - - $this->offset = func_get_arg(0); - if (func_num_args() > 1) { - $this->totalNumberOfLines = func_get_arg(1); - } - if (func_num_args() > 2) { - $this->skippedLineNumbers = func_get_arg(2); - } - } - } - /** * Parses a YAML file into a PHP value. * @@ -61,7 +46,7 @@ public function __construct() * * @throws ParseException If the file could not be read or the YAML is not valid */ - public function parseFile($filename, $flags = 0) + public function parseFile(string $filename, int $flags = 0) { if (!is_file($filename)) { throw new ParseException(sprintf('File "%s" does not exist.', $filename)); @@ -90,38 +75,8 @@ public function parseFile($filename, $flags = 0) * * @throws ParseException If the YAML is not valid */ - public function parse($value, $flags = 0) + public function parse(string $value, int $flags = 0) { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= Yaml::PARSE_OBJECT; - } - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= Yaml::PARSE_OBJECT_FOR_MAP; - } - } - - if (Yaml::PARSE_KEYS_AS_STRINGS & $flags) { - @trigger_error('Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.', E_USER_DEPRECATED); - } - if (false === preg_match('//u', $value)) { throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename); } @@ -129,7 +84,6 @@ public function parse($value, $flags = 0) $this->refs = array(); $mbEncoding = null; - $e = null; $data = null; if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { @@ -139,28 +93,31 @@ public function parse($value, $flags = 0) try { $data = $this->doParse($value, $flags); - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - - if (null !== $mbEncoding) { - mb_internal_encoding($mbEncoding); - } - - $this->lines = array(); - $this->currentLine = ''; - $this->refs = array(); - $this->skippedLineNumbers = array(); - $this->locallySkippedLineNumbers = array(); - - if (null !== $e) { - throw $e; + } finally { + if (null !== $mbEncoding) { + mb_internal_encoding($mbEncoding); + } + $this->lines = array(); + $this->currentLine = ''; + $this->refs = array(); + $this->skippedLineNumbers = array(); + $this->locallySkippedLineNumbers = array(); } return $data; } - private function doParse($value, $flags) + /** + * @internal + * + * @return int + */ + public function getLastLineNumberBeforeDeprecation(): int + { + return $this->getRealCurrentLineNb(); + } + + private function doParse(string $value, int $flags) { $this->currentLineNb = -1; $this->currentLine = ''; @@ -216,12 +173,12 @@ private function doParse($value, $flags) } if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) { - @trigger_error($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'), E_USER_DEPRECATED); + throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } // array if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { - $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags); + $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true) ?? '', $flags); } elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) { $data[] = new TaggedValue( $subTag, @@ -255,15 +212,7 @@ private function doParse($value, $flags) $context = 'mapping'; try { - $i = 0; - $evaluateKey = !(Yaml::PARSE_KEYS_AS_STRINGS & $flags); - - // constants in key will be evaluated anyway - if (isset($values['key'][0]) && '!' === $values['key'][0] && Yaml::PARSE_CONSTANT & $flags) { - $evaluateKey = true; - } - - $key = Inline::parseScalar($values['key'], 0, null, $i, $evaluateKey); + $key = Inline::parseScalar($values['key']); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); @@ -272,8 +221,7 @@ private function doParse($value, $flags) } if (!is_string($key) && !is_int($key)) { - $keyType = is_numeric($key) ? 'numeric key' : 'non-string key'; - @trigger_error($this->getDeprecationMessage(sprintf('Implicit casting of %s to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.', $keyType)), E_USER_DEPRECATED); + throw new ParseException(sprintf('%s keys are not supported. Quote your evaluable mapping keys instead.', is_numeric($key) ? 'Numeric' : 'Non-string'), $this->getRealCurrentLineNb() + 1, $this->currentLine); } // Convert float keys to strings, to avoid being converted to integers by PHP @@ -359,9 +307,11 @@ private function doParse($value, $flags) $data[$key] = null; } } else { - @trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine); } } else { + // remember the parsed line number here in case we need it to provide some contexts in error messages below + $realCurrentLineNbKey = $this->getRealCurrentLineNb(); $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); if ('<<' === $key) { $this->refs[$refMatches['ref']] = $value; @@ -380,7 +330,7 @@ private function doParse($value, $flags) $data[$key] = $value; } } else { - @trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $realCurrentLineNbKey + 1, $this->currentLine); } } } else { @@ -390,7 +340,7 @@ private function doParse($value, $flags) if ($allowOverwrite || !isset($data[$key])) { $data[$key] = $value; } else { - @trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine); } } if ($isRef) { @@ -403,7 +353,7 @@ private function doParse($value, $flags) } if (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1]) { - @trigger_error($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'), E_USER_DEPRECATED); + throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } // 1-liner optionally followed by newline(s) @@ -494,7 +444,7 @@ private function doParse($value, $flags) return empty($data) ? null : $data; } - private function parseBlock($offset, $yaml, $flags) + private function parseBlock(int $offset, string $yaml, int $flags) { $skippedLineNumbers = $this->skippedLineNumbers; @@ -522,7 +472,7 @@ private function parseBlock($offset, $yaml, $flags) * * @return int The current line number */ - public function getRealCurrentLineNb() + public function getRealCurrentLineNb(): int { $realCurrentLineNumber = $this->currentLineNb + $this->offset; @@ -542,7 +492,7 @@ public function getRealCurrentLineNb() * * @return int The current line indentation */ - private function getCurrentLineIndentation() + private function getCurrentLineIndentation(): int { return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); } @@ -550,14 +500,14 @@ private function getCurrentLineIndentation() /** * Returns the next embed block of YAML. * - * @param int $indentation The indent level at which the block is to be read, or null for default - * @param bool $inSequence True if the enclosing data structure is a sequence + * @param int|null $indentation The indent level at which the block is to be read, or null for default + * @param bool $inSequence True if the enclosing data structure is a sequence * * @return string A YAML string * * @throws ParseException When indentation problem are detected */ - private function getNextEmbedBlock($indentation = null, $inSequence = false) + private function getNextEmbedBlock(int $indentation = null, bool $inSequence = false): ?string { $oldLineIndentation = $this->getCurrentLineIndentation(); $blockScalarIndentations = array(); @@ -567,7 +517,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) } if (!$this->moveToNextLine()) { - return; + return null; } if (null === $indentation) { @@ -610,7 +560,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) } else { $this->moveToPreviousLine(); - return; + return null; } if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) { @@ -618,7 +568,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) // and therefore no nested list or mapping $this->moveToPreviousLine(); - return; + return null; } $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); @@ -678,7 +628,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) * * @return bool */ - private function moveToNextLine() + private function moveToNextLine(): bool { if ($this->currentLineNb >= count($this->lines) - 1) { return false; @@ -694,7 +644,7 @@ private function moveToNextLine() * * @return bool */ - private function moveToPreviousLine() + private function moveToPreviousLine(): bool { if ($this->currentLineNb < 1) { return false; @@ -716,7 +666,7 @@ private function moveToPreviousLine() * * @throws ParseException When reference does not exist */ - private function parseValue($value, $flags, $context) + private function parseValue(string $value, int $flags, string $context) { if (0 === strpos($value, '*')) { if (false !== $pos = strpos($value, '#')) { @@ -737,14 +687,12 @@ private function parseValue($value, $flags, $context) $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); - if ('' !== $matches['tag']) { + if ('' !== $matches['tag'] && '!' !== $matches['tag']) { if ('!!binary' === $matches['tag']) { return Inline::evaluateBinaryScalar($data); - } elseif ('tagged' === $matches['tag']) { - return new TaggedValue(substr($matches['tag'], 1), $data); - } elseif ('!' !== $matches['tag']) { - @trigger_error($this->getDeprecationMessage(sprintf('Using the custom tag "%s" for the value "%s" is deprecated since Symfony 3.3. It will be replaced by an instance of %s in 4.0.', $matches['tag'], $data, TaggedValue::class)), E_USER_DEPRECATED); } + + return new TaggedValue(substr($matches['tag'], 1), $data); } return $data; @@ -815,7 +763,7 @@ private function parseValue($value, $flags, $context) * * @return string The text value */ - private function parseBlockScalar($style, $chomping = '', $indentation = 0) + private function parseBlockScalar(string $style, string $chomping = '', int $indentation = 0): string { $notEOF = $this->moveToNextLine(); if (!$notEOF) { @@ -922,7 +870,7 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0) * * @return bool Returns true if the next line is indented, false otherwise */ - private function isNextLineIndented() + private function isNextLineIndented(): bool { $currentIndentation = $this->getCurrentLineIndentation(); $movements = 0; @@ -953,7 +901,7 @@ private function isNextLineIndented() * * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise */ - private function isCurrentLineEmpty() + private function isCurrentLineEmpty(): bool { return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); } @@ -963,7 +911,7 @@ private function isCurrentLineEmpty() * * @return bool Returns true if the current line is blank, false otherwise */ - private function isCurrentLineBlank() + private function isCurrentLineBlank(): bool { return '' == trim($this->currentLine, ' '); } @@ -973,7 +921,7 @@ private function isCurrentLineBlank() * * @return bool Returns true if the current line is a comment line, false otherwise */ - private function isCurrentLineComment() + private function isCurrentLineComment(): bool { //checking explicitly the first char of the trim is faster than loops or strpos $ltrimmedLine = ltrim($this->currentLine, ' '); @@ -981,7 +929,7 @@ private function isCurrentLineComment() return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; } - private function isCurrentLineLastLineInDocument() + private function isCurrentLineLastLineInDocument(): bool { return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); } @@ -993,7 +941,7 @@ private function isCurrentLineLastLineInDocument() * * @return string A cleaned up YAML string */ - private function cleanup($value) + private function cleanup(string $value): string { $value = str_replace(array("\r\n", "\r"), "\n", $value); @@ -1029,7 +977,7 @@ private function cleanup($value) * * @return bool Returns true if the next line starts unindented collection, false otherwise */ - private function isNextLineUnIndentedCollection() + private function isNextLineUnIndentedCollection(): bool { $currentIndentation = $this->getCurrentLineIndentation(); $movements = 0; @@ -1060,7 +1008,7 @@ private function isNextLineUnIndentedCollection() * * @return bool Returns true if the string is un-indented collection item, false otherwise */ - private function isStringUnIndentedCollectionItem() + private function isStringUnIndentedCollectionItem(): bool { return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } @@ -1070,7 +1018,7 @@ private function isStringUnIndentedCollectionItem() * * @return bool */ - private function isBlockScalarHeader() + private function isBlockScalarHeader(): bool { return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); } @@ -1088,7 +1036,7 @@ private function isBlockScalarHeader() * * @internal */ - public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0) + public static function preg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int { if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { switch (preg_last_error()) { @@ -1123,7 +1071,7 @@ public static function preg_match($pattern, $subject, &$matches = null, $flags = * Prevent values such as `!foo {quz: bar}` to be considered as * a mapping block. */ - private function trimTag($value) + private function trimTag(string $value): string { if ('!' === $value[0]) { return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' '); @@ -1132,14 +1080,14 @@ private function trimTag($value) return $value; } - private function getLineTag($value, $flags, $nextLineCheck = true) + private function getLineTag(string $value, int $flags, bool $nextLineCheck = true): ?string { if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) { - return; + return null; } if ($nextLineCheck && !$this->isNextLineIndented()) { - return; + return null; } $tag = substr($matches['tag'], 1); @@ -1155,17 +1103,4 @@ private function getLineTag($value, $flags, $nextLineCheck = true) throw new ParseException(sprintf('Tags support is not enabled. You must use the flag `Yaml::PARSE_CUSTOM_TAGS` to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename); } - - private function getDeprecationMessage($message) - { - $message = rtrim($message, '.'); - - if (null !== $this->filename) { - $message .= ' in '.$this->filename; - } - - $message .= ' on line '.($this->getRealCurrentLineNb() + 1); - - return $message.'.'; - } } diff --git a/src/Symfony/Component/Yaml/Tag/TaggedValue.php b/src/Symfony/Component/Yaml/Tag/TaggedValue.php index 000c1d992cdee..4ea3406135999 100644 --- a/src/Symfony/Component/Yaml/Tag/TaggedValue.php +++ b/src/Symfony/Component/Yaml/Tag/TaggedValue.php @@ -20,27 +20,17 @@ final class TaggedValue private $tag; private $value; - /** - * @param string $tag - * @param mixed $value - */ - public function __construct($tag, $value) + public function __construct(string $tag, $value) { $this->tag = $tag; $this->value = $value; } - /** - * @return string - */ - public function getTag() + public function getTag(): string { return $this->tag; } - /** - * @return mixed - */ public function getValue() { return $this->value; diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index b474391132286..0a8c023d017fe 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -77,35 +77,6 @@ public function testIndentationInConstructor() $this->assertEquals($expected, $dumper->dump($this->array, 4, 0)); } - /** - * @group legacy - */ - public function testSetIndentation() - { - $this->dumper->setIndentation(7); - - $expected = <<<'EOF' -'': bar -foo: '#bar' -'foo''bar': { } -bar: - - 1 - - foo -foobar: - foo: bar - bar: - - 1 - - foo - foobar: - foo: bar - bar: - - 1 - - foo - -EOF; - $this->assertEquals($expected, $this->dumper->dump($this->array, 4, 0)); - } - public function testSpecifications() { $files = $this->parser->parse(file_get_contents($this->path.'/index.yml')); @@ -213,16 +184,6 @@ public function testObjectSupportEnabled() $this->assertEquals('{ foo: !php/object \'O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}\', bar: 1 }', $dump, '->dump() is able to dump objects'); } - /** - * @group legacy - */ - public function testObjectSupportEnabledPassingTrue() - { - $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); - - $this->assertEquals('{ foo: !php/object \'O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}\', bar: 1 }', $dump, '->dump() is able to dump objects'); - } - public function testObjectSupportDisabledButNoExceptions() { $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1)); @@ -238,33 +199,6 @@ public function testObjectSupportDisabledWithExceptions() $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); } - /** - * @group legacy - * @expectedException \Symfony\Component\Yaml\Exception\DumpException - */ - public function testObjectSupportDisabledWithExceptionsPassingTrue() - { - $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true); - } - - public function testEmptyArray() - { - $dump = $this->dumper->dump(array()); - $this->assertEquals('{ }', $dump); - - $dump = $this->dumper->dump(array(), 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); - $this->assertEquals('[]', $dump); - - $dump = $this->dumper->dump(array(), 9, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); - $this->assertEquals('[]', $dump); - - $dump = $this->dumper->dump(new \ArrayObject(), 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE | Yaml::DUMP_OBJECT_AS_MAP); - $this->assertEquals('{ }', $dump); - - $dump = $this->dumper->dump(new \stdClass(), 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE | Yaml::DUMP_OBJECT_AS_MAP); - $this->assertEquals('{ }', $dump); - } - /** * @dataProvider getEscapeSequences */ diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml index 3f93c982bff2f..3ee795adf5236 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml @@ -518,16 +518,6 @@ php: | 'hexadecimal' => 0xC ) --- -test: Decimal Integer -deprecated: true -spec: 2.19 -yaml: | - decimal: +12,345 -php: | - array( - 'decimal' => 12345.0, - ) ---- # FIX: spec shows parens around -inf and NaN test: Floating point spec: 2.20 @@ -546,16 +536,6 @@ php: | 'float as whole number' => (float) 1 ) --- -test: Fixed Floating point -deprecated: true -spec: 2.20 -yaml: | - fixed: 1,230.15 -php: | - array( - 'fixed' => 1230.15, - ) ---- test: Timestamps todo: true spec: 2.22 @@ -928,13 +908,12 @@ documents: 2 --- test: Explicit typing -deprecated: Using the non-specific tag "!" is deprecated since Symfony 3.4 as its behavior will change in 4.0. yaml: | integer: 12 - also int: ! "12" + no int: ! 12 string: !!str 12 php: | - array( 'integer' => 12, 'also int' => 12, 'string' => '12' ) + array( 'integer' => 12, 'no int' => '12', 'string' => '12' ) --- test: Private types todo: true @@ -1531,24 +1510,6 @@ php: | 'hexadecimal' => 12 ) --- -test: Decimal -deprecated: true -yaml: | - decimal: +12,345 -php: | - array( - 'decimal' => 12345.0, - ) ---- -test: Fixed Float -deprecated: true -yaml: | - fixed: 1,230.15 -php: | - array( - 'fixed' => 1230.15, - ) ---- test: Float yaml: | canonical: 1.23015e+3 diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml index dea0be010b58c..f1a7832956b39 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml @@ -182,34 +182,6 @@ php: | 'simple' => 12, ) --- -test: Positive Big Integer -deprecated: true -dump_skip: true -brief: > - An integer is a series of numbers, optionally - starting with a positive or negative sign. Integers - may also contain commas for readability. -yaml: | - one-thousand: 1,000 -php: | - array( - 'one-thousand' => 1000.0, - ) ---- -test: Negative Big Integer -deprecated: true -dump_skip: true -brief: > - An integer is a series of numbers, optionally - starting with a positive or negative sign. Integers - may also contain commas for readability. -yaml: | - negative one-thousand: -1,000 -php: | - array( - 'negative one-thousand' => -1000.0 - ) ---- test: Floats dump_skip: true brief: > @@ -225,20 +197,6 @@ php: | 'scientific notation' => 1000.09 ) --- -test: Larger Float -dump_skip: true -deprecated: true -brief: > - Floats are represented by numbers with decimals, - allowing for scientific notation, as well as - positive and negative infinity and "not a number." -yaml: | - larger float: 1,000.09 -php: | - array( - 'larger float' => 1000.09, - ) ---- test: Time todo: true brief: > diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/legacyBooleanMappingKeys.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/legacyBooleanMappingKeys.yml deleted file mode 100644 index 5e8d091707d51..0000000000000 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/legacyBooleanMappingKeys.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- %YAML:1.0 -test: Miscellaneous -spec: 2.21 -yaml: | - true: true - false: false -php: | - array( - 1 => true, - 0 => false, - ) ---- -test: Boolean -yaml: | - false: used as key - logical: true - answer: false -php: | - array( - false => 'used as key', - 'logical' => true, - 'answer' => false - ) diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/legacyNonStringKeys.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/legacyNonStringKeys.yml deleted file mode 100644 index 4e28201856d2a..0000000000000 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/legacyNonStringKeys.yml +++ /dev/null @@ -1,2 +0,0 @@ -- legacyBooleanMappingKeys -- legacyNullMappingKey diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/legacyNullMappingKey.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/legacyNullMappingKey.yml deleted file mode 100644 index 551a6205e137d..0000000000000 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/legacyNullMappingKey.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- %YAML:1.0 -test: Miscellaneous -spec: 2.21 -yaml: | - null: ~ -php: | - array( - '' => null, - ) diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 3d8606578e56a..2cadfe36b8dce 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Inline; +use Symfony\Component\Yaml\Tag\TaggedValue; use Symfony\Component\Yaml\Yaml; class InlineTest extends TestCase @@ -80,38 +81,6 @@ public function testParsePhpConstantThrowsExceptionOnInvalidType() Inline::parse('!php/const PHP_INT_MAX', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); } - /** - * @group legacy - * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 1. - * @dataProvider getTestsForParseLegacyPhpConstants - */ - public function testDeprecatedConstantTag($yaml, $expectedValue) - { - $this->assertSame($expectedValue, Inline::parse($yaml, Yaml::PARSE_CONSTANT)); - } - - public function getTestsForParseLegacyPhpConstants() - { - return array( - array('!php/const:Symfony\Component\Yaml\Yaml::PARSE_CONSTANT', Yaml::PARSE_CONSTANT), - array('!php/const:PHP_INT_MAX', PHP_INT_MAX), - array('[!php/const:PHP_INT_MAX]', array(PHP_INT_MAX)), - array('{ foo: !php/const:PHP_INT_MAX }', array('foo' => PHP_INT_MAX)), - array('!php/const:NULL', null), - ); - } - - /** - * @group legacy - * @dataProvider getTestsForParseWithMapObjects - */ - public function testParseWithMapObjectsPassingTrue($yaml, $value) - { - $actual = Inline::parse($yaml, false, false, true); - - $this->assertSame(serialize($value), serialize($actual)); - } - /** * @dataProvider getTestsForDump */ @@ -194,13 +163,12 @@ public function testParseInvalidMappingKeyShouldThrowException() } /** - * @group legacy - * @expectedDeprecation Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since Symfony 3.2 and will throw a ParseException in 4.0 on line 1. - * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0 + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}") */ public function testParseMappingKeyWithColonNotFollowedBySpace() { - Inline::parse('{1:""}'); + Inline::parse('{foo:""}'); } /** @@ -235,15 +203,6 @@ public function testParseReferences($yaml, $expected) $this->assertSame($expected, Inline::parse($yaml, 0, array('var' => 'var-value'))); } - /** - * @group legacy - * @dataProvider getDataForParseReferences - */ - public function testParseReferencesAsFifthArgument($yaml, $expected) - { - $this->assertSame($expected, Inline::parse($yaml, false, false, false, array('var' => 'var-value'))); - } - public function getDataForParseReferences() { return array( @@ -268,19 +227,6 @@ public function testParseMapReferenceInSequence() $this->assertSame(array($foo), Inline::parse('[*foo]', 0, array('foo' => $foo))); } - /** - * @group legacy - */ - public function testParseMapReferenceInSequenceAsFifthArgument() - { - $foo = array( - 'a' => 'Steve', - 'b' => 'Clark', - 'c' => 'Brian', - ); - $this->assertSame(array($foo), Inline::parse('[*foo]', false, false, false, array('foo' => $foo))); - } - /** * @expectedException \Symfony\Component\Yaml\Exception\ParseException * @expectedExceptionMessage A reference must contain at least one character at line 1. @@ -306,9 +252,9 @@ public function testParseUnquotedScalarStartingWithReservedIndicator($indicator) { if (method_exists($this, 'expectExceptionMessage')) { $this->expectException(ParseException::class); - $this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator)); + $this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo").', $indicator)); } else { - $this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator)); + $this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo").', $indicator)); } Inline::parse(sprintf('{ foo: %sfoo }', $indicator)); @@ -326,9 +272,9 @@ public function testParseUnquotedScalarStartingWithScalarIndicator($indicator) { if (method_exists($this, 'expectExceptionMessage')) { $this->expectException(ParseException::class); - $this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator)); + $this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo").', $indicator)); } else { - $this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator)); + $this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo").', $indicator)); } Inline::parse(sprintf('{ foo: %sfoo }', $indicator)); @@ -336,17 +282,7 @@ public function testParseUnquotedScalarStartingWithScalarIndicator($indicator) public function getScalarIndicators() { - return array(array('|'), array('>')); - } - - /** - * @group legacy - * @expectedDeprecation Not quoting the scalar "%bar " starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0 on line 1. - * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0 - */ - public function testParseUnquotedScalarStartingWithPercentCharacter() - { - Inline::parse('{ foo: %bar }'); + return array(array('|'), array('>'), array('%')); } /** @@ -611,12 +547,7 @@ public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $ $expected = new \DateTime($yaml); $expected->setTimeZone(new \DateTimeZone('UTC')); $expected->setDate($year, $month, $day); - - if (\PHP_VERSION_ID >= 70100) { - $expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); - } else { - $expected->setTime($hour, $minute, $second); - } + $expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); $date = Inline::parse($yaml, Yaml::PARSE_DATETIME); $this->assertEquals($expected, $date); @@ -641,11 +572,7 @@ public function testParseNestedTimestampListAsDateTimeObject($yaml, $year, $mont $expected = new \DateTime($yaml); $expected->setTimeZone(new \DateTimeZone('UTC')); $expected->setDate($year, $month, $day); - if (\PHP_VERSION_ID >= 70100) { - $expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); - } else { - $expected->setTime($hour, $minute, $second); - } + $expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); $expectedNested = array('nested' => array($expected)); $yamlNested = "{nested: [$yaml]}"; @@ -736,12 +663,12 @@ public function testVeryLongQuotedStrings() } /** - * @group legacy - * @expectedDeprecation Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0 on line 1. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Missing mapping key */ - public function testOmittedMappingKeyIsParsedAsColon() + public function testMappingKeysCannotBeOmitted() { - $this->assertSame(array(':' => 'foo'), Inline::parse('{: foo}')); + Inline::parse('{: foo}'); } /** @@ -766,8 +693,9 @@ public function testTheEmptyStringIsAValidMappingKey() } /** - * @group legacy - * @expectedDeprecation Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Implicit casting of incompatible mapping keys to strings is not supported. Quote your evaluable mapping keys instead + * * @dataProvider getNotPhpCompatibleMappingKeyData */ public function testImplicitStringCastingOfMappingKeysIsDeprecated($yaml, $expected) @@ -775,17 +703,6 @@ public function testImplicitStringCastingOfMappingKeysIsDeprecated($yaml, $expec $this->assertSame($expected, Inline::parse($yaml)); } - /** - * @group legacy - * @expectedDeprecation Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead. - * @expectedDeprecation Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1. - * @dataProvider getNotPhpCompatibleMappingKeyData - */ - public function testExplicitStringCastingOfMappingKeys($yaml, $expected) - { - $this->assertSame($expected, Yaml::parse($yaml, Yaml::PARSE_KEYS_AS_STRINGS)); - } - public function getNotPhpCompatibleMappingKeyData() { return array( @@ -796,12 +713,39 @@ public function getNotPhpCompatibleMappingKeyData() ); } - /** - * @group legacy - * @expectedDeprecation Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead on line 1. - */ - public function testDeprecatedStrTag() + public function testTagWithoutValueInSequence() { - $this->assertSame(array('foo' => 'bar'), Inline::parse('{ foo: !str bar }')); + $value = Inline::parse('[!foo]', Yaml::PARSE_CUSTOM_TAGS); + + $this->assertInstanceOf(TaggedValue::class, $value[0]); + $this->assertSame('foo', $value[0]->getTag()); + $this->assertSame('', $value[0]->getValue()); + } + + public function testTagWithEmptyValueInSequence() + { + $value = Inline::parse('[!foo ""]', Yaml::PARSE_CUSTOM_TAGS); + + $this->assertInstanceOf(TaggedValue::class, $value[0]); + $this->assertSame('foo', $value[0]->getTag()); + $this->assertSame('', $value[0]->getValue()); + } + + public function testTagWithoutValueInMapping() + { + $value = Inline::parse('{foo: !bar}', Yaml::PARSE_CUSTOM_TAGS); + + $this->assertInstanceOf(TaggedValue::class, $value['foo']); + $this->assertSame('bar', $value['foo']->getTag()); + $this->assertSame('', $value['foo']->getValue()); + } + + public function testTagWithEmptyValueInMapping() + { + $value = Inline::parse('{foo: !bar ""}', Yaml::PARSE_CUSTOM_TAGS); + + $this->assertInstanceOf(TaggedValue::class, $value['foo']); + $this->assertSame('bar', $value['foo']->getTag()); + $this->assertSame('', $value['foo']->getValue()); } } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 9d605280579fe..fe3f07986028b 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -37,34 +37,9 @@ protected function tearDown() /** * @dataProvider getDataFormSpecifications */ - public function testSpecifications($expected, $yaml, $comment, $deprecated) + public function testSpecifications($expected, $yaml, $comment) { - $deprecations = array(); - - if ($deprecated) { - set_error_handler(function ($type, $msg) use (&$deprecations) { - if (E_USER_DEPRECATED !== $type) { - restore_error_handler(); - - if (class_exists('PHPUnit_Util_ErrorHandler')) { - return call_user_func_array('PHPUnit_Util_ErrorHandler::handleError', func_get_args()); - } - - return call_user_func_array('PHPUnit\Util\ErrorHandler::handleError', func_get_args()); - } - - $deprecations[] = $msg; - }); - } - $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment); - - if ($deprecated) { - restore_error_handler(); - - $this->assertCount(1, $deprecations); - $this->assertContains(true !== $deprecated ? $deprecated : 'Using the comma as a group separator for floats is deprecated since Symfony 3.2 and will be removed in 4.0 on line 1.', $deprecations[0]); - } } public function getDataFormSpecifications() @@ -72,35 +47,11 @@ public function getDataFormSpecifications() return $this->loadTestsFromFixtureFiles('index.yml'); } - /** - * @group legacy - * @expectedDeprecationMessage Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable - * @dataProvider getNonStringMappingKeysData - */ - public function testNonStringMappingKeys($expected, $yaml, $comment) - { - $this->assertSame($expected, var_export($this->parser->parse($yaml, Yaml::PARSE_KEYS_AS_STRINGS), true), $comment); - } - public function getNonStringMappingKeysData() { return $this->loadTestsFromFixtureFiles('nonStringKeys.yml'); } - /** - * @group legacy - * @dataProvider getLegacyNonStringMappingKeysData - */ - public function testLegacyNonStringMappingKeys($expected, $yaml, $comment) - { - $this->assertSame($expected, var_export($this->parser->parse($yaml), true), $comment); - } - - public function getLegacyNonStringMappingKeysData() - { - return $this->loadTestsFromFixtureFiles('legacyNonStringKeys.yml'); - } - public function testTabsInYaml() { // test tabs in YAML @@ -479,50 +430,12 @@ public function testObjectSupportEnabled() $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects'); } - /** - * @group legacy - */ - public function testObjectSupportEnabledPassingTrue() + public function testObjectSupportDisabledButNoExceptions() { $input = <<<'EOF' -foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";} +foo: !php/object O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} bar: 1 EOF; - $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects'); - } - - /** - * @group legacy - * @dataProvider deprecatedObjectValueProvider - */ - public function testObjectSupportEnabledWithDeprecatedTag($yaml) - { - $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($yaml, Yaml::PARSE_OBJECT), '->parse() is able to parse objects'); - } - - public function deprecatedObjectValueProvider() - { - return array( - array( - <<assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects'); } @@ -536,15 +449,6 @@ public function testObjectForMap($yaml, $expected) $this->assertEquals($expected, $this->parser->parse($yaml, $flags)); } - /** - * @group legacy - * @dataProvider getObjectForMapTests - */ - public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected) - { - $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true)); - } - public function getObjectForMapTests() { $tests = array(); @@ -609,11 +513,15 @@ public function getObjectForMapTests() } /** - * @dataProvider invalidDumpedObjectProvider * @expectedException \Symfony\Component\Yaml\Exception\ParseException */ - public function testObjectsSupportDisabledWithExceptions($yaml) + public function testObjectsSupportDisabledWithExceptions() { + $yaml = <<<'EOF' +foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} +bar: 1 +EOF; + $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); } @@ -628,33 +536,6 @@ public function testCanParseContentWithTrailingSpaces() $this->assertSame($expected, $this->parser->parse($yaml)); } - /** - * @group legacy - * @dataProvider invalidDumpedObjectProvider - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml) - { - $this->parser->parse($yaml, true); - } - - public function invalidDumpedObjectProvider() - { - $yamlTag = <<<'EOF' -foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} -bar: 1 -EOF; - $localTag = <<<'EOF' -foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} -bar: 1 -EOF; - - return array( - 'yaml-tag' => array($yamlTag), - 'local-tag' => array($localTag), - ); - } - /** * @requires extension iconv */ @@ -820,6 +701,9 @@ public function testScalarInSequence() } /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Duplicate key "child" detected + * * > It is an error for two equal keys to appear in the same mapping node. * > In such a case the YAML processor may continue, ignoring the second * > `key: value` pair and issuing an appropriate warning. This strategy @@ -828,7 +712,6 @@ public function testScalarInSequence() * * @see http://yaml.org/spec/1.2/spec.html#id2759572 * @see http://yaml.org/spec/1.1/#id932806 - * @group legacy */ public function testMappingDuplicateKeyBlock() { @@ -849,7 +732,8 @@ public function testMappingDuplicateKeyBlock() } /** - * @group legacy + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Duplicate key "child" detected */ public function testMappingDuplicateKeyFlow() { @@ -866,13 +750,13 @@ public function testMappingDuplicateKeyFlow() } /** - * @group legacy + * @expectedException \Symfony\Component\Yaml\Exception\ParseException * @dataProvider getParseExceptionOnDuplicateData - * @expectedDeprecation Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated %s and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d. - * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0 */ public function testParseExceptionOnDuplicate($input, $duplicateKey, $lineNumber) { + $this->expectExceptionMessage(sprintf('Duplicate key "%s" detected at line %d', $duplicateKey, $lineNumber)); + Yaml::parse($input); } @@ -1095,8 +979,8 @@ public function testYamlDirective() } /** - * @group legacy - * @expectedDeprecation Implicit casting of numeric key to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 2. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Numeric keys are not supported. Quote your evaluable mapping keys instead */ public function testFloatKeys() { @@ -1106,19 +990,12 @@ public function testFloatKeys() 1.3: "baz" EOF; - $expected = array( - 'foo' => array( - '1.2' => 'bar', - '1.3' => 'baz', - ), - ); - - $this->assertEquals($expected, $this->parser->parse($yaml)); + $this->parser->parse($yaml); } /** - * @group legacy - * @expectedDeprecation Implicit casting of non-string key to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Non-string keys are not supported. Quote your evaluable mapping keys instead */ public function testBooleanKeys() { @@ -1127,12 +1004,7 @@ public function testBooleanKeys() false: bar EOF; - $expected = array( - 1 => 'foo', - 0 => 'bar', - ); - - $this->assertEquals($expected, $this->parser->parse($yaml)); + $this->parser->parse($yaml); } public function testExplicitStringCasting() @@ -1694,6 +1566,18 @@ public function testCustomTagSupport($expected, $yaml) public function taggedValuesProvider() { return array( + 'scalars' => array( + array( + 'foo' => new TaggedValue('inline', 'bar'), + 'quz' => new TaggedValue('long', 'this is a long text'), + ), + << + this is a long + text +YAML + ), 'sequences' => array( array(new TaggedValue('foo', array('yaml')), new TaggedValue('quz', array('bar'))), << array( + array(new TaggedValue('foo', 'bar')), + '[ !foo bar ]', + ), ); } + public function testNonSpecificTagSupport() + { + $this->assertSame('12', $this->parser->parse('! 12')); + } + /** * @expectedException \Symfony\Component\Yaml\Exception\ParseException * @expectedExceptionMessage Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!iterator" at line 1 (near "!iterator [foo]"). @@ -1731,12 +1624,21 @@ public function testCustomTagsDisabled() } /** - * @group legacy - * @expectedDeprecation Using the unquoted scalar value "!iterator foo" is deprecated since Symfony 3.3 and will be considered as a tagged value in 4.0. You must quote it on line 1. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!iterator" at line 1 (near "!iterator foo"). */ public function testUnsupportedTagWithScalar() { - $this->assertEquals('!iterator foo', $this->parser->parse('!iterator foo')); + $this->parser->parse('!iterator foo'); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage The string "!!iterator foo" could not be parsed as it uses an unsupported built-in tag at line 1 (near "!!iterator foo"). + */ + public function testUnsupportedBuiltInTagWithScalar() + { + $this->parser->parse('!!iterator foo'); } /** @@ -1749,8 +1651,8 @@ public function testExceptionWhenUsingUnsuportedBuiltInTags() } /** - * @group legacy - * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Complex mappings are not supported at line 1 (near "? "1""). */ public function testComplexMappingThrowsParseException() { @@ -1764,8 +1666,8 @@ public function testComplexMappingThrowsParseException() } /** - * @group legacy - * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 2. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Complex mappings are not supported at line 2 (near "? "1""). */ public function testComplexMappingNestedInMappingThrowsParseException() { @@ -1780,8 +1682,8 @@ public function testComplexMappingNestedInMappingThrowsParseException() } /** - * @group legacy - * @expectedDeprecation Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line 1. + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Complex mappings are not supported at line 1 (near "- ? "1""). */ public function testComplexMappingNestedInSequenceThrowsParseException() { @@ -1830,7 +1732,7 @@ private function loadTestsFromFixtureFiles($testsFile) } else { eval('$expected = '.trim($test['php']).';'); - $tests[] = array(var_export($expected, true), $test['yaml'], $test['test'], isset($test['deprecated']) ? $test['deprecated'] : false); + $tests[] = array(var_export($expected, true), $test['yaml'], $test['test']); } } } @@ -1893,62 +1795,6 @@ public function testPhpConstantTagMappingKey() $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT)); } - /** - * @group legacy - * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 2. - * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 4. - * @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 5. - */ - public function testDeprecatedPhpConstantTagMappingKey() - { - $yaml = << array( - 'foo' => array( - 'from' => array( - 'bar', - ), - 'to' => 'baz', - ), - ), - ); - - $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT)); - } - - /** - * @group legacy - * @expectedDeprecation Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead. - */ - public function testPhpConstantTagMappingKeyWithKeysCastToStrings() - { - $yaml = << array( - 'foo' => array( - 'from' => array( - 'bar', - ), - 'to' => 'baz', - ), - ), - ); - - $this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT | Yaml::PARSE_KEYS_AS_STRINGS)); - } - public function testMergeKeysWhenMappingsAreParsedAsObjects() { $yaml = <<unescapeCharacter($match[0]); @@ -64,7 +64,7 @@ public function unescapeDoubleQuotedString($value) * * @return string The unescaped character */ - private function unescapeCharacter($value) + private function unescapeCharacter(string $value): string { switch ($value[1]) { case '0': @@ -120,12 +120,8 @@ private function unescapeCharacter($value) /** * Get the UTF-8 character for the given code point. - * - * @param int $c The unicode code point - * - * @return string The corresponding UTF-8 character */ - private static function utf8chr($c) + private static function utf8chr(int $c): string { if (0x80 > $c %= 0x200000) { return chr($c); diff --git a/src/Symfony/Component/Yaml/Yaml.php b/src/Symfony/Component/Yaml/Yaml.php index d429a3391cb43..0a97c2198a3fc 100644 --- a/src/Symfony/Component/Yaml/Yaml.php +++ b/src/Symfony/Component/Yaml/Yaml.php @@ -34,11 +34,6 @@ class Yaml const PARSE_CUSTOM_TAGS = 512; const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024; - /** - * @deprecated since version 3.4, to be removed in 4.0. Quote your evaluable keys instead. - */ - const PARSE_KEYS_AS_STRINGS = 2048; - /** * Parses a YAML file into a PHP value. * @@ -55,7 +50,7 @@ class Yaml * * @throws ParseException If the file could not be read or the YAML is not valid */ - public static function parseFile($filename, $flags = 0) + public static function parseFile(string $filename, int $flags = 0) { $yaml = new Parser(); @@ -78,34 +73,8 @@ public static function parseFile($filename, $flags = 0) * * @throws ParseException If the YAML is not valid */ - public static function parse($input, $flags = 0) + public static function parse(string $input, int $flags = 0) { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= self::PARSE_OBJECT; - } - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= self::PARSE_OBJECT_FOR_MAP; - } - } - $yaml = new Parser(); return $yaml->parse($input, $flags); @@ -124,26 +93,8 @@ public static function parse($input, $flags = 0) * * @return string A YAML string representing the original PHP value */ - public static function dump($input, $inline = 2, $indent = 4, $flags = 0) + public static function dump($input, int $inline = 2, int $indent = 4, int $flags = 0): string { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = self::DUMP_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 5) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(4)) { - $flags |= self::DUMP_OBJECT; - } - } - $yaml = new Dumper($indent); return $yaml->dump($input, $inline, 0, $flags); diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json index a31a2cef426eb..b99fabd2b6270 100644 --- a/src/Symfony/Component/Yaml/composer.json +++ b/src/Symfony/Component/Yaml/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "require-dev": { "symfony/console": "~3.4|~4.0" @@ -36,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } } }