diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6b5015a456..de1ff92c89 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,12 +33,6 @@ jobs: strategy: matrix: image: - - "php7.1-phpunit4.8-wordpress5.3" - - "php7.2-phpunit4.8-wordpress5.4" - - "php7.3-phpunit7.5-wordpress5.5" - - "php7.3-phpunit7.5-wordpress5.6" - - "php7.3-phpunit7.5-wordpress5.7" - - "php7.3-phpunit7.5-wordpress5.8" - "php7.4-phpunit7.5-wordpress5.9" - "php8.0-phpunit7.5woo-wordpress5.6" phpunit-xml: diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 550c1a6660..ae488067b2 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -96,11 +96,11 @@ - + - - + + @@ -112,6 +112,7 @@ */vendor/* src/libraries/action-scheduler/* + src/modules/* */ext/dependencies/* */modules/food-kg/includes/polyfills/mbstring.php */modules/**/scoper.inc.php diff --git a/src/admin/class-wordlift-admin-entity-type-settings.php b/src/admin/class-wordlift-admin-entity-type-settings.php index 807dddbbc6..bc74393f37 100644 --- a/src/admin/class-wordlift-admin-entity-type-settings.php +++ b/src/admin/class-wordlift-admin-entity-type-settings.php @@ -95,7 +95,7 @@ public function admin_menu() { */ // @todo: use the new {@link Wordlift_Admin_Page}. add_submenu_page( - null, + 'wl_entity_type_settings', __( 'Edit Entity term', 'wordlift' ), __( 'Edit Entity term', 'wordlift' ), 'manage_options', diff --git a/src/admin/elements/class-wordlift-admin-select-element.php b/src/admin/elements/class-wordlift-admin-select-element.php index b79368668c..9c7c01d449 100644 --- a/src/admin/elements/class-wordlift-admin-select-element.php +++ b/src/admin/elements/class-wordlift-admin-select-element.php @@ -56,7 +56,7 @@ class="" $this->print_notice( $params['notice'] ); // Print the field description. - echo wp_kses( $this->get_description( $params['description'] ), array( 'p' => array() ) ); + echo wp_kses( $this->get_description( $params['description'] ) ?? '', array( 'p' => array() ) ); return $this; } diff --git a/src/classes/common/background-process/class-background-process.php b/src/classes/common/background-process/class-background-process.php index 5cbe582379..8592ab157b 100644 --- a/src/classes/common/background-process/class-background-process.php +++ b/src/classes/common/background-process/class-background-process.php @@ -67,7 +67,7 @@ private function is_started( $state ) { */ public function start() { $action = $this->get_action_key(); - $this->log->debug( "Trying to start ${action}..." ); + $this->log->debug( sprintf( 'Trying to start %s...', $action ) ); // Create a new Sync_Model state of `started`. if ( ! $this->is_started( self::get_state() ) ) { $this->log->debug( 'Starting...' ); @@ -96,7 +96,7 @@ public function start() { public function cancel() { $action = $this->action; - $this->log->debug( "Cancelling ${action}..." ); + $this->log->debug( sprintf( 'Cancelling %s...', $action ) ); // Cleanup the process data. $this->cancel_process(); diff --git a/src/classes/content/wordpress/class-wordpress-content-id.php b/src/classes/content/wordpress/class-wordpress-content-id.php index 2cb8974787..e59a86166f 100644 --- a/src/classes/content/wordpress/class-wordpress-content-id.php +++ b/src/classes/content/wordpress/class-wordpress-content-id.php @@ -60,6 +60,7 @@ public function get_type() { return $this->type; } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'id' => $this->id, diff --git a/src/classes/google-addon-integration/pages/class-import-page.php b/src/classes/google-addon-integration/pages/class-import-page.php index 0c3a542881..4e71edb1ee 100644 --- a/src/classes/google-addon-integration/pages/class-import-page.php +++ b/src/classes/google-addon-integration/pages/class-import-page.php @@ -23,7 +23,7 @@ public function get_menu_title() { } protected function get_parent_slug() { - return null; + return 'wl_google_addon_import'; } public function render() { diff --git a/src/classes/mappings/pages/class-edit-mappings-page.php b/src/classes/mappings/pages/class-edit-mappings-page.php index 621fe53817..282352f276 100644 --- a/src/classes/mappings/pages/class-edit-mappings-page.php +++ b/src/classes/mappings/pages/class-edit-mappings-page.php @@ -76,7 +76,7 @@ private function load_text_settings_for_edit_mapping_page( array $edit_mapping_s * @return null return null to avoid this page to be displayed in WordLift's menu. */ protected function get_parent_slug() { - return null; + return 'wl_edit_mappings'; } /** diff --git a/src/classes/relation/class-relation.php b/src/classes/relation/class-relation.php index 02ab5a3648..b383509bb2 100644 --- a/src/classes/relation/class-relation.php +++ b/src/classes/relation/class-relation.php @@ -147,6 +147,7 @@ public static function from_relation_instances( $instance ) { ); } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'subject' => $this->get_subject(), diff --git a/src/classes/relation/class-relations.php b/src/classes/relation/class-relations.php index 47a54c44c9..a36b01d49d 100644 --- a/src/classes/relation/class-relations.php +++ b/src/classes/relation/class-relations.php @@ -61,7 +61,7 @@ public function contains( Relation ...$values ) { return true; } - public function offsetSet( $offset, $value ) { + public function offsetSet( $offset, $value ): void { if ( $offset === null ) { $this->container[] = $value; } else { @@ -69,14 +69,15 @@ public function offsetSet( $offset, $value ) { } } - public function offsetExists( $offset ) { + public function offsetExists( $offset ): bool { return isset( $this->container[ $offset ] ); } - public function offsetUnset( $offset ) { + public function offsetUnset( $offset ): void { unset( $this->container[ $offset ] ); } + #[\ReturnTypeWillChange] public function offsetGet( $offset ) { return isset( $this->container[ $offset ] ) ? $this->container[ $offset ] : null; } @@ -85,6 +86,7 @@ public function toArray() { return $this->container; } + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->container; } diff --git a/src/classes/vocabulary-terms/class-entity-type.php b/src/classes/vocabulary-terms/class-entity-type.php index 58baa8bf3e..74a99e012e 100644 --- a/src/classes/vocabulary-terms/class-entity-type.php +++ b/src/classes/vocabulary-terms/class-entity-type.php @@ -104,8 +104,8 @@ public function save_field( $term_id ) { public function init_ui_and_save_handlers() { $taxonomies = Terms_Compat::get_public_taxonomies(); foreach ( $taxonomies as $taxonomy ) { - add_action( "${taxonomy}_edit_form_fields", array( $this, 'render_ui' ), 1 ); - add_action( "edited_${taxonomy}", array( $this, 'save_field' ) ); + add_action( "{$taxonomy}_edit_form_fields", array( $this, 'render_ui' ), 1 ); + add_action( "edited_{$taxonomy}", array( $this, 'save_field' ) ); } } diff --git a/src/classes/vocabulary-terms/class-term-metabox.php b/src/classes/vocabulary-terms/class-term-metabox.php index fb58a3bba9..dd62285dac 100644 --- a/src/classes/vocabulary-terms/class-term-metabox.php +++ b/src/classes/vocabulary-terms/class-term-metabox.php @@ -54,8 +54,8 @@ public function save_field( $term_id ) { public function init_all_custom_fields() { $taxonomies = Terms_Compat::get_public_taxonomies(); foreach ( $taxonomies as $taxonomy ) { - add_action( "${taxonomy}_edit_form", array( $this, 'render_ui' ), 1 ); - add_action( "edited_${taxonomy}", array( $this, 'save_field' ) ); + add_action( "{$taxonomy}_edit_form", array( $this, 'render_ui' ), 1 ); + add_action( "edited_{$taxonomy}", array( $this, 'save_field' ) ); } } diff --git a/src/classes/vocabulary-terms/jsonld/class-post-jsonld.php b/src/classes/vocabulary-terms/jsonld/class-post-jsonld.php index 0428068c84..8ec672f96e 100644 --- a/src/classes/vocabulary-terms/jsonld/class-post-jsonld.php +++ b/src/classes/vocabulary-terms/jsonld/class-post-jsonld.php @@ -23,7 +23,7 @@ public function init() { public function wl_post_jsonld_array( $data, $post_id ) { - $relations = $data['relations']; + $relations = $data['relations'] ?? null; if ( ! is_a( $relations, 'Wordlift\Relation\Relations' ) ) { return $data; diff --git a/src/includes/class-wordlift-entity-type-service.php b/src/includes/class-wordlift-entity-type-service.php index bc8d4ed08b..787779abe3 100644 --- a/src/includes/class-wordlift-entity-type-service.php +++ b/src/includes/class-wordlift-entity-type-service.php @@ -456,11 +456,15 @@ function ( $item ) { * * @param int $term_id The term ID. * - * @return array An array of custom fields (see `custom_fields` in Wordlift_Schema_Service). + * @return array|null An array of custom fields (see `custom_fields` in Wordlift_Schema_Service). * @since 3.32.0 */ public function get_custom_fields_for_term( $term_id ) { - $selected_entity_types = get_term_meta( $term_id, 'wl_entity_type' ); + $selected_entity_types = get_term_meta( $term_id, 'wl_entity_type' ); + if ( ! is_array( $selected_entity_types ) ) { + return null; + } + $selected_entity_types[] = 'thing'; $selected_entity_types = array_unique( $selected_entity_types ); diff --git a/src/includes/class-wordlift.php b/src/includes/class-wordlift.php index 34e24e2476..9028972e85 100644 --- a/src/includes/class-wordlift.php +++ b/src/includes/class-wordlift.php @@ -1698,7 +1698,9 @@ private function define_admin_hooks( $that ) { * @since 3.23.0 */ add_filter( - 'allowed_block_types', + version_compare( get_bloginfo( 'version' ), '5.8', '>=' ) + ? 'allowed_block_types_all' + : 'allowed_block_types', function ( $value ) { if ( true === $value ) { diff --git a/src/libraries/action-scheduler/.editorconfig b/src/libraries/action-scheduler/.editorconfig deleted file mode 100644 index c3dfa83750..0000000000 --- a/src/libraries/action-scheduler/.editorconfig +++ /dev/null @@ -1,24 +0,0 @@ -# This file is for unifying the coding style for different editors and IDEs -# editorconfig.org - -# WordPress Coding Standards -# https://make.wordpress.org/core/handbook/coding-standards/ - -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -tab_width = 4 -indent_style = tab -insert_final_newline = true -trim_trailing_whitespace = true - -[*.txt] -trim_trailing_whitespace = false - -[*.{md,json,yml}] -trim_trailing_whitespace = false -indent_style = space -indent_size = 2 diff --git a/src/libraries/action-scheduler/.gitattributes b/src/libraries/action-scheduler/.gitattributes deleted file mode 100644 index a7666eaa46..0000000000 --- a/src/libraries/action-scheduler/.gitattributes +++ /dev/null @@ -1,14 +0,0 @@ -docs export-ignore -tests export-ignore -codecov.yml export-ignore -.editorconfig export-ignore -.github export-ignore -.travis.yml export-ignore -.gitattributes export-ignore -.gitignore export-ignore -composer.* export-ignore -Gruntfile.js export-ignore -package.json export-ignore -package-lock.json export-ignore -phpcs.xml export-ignore -phpunit.* export-ignore diff --git a/src/libraries/action-scheduler/.github/release-drafter.yml b/src/libraries/action-scheduler/.github/release-drafter.yml deleted file mode 100644 index 702a6908c5..0000000000 --- a/src/libraries/action-scheduler/.github/release-drafter.yml +++ /dev/null @@ -1,15 +0,0 @@ -template: | - ## next release – date - - - - $CHANGES - - **Added** - **Changed** - **Deprecated** - **Removed** - **Fixed** - **Security** - -change-template: '* $TITLE (PR #$NUMBER)' diff --git a/src/libraries/action-scheduler/.github/workflows/pr-unit-tests.yml b/src/libraries/action-scheduler/.github/workflows/pr-unit-tests.yml deleted file mode 100644 index fc699449bc..0000000000 --- a/src/libraries/action-scheduler/.github/workflows/pr-unit-tests.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Run unit tests on PR -on: - pull_request -jobs: - test: - name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} MU ${{ matrix.multisite }} - timeout-minutes: 15 - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - # We test against the earliest and latest PHP versions for each major supported version. - php: [ '5.6', '7.0', '7.4', '8.0' ] - wp: [ '5.2', '5.3', '5.4', '5.5', '5.6', 'latest' ] - multisite: [ '0', '1' ] - exclude: - # Earlier versions of WordPress do not support PHP 7.4+ - - php: 7.4 - wp: 5.2 - - php: 8.0 - wp: 5.2 - - php: 8.0 - wp: 5.3 - - php: 8.0 - wp: 5.4 - - php: 8.0 - wp: 5.5 - services: - database: - image: mysql:5.6 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - tools: composer - extensions: mysql - coverage: none - - - name: Tool versions - run: | - php --version - composer --version - - - name: Get cached composer directories - uses: actions/cache@v2 - with: - path: ./vendor - key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} - - - name: Setup and install composer - run: composer install - - - name: Setup PHPUnit - run: | - # PHPUnit 5.7 when using PHP 5.6 - 7.0. - if [ "$(php -r "echo version_compare( PHP_VERSION, '7.1', '<' );")" ]; then - curl -L https://phar.phpunit.de/phpunit-5.7.phar -o /tmp/phpunit - OVERWRITE=1 - # PHPUnit 7.5 when using PHP 7.1 - 7.4. - elif [ "$(php -r "echo version_compare( PHP_VERSION, '8.0', '<' );")" ]; then - curl -L https://phar.phpunit.de/phpunit-7.5.phar -o /tmp/phpunit - OVERWRITE=1 - # PHPUnit 7.5 (Custom Fork) when using PHP 8.0+. - else - curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip - unzip -d /tmp/phpunit-fork /tmp/phpunit-7.5-fork.zip - composer --working-dir=/tmp/phpunit-fork/phpunit-add-compatibility-with-php8-to-phpunit-7 install - rm ./vendor/bin/phpunit - ln -sf /tmp/phpunit-fork/phpunit-add-compatibility-with-php8-to-phpunit-7/phpunit ./vendor/bin/phpunit - fi - - if [ $OVERWRITE ]; then - rm ./vendor/bin/phpunit - chmod +x /tmp/phpunit - mv /tmp/phpunit ./vendor/bin/phpunit - fi - - - name: Init DB and WP - run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} - - - name: Run tests - run: | - ./vendor/bin/phpunit --version - WP_MULTISITE=${{ matrix.multisite }} ./vendor/bin/phpunit -c ./tests/phpunit.xml.dist - - - name: Code Coverage - run: | - bash <(curl -s https://codecov.io/bash) diff --git a/src/libraries/action-scheduler/.gitignore b/src/libraries/action-scheduler/.gitignore deleted file mode 100644 index 6056281678..0000000000 --- a/src/libraries/action-scheduler/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -phpunit.xml -vendor -.idea -node_modules -action-scheduler.zip diff --git a/src/libraries/action-scheduler/Gruntfile.js b/src/libraries/action-scheduler/Gruntfile.js deleted file mode 100644 index 4477049321..0000000000 --- a/src/libraries/action-scheduler/Gruntfile.js +++ /dev/null @@ -1,57 +0,0 @@ -module.exports = function( grunt ) { - 'use strict'; - - grunt.initConfig({ - // Check textdomain errors. - checktextdomain: { - options:{ - text_domain: 'action-scheduler', - keywords: [ - '__:1,2d', - '_e:1,2d', - '_x:1,2c,3d', - 'esc_html__:1,2d', - 'esc_html_e:1,2d', - 'esc_html_x:1,2c,3d', - 'esc_attr__:1,2d', - 'esc_attr_e:1,2d', - 'esc_attr_x:1,2c,3d', - '_ex:1,2c,3d', - '_n:1,2,4d', - '_nx:1,2,4c,5d', - '_n_noop:1,2,3d', - '_nx_noop:1,2,3c,4d' - ] - }, - files: { - src: [ - '**/*.php', - '!node_modules/**', - '!tests/**', - '!vendor/**', - '!tmp/**' - ], - expand: true - } - }, - - // PHP Code Sniffer. - phpcs: { - options: { - bin: 'vendor/bin/phpcs' - }, - dist: { - src: [ - '**/*.php', // Include all php files. - '!deprecated/**', - '!node_modules/**', - '!vendor/**' - ] - } - } - }); - - // Load NPM tasks to be used here. - grunt.loadNpmTasks( 'grunt-phpcs' ); - grunt.loadNpmTasks( 'grunt-checktextdomain' ); -}; diff --git a/src/libraries/action-scheduler/action-scheduler.php b/src/libraries/action-scheduler/action-scheduler.php index 7b24d3b87c..b950a70b26 100644 --- a/src/libraries/action-scheduler/action-scheduler.php +++ b/src/libraries/action-scheduler/action-scheduler.php @@ -5,7 +5,7 @@ * Description: A robust scheduling library for use in WordPress plugins. * Author: Automattic * Author URI: https://automattic.com/ - * Version: 3.5.3 + * Version: 3.5.4 * License: GPLv3 * * Copyright 2019 Automattic, Inc. (https://automattic.com/contact/) @@ -26,29 +26,27 @@ * @package ActionScheduler */ -if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_3' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION. +if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_4' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION. if ( ! class_exists( 'ActionScheduler_Versions', false ) ) { require_once __DIR__ . '/classes/ActionScheduler_Versions.php'; add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 ); } - add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_5_dot_3', 0, 0 ); // WRCS: DEFINED_VERSION. + add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_5_dot_4', 0, 0 ); // WRCS: DEFINED_VERSION. /** * Registers this version of Action Scheduler. */ - function action_scheduler_register_3_dot_5_dot_3() { - // WRCS: DEFINED_VERSION. + function action_scheduler_register_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION. $versions = ActionScheduler_Versions::instance(); - $versions->register( '3.5.3', 'action_scheduler_initialize_3_dot_5_dot_3' ); // WRCS: DEFINED_VERSION. + $versions->register( '3.5.4', 'action_scheduler_initialize_3_dot_5_dot_4' ); // WRCS: DEFINED_VERSION. } /** * Initializes this version of Action Scheduler. */ - function action_scheduler_initialize_3_dot_5_dot_3() { - // WRCS: DEFINED_VERSION. + function action_scheduler_initialize_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION. // A final safety check is required even here, because historic versions of Action Scheduler // followed a different pattern (in some unusual cases, we could reach this point and the // ActionScheduler class is already defined—so we need to guard against that). @@ -60,7 +58,7 @@ function action_scheduler_initialize_3_dot_5_dot_3() { // Support usage in themes - load this version if no plugin has loaded a version yet. if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) { - action_scheduler_initialize_3_dot_5_dot_3(); // WRCS: DEFINED_VERSION. + action_scheduler_initialize_3_dot_5_dot_4(); // WRCS: DEFINED_VERSION. do_action( 'action_scheduler_pre_theme_init' ); ActionScheduler_Versions::initialize_latest_version(); } diff --git a/src/libraries/action-scheduler/changelog.txt b/src/libraries/action-scheduler/changelog.txt index a033353693..69aef1ff75 100644 --- a/src/libraries/action-scheduler/changelog.txt +++ b/src/libraries/action-scheduler/changelog.txt @@ -1,5 +1,15 @@ *** Changelog *** += 3.5.4 - 2023-01-17 = +* Add pre filters during action registration. +* Async scheduling. +* Calculate timeouts based on total actions. +* Correctly order the parameters for `ActionScheduler_ActionFactory`'s calls to `single_unique`. +* Fetch action in memory first before releasing claim to avoid deadlock. +* PHP 8.2: declare property to fix creation of dynamic property warning. +* PHP 8.2: fix "Using ${var} in strings is deprecated, use {$var} instead". +* Prevent `undefined variable` warning for `$num_pastdue_actions`. + = 3.5.3 - 2022-11-09 = * Query actions with partial match. diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_ActionClaim.php b/src/libraries/action-scheduler/classes/ActionScheduler_ActionClaim.php index aa0aaa85f2..8b5681620e 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_ActionClaim.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_ActionClaim.php @@ -4,11 +4,11 @@ * Class ActionScheduler_ActionClaim */ class ActionScheduler_ActionClaim { - private $id = ''; + private $id = ''; private $action_ids = array(); public function __construct( $id, array $action_ids ) { - $this->id = $id; + $this->id = $id; $this->action_ids = $action_ids; } @@ -20,4 +20,4 @@ public function get_actions() { return $this->action_ids; } } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php b/src/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php index 0b6b1115f7..8e2e650181 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php @@ -24,7 +24,7 @@ public function get_stored_action( $status, $hook, array $args = array(), Action break; case ActionScheduler_Store::STATUS_CANCELED: $action_class = 'ActionScheduler_CanceledAction'; - if ( $schedule !== null && ! is_a( $schedule, 'ActionScheduler_CanceledSchedule' ) && ! is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) { + if ( ! is_null( $schedule ) && ! is_a( $schedule, 'ActionScheduler_CanceledSchedule' ) && ! is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) { $schedule = new ActionScheduler_CanceledSchedule( $schedule->get_date() ); } break; @@ -52,12 +52,9 @@ public function get_stored_action( $status, $hook, array $args = array(), Action /** * Enqueue an action to run one time, as soon as possible (rather a specific scheduled time). * - * This method creates a new action with the NULLSchedule. This schedule maps to a MySQL datetime string of - * 0000-00-00 00:00:00. This is done to create a psuedo "async action" type that is fully backward compatible. - * Existing queries to claim actions claim by date, meaning actions scheduled for 0000-00-00 00:00:00 will - * always be claimed prior to actions scheduled for a specific date. This makes sure that any async action is - * given priority in queue processing. This has the added advantage of making sure async actions can be - * claimed by both the existing WP Cron and WP CLI runners, as well as a new async request runner. + * This method creates a new action using the NullSchedule. In practice, this results in an action scheduled to + * execute "now". Therefore, it will generally run as soon as possible but is not prioritized ahead of other actions + * that are already past-due. * * @param string $hook The hook to trigger when this action runs. * @param array $args Args to pass when the hook is triggered. @@ -171,6 +168,7 @@ public function cron( $hook, $args = array(), $base_timestamp = null, $schedule return $this->cron_unique( $hook, $args, $base_timestamp, $schedule, $group, false ); } + /** * Create the first instance of an action recurring on a Cron schedule only if there is no pending or running action with same name and params. * @@ -224,7 +222,7 @@ public function repeat( $action ) { $schedule = $action->get_schedule(); $next = $schedule->get_next( as_get_datetime_object() ); - if ( $next === null || ! $schedule->is_recurring() ) { + if ( is_null( $next ) || ! $schedule->is_recurring() ) { throw new InvalidArgumentException( __( 'Invalid action - must be a recurring action.', 'action-scheduler' ) ); } diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_AdminView.php b/src/libraries/action-scheduler/classes/ActionScheduler_AdminView.php index d270820949..b747b0a1b6 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_AdminView.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_AdminView.php @@ -2,12 +2,11 @@ /** * Class ActionScheduler_AdminView - * * @codeCoverageIgnore */ class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated { - private static $admin_view = null; + private static $admin_view = NULL; private static $screen_id = 'tools_page_action-scheduler'; @@ -21,7 +20,7 @@ class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated { public static function instance() { if ( empty( self::$admin_view ) ) { - $class = apply_filters( 'action_scheduler_admin_view_class', 'ActionScheduler_AdminView' ); + $class = apply_filters('action_scheduler_admin_view_class', 'ActionScheduler_AdminView'); self::$admin_view = new $class(); } @@ -79,7 +78,7 @@ public function register_menu() { 'action-scheduler', array( $this, 'render_admin_ui' ) ); - add_action( 'load-' . $hook_suffix, array( $this, 'process_admin_ui' ) ); + add_action( 'load-' . $hook_suffix , array( $this, 'process_admin_ui' ) ); } /** @@ -120,20 +119,20 @@ protected function get_list_table() { */ public function maybe_check_pastdue_actions() { - // Filter to prevent checking actions (ex: inappropriate user). + # Filter to prevent checking actions (ex: inappropriate user). if ( ! apply_filters( 'action_scheduler_check_pastdue_actions', current_user_can( 'manage_options' ) ) ) { return; } - // Get last check transient. + # Get last check transient. $last_check = get_transient( 'action_scheduler_last_pastdue_actions_check' ); - // If transient exists, we're within interval, so bail. + # If transient exists, we're within interval, so bail. if ( ! empty( $last_check ) ) { return; } - // Perform the check. + # Perform the check. $this->check_pastdue_actions(); } @@ -144,31 +143,39 @@ public function maybe_check_pastdue_actions() { */ protected function check_pastdue_actions() { - // Set thresholds. - $threshold_seconds = (int) apply_filters( 'action_scheduler_pastdue_actions_seconds', DAY_IN_SECONDS ); - $threshhold_min = (int) apply_filters( 'action_scheduler_pastdue_actions_min', 1 ); + # Set thresholds. + $threshold_seconds = ( int ) apply_filters( 'action_scheduler_pastdue_actions_seconds', DAY_IN_SECONDS ); + $threshhold_min = ( int ) apply_filters( 'action_scheduler_pastdue_actions_min', 1 ); + + // Set fallback value for past-due actions count. + $num_pastdue_actions = 0; // Allow third-parties to preempt the default check logic. $check = apply_filters( 'action_scheduler_pastdue_actions_check_pre', null ); - // Scheduled actions query arguments. + // If no third-party preempted and there are no past-due actions, return early. + if ( ! is_null( $check ) ) { + return; + } + + # Scheduled actions query arguments. $query_args = array( 'date' => as_get_datetime_object( time() - $threshold_seconds ), 'status' => ActionScheduler_Store::STATUS_PENDING, 'per_page' => $threshhold_min, ); - // If no third-party preempted, run default check. - if ( $check === null ) { - $store = ActionScheduler_Store::instance(); - $num_pastdue_actions = (int) $store->query_actions( $query_args, 'count' ); + # If no third-party preempted, run default check. + if ( is_null( $check ) ) { + $store = ActionScheduler_Store::instance(); + $num_pastdue_actions = ( int ) $store->query_actions( $query_args, 'count' ); - // Check if past-due actions count is greater than or equal to threshold. + # Check if past-due actions count is greater than or equal to threshold. $check = ( $num_pastdue_actions >= $threshhold_min ); - $check = (bool) apply_filters( 'action_scheduler_pastdue_actions_check', $check, $num_pastdue_actions, $threshold_seconds, $threshhold_min ); + $check = ( bool ) apply_filters( 'action_scheduler_pastdue_actions_check', $check, $num_pastdue_actions, $threshold_seconds, $threshhold_min ); } - // If check failed, set transient and abort. + # If check failed, set transient and abort. if ( ! boolval( $check ) ) { $interval = apply_filters( 'action_scheduler_pastdue_actions_check_interval', round( $threshold_seconds / 4 ), $threshold_seconds ); set_transient( 'action_scheduler_last_pastdue_actions_check', time(), $interval ); @@ -176,16 +183,13 @@ protected function check_pastdue_actions() { return; } - $actions_url = add_query_arg( - array( - 'page' => 'action-scheduler', - 'status' => 'past-due', - 'order' => 'asc', - ), - admin_url( 'tools.php' ) - ); + $actions_url = add_query_arg( array( + 'page' => 'action-scheduler', + 'status' => 'past-due', + 'order' => 'asc', + ), admin_url( 'tools.php' ) ); - // Print notice. + # Print notice. echo '

'; printf( _n( @@ -200,7 +204,7 @@ protected function check_pastdue_actions() { ); echo '

'; - // Facilitate third-parties to evaluate and print notices. + # Facilitate third-parties to evaluate and print notices. do_action( 'action_scheduler_pastdue_actions_extra_notices', $query_args ); } diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_Compatibility.php b/src/libraries/action-scheduler/classes/ActionScheduler_Compatibility.php index a6bb9fe09d..85e0ed9da3 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_Compatibility.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_Compatibility.php @@ -86,7 +86,7 @@ public static function raise_memory_limit() { * @param int $limit The time limit in seconds. */ public static function raise_time_limit( $limit = 0 ) { - $limit = (int) $limit; + $limit = (int) $limit; $max_execution_time = (int) ini_get( 'max_execution_time' ); /* diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_DataController.php b/src/libraries/action-scheduler/classes/ActionScheduler_DataController.php index e445387836..eb69847b5f 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_DataController.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_DataController.php @@ -18,10 +18,10 @@ class ActionScheduler_DataController { const DATASTORE_CLASS = 'ActionScheduler_DBStore'; /** Logger data store class name. */ - const LOGGER_CLASS = 'ActionScheduler_DBLogger'; + const LOGGER_CLASS = 'ActionScheduler_DBLogger'; /** Migration status option name. */ - const STATUS_FLAG = 'action_scheduler_migration_status'; + const STATUS_FLAG = 'action_scheduler_migration_status'; /** Migration status option value. */ const STATUS_COMPLETE = 'complete'; diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_FatalErrorMonitor.php b/src/libraries/action-scheduler/classes/ActionScheduler_FatalErrorMonitor.php index 2bd28e247a..5fa67d681b 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_FatalErrorMonitor.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_FatalErrorMonitor.php @@ -5,9 +5,9 @@ */ class ActionScheduler_FatalErrorMonitor { /** @var ActionScheduler_ActionClaim */ - private $claim = null; + private $claim = NULL; /** @var ActionScheduler_Store */ - private $store = null; + private $store = NULL; private $action_id = 0; public function __construct( ActionScheduler_Store $store ) { @@ -18,19 +18,19 @@ public function attach( ActionScheduler_ActionClaim $claim ) { $this->claim = $claim; add_action( 'shutdown', array( $this, 'handle_unexpected_shutdown' ) ); add_action( 'action_scheduler_before_execute', array( $this, 'track_current_action' ), 0, 1 ); - add_action( 'action_scheduler_after_execute', array( $this, 'untrack_action' ), 0, 0 ); - add_action( 'action_scheduler_execution_ignored', array( $this, 'untrack_action' ), 0, 0 ); - add_action( 'action_scheduler_failed_execution', array( $this, 'untrack_action' ), 0, 0 ); + add_action( 'action_scheduler_after_execute', array( $this, 'untrack_action' ), 0, 0 ); + add_action( 'action_scheduler_execution_ignored', array( $this, 'untrack_action' ), 0, 0 ); + add_action( 'action_scheduler_failed_execution', array( $this, 'untrack_action' ), 0, 0 ); } public function detach() { - $this->claim = null; + $this->claim = NULL; $this->untrack_action(); remove_action( 'shutdown', array( $this, 'handle_unexpected_shutdown' ) ); remove_action( 'action_scheduler_before_execute', array( $this, 'track_current_action' ), 0 ); - remove_action( 'action_scheduler_after_execute', array( $this, 'untrack_action' ), 0 ); - remove_action( 'action_scheduler_execution_ignored', array( $this, 'untrack_action' ), 0 ); - remove_action( 'action_scheduler_failed_execution', array( $this, 'untrack_action' ), 0 ); + remove_action( 'action_scheduler_after_execute', array( $this, 'untrack_action' ), 0 ); + remove_action( 'action_scheduler_execution_ignored', array( $this, 'untrack_action' ), 0 ); + remove_action( 'action_scheduler_failed_execution', array( $this, 'untrack_action' ), 0 ); } public function track_current_action( $action_id ) { @@ -44,7 +44,7 @@ public function untrack_action() { public function handle_unexpected_shutdown() { if ( $error = error_get_last() ) { if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ) ) ) { - if ( ! empty( $this->action_id ) ) { + if ( !empty($this->action_id) ) { $this->store->mark_failure( $this->action_id ); do_action( 'action_scheduler_unexpected_shutdown', $this->action_id, $error ); } diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_ListTable.php b/src/libraries/action-scheduler/classes/ActionScheduler_ListTable.php index 7a14bea1c6..9e631f7543 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_ListTable.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_ListTable.php @@ -2,7 +2,6 @@ /** * Implements the admin view of the actions. - * * @codeCoverageIgnore */ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable { @@ -77,8 +76,8 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable { /** * Sets the current data store object into `store->action` and initialises the object. * - * @param ActionScheduler_Store $store - * @param ActionScheduler_Logger $logger + * @param ActionScheduler_Store $store + * @param ActionScheduler_Logger $logger * @param ActionScheduler_QueueRunner $runner */ public function __construct( ActionScheduler_Store $store, ActionScheduler_Logger $logger, ActionScheduler_QueueRunner $runner ) { @@ -126,9 +125,9 @@ public function __construct( ActionScheduler_Store $store, ActionScheduler_Logge $this->row_actions = array( 'hook' => array( - 'run' => array( - 'name' => __( 'Run', 'action-scheduler' ), - 'desc' => __( 'Process the action now as if it were run as part of a queue', 'action-scheduler' ), + 'run' => array( + 'name' => __( 'Run', 'action-scheduler' ), + 'desc' => __( 'Process the action now as if it were run as part of a queue', 'action-scheduler' ), ), 'cancel' => array( 'name' => __( 'Cancel', 'action-scheduler' ), @@ -235,7 +234,7 @@ private static function human_interval( $interval, $periods_to_include = 2 ) { if ( ! empty( $output ) ) { $output .= ' '; } - $output .= sprintf( _n( self::$time_periods[ $time_period_index ]['names'][0], self::$time_periods[ $time_period_index ]['names'][1], $periods_in_interval, 'action-scheduler' ), $periods_in_interval ); + $output .= sprintf( _n( self::$time_periods[ $time_period_index ]['names'][0], self::$time_periods[ $time_period_index ]['names'][1], $periods_in_interval, 'action-scheduler' ), $periods_in_interval ); $seconds_remaining -= $periods_in_interval * self::$time_periods[ $time_period_index ]['seconds']; $periods_included++; } @@ -313,7 +312,7 @@ public function column_log_entries( array $row ) { * Prints the logs entries inline. We do so to avoid loading Javascript and other hacks to show it in a modal. * * @param ActionScheduler_LogEntry $log_entry - * @param DateTimezone $timezone + * @param DateTimezone $timezone * @return string */ protected function get_log_entry_html( ActionScheduler_LogEntry $log_entry, DateTimezone $timezone ) { @@ -331,7 +330,7 @@ protected function get_log_entry_html( ActionScheduler_LogEntry $log_entry, Date * @return string */ protected function maybe_render_actions( $row, $column_name ) { - if ( 'pending' === strtolower( $row['status_name'] ) ) { + if ( 'pending' === strtolower( $row[ 'status_name' ] ) ) { return parent::maybe_render_actions( $row, $column_name ); } @@ -362,7 +361,7 @@ public function display_admin_notices() { if ( ! in_array( $wpdb->prefix . $table_name, $found_tables ) ) { $this->admin_notices[] = array( 'class' => 'error', - 'message' => __( 'It appears one or more database tables were missing. Attempting to re-create the missing table(s).', 'action-scheduler' ), + 'message' => __( 'It appears one or more database tables were missing. Attempting to re-create the missing table(s).' , 'action-scheduler' ), ); $this->recreate_tables(); parent::display_admin_notices(); @@ -393,7 +392,7 @@ public function display_admin_notices() { // No lock set or lock expired if ( false === $async_request_lock_expiration || $async_request_lock_expiration < time() ) { - $in_progress_url = add_query_arg( 'status', 'in-progress', remove_query_arg( 'status' ) ); + $in_progress_url = add_query_arg( 'status', 'in-progress', remove_query_arg( 'status' ) ); /* translators: %s: process URL */ $async_request_message = sprintf( __( 'A new queue has begun processing. View actions in-progress »', 'action-scheduler' ), esc_url( $in_progress_url ) ); } else { @@ -412,20 +411,20 @@ public function display_admin_notices() { if ( is_array( $notification ) ) { delete_transient( 'action_scheduler_admin_notice' ); - $action = $this->store->fetch_action( $notification['action_id'] ); + $action = $this->store->fetch_action( $notification['action_id'] ); $action_hook_html = '' . $action->get_hook() . ''; if ( 1 == $notification['success'] ) { $class = 'updated'; switch ( $notification['row_action_type'] ) { - case 'run': + case 'run' : /* translators: %s: action HTML */ $action_message_html = sprintf( __( 'Successfully executed action: %s', 'action-scheduler' ), $action_hook_html ); break; - case 'cancel': + case 'cancel' : /* translators: %s: action HTML */ $action_message_html = sprintf( __( 'Successfully canceled action: %s', 'action-scheduler' ), $action_hook_html ); break; - default: + default : /* translators: %s: action HTML */ $action_message_html = sprintf( __( 'Successfully processed change for action: %s', 'action-scheduler' ), $action_hook_html ); break; @@ -498,7 +497,7 @@ protected function get_schedule_display_string( ActionScheduler_Schedule $schedu * Deletes actions based on their ID. This is the handler for the bulk delete. It assumes the data * properly validated by the callee and it will delete the actions without any extra validation. * - * @param array $ids + * @param array $ids * @param string $ids_sql Inherited and unused */ protected function bulk_delete( array $ids, $ids_sql ) { @@ -548,23 +547,23 @@ protected function recreate_tables() { /** * Implements the logic behind processing an action once an action link is clicked on the list table. * - * @param int $action_id + * @param int $action_id * @param string $row_action_type The type of action to perform on the action. */ protected function process_row_action( $action_id, $row_action_type ) { try { switch ( $row_action_type ) { - case 'run': + case 'run' : $this->runner->process_action( $action_id, 'Admin List Table' ); break; - case 'cancel': + case 'cancel' : $this->store->cancel_action( $action_id ); break; } - $success = 1; + $success = 1; $error_message = ''; } catch ( Exception $e ) { - $success = 0; + $success = 0; $error_message = $e->getMessage(); } @@ -627,13 +626,11 @@ public function prepare_items() { ); } - $this->set_pagination_args( - array( - 'total_items' => $total_items, - 'per_page' => $per_page, - 'total_pages' => ceil( $total_items / $per_page ), - ) - ); + $this->set_pagination_args( array( + 'total_items' => $total_items, + 'per_page' => $per_page, + 'total_pages' => ceil( $total_items / $per_page ), + ) ); } /** diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_LogEntry.php b/src/libraries/action-scheduler/classes/ActionScheduler_LogEntry.php index d958a56159..649636debf 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_LogEntry.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_LogEntry.php @@ -8,12 +8,12 @@ class ActionScheduler_LogEntry { /** * @var int $action_id */ - protected $action_id = ''; + protected $action_id = ''; /** * @var string $message */ - protected $message = ''; + protected $message = ''; /** * @var Datetime $date @@ -23,8 +23,8 @@ class ActionScheduler_LogEntry { /** * Constructor * - * @param mixed $action_id Action ID - * @param string $message Message + * @param mixed $action_id Action ID + * @param string $message Message * @param Datetime $date Datetime object with the time when this log entry was created. If this parameter is * not provided a new Datetime object (with current time) will be created. */ @@ -44,7 +44,7 @@ public function __construct( $action_id, $message, $date = null ) { $this->action_id = $action_id; $this->message = $message; - $this->date = $date ? $date : new Datetime(); + $this->date = $date ? $date : new Datetime; } /** diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_NullLogEntry.php b/src/libraries/action-scheduler/classes/ActionScheduler_NullLogEntry.php index b2c8df658c..6f8f218aab 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_NullLogEntry.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_NullLogEntry.php @@ -8,4 +8,4 @@ public function __construct( $action_id = '', $message = '' ) { // nothing to see here } } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_OptionLock.php b/src/libraries/action-scheduler/classes/ActionScheduler_OptionLock.php index 22b3da88cf..4bc9a3fc26 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_OptionLock.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_OptionLock.php @@ -5,7 +5,6 @@ * for up-to a given duration. * * Class ActionScheduler_OptionLock - * * @since 3.0.0 */ class ActionScheduler_OptionLock extends ActionScheduler_Lock { diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php b/src/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php index 3b666d9b47..49cd44bb2a 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php @@ -25,13 +25,13 @@ class ActionScheduler_QueueCleaner { * @param int $batch_size The batch size. */ public function __construct( ActionScheduler_Store $store = null, $batch_size = 20 ) { - $this->store = $store ? $store : ActionScheduler_Store::instance(); + $this->store = $store ? $store : ActionScheduler_Store::instance(); $this->batch_size = $batch_size; } public function delete_old_actions() { $lifespan = apply_filters( 'action_scheduler_retention_period', $this->month_in_seconds ); - $cutoff = as_get_datetime_object( $lifespan . ' seconds ago' ); + $cutoff = as_get_datetime_object($lifespan.' seconds ago'); $statuses_to_purge = array( ActionScheduler_Store::STATUS_COMPLETE, @@ -39,15 +39,13 @@ public function delete_old_actions() { ); foreach ( $statuses_to_purge as $status ) { - $actions_to_delete = $this->store->query_actions( - array( - 'status' => $status, - 'modified' => $cutoff, - 'modified_compare' => '<=', - 'per_page' => $this->get_batch_size(), - 'orderby' => 'none', - ) - ); + $actions_to_delete = $this->store->query_actions( array( + 'status' => $status, + 'modified' => $cutoff, + 'modified_compare' => '<=', + 'per_page' => $this->get_batch_size(), + 'orderby' => 'none', + ) ); foreach ( $actions_to_delete as $action_id ) { try { @@ -86,17 +84,15 @@ public function reset_timeouts( $time_limit = 300 ) { if ( $timeout < 0 ) { return; } - $cutoff = as_get_datetime_object( $timeout . ' seconds ago' ); - $actions_to_reset = $this->store->query_actions( - array( - 'status' => ActionScheduler_Store::STATUS_PENDING, - 'modified' => $cutoff, - 'modified_compare' => '<=', - 'claimed' => true, - 'per_page' => $this->get_batch_size(), - 'orderby' => 'none', - ) - ); + $cutoff = as_get_datetime_object($timeout.' seconds ago'); + $actions_to_reset = $this->store->query_actions( array( + 'status' => ActionScheduler_Store::STATUS_PENDING, + 'modified' => $cutoff, + 'modified_compare' => '<=', + 'claimed' => true, + 'per_page' => $this->get_batch_size(), + 'orderby' => 'none', + ) ); foreach ( $actions_to_reset as $action_id ) { $this->store->unclaim_action( $action_id ); @@ -118,16 +114,14 @@ public function mark_failures( $time_limit = 300 ) { if ( $timeout < 0 ) { return; } - $cutoff = as_get_datetime_object( $timeout . ' seconds ago' ); - $actions_to_reset = $this->store->query_actions( - array( - 'status' => ActionScheduler_Store::STATUS_RUNNING, - 'modified' => $cutoff, - 'modified_compare' => '<=', - 'per_page' => $this->get_batch_size(), - 'orderby' => 'none', - ) - ); + $cutoff = as_get_datetime_object($timeout.' seconds ago'); + $actions_to_reset = $this->store->query_actions( array( + 'status' => ActionScheduler_Store::STATUS_RUNNING, + 'modified' => $cutoff, + 'modified_compare' => '<=', + 'per_page' => $this->get_batch_size(), + 'orderby' => 'none', + ) ); foreach ( $actions_to_reset as $action_id ) { $this->store->mark_failure( $action_id ); diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php b/src/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php index 38c02c6110..b890dca137 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php @@ -14,13 +14,16 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner { /** @var ActionScheduler_QueueRunner */ private static $runner = null; + /** @var int */ + private $processed_actions_count = 0; + /** * @return ActionScheduler_QueueRunner * @codeCoverageIgnore */ public static function instance() { - if ( empty( self::$runner ) ) { - $class = apply_filters( 'action_scheduler_queue_runner_class', 'ActionScheduler_QueueRunner' ); + if ( empty(self::$runner) ) { + $class = apply_filters('action_scheduler_queue_runner_class', 'ActionScheduler_QueueRunner'); self::$runner = new $class(); } return self::$runner; @@ -36,7 +39,7 @@ public static function instance() { public function __construct( ActionScheduler_Store $store = null, ActionScheduler_FatalErrorMonitor $monitor = null, ActionScheduler_QueueCleaner $cleaner = null, ActionScheduler_AsyncRequest_QueueRunner $async_request = null ) { parent::__construct( $store, $monitor, $cleaner ); - if ( $async_request === null ) { + if ( is_null( $async_request ) ) { $async_request = new ActionScheduler_AsyncRequest_QueueRunner( $this->store ); } @@ -114,7 +117,6 @@ public function maybe_dispatch_async_request() { * that was the only context in which this method was run, and the self::WP_CRON_HOOK hook had no context * passed along with it. New code calling this method directly, or by triggering the self::WP_CRON_HOOK, * should set a context as the first parameter. For an example of this, refer to the code seen in - * * @see ActionScheduler_AsyncRequest_QueueRunner::handle() * * @param string $context Optional identifer for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron' @@ -126,17 +128,18 @@ public function run( $context = 'WP Cron' ) { ActionScheduler_Compatibility::raise_time_limit( $this->get_time_limit() ); do_action( 'action_scheduler_before_process_queue' ); $this->run_cleanup(); - $processed_actions = 0; + + $this->processed_actions_count = 0; if ( false === $this->has_maximum_concurrent_batches() ) { $batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 ); do { - $processed_actions_in_batch = $this->do_batch( $batch_size, $context ); - $processed_actions += $processed_actions_in_batch; - } while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $processed_actions ) ); // keep going until we run out of actions, time, or memory + $processed_actions_in_batch = $this->do_batch( $batch_size, $context ); + $this->processed_actions_count += $processed_actions_in_batch; + } while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $this->processed_actions_count ) ); // keep going until we run out of actions, time, or memory } do_action( 'action_scheduler_after_process_queue' ); - return $processed_actions; + return $this->processed_actions_count; } /** @@ -145,14 +148,14 @@ public function run( $context = 'WP Cron' ) { * Actions are processed by claiming a set of pending actions then processing each one until either the batch * size is completed, or memory or time limits are reached, defined by @see $this->batch_limits_exceeded(). * - * @param int $size The maximum number of actions to process in the batch. + * @param int $size The maximum number of actions to process in the batch. * @param string $context Optional identifer for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron' * Generally, this should be capitalised and not localised as it's a proper noun. * @return int The number of actions processed. */ protected function do_batch( $size = 100, $context = '' ) { - $claim = $this->store->stake_claim( $size ); - $this->monitor->attach( $claim ); + $claim = $this->store->stake_claim($size); + $this->monitor->attach($claim); $processed_actions = 0; foreach ( $claim->get_actions() as $action_id ) { @@ -163,11 +166,11 @@ protected function do_batch( $size = 100, $context = '' ) { $this->process_action( $action_id, $context ); $processed_actions++; - if ( $this->batch_limits_exceeded( $processed_actions ) ) { + if ( $this->batch_limits_exceeded( $processed_actions + $this->processed_actions_count ) ) { break; } } - $this->store->release_claim( $claim ); + $this->store->release_claim($claim); $this->monitor->detach(); $this->clear_caches(); return $processed_actions; diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_Versions.php b/src/libraries/action-scheduler/classes/ActionScheduler_Versions.php index d8e538b1aa..915c2e6329 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_Versions.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_Versions.php @@ -7,16 +7,16 @@ class ActionScheduler_Versions { /** * @var ActionScheduler_Versions */ - private static $instance = null; + private static $instance = NULL; private $versions = array(); public function register( $version_string, $initialization_callback ) { - if ( isset( $this->versions[ $version_string ] ) ) { - return false; + if ( isset($this->versions[$version_string]) ) { + return FALSE; } - $this->versions[ $version_string ] = $initialization_callback; - return true; + $this->versions[$version_string] = $initialization_callback; + return TRUE; } public function get_versions() { @@ -24,20 +24,20 @@ public function get_versions() { } public function latest_version() { - $keys = array_keys( $this->versions ); - if ( empty( $keys ) ) { + $keys = array_keys($this->versions); + if ( empty($keys) ) { return false; } uasort( $keys, 'version_compare' ); - return end( $keys ); + return end($keys); } public function latest_version_callback() { $latest = $this->latest_version(); - if ( empty( $latest ) || ! isset( $this->versions[ $latest ] ) ) { + if ( empty($latest) || !isset($this->versions[$latest]) ) { return '__return_null'; } - return $this->versions[ $latest ]; + return $this->versions[$latest]; } /** @@ -45,7 +45,7 @@ public function latest_version_callback() { * @codeCoverageIgnore */ public static function instance() { - if ( empty( self::$instance ) ) { + if ( empty(self::$instance) ) { self::$instance = new self(); } return self::$instance; @@ -56,7 +56,7 @@ public static function instance() { */ public static function initialize_latest_version() { $self = self::instance(); - call_user_func( $self->latest_version_callback() ); + call_user_func($self->latest_version_callback()); } } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/ActionScheduler_WPCommentCleaner.php b/src/libraries/action-scheduler/classes/ActionScheduler_WPCommentCleaner.php index 498e4f0104..1ba552c50b 100644 --- a/src/libraries/action-scheduler/classes/ActionScheduler_WPCommentCleaner.php +++ b/src/libraries/action-scheduler/classes/ActionScheduler_WPCommentCleaner.php @@ -66,13 +66,7 @@ public static function has_logs() { * Attached to the migration complete hook 'action_scheduler/migration_complete'. */ public static function maybe_schedule_cleanup() { - if ( (bool) get_comments( - array( - 'type' => ActionScheduler_wpCommentLogger::TYPE, - 'number' => 1, - 'fields' => 'ids', - ) - ) ) { + if ( (bool) get_comments( array( 'type' => ActionScheduler_wpCommentLogger::TYPE, 'number' => 1, 'fields' => 'ids' ) ) ) { update_option( self::$has_logs_option_key, 'yes' ); if ( ! as_next_scheduled_action( self::$cleanup_hook ) ) { @@ -86,13 +80,7 @@ public static function maybe_schedule_cleanup() { */ public static function delete_all_action_comments() { global $wpdb; - $wpdb->delete( - $wpdb->comments, - array( - 'comment_type' => ActionScheduler_wpCommentLogger::TYPE, - 'comment_agent' => ActionScheduler_wpCommentLogger::AGENT, - ) - ); + $wpdb->delete( $wpdb->comments, array( 'comment_type' => ActionScheduler_wpCommentLogger::TYPE, 'comment_agent' => ActionScheduler_wpCommentLogger::AGENT ) ); delete_option( self::$has_logs_option_key ); } @@ -102,7 +90,7 @@ public static function delete_all_action_comments() { public static function register_admin_notice() { add_action( 'admin_notices', array( __CLASS__, 'print_admin_notice' ) ); } - + /** * Prints details about the orphaned action logs and includes information on where to learn more. */ diff --git a/src/libraries/action-scheduler/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php b/src/libraries/action-scheduler/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php index 06a27a8574..c33de68672 100644 --- a/src/libraries/action-scheduler/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php +++ b/src/libraries/action-scheduler/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php @@ -143,7 +143,7 @@ public function before_execute( $action_id ) { * * @author Jeremy Pry * - * @param int $action_id + * @param int $action_id * @param null|ActionScheduler_Action $action The instance of the action. Default to null for backward compatibility. */ public function after_execute( $action_id, $action = null ) { diff --git a/src/libraries/action-scheduler/classes/WP_CLI/Migration_Command.php b/src/libraries/action-scheduler/classes/WP_CLI/Migration_Command.php index 73e2ccc8cf..066697e4e0 100644 --- a/src/libraries/action-scheduler/classes/WP_CLI/Migration_Command.php +++ b/src/libraries/action-scheduler/classes/WP_CLI/Migration_Command.php @@ -1,10 +1,12 @@ 'Migrates actions to the DB tables store', - 'synopsis' => array( - array( - 'type' => 'assoc', - 'name' => 'batch-size', - 'optional' => true, - 'default' => 100, - 'description' => 'The number of actions to process in each batch', - ), - array( - 'type' => 'assoc', - 'name' => 'free-memory-on', - 'optional' => true, - 'default' => 50, - 'description' => 'The number of actions to process between freeing memory. 0 disables freeing memory', - ), - array( - 'type' => 'assoc', - 'name' => 'pause', - 'optional' => true, - 'default' => 0, - 'description' => 'The number of seconds to pause when freeing memory', - ), - array( - 'type' => 'flag', - 'name' => 'dry-run', - 'optional' => true, - 'description' => 'Reports on the actions that would have been migrated, but does not change any data', - ), - ), - ) - ); + WP_CLI::add_command( 'action-scheduler migrate', [ $this, 'migrate' ], [ + 'shortdesc' => 'Migrates actions to the DB tables store', + 'synopsis' => [ + [ + 'type' => 'assoc', + 'name' => 'batch-size', + 'optional' => true, + 'default' => 100, + 'description' => 'The number of actions to process in each batch', + ], + [ + 'type' => 'assoc', + 'name' => 'free-memory-on', + 'optional' => true, + 'default' => 50, + 'description' => 'The number of actions to process between freeing memory. 0 disables freeing memory', + ], + [ + 'type' => 'assoc', + 'name' => 'pause', + 'optional' => true, + 'default' => 0, + 'description' => 'The number of seconds to pause when freeing memory', + ], + [ + 'type' => 'flag', + 'name' => 'dry-run', + 'optional' => true, + 'description' => 'Reports on the actions that would have been migrated, but does not change any data', + ], + ], + ] ); } /** @@ -83,14 +81,14 @@ public function migrate( $positional_args, $assoc_args ) { $runner = new Runner( $config ); $runner->init_destination(); - $batch_size = isset( $assoc_args['batch-size'] ) ? (int) $assoc_args['batch-size'] : 100; - $free_on = isset( $assoc_args['free-memory-on'] ) ? (int) $assoc_args['free-memory-on'] : 50; - $sleep = isset( $assoc_args['pause'] ) ? (int) $assoc_args['pause'] : 0; + $batch_size = isset( $assoc_args[ 'batch-size' ] ) ? (int) $assoc_args[ 'batch-size' ] : 100; + $free_on = isset( $assoc_args[ 'free-memory-on' ] ) ? (int) $assoc_args[ 'free-memory-on' ] : 50; + $sleep = isset( $assoc_args[ 'pause' ] ) ? (int) $assoc_args[ 'pause' ] : 0; \ActionScheduler_DataController::set_free_ticks( $free_on ); \ActionScheduler_DataController::set_sleep_time( $sleep ); do { - $actions_processed = $runner->run( $batch_size ); + $actions_processed = $runner->run( $batch_size ); $this->total_processed += $actions_processed; } while ( $actions_processed > 0 ); @@ -111,15 +109,12 @@ public function migrate( $positional_args, $assoc_args ) { * @return ActionScheduler\Migration\Config */ private function get_migration_config( $args ) { - $args = wp_parse_args( - $args, - array( - 'dry-run' => false, - ) - ); + $args = wp_parse_args( $args, [ + 'dry-run' => false, + ] ); $config = Controller::instance()->get_migration_config_object(); - $config->set_dry_run( ! empty( $args['dry-run'] ) ); + $config->set_dry_run( ! empty( $args[ 'dry-run' ] ) ); return $config; } @@ -128,61 +123,26 @@ private function get_migration_config( $args ) { * Hook command line logging into migration actions. */ private function init_logging() { - add_action( - 'action_scheduler/migrate_action_dry_run', - function ( $action_id ) { - WP_CLI::debug( sprintf( 'Dry-run: migrated action %d', $action_id ) ); - }, - 10, - 1 - ); - add_action( - 'action_scheduler/no_action_to_migrate', - function ( $action_id ) { - WP_CLI::debug( sprintf( 'No action found to migrate for ID %d', $action_id ) ); - }, - 10, - 1 - ); - add_action( - 'action_scheduler/migrate_action_failed', - function ( $action_id ) { - WP_CLI::warning( sprintf( 'Failed migrating action with ID %d', $action_id ) ); - }, - 10, - 1 - ); - add_action( - 'action_scheduler/migrate_action_incomplete', - function ( $source_id, $destination_id ) { - WP_CLI::warning( sprintf( 'Unable to remove source action with ID %d after migrating to new ID %d', $source_id, $destination_id ) ); - }, - 10, - 2 - ); - add_action( - 'action_scheduler/migrated_action', - function ( $source_id, $destination_id ) { - WP_CLI::debug( sprintf( 'Migrated source action with ID %d to new store with ID %d', $source_id, $destination_id ) ); - }, - 10, - 2 - ); - add_action( - 'action_scheduler/migration_batch_starting', - function ( $batch ) { - WP_CLI::debug( 'Beginning migration of batch: ' . print_r( $batch, true ) ); - }, - 10, - 1 - ); - add_action( - 'action_scheduler/migration_batch_complete', - function ( $batch ) { - WP_CLI::log( sprintf( 'Completed migration of %d actions', count( $batch ) ) ); - }, - 10, - 1 - ); + add_action( 'action_scheduler/migrate_action_dry_run', function ( $action_id ) { + WP_CLI::debug( sprintf( 'Dry-run: migrated action %d', $action_id ) ); + }, 10, 1 ); + add_action( 'action_scheduler/no_action_to_migrate', function ( $action_id ) { + WP_CLI::debug( sprintf( 'No action found to migrate for ID %d', $action_id ) ); + }, 10, 1 ); + add_action( 'action_scheduler/migrate_action_failed', function ( $action_id ) { + WP_CLI::warning( sprintf( 'Failed migrating action with ID %d', $action_id ) ); + }, 10, 1 ); + add_action( 'action_scheduler/migrate_action_incomplete', function ( $source_id, $destination_id ) { + WP_CLI::warning( sprintf( 'Unable to remove source action with ID %d after migrating to new ID %d', $source_id, $destination_id ) ); + }, 10, 2 ); + add_action( 'action_scheduler/migrated_action', function ( $source_id, $destination_id ) { + WP_CLI::debug( sprintf( 'Migrated source action with ID %d to new store with ID %d', $source_id, $destination_id ) ); + }, 10, 2 ); + add_action( 'action_scheduler/migration_batch_starting', function ( $batch ) { + WP_CLI::debug( 'Beginning migration of batch: ' . print_r( $batch, true ) ); + }, 10, 1 ); + add_action( 'action_scheduler/migration_batch_complete', function ( $batch ) { + WP_CLI::log( sprintf( 'Completed migration of %d actions', count( $batch ) ) ); + }, 10, 1 ); } } diff --git a/src/libraries/action-scheduler/classes/WP_CLI/ProgressBar.php b/src/libraries/action-scheduler/classes/WP_CLI/ProgressBar.php index b801d2b8f2..c86c74e838 100644 --- a/src/libraries/action-scheduler/classes/WP_CLI/ProgressBar.php +++ b/src/libraries/action-scheduler/classes/WP_CLI/ProgressBar.php @@ -38,7 +38,7 @@ class ProgressBar { * @param string $message Text to display before the progress bar. * @param integer $count Total number of ticks to be performed. * @param integer $interval Optional. The interval in milliseconds between updates. Default 100. - * + * * @throws Exception When this is not run within WP CLI */ public function __construct( $message, $count, $interval = 100 ) { diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler.php index 4558e5a797..e8873f11e9 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler.php @@ -1,22 +1,21 @@ date_create( 'now', timezone_open( 'UTC' ) )->format( 'Y-m-d H:i:s' ), 'date_compare' => '<', 'per_page' => 1, - 'offset' => $consistent_failure_threshold - 1, + 'offset' => $consistent_failure_threshold - 1 ); $first_failing_action_id = $this->store->query_actions( $query_args ); @@ -223,9 +223,14 @@ protected function get_execution_time() { * @return bool */ protected function time_likely_to_be_exceeded( $processed_actions ) { + $execution_time = $this->get_execution_time(); + $max_execution_time = $this->get_time_limit(); + + // Safety against division by zero errors. + if ( 0 === $processed_actions ) { + return $execution_time >= $max_execution_time; + } - $execution_time = $this->get_execution_time(); - $max_execution_time = $this->get_time_limit(); $time_per_action = $execution_time / $processed_actions; $estimated_time = $execution_time + ( $time_per_action * 3 ); $likely_to_be_exceeded = $estimated_time > $max_execution_time; diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_RecurringSchedule.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_RecurringSchedule.php index 430d91afa2..131d4757d8 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_RecurringSchedule.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_RecurringSchedule.php @@ -15,14 +15,14 @@ abstract class ActionScheduler_Abstract_RecurringSchedule extends ActionSchedule * * @var DateTime */ - private $first_date = null; + private $first_date = NULL; /** * Timestamp equivalent of @see $this->first_date * * @var int */ - protected $first_timestamp = null; + protected $first_timestamp = NULL; /** * The recurrance between each time an action is run using this schedule. @@ -35,8 +35,8 @@ abstract class ActionScheduler_Abstract_RecurringSchedule extends ActionSchedule protected $recurrence; /** - * @param DateTime $date The date & time to run the action. - * @param mixed $recurrence The data used to determine the schedule's recurrance. + * @param DateTime $date The date & time to run the action. + * @param mixed $recurrence The data used to determine the schedule's recurrance. * @param DateTime|null $first (Optional) The date & time the first instance of this interval schedule ran. Default null, meaning this is the first instance. */ public function __construct( DateTime $date, $recurrence, DateTime $first = null ) { @@ -70,19 +70,15 @@ public function get_recurrence() { /** * For PHP 5.2 compat, since DateTime objects can't be serialized - * * @return array */ public function __sleep() { - $sleep_params = parent::__sleep(); + $sleep_params = parent::__sleep(); $this->first_timestamp = $this->first_date->getTimestamp(); - return array_merge( - $sleep_params, - array( - 'first_timestamp', - 'recurrence', - ) - ); + return array_merge( $sleep_params, array( + 'first_timestamp', + 'recurrence' + ) ); } /** diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schedule.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schedule.php index 369249f6b8..2631ef554f 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schedule.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schedule.php @@ -10,14 +10,14 @@ abstract class ActionScheduler_Abstract_Schedule extends ActionScheduler_Schedul * * @var DateTime */ - private $scheduled_date = null; + private $scheduled_date = NULL; /** * Timestamp equivalent of @see $this->scheduled_date * * @var int */ - protected $scheduled_timestamp = null; + protected $scheduled_timestamp = NULL; /** * @param DateTime $date The date & time to run the action. @@ -67,7 +67,6 @@ public function get_date() { /** * For PHP 5.2 compat, since DateTime objects can't be serialized - * * @return array */ public function __sleep() { diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schema.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schema.php index bf0ee92448..2334fda10e 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schema.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Abstract_Schema.php @@ -1,5 +1,6 @@ get_table_definition( $table ); if ( $definition ) { $updated = dbDelta( $definition ); @@ -147,7 +148,7 @@ private function update_table( $table ) { * table prefix for the current blog */ protected function get_full_table_name( $table ) { - return $GLOBALS['wpdb']->prefix . $table; + return $GLOBALS[ 'wpdb' ]->prefix . $table; } /** diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Lock.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Lock.php index e8e218021a..86e8528512 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Lock.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Lock.php @@ -8,7 +8,7 @@ abstract class ActionScheduler_Lock { /** @var ActionScheduler_Lock */ - private static $locker = null; + private static $locker = NULL; /** @var int */ protected static $lock_duration = MINUTE_IN_SECONDS; @@ -54,7 +54,7 @@ protected function get_duration( $lock_type ) { */ public static function instance() { if ( empty( self::$locker ) ) { - $class = apply_filters( 'action_scheduler_lock_class', 'ActionScheduler_OptionLock' ); + $class = apply_filters( 'action_scheduler_lock_class', 'ActionScheduler_OptionLock' ); self::$locker = new $class(); } return self::$locker; diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Logger.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Logger.php index 5dbd436467..c3afd04b63 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Logger.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Logger.php @@ -2,31 +2,30 @@ /** * Class ActionScheduler_Logger - * * @codeCoverageIgnore */ abstract class ActionScheduler_Logger { - private static $logger = null; + private static $logger = NULL; /** * @return ActionScheduler_Logger */ public static function instance() { - if ( empty( self::$logger ) ) { - $class = apply_filters( 'action_scheduler_logger_class', 'ActionScheduler_wpCommentLogger' ); + if ( empty(self::$logger) ) { + $class = apply_filters('action_scheduler_logger_class', 'ActionScheduler_wpCommentLogger'); self::$logger = new $class(); } return self::$logger; } /** - * @param string $action_id - * @param string $message + * @param string $action_id + * @param string $message * @param DateTime $date * * @return string The log entry ID */ - abstract public function log( $action_id, $message, DateTime $date = null ); + abstract public function log( $action_id, $message, DateTime $date = NULL ); /** * @param string $entry_id @@ -42,6 +41,7 @@ abstract public function get_entry( $entry_id ); */ abstract public function get_logs( $action_id ); + /** * @codeCoverageIgnore */ @@ -86,7 +86,7 @@ public function log_started_action( $action_id, $context = '' ) { $this->log( $action_id, $message ); } - public function log_completed_action( $action_id, $action = null, $context = '' ) { + public function log_completed_action( $action_id, $action = NULL, $context = '' ) { if ( ! empty( $context ) ) { /* translators: %s: context */ $message = sprintf( __( 'action complete via %s', 'action-scheduler' ), $context ); @@ -134,14 +134,14 @@ public function log_ignored_action( $action_id, $context = '' ) { } /** - * @param string $action_id + * @param string $action_id * @param Exception|NULL $exception The exception which occured when fetching the action. NULL by default for backward compatibility. * * @return ActionScheduler_LogEntry[] */ - public function log_failed_fetch_action( $action_id, Exception $exception = null ) { + public function log_failed_fetch_action( $action_id, Exception $exception = NULL ) { - if ( $exception !== null ) { + if ( ! is_null( $exception ) ) { /* translators: %s: exception message */ $log_message = sprintf( __( 'There was a failure fetching this action: %s', 'action-scheduler' ), $exception->getMessage() ); } else { diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Store.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Store.php index b00ad07218..a555293325 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Store.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_Store.php @@ -2,7 +2,6 @@ /** * Class ActionScheduler_Store - * * @codeCoverageIgnore */ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated { @@ -14,20 +13,20 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated { const DEFAULT_CLASS = 'ActionScheduler_wpPostStore'; /** @var ActionScheduler_Store */ - private static $store = null; + private static $store = NULL; /** @var int */ protected static $max_args_length = 191; /** * @param ActionScheduler_Action $action - * @param DateTime $scheduled_date Optional Date of the first instance - * to store. Otherwise uses the first date of the action's - * schedule. + * @param DateTime $scheduled_date Optional Date of the first instance + * to store. Otherwise uses the first date of the action's + * schedule. * * @return int The action ID */ - abstract public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = null ); + abstract public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ); /** * @param string $action_id @@ -142,13 +141,10 @@ abstract public function action_counts(); public function extra_action_counts() { $extra_actions = array(); - $pastdue_action_counts = (int) $this->query_actions( - array( - 'status' => self::STATUS_PENDING, - 'date' => as_get_datetime_object(), - ), - 'count' - ); + $pastdue_action_counts = ( int ) $this->query_actions( array( + 'status' => self::STATUS_PENDING, + 'date' => as_get_datetime_object(), + ), 'count' ); if ( $pastdue_action_counts ) { $extra_actions['past-due'] = $pastdue_action_counts; @@ -180,6 +176,7 @@ abstract public function delete_action( $action_id ); */ abstract public function get_date( $action_id ); + /** * @param int $max_actions * @param DateTime $before_date Claim only actions schedule before the given date. Defaults to now. @@ -244,7 +241,7 @@ abstract public function find_actions_by_claim_id( $claim_id ); * @return string */ protected function validate_sql_comparator( $comparison_operator ) { - if ( in_array( $comparison_operator, array( '!=', '>', '>=', '<', '<=', '=' ) ) ) { + if ( in_array( $comparison_operator, array('!=', '>', '>=', '<', '<=', '=') ) ) { return $comparison_operator; } return '='; @@ -254,13 +251,13 @@ protected function validate_sql_comparator( $comparison_operator ) { * Get the time MySQL formated date/time string for an action's (next) scheduled date. * * @param ActionScheduler_Action $action - * @param DateTime $scheduled_date (optional) + * @param DateTime $scheduled_date (optional) * @return string */ - protected function get_scheduled_date_string( ActionScheduler_Action $action, DateTime $scheduled_date = null ) { + protected function get_scheduled_date_string( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ) { $next = null === $scheduled_date ? $action->get_schedule()->get_date() : $scheduled_date; if ( ! $next ) { - return '0000-00-00 00:00:00'; + $next = date_create(); } $next->setTimezone( new DateTimeZone( 'UTC' ) ); @@ -271,13 +268,13 @@ protected function get_scheduled_date_string( ActionScheduler_Action $action, Da * Get the time MySQL formated date/time string for an action's (next) scheduled date. * * @param ActionScheduler_Action $action - * @param DateTime $scheduled_date (optional) + * @param DateTime $scheduled_date (optional) * @return string */ - protected function get_scheduled_date_string_local( ActionScheduler_Action $action, DateTime $scheduled_date = null ) { + protected function get_scheduled_date_string_local( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ) { $next = null === $scheduled_date ? $action->get_schedule()->get_date() : $scheduled_date; if ( ! $next ) { - return '0000-00-00 00:00:00'; + $next = date_create(); } ActionScheduler_TimezoneHelper::set_local_timezone( $next ); @@ -417,17 +414,15 @@ public function get_status_labels() { * Check if there are any pending scheduled actions due to run. * * @param ActionScheduler_Action $action - * @param DateTime $scheduled_date (optional) + * @param DateTime $scheduled_date (optional) * @return string */ public function has_pending_actions_due() { - $pending_actions = $this->query_actions( - array( - 'date' => as_get_datetime_object(), - 'status' => self::STATUS_PENDING, - 'orderby' => 'none', - ) - ); + $pending_actions = $this->query_actions( array( + 'date' => as_get_datetime_object(), + 'status' => ActionScheduler_Store::STATUS_PENDING, + 'orderby' => 'none', + ) ); return ! empty( $pending_actions ); } @@ -447,7 +442,7 @@ public function mark_migrated( $action_id ) {} */ public static function instance() { if ( empty( self::$store ) ) { - $class = apply_filters( 'action_scheduler_store_class', self::DEFAULT_CLASS ); + $class = apply_filters( 'action_scheduler_store_class', self::DEFAULT_CLASS ); self::$store = new $class(); } return self::$store; diff --git a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_TimezoneHelper.php b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_TimezoneHelper.php index 73f0000dfe..fd01449412 100644 --- a/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_TimezoneHelper.php +++ b/src/libraries/action-scheduler/classes/abstracts/ActionScheduler_TimezoneHelper.php @@ -4,7 +4,7 @@ * Class ActionScheduler_TimezoneHelper */ abstract class ActionScheduler_TimezoneHelper { - private static $local_timezone = null; + private static $local_timezone = NULL; /** * Set a DateTime's timezone to the WordPress site's timezone, or a UTC offset @@ -99,21 +99,21 @@ protected static function get_local_timezone_offset() { /** * @deprecated 2.1.0 */ - public static function get_local_timezone( $reset = false ) { + public static function get_local_timezone( $reset = FALSE ) { _deprecated_function( __FUNCTION__, '2.1.0', 'ActionScheduler_TimezoneHelper::set_local_timezone()' ); if ( $reset ) { - self::$local_timezone = null; + self::$local_timezone = NULL; } - if ( ! isset( self::$local_timezone ) ) { - $tzstring = get_option( 'timezone_string' ); + if ( !isset(self::$local_timezone) ) { + $tzstring = get_option('timezone_string'); - if ( empty( $tzstring ) ) { - $gmt_offset = get_option( 'gmt_offset' ); + if ( empty($tzstring) ) { + $gmt_offset = get_option('gmt_offset'); if ( $gmt_offset == 0 ) { $tzstring = 'UTC'; } else { $gmt_offset *= HOUR_IN_SECONDS; - $tzstring = timezone_name_from_abbr( '', $gmt_offset, 1 ); + $tzstring = timezone_name_from_abbr( '', $gmt_offset, 1 ); // If there's no timezone string, try again with no DST. if ( false === $tzstring ) { @@ -145,7 +145,7 @@ public static function get_local_timezone( $reset = false ) { } } - self::$local_timezone = new DateTimeZone( $tzstring ); + self::$local_timezone = new DateTimeZone($tzstring); } return self::$local_timezone; } diff --git a/src/libraries/action-scheduler/classes/actions/ActionScheduler_Action.php b/src/libraries/action-scheduler/classes/actions/ActionScheduler_Action.php index 0e7e201b59..f538f506b5 100644 --- a/src/libraries/action-scheduler/classes/actions/ActionScheduler_Action.php +++ b/src/libraries/action-scheduler/classes/actions/ActionScheduler_Action.php @@ -7,15 +7,15 @@ class ActionScheduler_Action { protected $hook = ''; protected $args = array(); /** @var ActionScheduler_Schedule */ - protected $schedule = null; - protected $group = ''; + protected $schedule = NULL; + protected $group = ''; - public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) { + public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = NULL, $group = '' ) { $schedule = empty( $schedule ) ? new ActionScheduler_NullSchedule() : $schedule; - $this->set_hook( $hook ); - $this->set_schedule( $schedule ); - $this->set_args( $args ); - $this->set_group( $group ); + $this->set_hook($hook); + $this->set_schedule($schedule); + $this->set_args($args); + $this->set_group($group); } /** @@ -91,6 +91,6 @@ public function get_group() { * @return bool If the action has been finished */ public function is_finished() { - return false; + return FALSE; } } diff --git a/src/libraries/action-scheduler/classes/actions/ActionScheduler_CanceledAction.php b/src/libraries/action-scheduler/classes/actions/ActionScheduler_CanceledAction.php index 81510ddcc5..8bbc5d18d5 100644 --- a/src/libraries/action-scheduler/classes/actions/ActionScheduler_CanceledAction.php +++ b/src/libraries/action-scheduler/classes/actions/ActionScheduler_CanceledAction.php @@ -9,14 +9,14 @@ class ActionScheduler_CanceledAction extends ActionScheduler_FinishedAction { /** - * @param string $hook - * @param array $args + * @param string $hook + * @param array $args * @param ActionScheduler_Schedule $schedule - * @param string $group + * @param string $group */ public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) { parent::__construct( $hook, $args, $schedule, $group ); - if ( $schedule === null ) { + if ( is_null( $schedule ) ) { $this->set_schedule( new ActionScheduler_NullSchedule() ); } } diff --git a/src/libraries/action-scheduler/classes/actions/ActionScheduler_FinishedAction.php b/src/libraries/action-scheduler/classes/actions/ActionScheduler_FinishedAction.php index 7895dc0c36..b23a56c66b 100644 --- a/src/libraries/action-scheduler/classes/actions/ActionScheduler_FinishedAction.php +++ b/src/libraries/action-scheduler/classes/actions/ActionScheduler_FinishedAction.php @@ -10,7 +10,7 @@ public function execute() { } public function is_finished() { - return true; + return TRUE; } } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/actions/ActionScheduler_NullAction.php b/src/libraries/action-scheduler/classes/actions/ActionScheduler_NullAction.php index f70c08dc4b..cd5dc3b0f9 100644 --- a/src/libraries/action-scheduler/classes/actions/ActionScheduler_NullAction.php +++ b/src/libraries/action-scheduler/classes/actions/ActionScheduler_NullAction.php @@ -5,7 +5,7 @@ */ class ActionScheduler_NullAction extends ActionScheduler_Action { - public function __construct( $hook = '', array $args = array(), ActionScheduler_Schedule $schedule = null ) { + public function __construct( $hook = '', array $args = array(), ActionScheduler_Schedule $schedule = NULL ) { $this->set_schedule( new ActionScheduler_NullSchedule() ); } @@ -13,4 +13,4 @@ public function execute() { // don't execute } } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBLogger.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBLogger.php index 31e6efd28b..37bfd0d44e 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBLogger.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBLogger.php @@ -72,7 +72,7 @@ private function create_entry_from_db_record( $record ) { return new ActionScheduler_NullLogEntry(); } - if ( $record->log_date_gmt === null ) { + if ( is_null( $record->log_date_gmt ) ) { $date = as_get_datetime_object( ActionScheduler_StoreSchema::DEFAULT_DATE ); } else { $date = as_get_datetime_object( $record->log_date_gmt ); diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php index 5c0dd0b363..5009454f74 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php @@ -139,7 +139,7 @@ private function build_insert_sql( array $data, $unique ) { $placeholder_sql = implode( ', ', $placeholders ); $where_clause = $this->build_where_clause_for_insert( $data, $table_name, $unique ); // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $column_sql and $where_clause are already prepared. $placeholder_sql is hardcoded. - $insert_query = $wpdb->prepare( + $insert_query = $wpdb->prepare( " INSERT INTO $table_name ( $column_sql ) SELECT $placeholder_sql FROM DUAL @@ -310,7 +310,7 @@ public function fetch_action( $action_id ) { 'last_attempt_gmt', ); foreach ( $date_fields as $date_field ) { - if ( $data->$date_field === null ) { + if ( is_null( $data->$date_field ) ) { $data->$date_field = ActionScheduler_StoreSchema::DEFAULT_DATE; } } @@ -375,25 +375,22 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele throw new InvalidArgumentException( __( 'Invalid value for select or count parameter. Cannot query actions.', 'action-scheduler' ) ); } - $query = wp_parse_args( - $query, - array( - 'hook' => '', - 'args' => null, - 'partial_args_matching' => 'off', // can be 'like' or 'json' - 'date' => null, - 'date_compare' => '<=', - 'modified' => null, - 'modified_compare' => '<=', - 'group' => '', - 'status' => '', - 'claimed' => null, - 'per_page' => 5, - 'offset' => 0, - 'orderby' => 'date', - 'order' => 'ASC', - ) - ); + $query = wp_parse_args( $query, array( + 'hook' => '', + 'args' => null, + 'partial_args_matching' => 'off', // can be 'like' or 'json' + 'date' => null, + 'date_compare' => '<=', + 'modified' => null, + 'modified_compare' => '<=', + 'group' => '', + 'status' => '', + 'claimed' => null, + 'per_page' => 5, + 'offset' => 0, + 'orderby' => 'date', + 'order' => 'ASC', + ) ); /** @var \wpdb $wpdb */ global $wpdb; @@ -410,26 +407,26 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele } $sql = ( 'count' === $select_or_count ) ? 'SELECT count(a.action_id)' : 'SELECT a.action_id'; - $sql .= " FROM {$wpdb->actionscheduler_actions} a"; + $sql .= " FROM {$wpdb->actionscheduler_actions} a"; $sql_params = array(); if ( ! empty( $query['group'] ) || 'group' === $query['orderby'] ) { $sql .= " LEFT JOIN {$wpdb->actionscheduler_groups} g ON g.group_id=a.group_id"; } - $sql .= ' WHERE 1=1'; + $sql .= " WHERE 1=1"; if ( ! empty( $query['group'] ) ) { - $sql .= ' AND g.slug=%s'; + $sql .= " AND g.slug=%s"; $sql_params[] = $query['group']; } if ( ! empty( $query['hook'] ) ) { - $sql .= ' AND a.hook=%s'; + $sql .= " AND a.hook=%s"; $sql_params[] = $query['hook']; } - if ( $query['args'] !== null ) { + if ( ! is_null( $query['args'] ) ) { switch ( $query['partial_args_matching'] ) { case 'json': if ( ! $supports_json ) { @@ -448,28 +445,26 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele } $placeholder = isset( $supported_types[ $value_type ] ) ? $supported_types[ $value_type ] : false; if ( ! $placeholder ) { - throw new \RuntimeException( - sprintf( + throw new \RuntimeException( sprintf( /* translators: %s: provided value type */ - __( 'The value type for the JSON partial matching is not supported. Must be either integer, boolean, double or string. %s type provided.', 'action-scheduler' ), - $value_type - ) - ); + __( 'The value type for the JSON partial matching is not supported. Must be either integer, boolean, double or string. %s type provided.', 'action-scheduler' ), + $value_type + ) ); } - $sql .= ' AND JSON_EXTRACT(a.args, %s)=' . $placeholder; - $sql_params[] = '$.' . $key; + $sql .= ' AND JSON_EXTRACT(a.args, %s)='.$placeholder; + $sql_params[] = '$.'.$key; $sql_params[] = $value; } break; case 'like': foreach ( $query['args'] as $key => $value ) { - $sql .= ' AND a.args LIKE %s'; + $sql .= ' AND a.args LIKE %s'; $json_partial = $wpdb->esc_like( trim( json_encode( array( $key => $value ) ), '{}' ) ); $sql_params[] = "%{$json_partial}%"; } break; case 'off': - $sql .= ' AND a.args=%s'; + $sql .= " AND a.args=%s"; $sql_params[] = $this->get_args_for_query( $query['args'] ); break; default: @@ -506,7 +501,7 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele $sql .= ' AND a.claim_id != 0'; } elseif ( false === $query['claimed'] ) { $sql .= ' AND a.claim_id = 0'; - } elseif ( $query['claimed'] !== null ) { + } elseif ( ! is_null( $query['claimed'] ) ) { $sql .= ' AND a.claim_id = %d'; $sql_params[] = $query['claimed']; } @@ -819,7 +814,7 @@ protected function claim_actions( $claim_id, $limit, \DateTime $before_date = nu global $wpdb; $now = as_get_datetime_object(); - $date = $before_date === null ? $now : clone $before_date; + $date = is_null( $before_date ) ? $now : clone $before_date; // can't use $wpdb->update() because of the <= condition. $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s"; @@ -835,7 +830,7 @@ protected function claim_actions( $claim_id, $limit, \DateTime $before_date = nu if ( ! empty( $hooks ) ) { $placeholders = array_fill( 0, count( $hooks ), '%s' ); - $where .= ' AND hook IN (' . join( ', ', $placeholders ) . ')'; + $where .= ' AND hook IN (' . join( ', ', $placeholders ) . ')'; $params = array_merge( $params, array_values( $hooks ) ); } @@ -849,7 +844,7 @@ protected function claim_actions( $claim_id, $limit, \DateTime $before_date = nu throw new InvalidArgumentException( sprintf( __( 'The group "%s" does not exist.', 'action-scheduler' ), $group ) ); } - $where .= ' AND group_id = %d'; + $where .= ' AND group_id = %d'; $params[] = $group_id; } @@ -940,8 +935,31 @@ public function find_actions_by_claim_id( $claim_id ) { public function release_claim( ActionScheduler_ActionClaim $claim ) { /** @var \wpdb $wpdb */ global $wpdb; - $wpdb->update( $wpdb->actionscheduler_actions, array( 'claim_id' => 0 ), array( 'claim_id' => $claim->get_id() ), array( '%d' ), array( '%d' ) ); + /** + * Deadlock warning: This function modifies actions to release them from claims that have been processed. Earlier, we used to it in a atomic query, i.e. we would update all actions belonging to a particular claim_id with claim_id = 0. + * While this was functionally correct, it would cause deadlock, since this update query will hold a lock on the claim_id_.. index on the action table. + * This allowed the possibility of a race condition, where the claimer query is also running at the same time, then the claimer query will also try to acquire a lock on the claim_id_.. index, and in this case if claim release query has already progressed to the point of acquiring the lock, but have not updated yet, it would cause a deadlock. + * + * We resolve this by getting all the actions_id that we want to release claim from in a separate query, and then releasing the claim on each of them. This way, our lock is acquired on the action_id index instead of the claim_id index. Note that the lock on claim_id will still be acquired, but it will only when we actually make the update, rather than when we select the actions. + */ + $action_ids = $wpdb->get_col( $wpdb->prepare( "SELECT action_id FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d", $claim->get_id() ) ); + + $row_updates = 0; + if ( count( $action_ids ) > 0 ) { + $action_id_string = implode( ',', array_map( 'absint', $action_ids ) ); + $row_updates = $wpdb->query( "UPDATE {$wpdb->actionscheduler_actions} SET claim_id = 0 WHERE action_id IN ({$action_id_string})" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + } + $wpdb->delete( $wpdb->actionscheduler_claims, array( 'claim_id' => $claim->get_id() ), array( '%d' ) ); + + if ( $row_updates < count( $action_ids ) ) { + throw new RuntimeException( + sprintf( + __( 'Unable to release actions from claim id %d.', 'woocommerce' ), + $claim->get_id() + ) + ); + } } /** diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_HybridStore.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_HybridStore.php index d8e01e23fc..22d61a606a 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_HybridStore.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_HybridStore.php @@ -1,9 +1,9 @@ primary_store->init(); $this->secondary_store->init(); - remove_action( 'action_scheduler/created_table', array( $this, 'set_autoincrement' ), 10 ); + remove_action( 'action_scheduler/created_table', [ $this, 'set_autoincrement' ], 10 ); } /** @@ -78,7 +78,7 @@ public function set_autoincrement( $table_name, $table_suffix ) { /** @var \wpdb $wpdb */ global $wpdb; /** - * A default date of '0000-00-00 00:00:00' is invalid in MySQL 5.7 when configured with + * A default date of '0000-00-00 00:00:00' is invalid in MySQL 5.7 when configured with * sql_mode including both STRICT_TRANS_TABLES and NO_ZERO_DATE. */ $default_date = new DateTime( 'tomorrow' ); @@ -88,7 +88,7 @@ public function set_autoincrement( $table_name, $table_suffix ) { $row_count = $wpdb->insert( $wpdb->{ActionScheduler_StoreSchema::ACTIONS_TABLE}, - array( + [ 'action_id' => $this->demarkation_id, 'hook' => '', 'status' => '', @@ -96,12 +96,12 @@ public function set_autoincrement( $table_name, $table_suffix ) { 'scheduled_date_local' => $date_local, 'last_attempt_gmt' => $date_gmt, 'last_attempt_local' => $date_local, - ) + ] ); if ( $row_count > 0 ) { $wpdb->delete( $wpdb->{ActionScheduler_StoreSchema::ACTIONS_TABLE}, - array( 'action_id' => $this->demarkation_id ) + [ 'action_id' => $this->demarkation_id ] ); } } @@ -140,10 +140,10 @@ private function set_demarkation_id( $id = null ) { * * @return string */ - public function find_action( $hook, $params = array() ) { + public function find_action( $hook, $params = [] ) { $found_unmigrated_action = $this->secondary_store->find_action( $hook, $params ); if ( ! empty( $found_unmigrated_action ) ) { - $this->migrate( array( $found_unmigrated_action ) ); + $this->migrate( [ $found_unmigrated_action ] ); } return $this->primary_store->find_action( $hook, $params ); @@ -154,12 +154,12 @@ public function find_action( $hook, $params = array() ) { * If any are found, migrate them immediately. Then the secondary * store will contain the canonical results. * - * @param array $query + * @param array $query * @param string $query_type Whether to select or count the results. Default, select. * * @return int[] */ - public function query_actions( $query = array(), $query_type = 'select' ) { + public function query_actions( $query = [], $query_type = 'select' ) { $found_unmigrated_actions = $this->secondary_store->query_actions( $query, 'select' ); if ( ! empty( $found_unmigrated_actions ) ) { $this->migrate( $found_unmigrated_actions ); @@ -352,19 +352,19 @@ public function get_status( $action_id ) { */ protected function get_store_from_action_id( $action_id, $primary_first = false ) { if ( $primary_first ) { - $stores = array( + $stores = [ $this->primary_store, $this->secondary_store, - ); + ]; } elseif ( $action_id < $this->demarkation_id ) { - $stores = array( + $stores = [ $this->secondary_store, $this->primary_store, - ); + ]; } else { - $stores = array( + $stores = [ $this->primary_store, - ); + ]; } foreach ( $stores as $store ) { @@ -376,8 +376,7 @@ protected function get_store_from_action_id( $action_id, $primary_first = false return null; } - /* - * * * * * * * * * * * * * * * * * * * * * * * * * * + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * All claim-related functions should operate solely * on the primary store. * * * * * * * * * * * * * * * * * * * * * * * * * * */ diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpCommentLogger.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpCommentLogger.php index a18b6662a7..7215ddd94a 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpCommentLogger.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpCommentLogger.php @@ -5,17 +5,17 @@ */ class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger { const AGENT = 'ActionScheduler'; - const TYPE = 'action_log'; + const TYPE = 'action_log'; /** - * @param string $action_id - * @param string $message + * @param string $action_id + * @param string $message * @param DateTime $date * * @return string The log entry ID */ - public function log( $action_id, $message, DateTime $date = null ) { - if ( empty( $date ) ) { + public function log( $action_id, $message, DateTime $date = NULL ) { + if ( empty($date) ) { $date = as_get_datetime_object(); } else { $date = as_get_datetime_object( clone $date ); @@ -26,18 +26,18 @@ public function log( $action_id, $message, DateTime $date = null ) { protected function create_wp_comment( $action_id, $message, DateTime $date ) { - $comment_date_gmt = $date->format( 'Y-m-d H:i:s' ); + $comment_date_gmt = $date->format('Y-m-d H:i:s'); ActionScheduler_TimezoneHelper::set_local_timezone( $date ); $comment_data = array( - 'comment_post_ID' => $action_id, - 'comment_date' => $date->format( 'Y-m-d H:i:s' ), + 'comment_post_ID' => $action_id, + 'comment_date' => $date->format('Y-m-d H:i:s'), 'comment_date_gmt' => $comment_date_gmt, - 'comment_author' => self::AGENT, - 'comment_content' => $message, - 'comment_agent' => self::AGENT, - 'comment_type' => self::TYPE, + 'comment_author' => self::AGENT, + 'comment_content' => $message, + 'comment_agent' => self::AGENT, + 'comment_type' => self::TYPE, ); - return wp_insert_comment( $comment_data ); + return wp_insert_comment($comment_data); } /** @@ -47,7 +47,7 @@ protected function create_wp_comment( $action_id, $message, DateTime $date ) { */ public function get_entry( $entry_id ) { $comment = $this->get_comment( $entry_id ); - if ( empty( $comment ) || $comment->comment_type != self::TYPE ) { + if ( empty($comment) || $comment->comment_type != self::TYPE ) { return new ActionScheduler_NullLogEntry(); } @@ -63,22 +63,20 @@ public function get_entry( $entry_id ) { */ public function get_logs( $action_id ) { $status = 'all'; - if ( get_post_status( $action_id ) == 'trash' ) { + if ( get_post_status($action_id) == 'trash' ) { $status = 'post-trashed'; } - $comments = get_comments( - array( - 'post_id' => $action_id, - 'orderby' => 'comment_date_gmt', - 'order' => 'ASC', - 'type' => self::TYPE, - 'status' => $status, - ) - ); - $logs = array(); + $comments = get_comments(array( + 'post_id' => $action_id, + 'orderby' => 'comment_date_gmt', + 'order' => 'ASC', + 'type' => self::TYPE, + 'status' => $status, + )); + $logs = array(); foreach ( $comments as $c ) { $entry = $this->get_entry( $c ); - if ( ! empty( $entry ) ) { + if ( !empty($entry) ) { $logs[] = $entry; } } @@ -89,27 +87,29 @@ protected function get_comment( $comment_id ) { return get_comment( $comment_id ); } + + /** * @param WP_Comment_Query $query */ public function filter_comment_queries( $query ) { - foreach ( array( 'ID', 'parent', 'post_author', 'post_name', 'post_parent', 'type', 'post_type', 'post_id', 'post_ID' ) as $key ) { - if ( ! empty( $query->query_vars[ $key ] ) ) { + foreach ( array('ID', 'parent', 'post_author', 'post_name', 'post_parent', 'type', 'post_type', 'post_id', 'post_ID') as $key ) { + if ( !empty($query->query_vars[$key]) ) { return; // don't slow down queries that wouldn't include action_log comments anyway } } - $query->query_vars['action_log_filter'] = true; + $query->query_vars['action_log_filter'] = TRUE; add_filter( 'comments_clauses', array( $this, 'filter_comment_query_clauses' ), 10, 2 ); } /** - * @param array $clauses + * @param array $clauses * @param WP_Comment_Query $query * * @return array */ public function filter_comment_query_clauses( $clauses, $query ) { - if ( ! empty( $query->query_vars['action_log_filter'] ) ) { + if ( !empty($query->query_vars['action_log_filter']) ) { $clauses['where'] .= $this->get_where_clause(); } return $clauses; @@ -119,7 +119,7 @@ public function filter_comment_query_clauses( $clauses, $query ) { * Make sure Action Scheduler logs are excluded from comment feeds, which use WP_Query, not * the WP_Comment_Query class handled by @see self::filter_comment_queries(). * - * @param string $where + * @param string $where * @param WP_Query $query * * @return string @@ -145,7 +145,7 @@ protected function get_where_clause() { * Remove action log entries from wp_count_comments() * * @param array $stats - * @param int $post_id + * @param int $post_id * * @return object */ @@ -174,15 +174,9 @@ protected function get_comment_count() { $count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} WHERE comment_type NOT IN('order_note','action_log') GROUP BY comment_approved", ARRAY_A ); - $total = 0; - $stats = array(); - $approved = array( - '0' => 'moderated', - '1' => 'approved', - 'spam' => 'spam', - 'trash' => 'trash', - 'post-trashed' => 'post-trashed', - ); + $total = 0; + $stats = array(); + $approved = array( '0' => 'moderated', '1' => 'approved', 'spam' => 'spam', 'trash' => 'trash', 'post-trashed' => 'post-trashed' ); foreach ( (array) $count as $row ) { // Don't count post-trashed toward totals @@ -237,10 +231,10 @@ public function init() { } public function disable_comment_counting() { - wp_defer_comment_counting( true ); + wp_defer_comment_counting(true); } public function enable_comment_counting() { - wp_defer_comment_counting( false ); + wp_defer_comment_counting(false); } } diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore.php index 8a90d982d4..7883ca82bf 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore.php @@ -364,7 +364,7 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele $sql .= ' AND p.post_title=%s'; $sql_params[] = $query['hook']; } - if ( $query['args'] !== null ) { + if ( ! is_null( $query['args'] ) ) { $sql .= ' AND p.post_content=%s'; $sql_params[] = wp_json_encode( $query['args'] ); } @@ -398,7 +398,7 @@ protected function get_query_actions_sql( array $query, $select_or_count = 'sele $sql .= " AND p.post_password != ''"; } elseif ( false === $query['claimed'] ) { $sql .= " AND p.post_password = ''"; - } elseif ( $query['claimed'] !== null ) { + } elseif ( ! is_null( $query['claimed'] ) ) { $sql .= ' AND p.post_password = %s'; $sql_params[] = $query['claimed']; } diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostStatusRegistrar.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostStatusRegistrar.php index 392980a369..246bc347bf 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostStatusRegistrar.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostStatusRegistrar.php @@ -2,7 +2,6 @@ /** * Class ActionScheduler_wpPostStore_PostStatusRegistrar - * * @codeCoverageIgnore */ class ActionScheduler_wpPostStore_PostStatusRegistrar { diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostTypeRegistrar.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostTypeRegistrar.php index ab61b0d349..8c63bd0f7a 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostTypeRegistrar.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_PostTypeRegistrar.php @@ -2,7 +2,6 @@ /** * Class ActionScheduler_wpPostStore_PostTypeRegistrar - * * @codeCoverageIgnore */ class ActionScheduler_wpPostStore_PostTypeRegistrar { @@ -17,35 +16,35 @@ public function register() { */ protected function post_type_args() { $args = array( - 'label' => __( 'Scheduled Actions', 'action-scheduler' ), - 'description' => __( 'Scheduled actions are hooks triggered on a cetain date and time.', 'action-scheduler' ), - 'public' => false, + 'label' => __( 'Scheduled Actions', 'action-scheduler' ), + 'description' => __( 'Scheduled actions are hooks triggered on a cetain date and time.', 'action-scheduler' ), + 'public' => false, 'map_meta_cap' => true, 'hierarchical' => false, - 'supports' => array( 'title', 'editor', 'comments' ), - 'rewrite' => false, - 'query_var' => false, - 'can_export' => true, - 'ep_mask' => EP_NONE, - 'labels' => array( - 'name' => __( 'Scheduled Actions', 'action-scheduler' ), - 'singular_name' => __( 'Scheduled Action', 'action-scheduler' ), - 'menu_name' => _x( 'Scheduled Actions', 'Admin menu name', 'action-scheduler' ), - 'add_new' => __( 'Add', 'action-scheduler' ), - 'add_new_item' => __( 'Add New Scheduled Action', 'action-scheduler' ), - 'edit' => __( 'Edit', 'action-scheduler' ), - 'edit_item' => __( 'Edit Scheduled Action', 'action-scheduler' ), - 'new_item' => __( 'New Scheduled Action', 'action-scheduler' ), - 'view' => __( 'View Action', 'action-scheduler' ), - 'view_item' => __( 'View Action', 'action-scheduler' ), - 'search_items' => __( 'Search Scheduled Actions', 'action-scheduler' ), - 'not_found' => __( 'No actions found', 'action-scheduler' ), + 'supports' => array('title', 'editor','comments'), + 'rewrite' => false, + 'query_var' => false, + 'can_export' => true, + 'ep_mask' => EP_NONE, + 'labels' => array( + 'name' => __( 'Scheduled Actions', 'action-scheduler' ), + 'singular_name' => __( 'Scheduled Action', 'action-scheduler' ), + 'menu_name' => _x( 'Scheduled Actions', 'Admin menu name', 'action-scheduler' ), + 'add_new' => __( 'Add', 'action-scheduler' ), + 'add_new_item' => __( 'Add New Scheduled Action', 'action-scheduler' ), + 'edit' => __( 'Edit', 'action-scheduler' ), + 'edit_item' => __( 'Edit Scheduled Action', 'action-scheduler' ), + 'new_item' => __( 'New Scheduled Action', 'action-scheduler' ), + 'view' => __( 'View Action', 'action-scheduler' ), + 'view_item' => __( 'View Action', 'action-scheduler' ), + 'search_items' => __( 'Search Scheduled Actions', 'action-scheduler' ), + 'not_found' => __( 'No actions found', 'action-scheduler' ), 'not_found_in_trash' => __( 'No actions found in trash', 'action-scheduler' ), ), ); - $args = apply_filters( 'action_scheduler_post_type_args', $args ); + $args = apply_filters('action_scheduler_post_type_args', $args); return $args; } } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_TaxonomyRegistrar.php b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_TaxonomyRegistrar.php index f652dead8c..367401f7e2 100644 --- a/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_TaxonomyRegistrar.php +++ b/src/libraries/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore_TaxonomyRegistrar.php @@ -2,7 +2,6 @@ /** * Class ActionScheduler_wpPostStore_TaxonomyRegistrar - * * @codeCoverageIgnore */ class ActionScheduler_wpPostStore_TaxonomyRegistrar { @@ -12,16 +11,16 @@ public function register() { protected function taxonomy_args() { $args = array( - 'label' => __( 'Action Group', 'action-scheduler' ), - 'public' => false, - 'hierarchical' => false, + 'label' => __( 'Action Group', 'action-scheduler' ), + 'public' => false, + 'hierarchical' => false, 'show_admin_column' => true, - 'query_var' => false, - 'rewrite' => false, + 'query_var' => false, + 'rewrite' => false, ); $args = apply_filters( 'action_scheduler_taxonomy_args', $args ); return $args; } } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/migration/ActionMigrator.php b/src/libraries/action-scheduler/classes/migration/ActionMigrator.php index 2ff8e64839..c77d0832cd 100644 --- a/src/libraries/action-scheduler/classes/migration/ActionMigrator.php +++ b/src/libraries/action-scheduler/classes/migration/ActionMigrator.php @@ -1,5 +1,6 @@ get_schedule()->get_date() ) { + if ( is_null( $action ) || empty( $status ) || ! $action->get_schedule()->get_date() ) { // null action or empty status means the fetch operation failed or the action didn't exist // null schedule means it's missing vital data // delete it and move on @@ -78,10 +79,10 @@ public function migrate( $source_action_id ) { try { switch ( $status ) { - case \ActionScheduler_Store::STATUS_FAILED: + case \ActionScheduler_Store::STATUS_FAILED : $this->destination->mark_failure( $destination_action_id ); break; - case \ActionScheduler_Store::STATUS_CANCELED: + case \ActionScheduler_Store::STATUS_CANCELED : $this->destination->cancel_action( $destination_action_id ); break; } diff --git a/src/libraries/action-scheduler/classes/migration/ActionScheduler_DBStoreMigrator.php b/src/libraries/action-scheduler/classes/migration/ActionScheduler_DBStoreMigrator.php index 29793c62b6..41c21da256 100644 --- a/src/libraries/action-scheduler/classes/migration/ActionScheduler_DBStoreMigrator.php +++ b/src/libraries/action-scheduler/classes/migration/ActionScheduler_DBStoreMigrator.php @@ -17,13 +17,13 @@ class ActionScheduler_DBStoreMigrator extends ActionScheduler_DBStore { * that when first saving the action. * * @param ActionScheduler_Action $action - * @param \DateTime $scheduled_date Optional date of the first instance to store. - * @param \DateTime $last_attempt_date Optional date the action was last attempted. + * @param \DateTime $scheduled_date Optional date of the first instance to store. + * @param \DateTime $last_attempt_date Optional date the action was last attempted. * * @return string The action ID * @throws \RuntimeException When the action is not saved. */ - public function save_action( ActionScheduler_Action $action, \DateTime $scheduled_date = null, \DateTime $last_attempt_date = null ) { + public function save_action( ActionScheduler_Action $action, \DateTime $scheduled_date = null, \DateTime $last_attempt_date = null ){ try { /** @var \wpdb $wpdb */ global $wpdb; @@ -31,10 +31,10 @@ public function save_action( ActionScheduler_Action $action, \DateTime $schedule $action_id = parent::save_action( $action, $scheduled_date ); if ( null !== $last_attempt_date ) { - $data = array( + $data = [ 'last_attempt_gmt' => $this->get_scheduled_date_string( $action, $last_attempt_date ), 'last_attempt_local' => $this->get_scheduled_date_string_local( $action, $last_attempt_date ), - ); + ]; $wpdb->update( $wpdb->actionscheduler_actions, $data, array( 'action_id' => $action_id ), array( '%s', '%s' ), array( '%d' ) ); } diff --git a/src/libraries/action-scheduler/classes/migration/BatchFetcher.php b/src/libraries/action-scheduler/classes/migration/BatchFetcher.php index 428047a532..48728010fe 100644 --- a/src/libraries/action-scheduler/classes/migration/BatchFetcher.php +++ b/src/libraries/action-scheduler/classes/migration/BatchFetcher.php @@ -1,7 +1,9 @@ $now, 'per_page' => $count, 'offset' => 0, 'orderby' => 'date', 'order' => 'ASC', - ); + ]; - $priorities = array( + $priorities = [ Store::STATUS_PENDING, Store::STATUS_FAILED, Store::STATUS_CANCELED, Store::STATUS_COMPLETE, Store::STATUS_RUNNING, '', // any other unanticipated status - ); + ]; foreach ( $priorities as $status ) { - yield wp_parse_args( - array( - 'status' => $status, - 'date_compare' => '<=', - ), - $args - ); - yield wp_parse_args( - array( - 'status' => $status, - 'date_compare' => '>=', - ), - $args - ); + yield wp_parse_args( [ + 'status' => $status, + 'date_compare' => '<=', + ], $args ); + yield wp_parse_args( [ + 'status' => $status, + 'date_compare' => '>=', + ], $args ); } } -} +} \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/migration/Config.php b/src/libraries/action-scheduler/classes/migration/Config.php index 9abf79749a..50f41ff49a 100644 --- a/src/libraries/action-scheduler/classes/migration/Config.php +++ b/src/libraries/action-scheduler/classes/migration/Config.php @@ -1,5 +1,6 @@ batch_fetcher->fetch( $batch_size ); + $batch = $this->batch_fetcher->fetch( $batch_size ); $batch_size = count( $batch ); if ( ! $batch_size ) { @@ -101,17 +102,14 @@ public function migrate_actions( array $action_ids ) { foreach ( $action_ids as $source_action_id ) { $destination_action_id = $this->action_migrator->migrate( $source_action_id ); if ( $destination_action_id ) { - $this->destination_logger->log( - $destination_action_id, - sprintf( + $this->destination_logger->log( $destination_action_id, sprintf( /* translators: 1: source action ID 2: source store class 3: destination action ID 4: destination store class */ - __( 'Migrated action with ID %1$d in %2$s to ID %3$d in %4$s', 'action-scheduler' ), - $source_action_id, - get_class( $this->source_store ), - $destination_action_id, - get_class( $this->destination_store ) - ) - ); + __( 'Migrated action with ID %1$d in %2$s to ID %3$d in %4$s', 'action-scheduler' ), + $source_action_id, + get_class( $this->source_store ), + $destination_action_id, + get_class( $this->destination_store ) + ) ); } if ( $this->progress_bar ) { diff --git a/src/libraries/action-scheduler/classes/migration/Scheduler.php b/src/libraries/action-scheduler/classes/migration/Scheduler.php index c32d840377..dcbe2db5fa 100644 --- a/src/libraries/action-scheduler/classes/migration/Scheduler.php +++ b/src/libraries/action-scheduler/classes/migration/Scheduler.php @@ -1,5 +1,6 @@ __wakeup() for details. **/ - private $timestamp = null; + private $timestamp = NULL; /** * @param DateTime $after @@ -48,7 +48,7 @@ public function is_recurring() { * map the old property names with matching visibility. */ public function __wakeup() { - if ( $this->timestamp !== null ) { + if ( ! is_null( $this->timestamp ) ) { $this->scheduled_timestamp = $this->timestamp; unset( $this->timestamp ); } diff --git a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_CronSchedule.php b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_CronSchedule.php index f11bf19868..7859307ac8 100644 --- a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_CronSchedule.php +++ b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_CronSchedule.php @@ -8,20 +8,20 @@ class ActionScheduler_CronSchedule extends ActionScheduler_Abstract_RecurringSch /** * Deprecated property @see $this->__wakeup() for details. **/ - private $start_timestamp = null; + private $start_timestamp = NULL; /** * Deprecated property @see $this->__wakeup() for details. **/ - private $cron = null; + private $cron = NULL; /** * Wrapper for parent constructor to accept a cron expression string and map it to a CronExpression for this * objects $recurrence property. * - * @param DateTime $start The date & time to run the action at or after. If $start aligns with the CronSchedule passed via $recurrence, it will be used. If it does not align, the first matching date after it will be used. + * @param DateTime $start The date & time to run the action at or after. If $start aligns with the CronSchedule passed via $recurrence, it will be used. If it does not align, the first matching date after it will be used. * @param CronExpression|string $recurrence The CronExpression used to calculate the schedule's next instance. - * @param DateTime|null $first (Optional) The date & time the first instance of this interval schedule ran. Default null, meaning this is the first instance. + * @param DateTime|null $first (Optional) The date & time the first instance of this interval schedule ran. Default null, meaning this is the first instance. */ public function __construct( DateTime $start, $recurrence, DateTime $first = null ) { if ( ! is_a( $recurrence, 'CronExpression' ) ) { @@ -75,13 +75,10 @@ public function __sleep() { $this->start_timestamp = $this->scheduled_timestamp; $this->cron = $this->recurrence; - return array_merge( - $sleep_params, - array( - 'start_timestamp', - 'cron', - ) - ); + return array_merge( $sleep_params, array( + 'start_timestamp', + 'cron' + ) ); } /** @@ -90,12 +87,12 @@ public function __sleep() { * For more background, @see ActionScheduler_Abstract_RecurringSchedule::__wakeup(). */ public function __wakeup() { - if ( $this->scheduled_timestamp === null && $this->start_timestamp !== null ) { + if ( is_null( $this->scheduled_timestamp ) && ! is_null( $this->start_timestamp ) ) { $this->scheduled_timestamp = $this->start_timestamp; unset( $this->start_timestamp ); } - if ( $this->recurrence === null && $this->cron !== null ) { + if ( is_null( $this->recurrence ) && ! is_null( $this->cron ) ) { $this->recurrence = $this->cron; unset( $this->cron ); } diff --git a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_IntervalSchedule.php b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_IntervalSchedule.php index 94ebfe07c5..11a591e80b 100644 --- a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_IntervalSchedule.php +++ b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_IntervalSchedule.php @@ -8,12 +8,12 @@ class ActionScheduler_IntervalSchedule extends ActionScheduler_Abstract_Recurrin /** * Deprecated property @see $this->__wakeup() for details. **/ - private $start_timestamp = null; + private $start_timestamp = NULL; /** * Deprecated property @see $this->__wakeup() for details. **/ - private $interval_in_seconds = null; + private $interval_in_seconds = NULL; /** * Calculate when this schedule should start after a given date & time using @@ -55,13 +55,10 @@ public function __sleep() { $this->start_timestamp = $this->scheduled_timestamp; $this->interval_in_seconds = $this->recurrence; - return array_merge( - $sleep_params, - array( - 'start_timestamp', - 'interval_in_seconds', - ) - ); + return array_merge( $sleep_params, array( + 'start_timestamp', + 'interval_in_seconds' + ) ); } /** @@ -70,12 +67,12 @@ public function __sleep() { * For more background, @see ActionScheduler_Abstract_RecurringSchedule::__wakeup(). */ public function __wakeup() { - if ( $this->scheduled_timestamp === null && $this->start_timestamp !== null ) { + if ( is_null( $this->scheduled_timestamp ) && ! is_null( $this->start_timestamp ) ) { $this->scheduled_timestamp = $this->start_timestamp; unset( $this->start_timestamp ); } - if ( $this->recurrence === null && $this->interval_in_seconds !== null ) { + if ( is_null( $this->recurrence ) && ! is_null( $this->interval_in_seconds ) ) { $this->recurrence = $this->interval_in_seconds; unset( $this->interval_in_seconds ); } diff --git a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_NullSchedule.php b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_NullSchedule.php index 88d6196538..1b1afec021 100644 --- a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_NullSchedule.php +++ b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_NullSchedule.php @@ -5,6 +5,9 @@ */ class ActionScheduler_NullSchedule extends ActionScheduler_SimpleSchedule { + /** @var DateTime|null */ + protected $scheduled_date; + /** * Make the $date param optional and default to null. * @@ -16,7 +19,6 @@ public function __construct( DateTime $date = null ) { /** * This schedule has no scheduled DateTime, so we need to override the parent __sleep() - * * @return array */ public function __sleep() { diff --git a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_Schedule.php b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_Schedule.php index bfead893cf..d61a9f7c92 100644 --- a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_Schedule.php +++ b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_Schedule.php @@ -8,11 +8,11 @@ interface ActionScheduler_Schedule { * @param DateTime $after * @return DateTime|null */ - public function next( DateTime $after = null ); + public function next( DateTime $after = NULL ); /** * @return bool */ public function is_recurring(); } - + \ No newline at end of file diff --git a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_SimpleSchedule.php b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_SimpleSchedule.php index a1c8c33b66..454174c2ae 100644 --- a/src/libraries/action-scheduler/classes/schedules/ActionScheduler_SimpleSchedule.php +++ b/src/libraries/action-scheduler/classes/schedules/ActionScheduler_SimpleSchedule.php @@ -8,7 +8,7 @@ class ActionScheduler_SimpleSchedule extends ActionScheduler_Abstract_Schedule { /** * Deprecated property @see $this->__wakeup() for details. **/ - private $timestamp = null; + private $timestamp = NULL; /** * @param DateTime $after @@ -45,12 +45,9 @@ public function __sleep() { $this->timestamp = $this->scheduled_timestamp; - return array_merge( - $sleep_params, - array( - 'timestamp', - ) - ); + return array_merge( $sleep_params, array( + 'timestamp', + ) ); } /** @@ -65,7 +62,7 @@ public function __sleep() { */ public function __wakeup() { - if ( $this->scheduled_timestamp === null && $this->timestamp !== null ) { + if ( is_null( $this->scheduled_timestamp ) && ! is_null( $this->timestamp ) ) { $this->scheduled_timestamp = $this->timestamp; unset( $this->timestamp ); } diff --git a/src/libraries/action-scheduler/classes/schema/ActionScheduler_LoggerSchema.php b/src/libraries/action-scheduler/classes/schema/ActionScheduler_LoggerSchema.php index 5f139b0b83..c52d37ce0b 100644 --- a/src/libraries/action-scheduler/classes/schema/ActionScheduler_LoggerSchema.php +++ b/src/libraries/action-scheduler/classes/schema/ActionScheduler_LoggerSchema.php @@ -16,9 +16,9 @@ class ActionScheduler_LoggerSchema extends ActionScheduler_Abstract_Schema { protected $schema_version = 3; public function __construct() { - $this->tables = array( + $this->tables = [ self::LOG_TABLE, - ); + ]; } /** @@ -30,11 +30,12 @@ public function init() { protected function get_table_definition( $table ) { global $wpdb; - $table_name = $wpdb->$table; - $charset_collate = $wpdb->get_charset_collate(); + $table_name = $wpdb->$table; + $charset_collate = $wpdb->get_charset_collate(); switch ( $table ) { case self::LOG_TABLE: + $default_date = ActionScheduler_StoreSchema::DEFAULT_DATE; return "CREATE TABLE $table_name ( log_id bigint(20) unsigned NOT NULL auto_increment, diff --git a/src/libraries/action-scheduler/classes/schema/ActionScheduler_StoreSchema.php b/src/libraries/action-scheduler/classes/schema/ActionScheduler_StoreSchema.php index 9529e27638..d52f27f6fc 100644 --- a/src/libraries/action-scheduler/classes/schema/ActionScheduler_StoreSchema.php +++ b/src/libraries/action-scheduler/classes/schema/ActionScheduler_StoreSchema.php @@ -19,11 +19,11 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema { protected $schema_version = 6; public function __construct() { - $this->tables = array( + $this->tables = [ self::ACTIONS_TABLE, self::CLAIMS_TABLE, self::GROUPS_TABLE, - ); + ]; } /** @@ -42,18 +42,19 @@ protected function get_table_definition( $table ) { switch ( $table ) { case self::ACTIONS_TABLE: + return "CREATE TABLE {$table_name} ( action_id bigint(20) unsigned NOT NULL auto_increment, hook varchar(191) NOT NULL, status varchar(20) NOT NULL, - scheduled_date_gmt datetime NULL default '${default_date}', - scheduled_date_local datetime NULL default '${default_date}', + scheduled_date_gmt datetime NULL default '{$default_date}', + scheduled_date_local datetime NULL default '{$default_date}', args varchar($max_index_length), schedule longtext, group_id bigint(20) unsigned NOT NULL default '0', attempts int(11) NOT NULL default '0', - last_attempt_gmt datetime NULL default '${default_date}', - last_attempt_local datetime NULL default '${default_date}', + last_attempt_gmt datetime NULL default '{$default_date}', + last_attempt_local datetime NULL default '{$default_date}', claim_id bigint(20) unsigned NOT NULL default '0', extended_args varchar(8000) DEFAULT NULL, PRIMARY KEY (action_id), @@ -67,14 +68,16 @@ protected function get_table_definition( $table ) { ) $charset_collate"; case self::CLAIMS_TABLE: + return "CREATE TABLE {$table_name} ( claim_id bigint(20) unsigned NOT NULL auto_increment, - date_created_gmt datetime NULL default '${default_date}', + date_created_gmt datetime NULL default '{$default_date}', PRIMARY KEY (claim_id), KEY date_created_gmt (date_created_gmt) ) $charset_collate"; case self::GROUPS_TABLE: + return "CREATE TABLE {$table_name} ( group_id bigint(20) unsigned NOT NULL auto_increment, slug varchar(255) NOT NULL, @@ -108,16 +111,16 @@ public function update_schema_5_0( $table, $db_version ) { // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared $table_name = $wpdb->prefix . 'actionscheduler_actions'; - $table_list = $wpdb->get_col( "SHOW TABLES LIKE '${table_name}'" ); + $table_list = $wpdb->get_col( "SHOW TABLES LIKE '{$table_name}'" ); $default_date = self::DEFAULT_DATE; if ( ! empty( $table_list ) ) { $query = " - ALTER TABLE ${table_name} - MODIFY COLUMN scheduled_date_gmt datetime NULL default '${default_date}', - MODIFY COLUMN scheduled_date_local datetime NULL default '${default_date}', - MODIFY COLUMN last_attempt_gmt datetime NULL default '${default_date}', - MODIFY COLUMN last_attempt_local datetime NULL default '${default_date}' + ALTER TABLE {$table_name} + MODIFY COLUMN scheduled_date_gmt datetime NULL default '{$default_date}', + MODIFY COLUMN scheduled_date_local datetime NULL default '{$default_date}', + MODIFY COLUMN last_attempt_gmt datetime NULL default '{$default_date}', + MODIFY COLUMN last_attempt_local datetime NULL default '{$default_date}' "; $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } diff --git a/src/libraries/action-scheduler/codecov.yml b/src/libraries/action-scheduler/codecov.yml deleted file mode 100644 index fe69f96695..0000000000 --- a/src/libraries/action-scheduler/codecov.yml +++ /dev/null @@ -1,13 +0,0 @@ -codecov: - branch: master - -coverage: - ignore: - - tests/.* - - lib/.* - status: - project: false - patch: false - changes: false - -comment: false diff --git a/src/libraries/action-scheduler/composer.json b/src/libraries/action-scheduler/composer.json deleted file mode 100644 index 2ab9bf5a0a..0000000000 --- a/src/libraries/action-scheduler/composer.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "woocommerce/action-scheduler", - "description": "Action Scheduler for WordPress and WooCommerce", - "homepage": "https://actionscheduler.org/", - "type": "wordpress-plugin", - "license": "GPL-3.0-or-later", - "prefer-stable": true, - "minimum-stability": "dev", - "require": {}, - "require-dev": { - "phpunit/phpunit": "^7.5", - "wp-cli/wp-cli": "~2.5.0", - "woocommerce/woocommerce-sniffs": "0.1.0", - "yoast/phpunit-polyfills": "^1.0" - }, - "config": { - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true - }, - "platform": { - "php": "7.3" - } - }, - "archive": { - "exclude": [ - "node_modules" - ] - }, - "scripts": { - "test": [ - "./vendor/bin/phpunit tests -c tests/phpunit.xml.dist" - ], - "phpcs": [ - "phpcs -s -p" - ], - "phpcs-pre-commit": [ - "phpcs -s -p -n" - ], - "phpcbf": [ - "phpcbf -p" - ] - }, - "extra": { - "scripts-description": { - "test": "Run unit tests", - "phpcs": "Analyze code against the WordPress coding standards with PHP_CodeSniffer", - "phpcbf": "Fix coding standards warnings/errors automatically with PHP Code Beautifier" - } - } -} diff --git a/src/libraries/action-scheduler/composer.lock b/src/libraries/action-scheduler/composer.lock deleted file mode 100644 index fd47cf9b51..0000000000 --- a/src/libraries/action-scheduler/composer.lock +++ /dev/null @@ -1,2602 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "70864a6e30dbbcb32cc2994aaf87a017", - "packages": [], - "packages-dev": [ - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.0", - "source": { - "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev" - }, - "require-dev": { - "composer/composer": "*", - "phpcompatibility/php-compatibility": "^9.0", - "sensiolabs/security-checker": "^4.1.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" - ], - "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" - }, - "time": "2020-06-25T14:57:39+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^8.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2020-11-10T18:47:58+00:00" - }, - { - "name": "mustache/mustache", - "version": "v2.14.1", - "source": { - "type": "git", - "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "579ffa5c96e1d292c060b3dd62811ff01ad8c24e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/579ffa5c96e1d292c060b3dd62811ff01ad8c24e", - "reference": "579ffa5c96e1d292c060b3dd62811ff01ad8c24e", - "shasum": "" - }, - "require": { - "php": ">=5.2.4" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~1.11", - "phpunit/phpunit": "~3.7|~4.0|~5.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Mustache": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" - } - ], - "description": "A Mustache implementation in PHP.", - "homepage": "https://github.com/bobthecow/mustache.php", - "keywords": [ - "mustache", - "templating" - ], - "support": { - "issues": "https://github.com/bobthecow/mustache.php/issues", - "source": "https://github.com/bobthecow/mustache.php/tree/v2.14.1" - }, - "time": "2022-01-21T06:08:36+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.10.2", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2020-11-13T09:40:50+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/master" - }, - "time": "2018-07-08T19:23:20+00:00" - }, - { - "name": "phar-io/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/master" - }, - "time": "2018-07-08T19:19:57+00:00" - }, - { - "name": "phpcompatibility/php-compatibility", - "version": "9.3.5", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" - }, - "conflict": { - "squizlabs/php_codesniffer": "2.6.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Wim Godden", - "homepage": "https://github.com/wimg", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" - } - ], - "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", - "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", - "keywords": [ - "compatibility", - "phpcs", - "standards" - ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibility" - }, - "time": "2019-12-27T09:44:58+00:00" - }, - { - "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "ddabec839cc003651f2ce695c938686d1086cf43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/ddabec839cc003651f2ce695c938686d1086cf43", - "reference": "ddabec839cc003651f2ce695c938686d1086cf43", - "shasum": "" - }, - "require": { - "phpcompatibility/php-compatibility": "^9.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7", - "paragonie/random_compat": "dev-master", - "paragonie/sodium_compat": "dev-master" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Wim Godden", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "lead" - } - ], - "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", - "homepage": "http://phpcompatibility.com/", - "keywords": [ - "compatibility", - "paragonie", - "phpcs", - "polyfill", - "standards" - ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" - }, - "time": "2021-02-15T10:24:51+00:00" - }, - { - "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "41bef18ba688af638b7310666db28e1ea9158b2f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/41bef18ba688af638b7310666db28e1ea9158b2f", - "reference": "41bef18ba688af638b7310666db28e1ea9158b2f", - "shasum": "" - }, - "require": { - "phpcompatibility/php-compatibility": "^9.0", - "phpcompatibility/phpcompatibility-paragonie": "^1.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Wim Godden", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "lead" - } - ], - "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", - "homepage": "http://phpcompatibility.com/", - "keywords": [ - "compatibility", - "phpcs", - "standards", - "wordpress" - ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" - }, - "time": "2019-08-28T14:22:28+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.5.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", - "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" - }, - "time": "2021-10-02T14:08:47+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.14.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" - }, - "time": "2021-09-10T09:02:12+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "6.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "suggest": { - "ext-xdebug": "^2.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/master" - }, - "time": "2018-10-31T16:06:48+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/28af674ff175d0768a5a978e6de83f697d4a7f05", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-07-19T06:46:01+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:20:02+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9c1da83261628cb24b6a6df371b6e312b3954768", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "abandoned": true, - "time": "2021-07-26T12:15:06+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "7.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.5-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/7.5.20" - }, - "time": "2020-01-08T08:45:45+00:00" - }, - { - "name": "rmccue/requests", - "version": "v1.8.1", - "source": { - "type": "git", - "url": "https://github.com/WordPress/Requests.git", - "reference": "82e6936366eac3af4d836c18b9d8c31028fe4cd5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/WordPress/Requests/zipball/82e6936366eac3af4d836c18b9d8c31028fe4cd5", - "reference": "82e6936366eac3af4d836c18b9d8c31028fe4cd5", - "shasum": "" - }, - "require": { - "php": ">=5.2" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7", - "php-parallel-lint/php-console-highlighter": "^0.5.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcompatibility/php-compatibility": "^9.0", - "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5", - "requests/test-server": "dev-master", - "squizlabs/php_codesniffer": "^3.5", - "wp-coding-standards/wpcs": "^2.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Requests": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Ryan McCue", - "homepage": "http://ryanmccue.info" - } - ], - "description": "A HTTP library written in PHP, for human beings.", - "homepage": "http://github.com/WordPress/Requests", - "keywords": [ - "curl", - "fsockopen", - "http", - "idna", - "ipv6", - "iri", - "sockets" - ], - "support": { - "issues": "https://github.com/WordPress/Requests/issues", - "source": "https://github.com/WordPress/Requests/tree/v1.8.1" - }, - "time": "2021-06-04T09:56:25+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:15:22+00:00" - }, - { - "name": "sebastian/comparator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:04:30+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:59:04+00:00" - }, - { - "name": "sebastian/environment", - "version": "4.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:53:42+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:47:53+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0" - }, - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:40:27+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:37:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:34:24+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "abandoned": true, - "time": "2020-11-30T07:30:19+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.6.1", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/f268ca40d54617c6e06757f83f699775c9b3ff2e", - "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2021-10-11T04:00:11+00:00" - }, - { - "name": "symfony/finder", - "version": "v5.3.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v5.3.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-08-04T21:20:46+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-02-19T12:13:01+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-28T13:41:28+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.10.0" - }, - "time": "2021-03-09T10:59:23+00:00" - }, - { - "name": "woocommerce/woocommerce-sniffs", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/woocommerce/woocommerce-sniffs.git", - "reference": "b72b7dd2e70aa6aed16f80cdae5b1e6cce2e4c79" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/b72b7dd2e70aa6aed16f80cdae5b1e6cce2e4c79", - "reference": "b72b7dd2e70aa6aed16f80cdae5b1e6cce2e4c79", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "0.7.0", - "php": ">=7.0", - "phpcompatibility/phpcompatibility-wp": "2.1.0", - "wp-coding-standards/wpcs": "2.3.0" - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Claudio Sanches", - "email": "claudio@automattic.com" - } - ], - "description": "WooCommerce sniffs", - "keywords": [ - "phpcs", - "standards", - "woocommerce", - "wordpress" - ], - "support": { - "issues": "https://github.com/woocommerce/woocommerce-sniffs/issues", - "source": "https://github.com/woocommerce/woocommerce-sniffs/tree/master" - }, - "time": "2020-08-06T18:23:45+00:00" - }, - { - "name": "wp-cli/mustangostang-spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "https://github.com/wp-cli/spyc.git", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/spyc/zipball/6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "psr-4": { - "Mustangostang\\": "src/" - }, - "files": [ - "includes/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP (WP-CLI fork)", - "homepage": "https://github.com/mustangostang/spyc/", - "support": { - "source": "https://github.com/wp-cli/spyc/tree/autoload" - }, - "time": "2017-04-25T11:26:20+00:00" - }, - { - "name": "wp-cli/php-cli-tools", - "version": "v0.11.13", - "source": { - "type": "git", - "url": "https://github.com/wp-cli/php-cli-tools.git", - "reference": "a2866855ac1abc53005c102e901553ad5772dc04" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/a2866855ac1abc53005c102e901553ad5772dc04", - "reference": "a2866855ac1abc53005c102e901553ad5772dc04", - "shasum": "" - }, - "require": { - "php": ">= 5.3.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "cli": "lib/" - }, - "files": [ - "lib/cli/cli.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@handbuilt.co", - "role": "Maintainer" - }, - { - "name": "James Logsdon", - "email": "jlogsdon@php.net", - "role": "Developer" - } - ], - "description": "Console utilities for PHP", - "homepage": "http://github.com/wp-cli/php-cli-tools", - "keywords": [ - "cli", - "console" - ], - "support": { - "issues": "https://github.com/wp-cli/php-cli-tools/issues", - "source": "https://github.com/wp-cli/php-cli-tools/tree/v0.11.13" - }, - "time": "2021-07-01T15:08:16+00:00" - }, - { - "name": "wp-cli/wp-cli", - "version": "v2.5.0", - "source": { - "type": "git", - "url": "https://github.com/wp-cli/wp-cli.git", - "reference": "0bcf0c54f4d35685211d435e25219cc7acbe6d48" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/0bcf0c54f4d35685211d435e25219cc7acbe6d48", - "reference": "0bcf0c54f4d35685211d435e25219cc7acbe6d48", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "mustache/mustache": "~2.13", - "php": "^5.6 || ^7.0 || ^8.0", - "rmccue/requests": "^1.8", - "symfony/finder": ">2.7", - "wp-cli/mustangostang-spyc": "^0.6.3", - "wp-cli/php-cli-tools": "~0.11.2" - }, - "require-dev": { - "roave/security-advisories": "dev-master", - "wp-cli/db-command": "^1.3 || ^2", - "wp-cli/entity-command": "^1.2 || ^2", - "wp-cli/extension-command": "^1.1 || ^2", - "wp-cli/package-command": "^1 || ^2", - "wp-cli/wp-cli-tests": "^3.0.7" - }, - "suggest": { - "ext-readline": "Include for a better --prompt implementation", - "ext-zip": "Needed to support extraction of ZIP archives when doing downloads or updates" - }, - "bin": [ - "bin/wp", - "bin/wp.bat" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5.x-dev" - } - }, - "autoload": { - "psr-0": { - "WP_CLI\\": "php/" - }, - "classmap": [ - "php/class-wp-cli.php", - "php/class-wp-cli-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "WP-CLI framework", - "homepage": "https://wp-cli.org", - "keywords": [ - "cli", - "wordpress" - ], - "support": { - "docs": "https://make.wordpress.org/cli/handbook/", - "issues": "https://github.com/wp-cli/wp-cli/issues", - "source": "https://github.com/wp-cli/wp-cli" - }, - "time": "2021-05-14T13:44:51+00:00" - }, - { - "name": "wp-coding-standards/wpcs", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "7da1894633f168fe244afc6de00d141f27517b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62", - "reference": "7da1894633f168fe244afc6de00d141f27517b62", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.3.1" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", - "phpcompatibility/php-compatibility": "^9.0", - "phpcsstandards/phpcsdevtools": "^1.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Contributors", - "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", - "keywords": [ - "phpcs", - "standards", - "wordpress" - ], - "support": { - "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", - "source": "https://github.com/WordPress/WordPress-Coding-Standards", - "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" - }, - "time": "2020-05-13T23:57:56+00:00" - }, - { - "name": "yoast/phpunit-polyfills", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "1a582ab1d91e86aa450340c4d35631a85314ff9f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/1a582ab1d91e86aa450340c4d35631a85314ff9f", - "reference": "1a582ab1d91e86aa450340c4d35631a85314ff9f", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "require-dev": { - "yoast/yoastcs": "^2.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev", - "dev-develop": "1.x-dev" - } - }, - "autoload": { - "files": [ - "phpunitpolyfills-autoload.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Team Yoast", - "email": "support@yoast.com", - "homepage": "https://yoast.com" - }, - { - "name": "Contributors", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" - } - ], - "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", - "keywords": [ - "phpunit", - "polyfill", - "testing" - ], - "support": { - "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", - "source": "https://github.com/Yoast/PHPUnit-Polyfills" - }, - "time": "2021-10-03T08:40:26+00:00" - } - ], - "aliases": [], - "minimum-stability": "dev", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "platform-overrides": { - "php": "7.3" - }, - "plugin-api-version": "2.2.0" -} diff --git a/src/libraries/action-scheduler/deprecated/ActionScheduler_AdminView_Deprecated.php b/src/libraries/action-scheduler/deprecated/ActionScheduler_AdminView_Deprecated.php index 1d8f6fd4e1..69b46d7bd4 100644 --- a/src/libraries/action-scheduler/deprecated/ActionScheduler_AdminView_Deprecated.php +++ b/src/libraries/action-scheduler/deprecated/ActionScheduler_AdminView_Deprecated.php @@ -68,7 +68,7 @@ public static function list_table_sortable_columns( $columns ) { * Print the content for our custom columns. * * @param string $column_name The key for the column for which we should output our content. - * @param int $post_id The ID of the 'scheduled-action' post for which this row relates. + * @param int $post_id The ID of the 'scheduled-action' post for which this row relates. */ public static function list_table_column_content( $column_name, $post_id ) { _deprecated_function( __METHOD__, '2.0.0' ); @@ -115,18 +115,18 @@ public static function admin_notices() { /** * Filter search queries to allow searching by Claim ID (i.e. post_password). * - * @param string $orderby MySQL orderby string. + * @param string $orderby MySQL orderby string. * @param WP_Query $query Instance of a WP_Query object * @return string MySQL orderby string. */ - public function custom_orderby( $orderby, $query ) { + public function custom_orderby( $orderby, $query ){ _deprecated_function( __METHOD__, '2.0.0' ); } /** * Filter search queries to allow searching by Claim ID (i.e. post_password). * - * @param string $search MySQL search string. + * @param string $search MySQL search string. * @param WP_Query $query Instance of a WP_Query object * @return string MySQL search string. */ @@ -144,4 +144,4 @@ public function post_updated_messages( $messages ) { _deprecated_function( __METHOD__, '2.0.0' ); return $messages; } -} +} \ No newline at end of file diff --git a/src/libraries/action-scheduler/deprecated/ActionScheduler_Store_Deprecated.php b/src/libraries/action-scheduler/deprecated/ActionScheduler_Store_Deprecated.php index 02dc8b7c12..002dc75b41 100644 --- a/src/libraries/action-scheduler/deprecated/ActionScheduler_Store_Deprecated.php +++ b/src/libraries/action-scheduler/deprecated/ActionScheduler_Store_Deprecated.php @@ -2,7 +2,6 @@ /** * Class ActionScheduler_Store_Deprecated - * * @codeCoverageIgnore */ abstract class ActionScheduler_Store_Deprecated { diff --git a/src/libraries/action-scheduler/deprecated/functions.php b/src/libraries/action-scheduler/deprecated/functions.php index 8a1c90f56d..f782c4b7f0 100644 --- a/src/libraries/action-scheduler/deprecated/functions.php +++ b/src/libraries/action-scheduler/deprecated/functions.php @@ -11,9 +11,9 @@ /** * Schedule an action to run one time * - * @param int $timestamp When the job will run + * @param int $timestamp When the job will run * @param string $hook The hook to trigger - * @param array $args Arguments to pass when the hook triggers + * @param array $args Arguments to pass when the hook triggers * @param string $group The group to assign this job to * * @return string The job ID @@ -26,10 +26,10 @@ function wc_schedule_single_action( $timestamp, $hook, $args = array(), $group = /** * Schedule a recurring action * - * @param int $timestamp When the first instance of the job will run - * @param int $interval_in_seconds How long to wait between runs + * @param int $timestamp When the first instance of the job will run + * @param int $interval_in_seconds How long to wait between runs * @param string $hook The hook to trigger - * @param array $args Arguments to pass when the hook triggers + * @param array $args Arguments to pass when the hook triggers * @param string $group The group to assign this job to * * @deprecated 2.1.0 @@ -44,7 +44,7 @@ function wc_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, /** * Schedule an action that recurs on a cron-like schedule. * - * @param int $timestamp The schedule will start on or after this time + * @param int $timestamp The schedule will start on or after this time * @param string $schedule A cron-link schedule string * @see http://en.wikipedia.org/wiki/Cron * * * * * * * @@ -57,7 +57,7 @@ function wc_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, * | +-------------------- hour (0 - 23) * +------------------------- min (0 - 59) * @param string $hook The hook to trigger - * @param array $args Arguments to pass when the hook triggers + * @param array $args Arguments to pass when the hook triggers * @param string $group The group to assign this job to * * @deprecated 2.1.0 @@ -73,7 +73,7 @@ function wc_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), * Cancel the next occurrence of a job. * * @param string $hook The hook that the job will trigger - * @param array $args Args that would have been passed to the job + * @param array $args Args that would have been passed to the job * @param string $group * * @deprecated 2.1.0 @@ -85,14 +85,14 @@ function wc_unschedule_action( $hook, $args = array(), $group = '' ) { /** * @param string $hook - * @param array $args + * @param array $args * @param string $group * * @deprecated 2.1.0 * * @return int|bool The timestamp for the next occurrence, or false if nothing was found */ -function wc_next_scheduled_action( $hook, $args = null, $group = '' ) { +function wc_next_scheduled_action( $hook, $args = NULL, $group = '' ) { _deprecated_function( __FUNCTION__, '2.1.0', 'as_next_scheduled_action()' ); return as_next_scheduled_action( $hook, $args, $group ); } @@ -100,20 +100,20 @@ function wc_next_scheduled_action( $hook, $args = null, $group = '' ) { /** * Find scheduled actions * - * @param array $args Possible arguments, with their default values: - * 'hook' => '' - the name of the action that will be triggered - * 'args' => NULL - the args array that will be passed with the action - * 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone. - * 'date_compare' => '<=' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '=' - * 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone. - * 'modified_compare' => '<=' - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '=' - * 'group' => '' - the group the action belongs to - * 'status' => '' - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING - * 'claimed' => NULL - TRUE to find claimed actions, FALSE to find unclaimed actions, a string to find a specific claim ID - * 'per_page' => 5 - Number of results to return - * 'offset' => 0 - * 'orderby' => 'date' - accepted values are 'hook', 'group', 'modified', or 'date' - * 'order' => 'ASC' + * @param array $args Possible arguments, with their default values: + * 'hook' => '' - the name of the action that will be triggered + * 'args' => NULL - the args array that will be passed with the action + * 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone. + * 'date_compare' => '<=' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '=' + * 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone. + * 'modified_compare' => '<=' - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '=' + * 'group' => '' - the group the action belongs to + * 'status' => '' - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING + * 'claimed' => NULL - TRUE to find claimed actions, FALSE to find unclaimed actions, a string to find a specific claim ID + * 'per_page' => 5 - Number of results to return + * 'offset' => 0 + * 'orderby' => 'date' - accepted values are 'hook', 'group', 'modified', or 'date' + * 'order' => 'ASC' * @param string $return_format OBJECT, ARRAY_A, or ids * * @deprecated 2.1.0 diff --git a/src/libraries/action-scheduler/docs/CNAME b/src/libraries/action-scheduler/docs/CNAME deleted file mode 100644 index 3b480b54c7..0000000000 --- a/src/libraries/action-scheduler/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -actionscheduler.org \ No newline at end of file diff --git a/src/libraries/action-scheduler/docs/_config.yml b/src/libraries/action-scheduler/docs/_config.yml deleted file mode 100644 index fe521d98c3..0000000000 --- a/src/libraries/action-scheduler/docs/_config.yml +++ /dev/null @@ -1,7 +0,0 @@ -title: Action Scheduler - Job Queue for WordPress -description: A scalable, traceable job queue for background processing large queues of tasks in WordPress. Designed for distribution in WordPress plugins - no server access required. -theme: jekyll-theme-hacker -permalink: /:slug/ -plugins: - - jekyll-seo-tag - - jekyll-sitemap diff --git a/src/libraries/action-scheduler/docs/_layouts/default.html b/src/libraries/action-scheduler/docs/_layouts/default.html deleted file mode 100644 index 3559804820..0000000000 --- a/src/libraries/action-scheduler/docs/_layouts/default.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - -{% seo %} - - - - -
- - - -
-

Usage | Admin | WP-CLI | Background Processing at Scale | API | FAQ | Version 3.0 -

action-scheduler

-

A scalable, traceable job queue for background processing large queues of tasks in WordPress. Designed for distribution in WordPress plugins - no server access required.

-
-
- -
-
- {{ content }} -
-
- - - - {% if site.google_analytics %} - - {% endif %} - - \ No newline at end of file diff --git a/src/libraries/action-scheduler/docs/admin.md b/src/libraries/action-scheduler/docs/admin.md deleted file mode 100644 index 5ef0b56cb8..0000000000 --- a/src/libraries/action-scheduler/docs/admin.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -description: Learn how to administer background jobs with the Action Scheduler job queue for WordPress. ---- -# Scheduled Actions Administration Screen - -Action Scheduler has a built in administration screen for monitoring, debugging and manually triggering scheduled actions. - -The administration interface is accesible through both: - -1. **Tools > Scheduled Actions** -1. **WooCommerce > Status > Scheduled Actions**, when WooCommerce is installed. - -Among other tasks, from the admin screen you can: - -* run a pending action -* view the scheduled actions with a specific status, like the all actions which have failed or are in-progress (https://cldup.com/NNTwE88Xl8.png). -* view the log entries for a specific action to find out why it failed. -* sort scheduled actions by hook name, scheduled date, claim ID or group name. - -Still have questions? Check out the [FAQ](/faq). - -![](https://cldup.com/5BA2BNB1sw.png) diff --git a/src/libraries/action-scheduler/docs/android-chrome-192x192.png b/src/libraries/action-scheduler/docs/android-chrome-192x192.png deleted file mode 100644 index 36475ecb05..0000000000 Binary files a/src/libraries/action-scheduler/docs/android-chrome-192x192.png and /dev/null differ diff --git a/src/libraries/action-scheduler/docs/android-chrome-256x256.png b/src/libraries/action-scheduler/docs/android-chrome-256x256.png deleted file mode 100644 index 881cb44034..0000000000 Binary files a/src/libraries/action-scheduler/docs/android-chrome-256x256.png and /dev/null differ diff --git a/src/libraries/action-scheduler/docs/api.md b/src/libraries/action-scheduler/docs/api.md deleted file mode 100644 index a1cc3be6bb..0000000000 --- a/src/libraries/action-scheduler/docs/api.md +++ /dev/null @@ -1,255 +0,0 @@ ---- -description: Reference guide for background processing functions provided by the Action Scheduler job queue for WordPress. ---- -# API Reference - -Action Scheduler provides a range of functions for scheduling hooks to run at some time in the future on one or more occassions. - -To understand the scheduling functions, it can help to think of them as extensions to WordPress' `do_action()` function that add the ability to delay and repeat when the hook will be triggered. - -## WP-Cron APIs vs. Action Scheduler APIs - -The Action Scheduler API functions are designed to mirror the WordPress [WP-Cron API functions](http://codex.wordpress.org/Category:WP-Cron_Functions). - -Functions return similar values and accept similar arguments to their WP-Cron counterparts. The notable differences are: - -* `as_schedule_single_action()` & `as_schedule_recurring_action()` will return the ID of the scheduled action rather than boolean indicating whether the event was scheduled -* `as_schedule_recurring_action()` takes an interval in seconds as the recurring interval rather than an arbitrary string -* `as_schedule_single_action()` & `as_schedule_recurring_action()` can accept a `$group` parameter to group different actions for the one plugin together. -* the `wp_` prefix is substituted with `as_` and the term `event` is replaced with `action` - -## API Function Availability - -As mentioned in the [Usage - Load Order](usage.md#load-order) section, Action Scheduler will initialize itself on the `'init'` hook with priority `1`. While API functions are loaded prior to this and can be called, they should not be called until after `'init'` with priority `1`, because each component, like the data store, has not yet been initialized. - -Do not use Action Scheduler API functions prior to `'init'` hook with priority `1`. Doing so could lead to unexpected results, like data being stored in the incorrect location. - -Action Scheduler provides `Action_Scheduler::is_initialized()` for use in hooks to confirm that the data stores have been initialized. - -## Function Reference / `as_enqueue_async_action()` - -### Description - -Enqueue an action to run one time, as soon as possible. - -### Usage - -```php -as_enqueue_async_action( $hook, $args, $group ); -``` - -### Parameters - -- **$hook** (string)(required) Name of the action hook. -- **$args** (array) Arguments to pass to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group to assign this job to. Default: _''_. -- **$unique** (boolean) Whether the action should be unique. Default: _`false`_. - -### Return value - -`(integer)` the action's ID. - - -## Function Reference / `as_schedule_single_action()` - -### Description - -Schedule an action to run one time at some defined point in the future. - -### Usage - -```php -as_schedule_single_action( $timestamp, $hook, $args, $group ); -``` - -### Parameters - -- **$timestamp** (integer)(required) The Unix timestamp representing the date you want the action to run. -- **$hook** (string)(required) Name of the action hook. -- **$args** (array) Arguments to pass to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group to assign this job to. Default: _''_. -- **$unique** (boolean) Whether the action should be unique. Default: _`false`_. - -### Return value - -`(integer)` the action's ID. - - -## Function Reference / `as_schedule_recurring_action()` - -### Description - -Schedule an action to run repeatedly with a specified interval in seconds. - -### Usage - -```php -as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args, $group ); -``` - -### Parameters - -- **$timestamp** (integer)(required) The Unix timestamp representing the date you want the action to run. -- **$interval_in_seconds** (integer)(required) How long to wait between runs. -- **$hook** (string)(required) Name of the action hook. -- **$args** (array) Arguments to pass to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group to assign this job to. Default: _''_. -- **$unique** (boolean) Whether the action should be unique. Default: _`false`_. - -### Return value - -`(integer)` the action's ID. - - -## Function Reference / `as_schedule_cron_action()` - -### Description - -Schedule an action that recurs on a cron-like schedule. - -If execution of a cron-like action is delayed, the next attempt will still be scheduled according to the provided cron expression. - -### Usage - -```php -as_schedule_cron_action( $timestamp, $schedule, $hook, $args, $group ); -``` - -### Parameters - -- **$timestamp** (integer)(required) The Unix timestamp representing the date you want the action to run. -- **$schedule** (string)(required) $schedule A cron-like schedule string, see http://en.wikipedia.org/wiki/Cron. -- **$hook** (string)(required) Name of the action hook. -- **$args** (array) Arguments to pass to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group to assign this job to. Default: _''_. -- **$unique** (boolean) Whether the action should be unique. Default: _`false`_. - -### Return value - -`(integer)` the action's ID. - - -## Function Reference / `as_unschedule_action()` - -### Description - -Cancel the next occurrence of a scheduled action. - -### Usage - -```php -as_unschedule_action( $hook, $args, $group ); -``` - -### Parameters - -- **$hook** (string)(required) Name of the action hook. -- **$args** (array) Arguments passed to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group the job is assigned to. Default: _''_. - -### Return value - -`(null)` - -## Function Reference / `as_unschedule_all_actions()` - -### Description - -Cancel all occurrences of a scheduled action. - -### Usage - -```php -as_unschedule_all_actions( $hook, $args, $group ) -``` - -### Parameters - -- **$hook** (string)(required) Name of the action hook. -- **$args** (array) Arguments passed to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group the job is assigned to. Default: _''_. - -### Return value - -`(string|null)` The scheduled action ID if a scheduled action was found, or null if no matching action found. - - -## Function Reference / `as_next_scheduled_action()` - -### Description - -Returns the next timestamp for a scheduled action. - -### Usage - -```php -as_next_scheduled_action( $hook, $args, $group ); -``` - -### Parameters - -- **$hook** (string)(required) Name of the action hook. Default: _none_. -- **$args** (array) Arguments passed to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group the job is assigned to. Default: _''_. - -### Return value - -`(integer|boolean)` The timestamp for the next occurrence of a pending scheduled action, true for an async or in-progress action or false if there is no matching action. - - -## Function Reference / `as_has_scheduled_action()` - -### Description - -Check if there is a scheduled action in the queue, but more efficiently than as_next_scheduled_action(). It's recommended to use this function when you need to know whether a specific action is currently scheduled. _Available since 3.3.0._ - -### Usage - -```php -as_has_scheduled_action( $hook, $args, $group ); -``` - -### Parameters - -- **$hook** (string)(required) Name of the action hook. Default: _none_. -- **$args** (array) Arguments passed to callbacks when the hook triggers. Default: _`array()`_. -- **$group** (string) The group the job is assigned to. Default: _''_. - -### Return value - -`(boolean)` True if a matching action is pending or in-progress, false otherwise. - - -## Function Reference / `as_get_scheduled_actions()` - -### Description - -Find scheduled actions. - -### Usage - -```php -as_get_scheduled_actions( $args, $return_format ); -``` - -### Parameters - -- **$args** (array) Arguments to search and filter results by. Possible arguments, with their default values: - * `'hook' => ''` - the name of the action that will be triggered - * `'args' => NULL` - the args array that will be passed with the action - * `'date' => NULL` - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). - * `'date_compare' => '<=`' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '=' - * `'modified' => NULL` - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). - * `'modified_compare' => '<='` - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '=' - * `'group' => ''` - the group the action belongs to - * `'status' => ''` - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING - * `'claimed' => NULL` - TRUE to find claimed actions, FALSE to find unclaimed actions, a string to find a specific claim ID - * `'per_page' => 5` - Number of results to return - * `'offset' => 0` - * `'orderby' => 'date'` - accepted values are 'hook', 'group', 'modified', or 'date' - * `'order' => 'ASC'` -- **$return_format** (string) The format in which to return the scheduled actions: 'OBJECT', 'ARRAY_A', or 'ids'. Default: _'OBJECT'_. - -### Return value - -`(array)` Array of action rows matching the criteria specified with `$args`. diff --git a/src/libraries/action-scheduler/docs/apple-touch-icon.png b/src/libraries/action-scheduler/docs/apple-touch-icon.png deleted file mode 100644 index 36475ecb05..0000000000 Binary files a/src/libraries/action-scheduler/docs/apple-touch-icon.png and /dev/null differ diff --git a/src/libraries/action-scheduler/docs/assets/css/style.scss b/src/libraries/action-scheduler/docs/assets/css/style.scss deleted file mode 100644 index a13de0d069..0000000000 --- a/src/libraries/action-scheduler/docs/assets/css/style.scss +++ /dev/null @@ -1,57 +0,0 @@ ---- ---- - -@import "{{ site.theme }}"; - -a { - text-shadow: none; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -header h1 a { - color: #b5e853; -} - -.container { - max-width: 700px; -} - -footer { - margin-top: 6em; - padding: 1.6em 0; - border-top: 1px dashed #b5e853; -} - -.footer-image { - text-align: center; - padding-top: 1em; -} - -.github-corner:hover .octo-arm { - animation:octocat-wave 560ms ease-in-out -} - -@keyframes octocat-wave { - 0%,100%{ - transform:rotate(0) - } - 20%,60%{ - transform:rotate(-25deg) - } - 40%,80%{ - transform:rotate(10deg) - } -} - -@media (max-width:500px){ - .github-corner:hover .octo-arm { - animation:none - } - .github-corner .octo-arm { - animation:octocat-wave 560ms ease-in-out - } -} \ No newline at end of file diff --git a/src/libraries/action-scheduler/docs/browserconfig.xml b/src/libraries/action-scheduler/docs/browserconfig.xml deleted file mode 100644 index f6244e65c8..0000000000 --- a/src/libraries/action-scheduler/docs/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #151515 - - - diff --git a/src/libraries/action-scheduler/docs/faq.md b/src/libraries/action-scheduler/docs/faq.md deleted file mode 100644 index 762b572939..0000000000 --- a/src/libraries/action-scheduler/docs/faq.md +++ /dev/null @@ -1,129 +0,0 @@ -## FAQ - -### Is it safe to release Action Scheduler in my plugin? Won't its functions conflict with another copy of the library? - -Action Scheduler is designed to be used and released in plugins. It avoids redeclaring public API functions when more than one copy of the library is being loaded by different plugins. It will also load only the most recent version of itself (by checking registered versions after all plugins are loaded on the `'plugins_loaded'` hook). - -To use it in your plugin (or theme), simply require the `action-scheduler/action-scheduler.php` file. Action Scheduler will take care of the rest. __Note:__ Action Scheduler is only loaded from a theme if it is not included in any active plugins. - -### I don't want to use WP-Cron. Does Action Scheduler depend on WP-Cron? - -By default, Action Scheduler is initiated by WP-Cron (and the `'shutdown'` hook on admin requests). However, it has no dependency on the WP-Cron system. You can initiate the Action Scheduler queue in other ways with just one or two lines of code. - -For example, you can start a queue directly by calling: - -```php -ActionScheduler::runner()->run(); -``` - -Or trigger the `'action_scheduler_run_queue'` hook and let Action Scheduler do it for you: - -```php -do_action( 'action_scheduler_run_queue', $context_identifier ); -``` - -Further customization can be done by extending the `ActionScheduler_Abstract_QueueRunner` class to create a custom Queue Runner. For an example of a customized queue runner, see the [`ActionScheduler_WPCLI_QueueRunner`](https://github.com/woocommerce/action-scheduler/blob/trunk/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php), which is used when running WP CLI. - -Want to create some other method for initiating Action Scheduler? [Open a new issue](https://github.com/woocommerce/action-scheduler/issues/new), we'd love to help you with it. - -### I don't want to use WP-Cron, ever. Does Action Scheduler replace WP-Cron? - -By default, Action Scheduler is designed to work alongside WP-Cron and not change any of its behaviour. This helps avoid unexpectedly overriding WP-Cron on sites installing your plugin, which may have nothing to do with WP-Cron. - -However, we can understand why you might want to replace WP-Cron completely in environments within your control, especially as it gets you the advantages of Action Scheduler. This should be possible without too much code. - -You could use the `'schedule_event'` hook in WordPress to use Action Scheduler for only newly scheduled WP-Cron jobs and map the `$event` param to Action Scheduler API functions. - -Alternatively, you can use a combination of the `'pre_update_option_cron'` and `'pre_option_cron'` hooks to override all new and previously scheduled WP-Cron jobs (similar to the way [Cavalcade](https://github.com/humanmade/Cavalcade) does it). - -If you'd like to create a plugin to do this automatically and want to share your work with others, [open a new issue to let us know](https://github.com/woocommerce/action-scheduler/issues/new), we'd love to help you with it. - -### How does Action Scheduler store its data? - -Action Scheduler 3.0 and newer stores data in custom tables prefixed with `actionscheduler_`. For the list of all tables and their schemas, refer to the `ActionScheduler_StoreSchema` class. - -Prior to Action 3.0, actions were a custom post type, and data was stored in `wp_posts`, `wp_postmeta` and related tables. - -Action Scheduler 3+ migrates data from the custom post type to custom tables. - -### Can I use a different storage scheme? - -Of course! Action Scheduler data storage is completely swappable, and always has been. - -If you choose to, you can actually store them anywhere, like in a remote storage service from Amazon Web Services. - -To implement a custom store: - -1. extend the abstract `ActionScheduler_Store` class, being careful to implement each of its methods -2. attach a callback to `'action_scheduler_store_class'` to tell Action Scheduler your class is the one which should be used to manage storage, e.g. - -``` -function eg_define_custom_store( $existing_storage_class ) { - return 'My_Radical_Action_Scheduler_Store'; -} -add_filter( 'action_scheduler_store_class', 'eg_define_custom_store', 10, 1 ); -``` - -Take a look at the `classes/data-stores/ActionScheduler_DBStore.php` class for an example implementation of `ActionScheduler_Store`. - -If you'd like to create a plugin to do this automatically and release it publicly to help others, [open a new issue to let us know](https://github.com/woocommerce/action-scheduler/issues/new), we'd love to help you with it. - -### Can I use a different storage scheme just for logging? - -Of course! Action Scheduler's logger is completely swappable, and always has been. You can also customise where logs are stored, and the storage mechanism. - -To implement a custom logger: - -1. extend the abstract `ActionScheduler_Logger` class, being careful to implement each of its methods -2. attach a callback to `'action_scheduler_logger_class'` to tell Action Scheduler your class is the one which should be used to manage logging, e.g. - -``` -function eg_define_custom_logger( $existing_storage_class ) { - return 'My_Radical_Action_Scheduler_Logger'; -} -add_filter( 'action_scheduler_logger_class', 'eg_define_custom_logger', 10, 1 ); -``` - -Take a look at the `classes/data-stores/ActionScheduler_DBLogger.php` class for an example implementation of `ActionScheduler_Logger`. - -### I want to run Action Scheduler only on a dedicated application server in my cluster. Can I do that? - -Wow, now you're really asking the tough questions. In theory, yes, this is possible. The `ActionScheduler_QueueRunner` class, which is responsible for running queues, is swappable via the `'action_scheduler_queue_runner_class'` filter. - -Because of this, you can effectively customise queue running however you need. Whether that means tweaking minor things, like not using WP-Cron at all to initiate queues by overriding `ActionScheduler_QueueRunner::init()`, or completely changing how and where queues are run, by overriding `ActionScheduler_QueueRunner::run()`. - -### Is Action Scheduler safe to use on my production site? - -Yes, absolutely! Action Scheduler is actively used on tens of thousands of production sites already. Right now it's responsible for scheduling everything from emails to payments. - -In fact, every month, Action Scheduler processes millions of payments as part of the [WooCommerce Subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/) extension. - -It requires no setup, and won't override any WordPress APIs (unless you want it to). - -### How does Action Scheduler work on WordPress Multisite? - -Action Scheduler is designed to manage the scheduled actions on a single site. It has no special handling for running queues across multiple sites in a multisite network. That said, because its storage and Queue Runner are completely swappable, it would be possible to write multisite handling classes to use with it. - -If you'd like to create a multisite plugin to do this and release it publicly to help others, [open a new issue to let us know](https://github.com/woocommerce/action-scheduler/issues/new), we'd love to help you with it. - -### How can I change the Action Scheduler User-Agent to better identify its requests? - -Action Scheduler has a filter available named `as_async_request_queue_runner_post_args` which can be used to filter the arguments that are being sent to the `wp_remote_post` call. - -The User-Agent parameter is just one of them and can be adjusted as follows: - -``` -function eg_define_custom_user_agent( $args ) { - $versions = ActionScheduler_Versions::instance(); - $args['user-agent'] = 'Action Scheduler/' . $versions->latest_version(); - - return $args; -} -add_filter( 'as_async_request_queue_runner_post_args', 'eg_define_custom_user_agent', 10, 1 ); -``` - -### My site has past-due actions; what can I do? - -Actions that are past-due have missed their scheduled date; because of [how WP Cron works](https://developer.wordpress.org/plugins/cron/), it is normal to have some past-due actions. - -If there are several past-due actions more than one day old, there may be something wrong with your site. If you need help determining the issue and are a WooCommerce.com customer, please [contact us](https://woocommerce.com/contact-us/). diff --git a/src/libraries/action-scheduler/docs/favicon-16x16.png b/src/libraries/action-scheduler/docs/favicon-16x16.png deleted file mode 100644 index c46600f590..0000000000 Binary files a/src/libraries/action-scheduler/docs/favicon-16x16.png and /dev/null differ diff --git a/src/libraries/action-scheduler/docs/favicon-32x32.png b/src/libraries/action-scheduler/docs/favicon-32x32.png deleted file mode 100644 index 7d088698d8..0000000000 Binary files a/src/libraries/action-scheduler/docs/favicon-32x32.png and /dev/null differ diff --git a/src/libraries/action-scheduler/docs/favicon.ico b/src/libraries/action-scheduler/docs/favicon.ico deleted file mode 100644 index 03560e3a61..0000000000 Binary files a/src/libraries/action-scheduler/docs/favicon.ico and /dev/null differ diff --git a/src/libraries/action-scheduler/docs/google14ef723abb376cd3.html b/src/libraries/action-scheduler/docs/google14ef723abb376cd3.html deleted file mode 100644 index f3bf171250..0000000000 --- a/src/libraries/action-scheduler/docs/google14ef723abb376cd3.html +++ /dev/null @@ -1 +0,0 @@ -google-site-verification: google14ef723abb376cd3.html \ No newline at end of file diff --git a/src/libraries/action-scheduler/docs/index.md b/src/libraries/action-scheduler/docs/index.md deleted file mode 100644 index 88379fa096..0000000000 --- a/src/libraries/action-scheduler/docs/index.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Action Scheduler - Background Processing Job Queue for WordPress ---- -## WordPress Job Queue with Background Processing - -Action Scheduler is a library for triggering a WordPress hook to run at some time in the future (or as soon as possible, in the case of an async action). Each hook can be scheduled with unique data, to allow callbacks to perform operations on that data. The hook can also be scheduled to run on one or more occassions. - -Think of it like an extension to `do_action()` which adds the ability to delay and repeat a hook. - -It just so happens, this functionality also creates a robust job queue for background processing large queues of tasks in WordPress. With the addition of logging and an [administration interface](/admin/), it also provide tracability on your tasks processed in the background. - -### Battle-Tested Background Processing - -Every month, Action Scheduler processes millions of payments for [Subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/), webhooks for [WooCommerce](https://wordpress.org/plugins/woocommerce/), as well as emails and other events for a range of other plugins. - -It's been seen on live sites processing queues in excess of 50,000 jobs and doing resource intensive operations, like processing payments and creating orders, in 10 concurrent queues at a rate of over 10,000 actions / hour without negatively impacting normal site operations. - -This is all possible on infrastructure and WordPress sites outside the control of the plugin author. - -Action Scheduler is specifically designed for distribution in WordPress plugins (and themes) - no server access required. If your plugin needs background processing, especially of large sets of tasks, Action Scheduler can help. - -### How it Works - -Action Scheduler stores the hook name, arguments and scheduled date for an action that should be triggered at some time in the future. - -The scheduler will attempt to run every minute by attaching itself as a callback to the `'action_scheduler_run_schedule'` hook, which is scheduled using WordPress's built-in [WP-Cron](http://codex.wordpress.org/Function_Reference/wp_cron) system. Once per minute on, it will also check on the `'shutdown'` hook of WP Admin requests whether there are pending actions, and if there are, it will initiate a queue via an async loopback request. - -When triggered, Action Scheduler will check for scheduled actions that have a due date at or before this point in time i.e. actions scheduled to run now or at sometime in the past. Action scheduled to run asynchronously, i.e. not scheduled, have a zero date, meaning they will always be due no matter when the check occurs. - -### Batch Processing Background Jobs - -If there are actions to be processed, Action Scheduler will stake a unique claim for a batch of 25 actions and begin processing that batch. The PHP process spawned to run the batch will then continue processing batches of 25 actions until it uses 90% of available memory or has been processing for 30 seconds. - -At that point, if there are additional actions to process, an asynchronous loopback request will be made to the site to continue processing actions in a new request. - -This process and the loopback requests will continue until all actions are processed. - -### Housekeeping - -Before processing a batch, the scheduler will remove any existing claims on actions which have been sitting in a queue for more than five minutes (or more specifically, 10 times the allowed time limit, which defaults to 30 seconds). - -Action Scheduler will also delete any actions which were completed or canceled more than a month ago. - -If an action runs for more than 5 minutes, Action Scheduler will assume the action has timed out and will mark it as failed. However, if all callbacks attached to the action were to successfully complete sometime after that 5 minute timeout, its status would later be updated to completed. - -### Traceable Background Processing - -Did your background job run? - -Never be left wondering with Action Scheduler's built-in record keeping. - -All events for each action are logged in the `actionscheduler_logs` table and displayed in the [administration interface](/admin/). - -The events logged by default include when an action: - * is created - * starts (including details of how it was run, e.g. via [WP CLI](/wp-cli/) or WP Cron) - * completes - * fails - -If it fails with an error that can be recorded, that error will be recorded in the log and visible in administration interface, making it possible to trace what went wrong at some point in the past on a site you didn't have access to in the past. - -## Credits - -Action Scheduler is developed and maintained by folks at [Automattic](http://automattic.com/) with significant early development completed by [Flightless](https://flightless.us/). - -Collaboration is cool. We'd love to work with you to improve Action Scheduler. [Pull Requests](https://github.com/woocommerce/action-scheduler/pulls) welcome. diff --git a/src/libraries/action-scheduler/docs/mstile-150x150.png b/src/libraries/action-scheduler/docs/mstile-150x150.png deleted file mode 100644 index 3a3ed19110..0000000000 Binary files a/src/libraries/action-scheduler/docs/mstile-150x150.png and /dev/null differ diff --git a/src/libraries/action-scheduler/docs/perf.md b/src/libraries/action-scheduler/docs/perf.md deleted file mode 100644 index 77c3deb348..0000000000 --- a/src/libraries/action-scheduler/docs/perf.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: WordPress Background Processing at Scale - Action Scheduler Job Queue -description: Learn how to do WordPress background processing at scale by tuning the Action Scheduler job queue's default WP Cron runner. ---- -# Background Processing at Scale - -Action Scheduler's default processing is designed to work reliably across all different hosting environments. In order to achieve that, the default processing thresholds are more conservative than many servers could support. - -Specifically, Action Scheduler will only process actions in a request until: - -* 90% of available memory is used -* processing another 3 actions would exceed 30 seconds of total request time, based on the average processing time for the current batch -* in a single concurrent queue - -While Action Scheduler will initiate an asychronous loopback request to process additional actions, on sites with very large queues, this can result in slow processing times. - -While using [WP CLI to process queues](/wp-cli/) is the best approach to increasing processing speed, on occasion, that is not a viable option. In these cases, it's also possible to increase the processing thresholds in Action Scheduler to increase the rate at which actions are processed by the default queue runner. - -## Increasing Time Limit - -By default, Action Scheduler will only process actions for a maximum of 30 seconds in each request. This time limit minimises the risk of a script timeout on unknown hosting environments, some of which enforce 30 second timeouts. - -If you know your host supports longer than this time limit for web requests, you can increase this time limit. This allows more actions to be processed in each request and reduces the lag between processing each queue, greatly speeding up the processing rate of scheduled actions. - -For example, the following snippet will increase the time limit to 2 minutes (120 seconds): - -```php -function eg_increase_time_limit( $time_limit ) { - return 120; -} -add_filter( 'action_scheduler_queue_runner_time_limit', 'eg_increase_time_limit' ); -``` - -Some of the known host time limits are: - -* 60 second on WP Engine -* 120 seconds on Pantheon -* 120 seconds on SiteGround - -## Increasing Batch Size - -By default, Action Scheduler will claim a batch of 25 actions. This small batch size is because the default time limit is only 30 seconds; however, if you know your actions are processing very quickly, e.g. taking microseconds not seconds, or that you have more than 30 second available to process each batch, increasing the batch size can slightly improve performance. - -This is because claiming a batch has some overhead, so the less often a batch needs to be claimed, the faster actions can be processed. - -For example, to increase the batch size to 100, we can use the following function: - -```php -function eg_increase_action_scheduler_batch_size( $batch_size ) { - return 100; -} -add_filter( 'action_scheduler_queue_runner_batch_size', 'eg_increase_action_scheduler_batch_size' ); -``` - -## Increasing Concurrent Batches - -By default, Action Scheduler will run only one concurrent batches of actions. This is to prevent consuming a lot of available connections or processes on your webserver. - -However, your server may allow a large number of connections, for example, because it has a high value for Apache's `MaxClients` setting or PHP-FPM's `pm.max_children` setting. - -If this is the case, you can use the `'action_scheduler_queue_runner_concurrent_batches'` filter to increase the number of concurrent batches allowed, and therefore speed up processing large numbers of actions scheduled to be processed simultaneously. - -For example, to increase the allowed number of concurrent queues to 10, we can use the following code: - -```php -function eg_increase_action_scheduler_concurrent_batches( $concurrent_batches ) { - return 10; -} -add_filter( 'action_scheduler_queue_runner_concurrent_batches', 'eg_increase_action_scheduler_concurrent_batches' ); -``` - -> WARNING: because of the async queue runner introduced in Action Scheduler 3.0 will continue asynchronous loopback requests to process actions, increasing the number of concurrent batches can substantially increase server load and take down a site. [WP CLI](/wp-cli/) is a better method to achieve higher throughput. - -## Increasing Initialisation Rate of Runners - -By default, Action scheduler initiates at most one queue runner every time the `'action_scheduler_run_queue'` action is triggered by WP Cron. - -Because this action is only triggered at most once every minute, then there will rarely be more than one queue processing actions even if the concurrent runners is increased. - -To handle larger queues on more powerful servers, it's possible to initiate additional queue runners whenever the `'action_scheduler_run_queue'` action is run. - -That can be done by initiated additional secure requests to our server via loopback requests. - -The code below demonstrates how to create 5 loopback requests each time a queue begins: - -```php -/** - * Trigger 5 additional loopback requests with unique URL params. - */ -function eg_request_additional_runners() { - - // allow self-signed SSL certificates - add_filter( 'https_local_ssl_verify', '__return_false', 100 ); - - for ( $i = 0; $i < 5; $i++ ) { - $response = wp_remote_post( admin_url( 'admin-ajax.php' ), array( - 'method' => 'POST', - 'timeout' => 45, - 'redirection' => 5, - 'httpversion' => '1.0', - 'blocking' => false, - 'headers' => array(), - 'body' => array( - 'action' => 'eg_create_additional_runners', - 'instance' => $i, - 'eg_nonce' => wp_create_nonce( 'eg_additional_runner_' . $i ), - ), - 'cookies' => array(), - ) ); - } -} -add_action( 'action_scheduler_run_queue', 'eg_request_additional_runners', 0 ); - -/** - * Handle requests initiated by eg_request_additional_runners() and start a queue runner if the request is valid. - */ -function eg_create_additional_runners() { - - if ( isset( $_POST['eg_nonce'] ) && isset( $_POST['instance'] ) && wp_verify_nonce( $_POST['eg_nonce'], 'eg_additional_runner_' . $_POST['instance'] ) ) { - ActionScheduler_QueueRunner::instance()->run(); - } - - wp_die(); -} -add_action( 'wp_ajax_nopriv_eg_create_additional_runners', 'eg_create_additional_runners', 0 ); -``` - -> WARNING: because of the processing rate of scheduled actions, this kind of increase can very easily take down a site. Use only on high powered servers and be sure to test before attempting to use it in production. - -## High Volume Plugin - -It's not necessary to add all of this code yourself, there is a handy plugin to get access to each of these increases - the [Action Scheduler - High Volume](https://github.com/woocommerce/action-scheduler-high-volume) plugin. diff --git a/src/libraries/action-scheduler/docs/safari-pinned-tab.svg b/src/libraries/action-scheduler/docs/safari-pinned-tab.svg deleted file mode 100644 index b67c32ba58..0000000000 --- a/src/libraries/action-scheduler/docs/safari-pinned-tab.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - -Created by potrace 1.11, written by Peter Selinger 2001-2013 - - - - - diff --git a/src/libraries/action-scheduler/docs/site.webmanifest b/src/libraries/action-scheduler/docs/site.webmanifest deleted file mode 100644 index de65106f48..0000000000 --- a/src/libraries/action-scheduler/docs/site.webmanifest +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "", - "short_name": "", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-256x256.png", - "sizes": "256x256", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/src/libraries/action-scheduler/docs/usage.md b/src/libraries/action-scheduler/docs/usage.md deleted file mode 100644 index e6a7529d73..0000000000 --- a/src/libraries/action-scheduler/docs/usage.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -description: Learn how to use the Action Scheduler background processing job queue for WordPress in your WordPress plugin. ---- -# Usage - -Using Action Scheduler requires: - -1. installing the library -1. scheduling an action - -## Installation - -There are two ways to install Action Scheduler: - -1. regular WordPress plugin; or -1. a library within your plugin's codebase. - -### Usage as a Plugin - -Action Scheduler includes the necessary file headers to be used as a standard WordPress plugin. - -To install it as a plugin: - -1. Download the .zip archive of the latest [stable release](https://github.com/woocommerce/action-scheduler/releases) -1. Go to the **Plugins > Add New > Upload** administration screen on your WordPress site -1. Select the archive file you just downloaded -1. Click **Install Now** -1. Click **Activate** - -Or clone the Git repository into your site's `wp-content/plugins` folder. - -Using Action Scheduler as a plugin can be handy for developing against newer versions, rather than having to update the subtree in your codebase. **When installed as a plugin, Action Scheduler does not provide any user interfaces for scheduling actions**. The only way to interact with Action Scheduler is via code. - -### Usage as a Library - -To use Action Scheduler as a library: - -1. include the Action Scheduler codebase -1. load the library by including the `action-scheduler.php` file - -Using a [subtree in your plugin, theme or site's Git repository](https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree) to include Action Scheduler is the recommended method. Composer can also be used. - -To include Action Scheduler as a git subtree: - -#### Step 1. Add the Repository as a Remote - -``` -git remote add -f subtree-action-scheduler https://github.com/woocommerce/action-scheduler.git -``` - -Adding the subtree as a remote allows us to refer to it in short from via the name `subtree-action-scheduler`, instead of the full GitHub URL. - -#### Step 2. Add the Repo as a Subtree - -``` -git subtree add --prefix libraries/action-scheduler subtree-action-scheduler trunk --squash -``` - -This will add the `trunk` branch of Action Scheduler to your repository in the folder `libraries/action-scheduler`. - -You can change the `--prefix` to change where the code is included. Or change the `trunk` branch to a tag, like `2.1.0` to include only a stable version. - -#### Step 3. Update the Subtree - -To update Action Scheduler to a new version, use the commands: - -``` -git fetch subtree-action-scheduler trunk -git subtree pull --prefix libraries/action-scheduler subtree-action-scheduler trunk --squash -``` - -### Loading Action Scheduler - -Regardless of how it is installed, to load Action Scheduler, you only need to include the `action-scheduler.php` file, e.g. - -```php - Action Scheduler -1. Take a screenshot of the action status counts at the top of the page. Example screenshot: https://cld.wthms.co/kwIqv7 - - -#### Stage 2: Install & Activate Action Scheduler 3.0+ as a plugin - -1. The migration will start almost immediately -1. Keep an eye on your error log -1. Report any notices, errors or other issues on GitHub - -#### Stage 3: Verify Migration - -1. Go to Tools > Action Scheduler -1. Take a screenshot of the action status counts at the top of the page. -1. Verify the counts match the status counts taken in Stage 1 (the Completed counts could be higher because actions will have been completed to run the migration). diff --git a/src/libraries/action-scheduler/docs/wp-cli.md b/src/libraries/action-scheduler/docs/wp-cli.md deleted file mode 100644 index 5a89abcec2..0000000000 --- a/src/libraries/action-scheduler/docs/wp-cli.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -description: Learn how to do WordPress background processing at scale with WP CLI and the Action Scheduler job queue. ---- -# WP CLI - -Action Scheduler has custom [WP CLI](http://wp-cli.org) commands available for processing actions. - -For large sites, WP CLI is a much better choice for running queues of actions than the default WP Cron runner. These are some common cases where WP CLI is a better option: - -* long-running tasks - Tasks that take a significant amount of time to run -* large queues - A large number of tasks will naturally take a longer time -* other plugins with extensive WP Cron usage - WP Cron's limited resources are spread across more tasks - -With a regular web request, you may have to deal with script timeouts enforced by hosts, or other restraints that make it more challenging to run Action Scheduler tasks. Utilizing WP CLI to run commands directly on the server give you more freedom. This means that you typically don't have the same constraints of a normal web request. - -If you choose to utilize WP CLI exclusively, you can disable the normal WP CLI queue runner by installing the [Action Scheduler - Disable Default Queue Runner](https://github.com/woocommerce/action-scheduler-disable-default-runner) plugin. Note that if you do this, you **must** run Action Scheduler via WP CLI or another method, otherwise no scheduled actions will be processed. - -## Commands - -These are the commands available to use with Action Scheduler: - -* `action-scheduler migrate` - - **Note**: This command is only available while the migration to custom tables is in progress. - -* `action-scheduler run` - - Options: - * `--batch-size` - This is the number of actions to run in a single batch. The default is `100`. - * `--batches` - This is the number of batches to run. Using 0 means that batches will continue running until there are no more actions to run. - * `--hooks` - Process only actions with specific hook or hooks, like `'woocommerce_scheduled_subscription_payment'`. By default, actions with any hook will be processed. Define multiple hooks as a comma separated string (without spaces), e.g. `--hooks=woocommerce_scheduled_subscription_trial_end,woocommerce_scheduled_subscription_payment,woocommerce_scheduled_subscription_expiration` - * `--group` - Process only actions in a specific group, like `'woocommerce-memberships'`. By default, actions in any group (or no group) will be processed. - * `--force` - By default, Action Scheduler limits the number of concurrent batches that can be run at once to ensure the server does not get overwhelmed. Using the `--force` flag overrides this behavior to force the WP CLI queue to run. - -The best way to get a full list of commands and their available options is to use WP CLI itself. This can be done by running `wp action-scheduler` to list all Action Scheduler commands, or by including the `--help` flag with any of the individual commands. This will provide all relevant parameters and flags for the command. - -## Cautionary Note on Action Dependencies when using `--group` or `--hooks` Options - -The `--group` and `--hooks` options should be used with caution if you have an implicit dependency between scheduled actions based on their schedule. - -For example, consider two scheduled actions for the same subscription: - -* `scheduled_payment` scheduled for `2015-11-13 00:00:00` and -* `scheduled_expiration` scheduled for `2015-11-13 00:01:00`. - -Under normal conditions, Action Scheduler will ensure the `scheduled_payment` action is run before the `scheduled_expiration` action. Becuase that's how they are scheduled. - -However, when using the `--hooks` option, the `scheduled_payment` and `scheduled_expiration` actions will be processed in separate queues. As a result, this dependency is not guaranteed. - -For example, consider a site with both: - -* 100,000 `scheduled_payment` actions, scheduled for `2015-11-13 00:00:00` -* 100 `scheduled_expiration` actions, scheduled for `2015-11-13 00:01:00` - -If two queue runners are running alongside each other with each runner dedicated to just one of these hooks, the queue runner handling expiration hooks will complete the processing of the expiration hooks more quickly than the queue runner handling all the payment actions. - -**Because of this, the `--group` and `--hooks` options should be used with caution to avoid processing actions with an implicit dependency based on their schedule in separate queues.** - -## Improving Performance with `--group` or `--hooks` - -Being able to run queues for specific hooks or groups of actions is valuable at scale. Why? Because it means you can restrict the concurrency for similar actions. - -For example, let's say you have 300,000 actions queued up comprised of: - -* 100,000 renewals payments -* 100,000 email notifications -* 100,000 membership status updates - -Action Scheduler's default WP Cron queue runner will process them all together. e.g. when it claims a batch of actions, some may be emails, some membership updates and some renewals. - -When you add concurrency to that, you can end up with issues. For example, if you have 3 queues running, they may all be attempting to process similar actions at the same time, which can lead to querying the same database tables with similar queries. Depending on the code/queries running, this can lead to database locks or other issues. - -If you can batch based on each action's group, then you can improve performance by processing like actions consecutively, but still processing the full set of actions concurrently. - -For example, if one queue is created to process emails, another to process membership updates, and another to process renewal payments, then the same queries won't be run at the same time, and 3 separate queues will be able to run more efficiently. - -The WP CLI runner can achieve this using the `--group` option. diff --git a/src/libraries/action-scheduler/functions.php b/src/libraries/action-scheduler/functions.php index 30ffc52b46..09ef353d9a 100644 --- a/src/libraries/action-scheduler/functions.php +++ b/src/libraries/action-scheduler/functions.php @@ -19,6 +19,26 @@ function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { return 0; } + + /** + * Provides an opportunity to short-circuit the default process for enqueuing async + * actions. + * + * Returning a value other than null from the filter will short-circuit the normal + * process. The expectation in such a scenario is that callbacks will return an integer + * representing the enqueued action ID (enqueued using some alternative process) or else + * zero. + * + * @param int|null $pre_option The value to return instead of the option value. + * @param string $hook Action hook. + * @param array $args Action arguments. + * @param string $group Action group. + */ + $pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group ); + if ( null !== $pre ) { + return is_int( $pre ) ? $pre : 0; + } + return ActionScheduler::factory()->async_unique( $hook, $args, $group, $unique ); } @@ -37,6 +57,27 @@ function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { return 0; } + + /** + * Provides an opportunity to short-circuit the default process for enqueuing single + * actions. + * + * Returning a value other than null from the filter will short-circuit the normal + * process. The expectation in such a scenario is that callbacks will return an integer + * representing the scheduled action ID (scheduled using some alternative process) or else + * zero. + * + * @param int|null $pre_option The value to return instead of the option value. + * @param int $timestamp When the action will run. + * @param string $hook Action hook. + * @param array $args Action arguments. + * @param string $group Action group. + */ + $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group ); + if ( null !== $pre ) { + return is_int( $pre ) ? $pre : 0; + } + return ActionScheduler::factory()->single_unique( $hook, $args, $timestamp, $group, $unique ); } @@ -56,6 +97,28 @@ function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { return 0; } + + /** + * Provides an opportunity to short-circuit the default process for enqueuing recurring + * actions. + * + * Returning a value other than null from the filter will short-circuit the normal + * process. The expectation in such a scenario is that callbacks will return an integer + * representing the scheduled action ID (scheduled using some alternative process) or else + * zero. + * + * @param int|null $pre_option The value to return instead of the option value. + * @param int $timestamp When the action will run. + * @param int $interval_in_seconds How long to wait between runs. + * @param string $hook Action hook. + * @param array $args Action arguments. + * @param string $group Action group. + */ + $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group ); + if ( null !== $pre ) { + return is_int( $pre ) ? $pre : 0; + } + return ActionScheduler::factory()->recurring_unique( $hook, $args, $timestamp, $interval_in_seconds, $group, $unique ); } @@ -87,6 +150,28 @@ function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { return 0; } + + /** + * Provides an opportunity to short-circuit the default process for enqueuing cron + * actions. + * + * Returning a value other than null from the filter will short-circuit the normal + * process. The expectation in such a scenario is that callbacks will return an integer + * representing the scheduled action ID (scheduled using some alternative process) or else + * zero. + * + * @param int|null $pre_option The value to return instead of the option value. + * @param int $timestamp When the action will run. + * @param string $schedule Cron-like schedule string. + * @param string $hook Action hook. + * @param array $args Action arguments. + * @param string $group Action group. + */ + $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group ); + if ( null !== $pre ) { + return is_int( $pre ) ? $pre : 0; + } + return ActionScheduler::factory()->cron_unique( $hook, $args, $timestamp, $schedule, $group, $unique ); } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression.php index 7adedeca76..7f33c378f9 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression.php @@ -13,295 +13,306 @@ * @author Michael Dowling * @link http://en.wikipedia.org/wiki/Cron */ -class CronExpression { - - const MINUTE = 0; - const HOUR = 1; - const DAY = 2; - const MONTH = 3; - const WEEKDAY = 4; - const YEAR = 5; - - /** - * @var array CRON expression parts - */ - private $cronParts; - - /** - * @var CronExpression_FieldFactory CRON field factory - */ - private $fieldFactory; - - /** - * @var array Order in which to test of cron parts - */ - private static $order = array( self::YEAR, self::MONTH, self::DAY, self::WEEKDAY, self::HOUR, self::MINUTE ); - - /** - * Factory method to create a new CronExpression. - * - * @param string $expression The CRON expression to create. There are - * several special predefined values which can be used to substitute the - * CRON expression: - * - * @yearly, @annually) - Run once a year, midnight, Jan. 1 - 0 0 1 1 * - * @monthly - Run once a month, midnight, first of month - 0 0 1 * * - * @weekly - Run once a week, midnight on Sun - 0 0 * * 0 - * @daily - Run once a day, midnight - 0 0 * * * - * @hourly - Run once an hour, first minute - 0 * * * * - * - * @param CronExpression_FieldFactory $fieldFactory (optional) Field factory to use - * - * @return CronExpression - */ - public static function factory( $expression, CronExpression_FieldFactory $fieldFactory = null ) { - $mappings = array( - '@yearly' => '0 0 1 1 *', - '@annually' => '0 0 1 1 *', - '@monthly' => '0 0 1 * *', - '@weekly' => '0 0 * * 0', - '@daily' => '0 0 * * *', - '@hourly' => '0 * * * *', - ); - - if ( isset( $mappings[ $expression ] ) ) { - $expression = $mappings[ $expression ]; - } - - return new self( $expression, $fieldFactory ? $fieldFactory : new CronExpression_FieldFactory() ); - } - - /** - * Parse a CRON expression - * - * @param string $expression CRON expression (e.g. '8 * * * *') - * @param CronExpression_FieldFactory $fieldFactory Factory to create cron fields - */ - public function __construct( $expression, CronExpression_FieldFactory $fieldFactory ) { - $this->fieldFactory = $fieldFactory; - $this->setExpression( $expression ); - } - - /** - * Set or change the CRON expression - * - * @param string $value CRON expression (e.g. 8 * * * *) - * - * @return CronExpression - * @throws InvalidArgumentException if not a valid CRON expression - */ - public function setExpression( $value ) { - $this->cronParts = preg_split( '/\s/', $value, -1, PREG_SPLIT_NO_EMPTY ); - if ( count( $this->cronParts ) < 5 ) { - throw new InvalidArgumentException( - $value . ' is not a valid CRON expression' - ); - } - - foreach ( $this->cronParts as $position => $part ) { - $this->setPart( $position, $part ); - } - - return $this; - } - - /** - * Set part of the CRON expression - * - * @param int $position The position of the CRON expression to set - * @param string $value The value to set - * - * @return CronExpression - * @throws InvalidArgumentException if the value is not valid for the part - */ - public function setPart( $position, $value ) { - if ( ! $this->fieldFactory->getField( $position )->validate( $value ) ) { - throw new InvalidArgumentException( - 'Invalid CRON field value ' . $value . ' as position ' . $position - ); - } - - $this->cronParts[ $position ] = $value; - - return $this; - } - - /** - * Get a next run date relative to the current date or a specific date - * - * @param string|DateTime $currentTime (optional) Relative calculation date - * @param int $nth (optional) Number of matches to skip before returning a - * matching next run date. 0, the default, will return the current - * date and time if the next run date falls on the current date and - * time. Setting this value to 1 will skip the first match and go to - * the second match. Setting this value to 2 will skip the first 2 - * matches and so on. - * @param bool $allowCurrentDate (optional) Set to TRUE to return the - * current date if it matches the cron expression - * - * @return DateTime - * @throws RuntimeException on too many iterations - */ - public function getNextRunDate( $currentTime = 'now', $nth = 0, $allowCurrentDate = false ) { - return $this->getRunDate( $currentTime, $nth, false, $allowCurrentDate ); - } - - /** - * Get a previous run date relative to the current date or a specific date - * - * @param string|DateTime $currentTime (optional) Relative calculation date - * @param int $nth (optional) Number of matches to skip before returning - * @param bool $allowCurrentDate (optional) Set to TRUE to return the - * current date if it matches the cron expression - * - * @return DateTime - * @throws RuntimeException on too many iterations - * @see CronExpression::getNextRunDate - */ - public function getPreviousRunDate( $currentTime = 'now', $nth = 0, $allowCurrentDate = false ) { - return $this->getRunDate( $currentTime, $nth, true, $allowCurrentDate ); - } - - /** - * Get multiple run dates starting at the current date or a specific date - * - * @param int $total Set the total number of dates to calculate - * @param string|DateTime $currentTime (optional) Relative calculation date - * @param bool $invert (optional) Set to TRUE to retrieve previous dates - * @param bool $allowCurrentDate (optional) Set to TRUE to return the - * current date if it matches the cron expression - * - * @return array Returns an array of run dates - */ - public function getMultipleRunDates( $total, $currentTime = 'now', $invert = false, $allowCurrentDate = false ) { - $matches = array(); - for ( $i = 0; $i < max( 0, $total ); $i++ ) { - $matches[] = $this->getRunDate( $currentTime, $i, $invert, $allowCurrentDate ); - } - - return $matches; - } - - /** - * Get all or part of the CRON expression - * - * @param string $part (optional) Specify the part to retrieve or NULL to - * get the full cron schedule string. - * - * @return string|null Returns the CRON expression, a part of the - * CRON expression, or NULL if the part was specified but not found - */ - public function getExpression( $part = null ) { - if ( null === $part ) { - return implode( ' ', $this->cronParts ); - } elseif ( array_key_exists( $part, $this->cronParts ) ) { - return $this->cronParts[ $part ]; - } - - return null; - } - - /** - * Helper method to output the full expression. - * - * @return string Full CRON expression - */ - public function __toString() { - return $this->getExpression(); - } - - /** - * Determine if the cron is due to run based on the current date or a - * specific date. This method assumes that the current number of - * seconds are irrelevant, and should be called once per minute. - * - * @param string|DateTime $currentTime (optional) Relative calculation date - * - * @return bool Returns TRUE if the cron is due to run or FALSE if not - */ - public function isDue( $currentTime = 'now' ) { - if ( 'now' === $currentTime ) { - $currentDate = date( 'Y-m-d H:i' ); - $currentTime = strtotime( $currentDate ); - } elseif ( $currentTime instanceof DateTime ) { - $currentDate = $currentTime->format( 'Y-m-d H:i' ); - $currentTime = strtotime( $currentDate ); - } else { - $currentTime = new DateTime( $currentTime ); - $currentTime->setTime( $currentTime->format( 'H' ), $currentTime->format( 'i' ), 0 ); - $currentDate = $currentTime->format( 'Y-m-d H:i' ); - $currentTime = (int) ( $currentTime->getTimestamp() ); - } - - return $this->getNextRunDate( $currentDate, 0, true )->getTimestamp() == $currentTime; - } - - /** - * Get the next or previous run date of the expression relative to a date - * - * @param string|DateTime $currentTime (optional) Relative calculation date - * @param int $nth (optional) Number of matches to skip before returning - * @param bool $invert (optional) Set to TRUE to go backwards in time - * @param bool $allowCurrentDate (optional) Set to TRUE to return the - * current date if it matches the cron expression - * - * @return DateTime - * @throws RuntimeException on too many iterations - */ - protected function getRunDate( $currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false ) { - if ( $currentTime instanceof DateTime ) { - $currentDate = $currentTime; - } else { - $currentDate = new DateTime( $currentTime ? $currentTime : 'now' ); - $currentDate->setTimezone( new DateTimeZone( date_default_timezone_get() ) ); - } - - $currentDate->setTime( $currentDate->format( 'H' ), $currentDate->format( 'i' ), 0 ); - $nextRun = clone $currentDate; - $nth = (int) $nth; - - // Set a hard limit to bail on an impossible date - for ( $i = 0; $i < 1000; $i++ ) { - - foreach ( self::$order as $position ) { - $part = $this->getExpression( $position ); - if ( null === $part ) { - continue; - } - - $satisfied = false; - // Get the field object used to validate this part - $field = $this->fieldFactory->getField( $position ); - // Check if this is singular or a list - if ( strpos( $part, ',' ) === false ) { - $satisfied = $field->isSatisfiedBy( $nextRun, $part ); - } else { - foreach ( array_map( 'trim', explode( ',', $part ) ) as $listPart ) { - if ( $field->isSatisfiedBy( $nextRun, $listPart ) ) { - $satisfied = true; - break; - } - } - } - - // If the field is not satisfied, then start over - if ( ! $satisfied ) { - $field->increment( $nextRun, $invert ); - continue 2; - } - } - - // Skip this match if needed - if ( ( ! $allowCurrentDate && $nextRun == $currentDate ) || --$nth > -1 ) { - $this->fieldFactory->getField( 0 )->increment( $nextRun, $invert ); - continue; - } - - return $nextRun; - } - - // @codeCoverageIgnoreStart - throw new RuntimeException( 'Impossible CRON expression' ); - // @codeCoverageIgnoreEnd - } +class CronExpression +{ + const MINUTE = 0; + const HOUR = 1; + const DAY = 2; + const MONTH = 3; + const WEEKDAY = 4; + const YEAR = 5; + + /** + * @var array CRON expression parts + */ + private $cronParts; + + /** + * @var CronExpression_FieldFactory CRON field factory + */ + private $fieldFactory; + + /** + * @var array Order in which to test of cron parts + */ + private static $order = array(self::YEAR, self::MONTH, self::DAY, self::WEEKDAY, self::HOUR, self::MINUTE); + + /** + * Factory method to create a new CronExpression. + * + * @param string $expression The CRON expression to create. There are + * several special predefined values which can be used to substitute the + * CRON expression: + * + * @yearly, @annually) - Run once a year, midnight, Jan. 1 - 0 0 1 1 * + * @monthly - Run once a month, midnight, first of month - 0 0 1 * * + * @weekly - Run once a week, midnight on Sun - 0 0 * * 0 + * @daily - Run once a day, midnight - 0 0 * * * + * @hourly - Run once an hour, first minute - 0 * * * * + * +*@param CronExpression_FieldFactory $fieldFactory (optional) Field factory to use + * + * @return CronExpression + */ + public static function factory($expression, CronExpression_FieldFactory $fieldFactory = null) + { + $mappings = array( + '@yearly' => '0 0 1 1 *', + '@annually' => '0 0 1 1 *', + '@monthly' => '0 0 1 * *', + '@weekly' => '0 0 * * 0', + '@daily' => '0 0 * * *', + '@hourly' => '0 * * * *' + ); + + if (isset($mappings[$expression])) { + $expression = $mappings[$expression]; + } + + return new self($expression, $fieldFactory ? $fieldFactory : new CronExpression_FieldFactory()); + } + + /** + * Parse a CRON expression + * + * @param string $expression CRON expression (e.g. '8 * * * *') + * @param CronExpression_FieldFactory $fieldFactory Factory to create cron fields + */ + public function __construct($expression, CronExpression_FieldFactory $fieldFactory) + { + $this->fieldFactory = $fieldFactory; + $this->setExpression($expression); + } + + /** + * Set or change the CRON expression + * + * @param string $value CRON expression (e.g. 8 * * * *) + * + * @return CronExpression + * @throws InvalidArgumentException if not a valid CRON expression + */ + public function setExpression($value) + { + $this->cronParts = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY); + if (count($this->cronParts) < 5) { + throw new InvalidArgumentException( + $value . ' is not a valid CRON expression' + ); + } + + foreach ($this->cronParts as $position => $part) { + $this->setPart($position, $part); + } + + return $this; + } + + /** + * Set part of the CRON expression + * + * @param int $position The position of the CRON expression to set + * @param string $value The value to set + * + * @return CronExpression + * @throws InvalidArgumentException if the value is not valid for the part + */ + public function setPart($position, $value) + { + if (!$this->fieldFactory->getField($position)->validate($value)) { + throw new InvalidArgumentException( + 'Invalid CRON field value ' . $value . ' as position ' . $position + ); + } + + $this->cronParts[$position] = $value; + + return $this; + } + + /** + * Get a next run date relative to the current date or a specific date + * + * @param string|DateTime $currentTime (optional) Relative calculation date + * @param int $nth (optional) Number of matches to skip before returning a + * matching next run date. 0, the default, will return the current + * date and time if the next run date falls on the current date and + * time. Setting this value to 1 will skip the first match and go to + * the second match. Setting this value to 2 will skip the first 2 + * matches and so on. + * @param bool $allowCurrentDate (optional) Set to TRUE to return the + * current date if it matches the cron expression + * + * @return DateTime + * @throws RuntimeException on too many iterations + */ + public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false) + { + return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate); + } + + /** + * Get a previous run date relative to the current date or a specific date + * + * @param string|DateTime $currentTime (optional) Relative calculation date + * @param int $nth (optional) Number of matches to skip before returning + * @param bool $allowCurrentDate (optional) Set to TRUE to return the + * current date if it matches the cron expression + * + * @return DateTime + * @throws RuntimeException on too many iterations + * @see CronExpression::getNextRunDate + */ + public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false) + { + return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate); + } + + /** + * Get multiple run dates starting at the current date or a specific date + * + * @param int $total Set the total number of dates to calculate + * @param string|DateTime $currentTime (optional) Relative calculation date + * @param bool $invert (optional) Set to TRUE to retrieve previous dates + * @param bool $allowCurrentDate (optional) Set to TRUE to return the + * current date if it matches the cron expression + * + * @return array Returns an array of run dates + */ + public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false) + { + $matches = array(); + for ($i = 0; $i < max(0, $total); $i++) { + $matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate); + } + + return $matches; + } + + /** + * Get all or part of the CRON expression + * + * @param string $part (optional) Specify the part to retrieve or NULL to + * get the full cron schedule string. + * + * @return string|null Returns the CRON expression, a part of the + * CRON expression, or NULL if the part was specified but not found + */ + public function getExpression($part = null) + { + if (null === $part) { + return implode(' ', $this->cronParts); + } elseif (array_key_exists($part, $this->cronParts)) { + return $this->cronParts[$part]; + } + + return null; + } + + /** + * Helper method to output the full expression. + * + * @return string Full CRON expression + */ + public function __toString() + { + return $this->getExpression(); + } + + /** + * Determine if the cron is due to run based on the current date or a + * specific date. This method assumes that the current number of + * seconds are irrelevant, and should be called once per minute. + * + * @param string|DateTime $currentTime (optional) Relative calculation date + * + * @return bool Returns TRUE if the cron is due to run or FALSE if not + */ + public function isDue($currentTime = 'now') + { + if ('now' === $currentTime) { + $currentDate = date('Y-m-d H:i'); + $currentTime = strtotime($currentDate); + } elseif ($currentTime instanceof DateTime) { + $currentDate = $currentTime->format('Y-m-d H:i'); + $currentTime = strtotime($currentDate); + } else { + $currentTime = new DateTime($currentTime); + $currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0); + $currentDate = $currentTime->format('Y-m-d H:i'); + $currentTime = (int)($currentTime->getTimestamp()); + } + + return $this->getNextRunDate($currentDate, 0, true)->getTimestamp() == $currentTime; + } + + /** + * Get the next or previous run date of the expression relative to a date + * + * @param string|DateTime $currentTime (optional) Relative calculation date + * @param int $nth (optional) Number of matches to skip before returning + * @param bool $invert (optional) Set to TRUE to go backwards in time + * @param bool $allowCurrentDate (optional) Set to TRUE to return the + * current date if it matches the cron expression + * + * @return DateTime + * @throws RuntimeException on too many iterations + */ + protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false) + { + if ($currentTime instanceof DateTime) { + $currentDate = $currentTime; + } else { + $currentDate = new DateTime($currentTime ? $currentTime : 'now'); + $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get())); + } + + $currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), 0); + $nextRun = clone $currentDate; + $nth = (int) $nth; + + // Set a hard limit to bail on an impossible date + for ($i = 0; $i < 1000; $i++) { + + foreach (self::$order as $position) { + $part = $this->getExpression($position); + if (null === $part) { + continue; + } + + $satisfied = false; + // Get the field object used to validate this part + $field = $this->fieldFactory->getField($position); + // Check if this is singular or a list + if (strpos($part, ',') === false) { + $satisfied = $field->isSatisfiedBy($nextRun, $part); + } else { + foreach (array_map('trim', explode(',', $part)) as $listPart) { + if ($field->isSatisfiedBy($nextRun, $listPart)) { + $satisfied = true; + break; + } + } + } + + // If the field is not satisfied, then start over + if (!$satisfied) { + $field->increment($nextRun, $invert); + continue 2; + } + } + + // Skip this match if needed + if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) { + $this->fieldFactory->getField(0)->increment($nextRun, $invert); + continue; + } + + return $nextRun; + } + + // @codeCoverageIgnoreStart + throw new RuntimeException('Impossible CRON expression'); + // @codeCoverageIgnoreEnd + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_AbstractField.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_AbstractField.php index a8b1eee797..f8d5c00ae7 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_AbstractField.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_AbstractField.php @@ -5,91 +5,96 @@ * * @author Michael Dowling */ -abstract class CronExpression_AbstractField implements CronExpression_FieldInterface { +abstract class CronExpression_AbstractField implements CronExpression_FieldInterface +{ + /** + * Check to see if a field is satisfied by a value + * + * @param string $dateValue Date value to check + * @param string $value Value to test + * + * @return bool + */ + public function isSatisfied($dateValue, $value) + { + if ($this->isIncrementsOfRanges($value)) { + return $this->isInIncrementsOfRanges($dateValue, $value); + } elseif ($this->isRange($value)) { + return $this->isInRange($dateValue, $value); + } - /** - * Check to see if a field is satisfied by a value - * - * @param string $dateValue Date value to check - * @param string $value Value to test - * - * @return bool - */ - public function isSatisfied( $dateValue, $value ) { - if ( $this->isIncrementsOfRanges( $value ) ) { - return $this->isInIncrementsOfRanges( $dateValue, $value ); - } elseif ( $this->isRange( $value ) ) { - return $this->isInRange( $dateValue, $value ); - } + return $value == '*' || $dateValue == $value; + } - return $value == '*' || $dateValue == $value; - } + /** + * Check if a value is a range + * + * @param string $value Value to test + * + * @return bool + */ + public function isRange($value) + { + return strpos($value, '-') !== false; + } - /** - * Check if a value is a range - * - * @param string $value Value to test - * - * @return bool - */ - public function isRange( $value ) { - return strpos( $value, '-' ) !== false; - } + /** + * Check if a value is an increments of ranges + * + * @param string $value Value to test + * + * @return bool + */ + public function isIncrementsOfRanges($value) + { + return strpos($value, '/') !== false; + } - /** - * Check if a value is an increments of ranges - * - * @param string $value Value to test - * - * @return bool - */ - public function isIncrementsOfRanges( $value ) { - return strpos( $value, '/' ) !== false; - } + /** + * Test if a value is within a range + * + * @param string $dateValue Set date value + * @param string $value Value to test + * + * @return bool + */ + public function isInRange($dateValue, $value) + { + $parts = array_map('trim', explode('-', $value, 2)); - /** - * Test if a value is within a range - * - * @param string $dateValue Set date value - * @param string $value Value to test - * - * @return bool - */ - public function isInRange( $dateValue, $value ) { - $parts = array_map( 'trim', explode( '-', $value, 2 ) ); + return $dateValue >= $parts[0] && $dateValue <= $parts[1]; + } - return $dateValue >= $parts[0] && $dateValue <= $parts[1]; - } + /** + * Test if a value is within an increments of ranges (offset[-to]/step size) + * + * @param string $dateValue Set date value + * @param string $value Value to test + * + * @return bool + */ + public function isInIncrementsOfRanges($dateValue, $value) + { + $parts = array_map('trim', explode('/', $value, 2)); + $stepSize = isset($parts[1]) ? $parts[1] : 0; + if ($parts[0] == '*' || $parts[0] === '0') { + return (int) $dateValue % $stepSize == 0; + } - /** - * Test if a value is within an increments of ranges (offset[-to]/step size) - * - * @param string $dateValue Set date value - * @param string $value Value to test - * - * @return bool - */ - public function isInIncrementsOfRanges( $dateValue, $value ) { - $parts = array_map( 'trim', explode( '/', $value, 2 ) ); - $stepSize = isset( $parts[1] ) ? $parts[1] : 0; - if ( $parts[0] == '*' || $parts[0] === '0' ) { - return (int) $dateValue % $stepSize == 0; - } + $range = explode('-', $parts[0], 2); + $offset = $range[0]; + $to = isset($range[1]) ? $range[1] : $dateValue; + // Ensure that the date value is within the range + if ($dateValue < $offset || $dateValue > $to) { + return false; + } - $range = explode( '-', $parts[0], 2 ); - $offset = $range[0]; - $to = isset( $range[1] ) ? $range[1] : $dateValue; - // Ensure that the date value is within the range - if ( $dateValue < $offset || $dateValue > $to ) { - return false; - } + for ($i = $offset; $i <= $to; $i+= $stepSize) { + if ($i == $dateValue) { + return true; + } + } - for ( $i = $offset; $i <= $to; $i += $stepSize ) { - if ( $i == $dateValue ) { - return true; - } - } - - return false; - } + return false; + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfMonthField.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfMonthField.php index 5f5aad0f46..40c1d6c6e7 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfMonthField.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfMonthField.php @@ -18,89 +18,93 @@ * * @author Michael Dowling */ -class CronExpression_DayOfMonthField extends CronExpression_AbstractField { +class CronExpression_DayOfMonthField extends CronExpression_AbstractField +{ + /** + * Get the nearest day of the week for a given day in a month + * + * @param int $currentYear Current year + * @param int $currentMonth Current month + * @param int $targetDay Target day of the month + * + * @return DateTime Returns the nearest date + */ + private static function getNearestWeekday($currentYear, $currentMonth, $targetDay) + { + $tday = str_pad($targetDay, 2, '0', STR_PAD_LEFT); + $target = new DateTime("$currentYear-$currentMonth-$tday"); + $currentWeekday = (int) $target->format('N'); - /** - * Get the nearest day of the week for a given day in a month - * - * @param int $currentYear Current year - * @param int $currentMonth Current month - * @param int $targetDay Target day of the month - * - * @return DateTime Returns the nearest date - */ - private static function getNearestWeekday( $currentYear, $currentMonth, $targetDay ) { - $tday = str_pad( $targetDay, 2, '0', STR_PAD_LEFT ); - $target = new DateTime( "$currentYear-$currentMonth-$tday" ); - $currentWeekday = (int) $target->format( 'N' ); + if ($currentWeekday < 6) { + return $target; + } - if ( $currentWeekday < 6 ) { - return $target; - } + $lastDayOfMonth = $target->format('t'); - $lastDayOfMonth = $target->format( 't' ); + foreach (array(-1, 1, -2, 2) as $i) { + $adjusted = $targetDay + $i; + if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) { + $target->setDate($currentYear, $currentMonth, $adjusted); + if ($target->format('N') < 6 && $target->format('m') == $currentMonth) { + return $target; + } + } + } + } - foreach ( array( -1, 1, -2, 2 ) as $i ) { - $adjusted = $targetDay + $i; - if ( $adjusted > 0 && $adjusted <= $lastDayOfMonth ) { - $target->setDate( $currentYear, $currentMonth, $adjusted ); - if ( $target->format( 'N' ) < 6 && $target->format( 'm' ) == $currentMonth ) { - return $target; - } - } - } - } + /** + * {@inheritdoc} + */ + public function isSatisfiedBy(DateTime $date, $value) + { + // ? states that the field value is to be skipped + if ($value == '?') { + return true; + } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy( DateTime $date, $value ) { - // ? states that the field value is to be skipped - if ( $value == '?' ) { - return true; - } + $fieldValue = $date->format('d'); - $fieldValue = $date->format( 'd' ); + // Check to see if this is the last day of the month + if ($value == 'L') { + return $fieldValue == $date->format('t'); + } - // Check to see if this is the last day of the month - if ( $value == 'L' ) { - return $fieldValue == $date->format( 't' ); - } + // Check to see if this is the nearest weekday to a particular value + if (strpos($value, 'W')) { + // Parse the target day + $targetDay = substr($value, 0, strpos($value, 'W')); + // Find out if the current day is the nearest day of the week + return $date->format('j') == self::getNearestWeekday( + $date->format('Y'), + $date->format('m'), + $targetDay + )->format('j'); + } - // Check to see if this is the nearest weekday to a particular value - if ( strpos( $value, 'W' ) ) { - // Parse the target day - $targetDay = substr( $value, 0, strpos( $value, 'W' ) ); - // Find out if the current day is the nearest day of the week - return $date->format( 'j' ) == self::getNearestWeekday( - $date->format( 'Y' ), - $date->format( 'm' ), - $targetDay - )->format( 'j' ); - } + return $this->isSatisfied($date->format('d'), $value); + } - return $this->isSatisfied( $date->format( 'd' ), $value ); - } + /** + * {@inheritdoc} + */ + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + $date->modify('previous day'); + $date->setTime(23, 59); + } else { + $date->modify('next day'); + $date->setTime(0, 0); + } - /** - * {@inheritdoc} - */ - public function increment( DateTime $date, $invert = false ) { - if ( $invert ) { - $date->modify( 'previous day' ); - $date->setTime( 23, 59 ); - } else { - $date->modify( 'next day' ); - $date->setTime( 0, 0 ); - } + return $this; + } - return $this; - } - - /** - * {@inheritdoc} - */ - public function validate( $value ) { - return (bool) preg_match( '/[\*,\/\-\?LW0-9A-Za-z]+/', $value ); - } + /** + * {@inheritdoc} + */ + public function validate($value) + { + return (bool) preg_match('/[\*,\/\-\?LW0-9A-Za-z]+/', $value); + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfWeekField.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfWeekField.php index 8f0aa3954a..e9f68a7cd6 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfWeekField.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_DayOfWeekField.php @@ -15,107 +15,110 @@ * * @author Michael Dowling */ -class CronExpression_DayOfWeekField extends CronExpression_AbstractField { +class CronExpression_DayOfWeekField extends CronExpression_AbstractField +{ + /** + * {@inheritdoc} + */ + public function isSatisfiedBy(DateTime $date, $value) + { + if ($value == '?') { + return true; + } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy( DateTime $date, $value ) { - if ( $value == '?' ) { - return true; - } + // Convert text day of the week values to integers + $value = str_ireplace( + array('SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'), + range(0, 6), + $value + ); - // Convert text day of the week values to integers - $value = str_ireplace( - array( 'SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT' ), - range( 0, 6 ), - $value - ); + $currentYear = $date->format('Y'); + $currentMonth = $date->format('m'); + $lastDayOfMonth = $date->format('t'); - $currentYear = $date->format( 'Y' ); - $currentMonth = $date->format( 'm' ); - $lastDayOfMonth = $date->format( 't' ); + // Find out if this is the last specific weekday of the month + if (strpos($value, 'L')) { + $weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L'))); + $tdate = clone $date; + $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth); + while ($tdate->format('w') != $weekday) { + $tdate->setDate($currentYear, $currentMonth, --$lastDayOfMonth); + } - // Find out if this is the last specific weekday of the month - if ( strpos( $value, 'L' ) ) { - $weekday = str_replace( '7', '0', substr( $value, 0, strpos( $value, 'L' ) ) ); - $tdate = clone $date; - $tdate->setDate( $currentYear, $currentMonth, $lastDayOfMonth ); - while ( $tdate->format( 'w' ) != $weekday ) { - $tdate->setDate( $currentYear, $currentMonth, --$lastDayOfMonth ); - } + return $date->format('j') == $lastDayOfMonth; + } - return $date->format( 'j' ) == $lastDayOfMonth; - } + // Handle # hash tokens + if (strpos($value, '#')) { + list($weekday, $nth) = explode('#', $value); + // Validate the hash fields + if ($weekday < 1 || $weekday > 5) { + throw new InvalidArgumentException("Weekday must be a value between 1 and 5. {$weekday} given"); + } + if ($nth > 5) { + throw new InvalidArgumentException('There are never more than 5 of a given weekday in a month'); + } + // The current weekday must match the targeted weekday to proceed + if ($date->format('N') != $weekday) { + return false; + } - // Handle # hash tokens - if ( strpos( $value, '#' ) ) { - list($weekday, $nth) = explode( '#', $value ); - // Validate the hash fields - if ( $weekday < 1 || $weekday > 5 ) { - throw new InvalidArgumentException( "Weekday must be a value between 1 and 5. {$weekday} given" ); - } - if ( $nth > 5 ) { - throw new InvalidArgumentException( 'There are never more than 5 of a given weekday in a month' ); - } - // The current weekday must match the targeted weekday to proceed - if ( $date->format( 'N' ) != $weekday ) { - return false; - } + $tdate = clone $date; + $tdate->setDate($currentYear, $currentMonth, 1); + $dayCount = 0; + $currentDay = 1; + while ($currentDay < $lastDayOfMonth + 1) { + if ($tdate->format('N') == $weekday) { + if (++$dayCount >= $nth) { + break; + } + } + $tdate->setDate($currentYear, $currentMonth, ++$currentDay); + } - $tdate = clone $date; - $tdate->setDate( $currentYear, $currentMonth, 1 ); - $dayCount = 0; - $currentDay = 1; - while ( $currentDay < $lastDayOfMonth + 1 ) { - if ( $tdate->format( 'N' ) == $weekday ) { - if ( ++$dayCount >= $nth ) { - break; - } - } - $tdate->setDate( $currentYear, $currentMonth, ++$currentDay ); - } + return $date->format('j') == $currentDay; + } - return $date->format( 'j' ) == $currentDay; - } + // Handle day of the week values + if (strpos($value, '-')) { + $parts = explode('-', $value); + if ($parts[0] == '7') { + $parts[0] = '0'; + } elseif ($parts[1] == '0') { + $parts[1] = '7'; + } + $value = implode('-', $parts); + } - // Handle day of the week values - if ( strpos( $value, '-' ) ) { - $parts = explode( '-', $value ); - if ( $parts[0] == '7' ) { - $parts[0] = '0'; - } elseif ( $parts[1] == '0' ) { - $parts[1] = '7'; - } - $value = implode( '-', $parts ); - } + // Test to see which Sunday to use -- 0 == 7 == Sunday + $format = in_array(7, str_split($value)) ? 'N' : 'w'; + $fieldValue = $date->format($format); - // Test to see which Sunday to use -- 0 == 7 == Sunday - $format = in_array( 7, str_split( $value ) ) ? 'N' : 'w'; - $fieldValue = $date->format( $format ); + return $this->isSatisfied($fieldValue, $value); + } - return $this->isSatisfied( $fieldValue, $value ); - } + /** + * {@inheritdoc} + */ + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + $date->modify('-1 day'); + $date->setTime(23, 59, 0); + } else { + $date->modify('+1 day'); + $date->setTime(0, 0, 0); + } - /** - * {@inheritdoc} - */ - public function increment( DateTime $date, $invert = false ) { - if ( $invert ) { - $date->modify( '-1 day' ); - $date->setTime( 23, 59, 0 ); - } else { - $date->modify( '+1 day' ); - $date->setTime( 0, 0, 0 ); - } + return $this; + } - return $this; - } - - /** - * {@inheritdoc} - */ - public function validate( $value ) { - return (bool) preg_match( '/[\*,\/\-0-9A-Z]+/', $value ); - } + /** + * {@inheritdoc} + */ + public function validate($value) + { + return (bool) preg_match('/[\*,\/\-0-9A-Z]+/', $value); + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldFactory.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldFactory.php index 637419cc38..556ba1a3e3 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldFactory.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldFactory.php @@ -6,49 +6,50 @@ * @author Michael Dowling * @link http://en.wikipedia.org/wiki/Cron */ -class CronExpression_FieldFactory { +class CronExpression_FieldFactory +{ + /** + * @var array Cache of instantiated fields + */ + private $fields = array(); - /** - * @var array Cache of instantiated fields - */ - private $fields = array(); + /** + * Get an instance of a field object for a cron expression position + * + * @param int $position CRON expression position value to retrieve + * + * @return CronExpression_FieldInterface + * @throws InvalidArgumentException if a position is not valid + */ + public function getField($position) + { + if (!isset($this->fields[$position])) { + switch ($position) { + case 0: + $this->fields[$position] = new CronExpression_MinutesField(); + break; + case 1: + $this->fields[$position] = new CronExpression_HoursField(); + break; + case 2: + $this->fields[$position] = new CronExpression_DayOfMonthField(); + break; + case 3: + $this->fields[$position] = new CronExpression_MonthField(); + break; + case 4: + $this->fields[$position] = new CronExpression_DayOfWeekField(); + break; + case 5: + $this->fields[$position] = new CronExpression_YearField(); + break; + default: + throw new InvalidArgumentException( + $position . ' is not a valid position' + ); + } + } - /** - * Get an instance of a field object for a cron expression position - * - * @param int $position CRON expression position value to retrieve - * - * @return CronExpression_FieldInterface - * @throws InvalidArgumentException if a position is not valid - */ - public function getField( $position ) { - if ( ! isset( $this->fields[ $position ] ) ) { - switch ( $position ) { - case 0: - $this->fields[ $position ] = new CronExpression_MinutesField(); - break; - case 1: - $this->fields[ $position ] = new CronExpression_HoursField(); - break; - case 2: - $this->fields[ $position ] = new CronExpression_DayOfMonthField(); - break; - case 3: - $this->fields[ $position ] = new CronExpression_MonthField(); - break; - case 4: - $this->fields[ $position ] = new CronExpression_DayOfWeekField(); - break; - case 5: - $this->fields[ $position ] = new CronExpression_YearField(); - break; - default: - throw new InvalidArgumentException( - $position . ' is not a valid position' - ); - } - } - - return $this->fields[ $position ]; - } + return $this->fields[$position]; + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldInterface.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldInterface.php index b98c6368c7..5d5109b70d 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldInterface.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_FieldInterface.php @@ -5,35 +5,35 @@ * * @author Michael Dowling */ -interface CronExpression_FieldInterface { +interface CronExpression_FieldInterface +{ + /** + * Check if the respective value of a DateTime field satisfies a CRON exp + * + * @param DateTime $date DateTime object to check + * @param string $value CRON expression to test against + * + * @return bool Returns TRUE if satisfied, FALSE otherwise + */ + public function isSatisfiedBy(DateTime $date, $value); - /** - * Check if the respective value of a DateTime field satisfies a CRON exp - * - * @param DateTime $date DateTime object to check - * @param string $value CRON expression to test against - * - * @return bool Returns TRUE if satisfied, FALSE otherwise - */ - public function isSatisfiedBy( DateTime $date, $value); + /** + * When a CRON expression is not satisfied, this method is used to increment + * or decrement a DateTime object by the unit of the cron field + * + * @param DateTime $date DateTime object to change + * @param bool $invert (optional) Set to TRUE to decrement + * + * @return CronExpression_FieldInterface + */ + public function increment(DateTime $date, $invert = false); - /** - * When a CRON expression is not satisfied, this method is used to increment - * or decrement a DateTime object by the unit of the cron field - * - * @param DateTime $date DateTime object to change - * @param bool $invert (optional) Set to TRUE to decrement - * - * @return CronExpression_FieldInterface - */ - public function increment( DateTime $date, $invert = false); - - /** - * Validates a CRON expression for a given field - * - * @param string $value CRON expression value to validate - * - * @return bool Returns TRUE if valid, FALSE otherwise - */ - public function validate( $value); + /** + * Validates a CRON expression for a given field + * + * @param string $value CRON expression value to validate + * + * @return bool Returns TRUE if valid, FALSE otherwise + */ + public function validate($value); } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_HoursField.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_HoursField.php index f0449ca6d5..088ca73c71 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_HoursField.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_HoursField.php @@ -5,40 +5,43 @@ * * @author Michael Dowling */ -class CronExpression_HoursField extends CronExpression_AbstractField { +class CronExpression_HoursField extends CronExpression_AbstractField +{ + /** + * {@inheritdoc} + */ + public function isSatisfiedBy(DateTime $date, $value) + { + return $this->isSatisfied($date->format('H'), $value); + } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy( DateTime $date, $value ) { - return $this->isSatisfied( $date->format( 'H' ), $value ); - } + /** + * {@inheritdoc} + */ + public function increment(DateTime $date, $invert = false) + { + // Change timezone to UTC temporarily. This will + // allow us to go back or forwards and hour even + // if DST will be changed between the hours. + $timezone = $date->getTimezone(); + $date->setTimezone(new DateTimeZone('UTC')); + if ($invert) { + $date->modify('-1 hour'); + $date->setTime($date->format('H'), 59); + } else { + $date->modify('+1 hour'); + $date->setTime($date->format('H'), 0); + } + $date->setTimezone($timezone); - /** - * {@inheritdoc} - */ - public function increment( DateTime $date, $invert = false ) { - // Change timezone to UTC temporarily. This will - // allow us to go back or forwards and hour even - // if DST will be changed between the hours. - $timezone = $date->getTimezone(); - $date->setTimezone( new DateTimeZone( 'UTC' ) ); - if ( $invert ) { - $date->modify( '-1 hour' ); - $date->setTime( $date->format( 'H' ), 59 ); - } else { - $date->modify( '+1 hour' ); - $date->setTime( $date->format( 'H' ), 0 ); - } - $date->setTimezone( $timezone ); + return $this; + } - return $this; - } - - /** - * {@inheritdoc} - */ - public function validate( $value ) { - return (bool) preg_match( '/[\*,\/\-0-9]+/', $value ); - } + /** + * {@inheritdoc} + */ + public function validate($value) + { + return (bool) preg_match('/[\*,\/\-0-9]+/', $value); + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MinutesField.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MinutesField.php index 64c8c48751..436acf2f56 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MinutesField.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MinutesField.php @@ -5,32 +5,35 @@ * * @author Michael Dowling */ -class CronExpression_MinutesField extends CronExpression_AbstractField { +class CronExpression_MinutesField extends CronExpression_AbstractField +{ + /** + * {@inheritdoc} + */ + public function isSatisfiedBy(DateTime $date, $value) + { + return $this->isSatisfied($date->format('i'), $value); + } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy( DateTime $date, $value ) { - return $this->isSatisfied( $date->format( 'i' ), $value ); - } + /** + * {@inheritdoc} + */ + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + $date->modify('-1 minute'); + } else { + $date->modify('+1 minute'); + } - /** - * {@inheritdoc} - */ - public function increment( DateTime $date, $invert = false ) { - if ( $invert ) { - $date->modify( '-1 minute' ); - } else { - $date->modify( '+1 minute' ); - } + return $this; + } - return $this; - } - - /** - * {@inheritdoc} - */ - public function validate( $value ) { - return (bool) preg_match( '/[\*,\/\-0-9]+/', $value ); - } + /** + * {@inheritdoc} + */ + public function validate($value) + { + return (bool) preg_match('/[\*,\/\-0-9]+/', $value); + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MonthField.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MonthField.php index a722fa7e5a..d3deb129f4 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MonthField.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_MonthField.php @@ -5,58 +5,51 @@ * * @author Michael Dowling */ -class CronExpression_MonthField extends CronExpression_AbstractField { +class CronExpression_MonthField extends CronExpression_AbstractField +{ + /** + * {@inheritdoc} + */ + public function isSatisfiedBy(DateTime $date, $value) + { + // Convert text month values to integers + $value = str_ireplace( + array( + 'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', + 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC' + ), + range(1, 12), + $value + ); - /** - * {@inheritdoc} - */ - public function isSatisfiedBy( DateTime $date, $value ) { - // Convert text month values to integers - $value = str_ireplace( - array( - 'JAN', - 'FEB', - 'MAR', - 'APR', - 'MAY', - 'JUN', - 'JUL', - 'AUG', - 'SEP', - 'OCT', - 'NOV', - 'DEC', - ), - range( 1, 12 ), - $value - ); + return $this->isSatisfied($date->format('m'), $value); + } - return $this->isSatisfied( $date->format( 'm' ), $value ); - } + /** + * {@inheritdoc} + */ + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + // $date->modify('last day of previous month'); // remove for php 5.2 compat + $date->modify('previous month'); + $date->modify($date->format('Y-m-t')); + $date->setTime(23, 59); + } else { + //$date->modify('first day of next month'); // remove for php 5.2 compat + $date->modify('next month'); + $date->modify($date->format('Y-m-01')); + $date->setTime(0, 0); + } - /** - * {@inheritdoc} - */ - public function increment( DateTime $date, $invert = false ) { - if ( $invert ) { - // $date->modify('last day of previous month'); // remove for php 5.2 compat - $date->modify( 'previous month' ); - $date->modify( $date->format( 'Y-m-t' ) ); - $date->setTime( 23, 59 ); - } else { - // $date->modify('first day of next month'); // remove for php 5.2 compat - $date->modify( 'next month' ); - $date->modify( $date->format( 'Y-m-01' ) ); - $date->setTime( 0, 0 ); - } + return $this; + } - return $this; - } - - /** - * {@inheritdoc} - */ - public function validate( $value ) { - return (bool) preg_match( '/[\*,\/\-0-9A-Z]+/', $value ); - } + /** + * {@inheritdoc} + */ + public function validate($value) + { + return (bool) preg_match('/[\*,\/\-0-9A-Z]+/', $value); + } } diff --git a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_YearField.php b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_YearField.php index 7d762c4af1..f11562e451 100644 --- a/src/libraries/action-scheduler/lib/cron-expression/CronExpression_YearField.php +++ b/src/libraries/action-scheduler/lib/cron-expression/CronExpression_YearField.php @@ -5,36 +5,39 @@ * * @author Michael Dowling */ -class CronExpression_YearField extends CronExpression_AbstractField { +class CronExpression_YearField extends CronExpression_AbstractField +{ + /** + * {@inheritdoc} + */ + public function isSatisfiedBy(DateTime $date, $value) + { + return $this->isSatisfied($date->format('Y'), $value); + } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy( DateTime $date, $value ) { - return $this->isSatisfied( $date->format( 'Y' ), $value ); - } + /** + * {@inheritdoc} + */ + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + $date->modify('-1 year'); + $date->setDate($date->format('Y'), 12, 31); + $date->setTime(23, 59, 0); + } else { + $date->modify('+1 year'); + $date->setDate($date->format('Y'), 1, 1); + $date->setTime(0, 0, 0); + } - /** - * {@inheritdoc} - */ - public function increment( DateTime $date, $invert = false ) { - if ( $invert ) { - $date->modify( '-1 year' ); - $date->setDate( $date->format( 'Y' ), 12, 31 ); - $date->setTime( 23, 59, 0 ); - } else { - $date->modify( '+1 year' ); - $date->setDate( $date->format( 'Y' ), 1, 1 ); - $date->setTime( 0, 0, 0 ); - } + return $this; + } - return $this; - } - - /** - * {@inheritdoc} - */ - public function validate( $value ) { - return (bool) preg_match( '/[\*,\/\-0-9]+/', $value ); - } + /** + * {@inheritdoc} + */ + public function validate($value) + { + return (bool) preg_match('/[\*,\/\-0-9]+/', $value); + } } diff --git a/src/libraries/action-scheduler/package-lock.json b/src/libraries/action-scheduler/package-lock.json deleted file mode 100644 index 734d8f0bf0..0000000000 --- a/src/libraries/action-scheduler/package-lock.json +++ /dev/null @@ -1,2306 +0,0 @@ -{ - "name": "action-scheduler", - "version": "3.5.3", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" - } - }, - "@samverschueren/stream-to-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", - "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", - "dev": true, - "requires": { - "any-observable": "^0.3.0" - } - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "12.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.7.tgz", - "integrity": "sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "dependencies": { - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - } - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - } - } - }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", - "dev": true - }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "dev": true, - "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "del": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", - "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", - "dev": true, - "requires": { - "globby": "^10.0.1", - "graceful-fs": "^4.2.2", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.1", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", - "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", - "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - }, - "dependencies": { - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - } - } - }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "fast-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", - "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2" - } - }, - "fastq": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", - "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", - "dev": true, - "requires": { - "reusify": "^1.0.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg==", - "dev": true, - "requires": { - "glob": "~5.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - } - }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "dev": true - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz", - "integrity": "sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "getobject": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", - "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", - "dev": true - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "globby": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", - "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", - "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "grunt": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.5.3.tgz", - "integrity": "sha512-mKwmo4X2d8/4c/BmcOETHek675uOqw0RuA/zy12jaspWqvTp4+ZeQF1W+OTpcbncnaBsfbQJ6l0l4j+Sn/GmaQ==", - "dev": true, - "requires": { - "dateformat": "~3.0.3", - "eventemitter2": "~0.4.13", - "exit": "~0.1.2", - "findup-sync": "~0.3.0", - "glob": "~7.1.6", - "grunt-cli": "~1.4.3", - "grunt-known-options": "~2.0.0", - "grunt-legacy-log": "~3.0.0", - "grunt-legacy-util": "~2.0.1", - "iconv-lite": "~0.4.13", - "js-yaml": "~3.14.0", - "minimatch": "~3.0.4", - "mkdirp": "~1.0.4", - "nopt": "~3.0.6", - "rimraf": "~3.0.2" - }, - "dependencies": { - "grunt-cli": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", - "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", - "dev": true, - "requires": { - "grunt-known-options": "~2.0.0", - "interpret": "~1.1.0", - "liftup": "~3.0.1", - "nopt": "~4.0.1", - "v8flags": "~3.2.0" - }, - "dependencies": { - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - } - } - }, - "grunt-checktextdomain": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/grunt-checktextdomain/-/grunt-checktextdomain-1.0.1.tgz", - "integrity": "sha1-slTQHh3pEwBdTbHFMD2QI7mD4Zs=", - "dev": true, - "requires": { - "chalk": "~0.2.1", - "text-table": "~0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-0.2.0.tgz", - "integrity": "sha1-NZq0sV3NZLptdHNLcsNjYKmvLBk=", - "dev": true - }, - "chalk": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.2.1.tgz", - "integrity": "sha1-dhPhV1FFshOGSD9/SFql/6jL0Qw=", - "dev": true, - "requires": { - "ansi-styles": "~0.2.0", - "has-color": "~0.1.0" - } - } - } - }, - "grunt-known-options": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", - "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", - "dev": true - }, - "grunt-legacy-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", - "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", - "dev": true, - "requires": { - "colors": "~1.1.2", - "grunt-legacy-log-utils": "~2.1.0", - "hooker": "~0.2.3", - "lodash": "~4.17.19" - } - }, - "grunt-legacy-log-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", - "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", - "dev": true, - "requires": { - "chalk": "~4.1.0", - "lodash": "~4.17.19" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "grunt-legacy-util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", - "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", - "dev": true, - "requires": { - "async": "~3.2.0", - "exit": "~0.1.2", - "getobject": "~1.0.0", - "hooker": "~0.2.3", - "lodash": "~4.17.21", - "underscore.string": "~3.3.5", - "which": "~2.0.2" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "grunt-phpcs": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/grunt-phpcs/-/grunt-phpcs-0.4.0.tgz", - "integrity": "sha1-oI1iX8ZEZeRTsr2T+BCyqB6Uvao=", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "husky": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/husky/-/husky-3.0.9.tgz", - "integrity": "sha512-Yolhupm7le2/MqC1VYLk/cNmYxsSsqKkTyBhzQHhPK1jFnC89mmmNVuGtLNabjDI6Aj8UNIr0KpRNuBkiC4+sg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "ci-info": "^2.0.0", - "cosmiconfig": "^5.2.1", - "execa": "^1.0.0", - "get-stdin": "^7.0.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^4.2.0", - "please-upgrade-node": "^3.2.0", - "read-pkg": "^5.2.0", - "run-node": "^1.0.0", - "slash": "^3.0.0" - }, - "dependencies": { - "get-stdin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", - "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", - "dev": true - }, - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - } - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", - "dev": true - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==", - "dev": true - }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "dev": true, - "requires": { - "symbol-observable": "^1.1.0" - } - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "requires": { - "is-unc-path": "^1.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "requires": { - "unc-path-regex": "^0.1.2" - } - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "liftup": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", - "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==", - "dev": true, - "requires": { - "extend": "^3.0.2", - "findup-sync": "^4.0.0", - "fined": "^1.2.0", - "flagged-respawn": "^1.0.1", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.1", - "rechoir": "^0.7.0", - "resolve": "^1.19.0" - }, - "dependencies": { - "findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" - } - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "lint-staged": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-9.4.2.tgz", - "integrity": "sha512-OFyGokJSWTn2M6vngnlLXjaHhi8n83VIZZ5/1Z26SULRUWgR3ITWpAEQC9Pnm3MC/EpCxlwts/mQWDHNji2+zA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "commander": "^2.20.0", - "cosmiconfig": "^5.2.1", - "debug": "^4.1.1", - "dedent": "^0.7.0", - "del": "^5.0.0", - "execa": "^2.0.3", - "listr": "^0.14.3", - "log-symbols": "^3.0.0", - "micromatch": "^4.0.2", - "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.1.1", - "string-argv": "^0.3.0", - "stringify-object": "^3.3.0" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz", - "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^3.0.0", - "onetime": "^5.1.0", - "p-finally": "^2.0.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "npm-run-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", - "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "p-finally": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", - "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", - "dev": true - }, - "path-key": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", - "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz", - "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "dev": true, - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "dependencies": { - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - } - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - } - }, - "make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", - "dev": true, - "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", - "dev": true, - "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "opencollective-postinstall": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", - "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", - "dev": true, - "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", - "dev": true, - "requires": { - "path-root-regex": "^0.1.0" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", - "dev": true - }, - "picomatch": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "requires": { - "resolve": "^1.9.0" - } - }, - "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "dependencies": { - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - } - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", - "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", - "dev": true - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, - "rxjs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", - "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "dev": true - }, - "underscore.string": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", - "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", - "dev": true, - "requires": { - "sprintf-js": "^1.1.1", - "util-deprecate": "^1.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } -} diff --git a/src/libraries/action-scheduler/package.json b/src/libraries/action-scheduler/package.json deleted file mode 100644 index 2e84b97326..0000000000 --- a/src/libraries/action-scheduler/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "action-scheduler", - "title": "Action Scheduler", - "version": "3.5.3", - "homepage": "https://actionscheduler.org/", - "repository": { - "type": "git", - "url": "https://github.com/woocommerce/action-scheduler.git" - }, - "license": "GPL-3.0+", - "main": "Gruntfile.js", - "scripts": { - "build": "composer install --no-dev && npm install --only=prod && composer archive --file=$npm_package_name --format=zip && npm run postarchive", - "postarchive": "rm -rf $npm_package_name && unzip $npm_package_name.zip -d $npm_package_name && rm $npm_package_name.zip && zip -r $npm_package_name.zip $npm_package_name && rm -rf $npm_package_name", - "check-textdomain": "grunt checktextdomain", - "phpcs": "grunt phpcs" - }, - "woorelease": { - "wp_org_slug": "action-scheduler" - }, - "devDependencies": { - "grunt": "1.5.3", - "grunt-checktextdomain": "1.0.1", - "grunt-phpcs": "0.4.0", - "husky": "3.0.9", - "lint-staged": "9.4.2" - }, - "engines": { - "node": ">=10.15.0", - "npm": ">=6.4.1" - }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, - "lint-staged": { - "*.php": [ - "php -d display_errors=1 -l", - "composer run-script phpcs-pre-commit" - ] - } -} diff --git a/src/libraries/action-scheduler/phpcs.xml b/src/libraries/action-scheduler/phpcs.xml deleted file mode 100644 index 0c6e96bad7..0000000000 --- a/src/libraries/action-scheduler/phpcs.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - WooCommerce dev PHP_CodeSniffer ruleset. - - - docs/ - */node_modules/* - */vendor/* - - - - - - - - - - - - - - - - classes/* - deprecated/* - lib/* - tests/* - - - classes/* - deprecated/* - lib/* - tests/* - - - - tests/ - - - - classes/* - deprecated/* - lib/* - tests/* - - diff --git a/src/libraries/action-scheduler/readme.txt b/src/libraries/action-scheduler/readme.txt index 34e10cd94d..3518b15444 100644 --- a/src/libraries/action-scheduler/readme.txt +++ b/src/libraries/action-scheduler/readme.txt @@ -3,7 +3,7 @@ Contributors: Automattic, wpmuguru, claudiosanches, peterfabian1000, vedjain, ja Tags: scheduler, cron Requires at least: 5.2 Tested up to: 6.0 -Stable tag: 3.5.3 +Stable tag: 3.5.4 License: GPLv3 Requires PHP: 5.6 @@ -47,6 +47,16 @@ Collaboration is cool. We'd love to work with you to improve Action Scheduler. [ == Changelog == += 3.5.4 - 2023-01-17 = +* Add pre filters during action registration. +* Async scheduling. +* Calculate timeouts based on total actions. +* Correctly order the parameters for `ActionScheduler_ActionFactory`'s calls to `single_unique`. +* Fetch action in memory first before releasing claim to avoid deadlock. +* PHP 8.2: declare property to fix creation of dynamic property warning. +* PHP 8.2: fix "Using ${var} in strings is deprecated, use {$var} instead". +* Prevent `undefined variable` warning for `$num_pastdue_actions`. + = 3.5.3 - 2022-11-09 = * Query actions with partial match. diff --git a/src/modules/common/composer.json b/src/modules/common/composer.json index b21d5cb14d..3b247fb010 100644 --- a/src/modules/common/composer.json +++ b/src/modules/common/composer.json @@ -5,7 +5,7 @@ "type": "wordpress-plugin", "homepage": "https://wordlift.io", "require": { - "php": ">=5.6.0", + "php": ">=7.4.0", "ext-dom": "*", "ext-iconv": "*", "ext-json": "*", @@ -13,11 +13,14 @@ "ext-mbstring": "*", "cweagans/composer-patches": "^1.7", "mcaskill/composer-exclude-files": "^2.1", - "symfony/config": "^3.4", - "symfony/dependency-injection": "^3.4", + "psr/container": "1.1.2", + "symfony/config": "^5.4", + "symfony/dependency-injection": "^5.4", + "symfony/expression-language": "^5.4", "symfony/polyfill-mbstring": "^1.19", - "symfony/yaml": "^3.4", - "psr/container": "1.0.0" + "symfony/polyfill-php81": "^1.31", + "symfony/service-contracts": "^2.5", + "symfony/yaml": "^5.4" }, "require-dev": { "automattic/vipwpcs": "^2.3", @@ -52,7 +55,7 @@ }, "discard-changes": true, "platform": { - "php": "7.2" + "php": "7.4" }, "sort-packages": true }, @@ -66,13 +69,12 @@ "php-scoper": { "path": "vendor/bin/php-scoper", "type": "phar", - "url": "https://github.com/humbug/php-scoper/releases/download/0.17.2/php-scoper.phar" + "url": "https://github.com/humbug/php-scoper/releases/download/0.18.14/php-scoper.phar" } }, "enable-patching": true }, - "scripts": { "post-install-cmd": [ "@prefix-dependencies" @@ -86,14 +88,15 @@ "phpmd": "phpmd . text phpmd.xml", "phpstan": "phpstan analyse --memory-limit=512M", "prefix-dependencies": [ - "php-scoper add-prefix --output-dir=./third-party ", + "php-scoper add-prefix --output-dir=./third-party --force", "echo '{ \"autoload\": { \"classmap\": [\"\"] } }' > ./third-party/composer.json", - "@composer dump-autoload --working-dir ./third-party --no-dev --classmap-authoritative", + "@composer dump-autoload --working-dir ./third-party --classmap-authoritative", "sed -i'.bak' -e 's/Composer\\\\Autoload/Wordlift_Modules_Common_Composer\\\\Autoload/' third-party/vendor/composer/*.php && rm -rf third-party/vendor/composer/*.php.bak", "echo '{ \"autoload\": { \"classmap\": [\"\"] } }' > ./includes/composer.json", "@composer dump-autoload --working-dir ./includes --no-dev --classmap-authoritative", "sed -i'.bak' -e 's/Composer\\\\Autoload/Wordlift_Modules_Common_Composer\\\\Autoload/' includes/vendor/composer/*.php && rm -rf includes/vendor/composer/*.php.bak", - "rm -fr vendor" + "mv vendor/composer/autoload_files.php third-party/vendor/composer", + "rm -rf vendor" ] } } diff --git a/src/modules/common/includes/Api/Cursor_Page.php b/src/modules/common/includes/Api/Cursor_Page.php index 4400286c76..aad403c6c4 100644 --- a/src/modules/common/includes/Api/Cursor_Page.php +++ b/src/modules/common/includes/Api/Cursor_Page.php @@ -195,6 +195,7 @@ public function unserialize( $data ) { $this->__unserialize( unserialize( $data ) ); } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'self' => $this->get_self(), diff --git a/src/modules/common/includes/vendor/composer/ClassLoader.php b/src/modules/common/includes/vendor/composer/ClassLoader.php index e065b25610..9bf82f4fc3 100644 --- a/src/modules/common/includes/vendor/composer/ClassLoader.php +++ b/src/modules/common/includes/vendor/composer/ClassLoader.php @@ -45,35 +45,34 @@ class ClassLoader /** @var \Closure(string):void */ private static $includeFile; - /** @var ?string */ + /** @var string|null */ private $vendorDir; // PSR-4 /** - * @var array[] - * @psalm-var array> + * @var array> */ private $prefixLengthsPsr4 = array(); /** - * @var array[] - * @psalm-var array> + * @var array> */ private $prefixDirsPsr4 = array(); /** - * @var array[] - * @psalm-var array + * @var list */ private $fallbackDirsPsr4 = array(); // PSR-0 /** - * @var array[] - * @psalm-var array> + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> */ private $prefixesPsr0 = array(); /** - * @var array[] - * @psalm-var array + * @var list */ private $fallbackDirsPsr0 = array(); @@ -81,8 +80,7 @@ class ClassLoader private $useIncludePath = false; /** - * @var string[] - * @psalm-var array + * @var array */ private $classMap = array(); @@ -90,21 +88,20 @@ class ClassLoader private $classMapAuthoritative = false; /** - * @var bool[] - * @psalm-var array + * @var array */ private $missingClasses = array(); - /** @var ?string */ + /** @var string|null */ private $apcuPrefix; /** - * @var self[] + * @var array */ private static $registeredLoaders = array(); /** - * @param ?string $vendorDir + * @param string|null $vendorDir */ public function __construct($vendorDir = null) { @@ -113,7 +110,7 @@ public function __construct($vendorDir = null) } /** - * @return string[] + * @return array> */ public function getPrefixes() { @@ -125,8 +122,7 @@ public function getPrefixes() } /** - * @return array[] - * @psalm-return array> + * @return array> */ public function getPrefixesPsr4() { @@ -134,8 +130,7 @@ public function getPrefixesPsr4() } /** - * @return array[] - * @psalm-return array + * @return list */ public function getFallbackDirs() { @@ -143,8 +138,7 @@ public function getFallbackDirs() } /** - * @return array[] - * @psalm-return array + * @return list */ public function getFallbackDirsPsr4() { @@ -152,8 +146,7 @@ public function getFallbackDirsPsr4() } /** - * @return string[] Array of classname => path - * @psalm-return array + * @return array Array of classname => path */ public function getClassMap() { @@ -161,8 +154,7 @@ public function getClassMap() } /** - * @param string[] $classMap Class to filename map - * @psalm-param array $classMap + * @param array $classMap Class to filename map * * @return void */ @@ -179,24 +171,25 @@ public function addClassMap(array $classMap) * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * - * @param string $prefix The prefix - * @param string[]|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories * * @return void */ public function add($prefix, $paths, $prepend = false) { + $paths = (array) $paths; if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( - (array) $paths, + $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, - (array) $paths + $paths ); } @@ -205,19 +198,19 @@ public function add($prefix, $paths, $prepend = false) $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; + $this->prefixesPsr0[$first][$prefix] = $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, + $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], - (array) $paths + $paths ); } } @@ -226,9 +219,9 @@ public function add($prefix, $paths, $prepend = false) * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param string[]|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException * @@ -236,17 +229,18 @@ public function add($prefix, $paths, $prepend = false) */ public function addPsr4($prefix, $paths, $prepend = false) { + $paths = (array) $paths; if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( - (array) $paths, + $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, - (array) $paths + $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { @@ -256,18 +250,18 @@ public function addPsr4($prefix, $paths, $prepend = false) throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; + $this->prefixDirsPsr4[$prefix] = $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, + $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], - (array) $paths + $paths ); } } @@ -276,8 +270,8 @@ public function addPsr4($prefix, $paths, $prepend = false) * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * - * @param string $prefix The prefix - * @param string[]|string $paths The PSR-0 base directories + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories * * @return void */ @@ -294,8 +288,8 @@ public function set($prefix, $paths) * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param string[]|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException * @@ -481,9 +475,9 @@ public function findFile($class) } /** - * Returns the currently registered loaders indexed by their corresponding vendor directories. + * Returns the currently registered loaders keyed by their corresponding vendor directories. * - * @return self[] + * @return array */ public static function getRegisteredLoaders() { diff --git a/src/modules/common/load.php b/src/modules/common/load.php index b898ef4a69..c6499bd0dc 100644 --- a/src/modules/common/load.php +++ b/src/modules/common/load.php @@ -3,6 +3,16 @@ * Scoper autoload. */ +// Autoload package files manually because composer dump-autoload can't generate the autoload_files after scoper add-prefix. More info: https://github.com/humbug/php-scoper/issues/1036. +if ( file_exists( __DIR__ . '/third-party/vendor/composer/autoload_files.php' ) ) { + $files = require __DIR__ . '/third-party/vendor/composer/autoload_files.php'; + foreach ( $files as $file ) { + if ( file_exists( $file ) ) { + require_once $file; + } + } +} + // Autoloader for dependencies. if ( file_exists( __DIR__ . '/third-party/vendor/scoper-autoload.php' ) ) { require __DIR__ . '/third-party/vendor/scoper-autoload.php'; diff --git a/src/modules/common/scoper.inc.php b/src/modules/common/scoper.inc.php index 0b71f9ebb6..fe450301e0 100644 --- a/src/modules/common/scoper.inc.php +++ b/src/modules/common/scoper.inc.php @@ -31,47 +31,23 @@ // For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#finders-and-paths 'finders' => array( Finder::create() - ->files() - ->ignoreVCS( true ) - ->notName( '/LICENSE|.*\\.md|.*\\.dist|Makefile|composer\\.json|composer\\.lock/' ) - ->exclude( - array( - 'doc', - 'test', - 'test_old', - 'tests', - 'Tests', - 'vendor-bin', - ) - ) - ->in( - array( - 'vendor/cweagans/composer-patches', - 'vendor/mcaskill/composer-exclude-files', - 'vendor/psr/container', - 'vendor/symfony/config', - 'vendor/symfony/dependency-injection', - 'vendor/symfony/filesystem', - 'vendor/symfony/polyfill-ctype', - 'vendor/symfony/polyfill-php73', - 'vendor/symfony/polyfill-php80', - 'vendor/symfony/yaml', - ) - ), - - // Symfony mbstring polyfill. - Finder::create() - ->files() - ->ignoreVCS( true ) - ->ignoreDotFiles( true ) - ->name( '/\.*.php8?/' ) - ->in( 'vendor/symfony/polyfill-mbstring/Resources' ) - ->append( - array( - 'vendor/symfony/polyfill-mbstring/Mbstring.php', - 'vendor/symfony/polyfill-mbstring/composer.json', - ) - ), + ->files() + ->ignoreVCS( true ) + ->notName( '/LICENSE|.*\\.md|.*\\.dist|Makefile/' ) + ->exclude( + array( + 'doc', + 'test', + 'test_old', + 'tests', + 'Tests', + 'vendor-bin', + ) + ) + ->path( '#^mcaskill/#' ) + ->path( '#^psr/#' ) + ->path( '#^symfony/#' ) + ->in( 'vendor' ), Finder::create()->append( array( @@ -92,7 +68,22 @@ // heart contents. // // For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#patchers - 'patchers' => array(), + 'patchers' => array( + function ( $file_path, $prefix, $contents ) { + // Prefix the Stringable interface preventing get default one, because of scoper-autoload.php might not load the alias yet. + if ( preg_match( '#vendor/symfony/polyfill-php80/PhpToken\.php$#', $file_path ) ) { + $contents = strtr( + $contents, + array( + "namespace $prefix\\Symfony\\Polyfill\\Php80;" => "namespace $prefix\\Symfony\\Polyfill\\Php80;\n\nuse $prefix\\Stringable;", + "class PhpToken implements \\Stringable" => "class PhpToken implements Stringable", + ) + ); + } + + return $contents; + } + ), // List of symbols to consider internal i.e. to leave untouched. // @@ -109,4 +100,8 @@ 'exclude-constants' => $wp_constants, + 'expose-global-constants' => true, + 'expose-global-classes' => true, + 'expose-global-functions' => true, + ); diff --git a/src/modules/common/third-party/vendor/autoload.php b/src/modules/common/third-party/vendor/autoload.php index 068bd5dc65..a972628695 100644 --- a/src/modules/common/third-party/vendor/autoload.php +++ b/src/modules/common/third-party/vendor/autoload.php @@ -22,4 +22,4 @@ require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInitbd04ee84a816dcd5effd91d716ec9608::getLoader(); +return ComposerAutoloaderInitdfa501c6e5f181f14bf3ca6f0583434e::getLoader(); diff --git a/src/modules/common/third-party/vendor/composer/ClassLoader.php b/src/modules/common/third-party/vendor/composer/ClassLoader.php index e065b25610..9bf82f4fc3 100644 --- a/src/modules/common/third-party/vendor/composer/ClassLoader.php +++ b/src/modules/common/third-party/vendor/composer/ClassLoader.php @@ -45,35 +45,34 @@ class ClassLoader /** @var \Closure(string):void */ private static $includeFile; - /** @var ?string */ + /** @var string|null */ private $vendorDir; // PSR-4 /** - * @var array[] - * @psalm-var array> + * @var array> */ private $prefixLengthsPsr4 = array(); /** - * @var array[] - * @psalm-var array> + * @var array> */ private $prefixDirsPsr4 = array(); /** - * @var array[] - * @psalm-var array + * @var list */ private $fallbackDirsPsr4 = array(); // PSR-0 /** - * @var array[] - * @psalm-var array> + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> */ private $prefixesPsr0 = array(); /** - * @var array[] - * @psalm-var array + * @var list */ private $fallbackDirsPsr0 = array(); @@ -81,8 +80,7 @@ class ClassLoader private $useIncludePath = false; /** - * @var string[] - * @psalm-var array + * @var array */ private $classMap = array(); @@ -90,21 +88,20 @@ class ClassLoader private $classMapAuthoritative = false; /** - * @var bool[] - * @psalm-var array + * @var array */ private $missingClasses = array(); - /** @var ?string */ + /** @var string|null */ private $apcuPrefix; /** - * @var self[] + * @var array */ private static $registeredLoaders = array(); /** - * @param ?string $vendorDir + * @param string|null $vendorDir */ public function __construct($vendorDir = null) { @@ -113,7 +110,7 @@ public function __construct($vendorDir = null) } /** - * @return string[] + * @return array> */ public function getPrefixes() { @@ -125,8 +122,7 @@ public function getPrefixes() } /** - * @return array[] - * @psalm-return array> + * @return array> */ public function getPrefixesPsr4() { @@ -134,8 +130,7 @@ public function getPrefixesPsr4() } /** - * @return array[] - * @psalm-return array + * @return list */ public function getFallbackDirs() { @@ -143,8 +138,7 @@ public function getFallbackDirs() } /** - * @return array[] - * @psalm-return array + * @return list */ public function getFallbackDirsPsr4() { @@ -152,8 +146,7 @@ public function getFallbackDirsPsr4() } /** - * @return string[] Array of classname => path - * @psalm-return array + * @return array Array of classname => path */ public function getClassMap() { @@ -161,8 +154,7 @@ public function getClassMap() } /** - * @param string[] $classMap Class to filename map - * @psalm-param array $classMap + * @param array $classMap Class to filename map * * @return void */ @@ -179,24 +171,25 @@ public function addClassMap(array $classMap) * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * - * @param string $prefix The prefix - * @param string[]|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories * * @return void */ public function add($prefix, $paths, $prepend = false) { + $paths = (array) $paths; if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( - (array) $paths, + $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, - (array) $paths + $paths ); } @@ -205,19 +198,19 @@ public function add($prefix, $paths, $prepend = false) $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; + $this->prefixesPsr0[$first][$prefix] = $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, + $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], - (array) $paths + $paths ); } } @@ -226,9 +219,9 @@ public function add($prefix, $paths, $prepend = false) * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param string[]|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException * @@ -236,17 +229,18 @@ public function add($prefix, $paths, $prepend = false) */ public function addPsr4($prefix, $paths, $prepend = false) { + $paths = (array) $paths; if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( - (array) $paths, + $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, - (array) $paths + $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { @@ -256,18 +250,18 @@ public function addPsr4($prefix, $paths, $prepend = false) throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; + $this->prefixDirsPsr4[$prefix] = $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, + $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], - (array) $paths + $paths ); } } @@ -276,8 +270,8 @@ public function addPsr4($prefix, $paths, $prepend = false) * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * - * @param string $prefix The prefix - * @param string[]|string $paths The PSR-0 base directories + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories * * @return void */ @@ -294,8 +288,8 @@ public function set($prefix, $paths) * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param string[]|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException * @@ -481,9 +475,9 @@ public function findFile($class) } /** - * Returns the currently registered loaders indexed by their corresponding vendor directories. + * Returns the currently registered loaders keyed by their corresponding vendor directories. * - * @return self[] + * @return array */ public static function getRegisteredLoaders() { diff --git a/src/modules/common/third-party/vendor/composer/autoload_classmap.php b/src/modules/common/third-party/vendor/composer/autoload_classmap.php index 8aead769f4..c518abed16 100644 --- a/src/modules/common/third-party/vendor/composer/autoload_classmap.php +++ b/src/modules/common/third-party/vendor/composer/autoload_classmap.php @@ -8,13 +8,95 @@ return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Wordlift\\Modules\\Common\\Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Wordlift\\Modules\\Common\\CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php', 'Wordlift\\Modules\\Common\\JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', 'Wordlift\\Modules\\Common\\McAskill\\Composer\\ExcludeFilePlugin' => $vendorDir . '/mcaskill/composer-exclude-files/src/ExcludeFilePlugin.php', 'Wordlift\\Modules\\Common\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\CacheException' => $vendorDir . '/psr/cache/src/CacheException.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\CacheItemInterface' => $vendorDir . '/psr/cache/src/CacheItemInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\CacheItemPoolInterface' => $vendorDir . '/psr/cache/src/CacheItemPoolInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\InvalidArgumentException' => $vendorDir . '/psr/cache/src/InvalidArgumentException.php', 'Wordlift\\Modules\\Common\\Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php', 'Wordlift\\Modules\\Common\\Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php', 'Wordlift\\Modules\\Common\\Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php', + 'Wordlift\\Modules\\Common\\ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'Wordlift\\Modules\\Common\\Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\AbstractTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => $vendorDir . '/symfony/cache/Adapter/AdapterInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => $vendorDir . '/symfony/cache/Adapter/ApcuAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/ArrayAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => $vendorDir . '/symfony/cache/Adapter/ChainAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\CouchbaseBucketAdapter' => $vendorDir . '/symfony/cache/Adapter/CouchbaseBucketAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\CouchbaseCollectionAdapter' => $vendorDir . '/symfony/cache/Adapter/CouchbaseCollectionAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => $vendorDir . '/symfony/cache/Adapter/DoctrineAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\DoctrineDbalAdapter' => $vendorDir . '/symfony/cache/Adapter/DoctrineDbalAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\FilesystemTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\LazyValue' => $vendorDir . '/symfony/cache/Adapter/PhpFilesAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => $vendorDir . '/symfony/cache/Adapter/MemcachedAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\NullAdapter' => $vendorDir . '/symfony/cache/Adapter/NullAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ParameterNormalizer' => $vendorDir . '/symfony/cache/Adapter/ParameterNormalizer.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => $vendorDir . '/symfony/cache/Adapter/PdoAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpArrayAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpFilesAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => $vendorDir . '/symfony/cache/Adapter/ProxyAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\Psr16Adapter' => $vendorDir . '/symfony/cache/Adapter/Psr16Adapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\RedisTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapterInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TraceableAdapter' => $vendorDir . '/symfony/cache/Adapter/TraceableAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TraceableAdapterEvent' => $vendorDir . '/symfony/cache/Adapter/TraceableAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\CacheItem' => $vendorDir . '/symfony/cache/CacheItem.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => $vendorDir . '/symfony/cache/DataCollector/CacheDataCollector.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CacheCollectorPass' => $vendorDir . '/symfony/cache/DependencyInjection/CacheCollectorPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CachePoolClearerPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolClearerPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPrunerPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolPrunerPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DoctrineProvider' => $vendorDir . '/symfony/cache/DoctrineProvider.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Exception\\CacheException' => $vendorDir . '/symfony/cache/Exception/CacheException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/cache/Exception/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Exception\\LogicException' => $vendorDir . '/symfony/cache/Exception/LogicException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\LockRegistry' => $vendorDir . '/symfony/cache/LockRegistry.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller' => $vendorDir . '/symfony/cache/Marshaller/DefaultMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\DeflateMarshaller' => $vendorDir . '/symfony/cache/Marshaller/DeflateMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\MarshallerInterface' => $vendorDir . '/symfony/cache/Marshaller/MarshallerInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\SodiumMarshaller' => $vendorDir . '/symfony/cache/Marshaller/SodiumMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\TagAwareMarshaller' => $vendorDir . '/symfony/cache/Marshaller/TagAwareMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Messenger\\EarlyExpirationDispatcher' => $vendorDir . '/symfony/cache/Messenger/EarlyExpirationDispatcher.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Messenger\\EarlyExpirationHandler' => $vendorDir . '/symfony/cache/Messenger/EarlyExpirationHandler.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Messenger\\EarlyExpirationMessage' => $vendorDir . '/symfony/cache/Messenger/EarlyExpirationMessage.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\PruneableInterface' => $vendorDir . '/symfony/cache/PruneableInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Psr16Cache' => $vendorDir . '/symfony/cache/Psr16Cache.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\ResettableInterface' => $vendorDir . '/symfony/cache/ResettableInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\AbstractAdapterTrait' => $vendorDir . '/symfony/cache/Traits/AbstractAdapterTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\ContractsTrait' => $vendorDir . '/symfony/cache/Traits/ContractsTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemCommonTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\ProxyTrait' => $vendorDir . '/symfony/cache/Traits/ProxyTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisClusterNodeProxy' => $vendorDir . '/symfony/cache/Traits/RedisClusterNodeProxy.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisClusterProxy' => $vendorDir . '/symfony/cache/Traits/RedisClusterProxy.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisProxy' => $vendorDir . '/symfony/cache/Traits/RedisProxy.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisTrait' => $vendorDir . '/symfony/cache/Traits/RedisTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ClassBuilder' => $vendorDir . '/symfony/config/Builder/ClassBuilder.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ConfigBuilderGenerator' => $vendorDir . '/symfony/config/Builder/ConfigBuilderGenerator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ConfigBuilderGeneratorInterface' => $vendorDir . '/symfony/config/Builder/ConfigBuilderGeneratorInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ConfigBuilderInterface' => $vendorDir . '/symfony/config/Builder/ConfigBuilderInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\Method' => $vendorDir . '/symfony/config/Builder/Method.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\Property' => $vendorDir . '/symfony/config/Builder/Property.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ConfigCache' => $vendorDir . '/symfony/config/ConfigCache.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ConfigCacheFactory' => $vendorDir . '/symfony/config/ConfigCacheFactory.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => $vendorDir . '/symfony/config/ConfigCacheFactoryInterface.php', @@ -24,6 +106,7 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\BooleanNode' => $vendorDir . '/symfony/config/Definition/BooleanNode.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\BuilderAwareInterface' => $vendorDir . '/symfony/config/Definition/Builder/BuilderAwareInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/EnumNodeDefinition.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ExprBuilder.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/FloatNodeDefinition.php', @@ -59,10 +142,9 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => $vendorDir . '/symfony/config/Definition/PrototypedArrayNode.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\ScalarNode' => $vendorDir . '/symfony/config/Definition/ScalarNode.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\VariableNode' => $vendorDir . '/symfony/config/Definition/VariableNode.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\DependencyInjection\\ConfigCachePass' => $vendorDir . '/symfony/config/DependencyInjection/ConfigCachePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => $vendorDir . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => $vendorDir . '/symfony/config/Exception/FileLoaderLoadException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => $vendorDir . '/symfony/config/Exception/FileLocatorFileNotFoundException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\LoaderLoadException' => $vendorDir . '/symfony/config/Exception/LoaderLoadException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\FileLocator' => $vendorDir . '/symfony/config/FileLocator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\FileLocatorInterface' => $vendorDir . '/symfony/config/FileLocatorInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\DelegatingLoader' => $vendorDir . '/symfony/config/Loader/DelegatingLoader.php', @@ -72,6 +154,7 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\LoaderInterface' => $vendorDir . '/symfony/config/Loader/LoaderInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\LoaderResolver' => $vendorDir . '/symfony/config/Loader/LoaderResolver.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => $vendorDir . '/symfony/config/Loader/LoaderResolverInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\ParamConfigurator' => $vendorDir . '/symfony/config/Loader/ParamConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ResourceCheckerConfigCache' => $vendorDir . '/symfony/config/ResourceCheckerConfigCache.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => $vendorDir . '/symfony/config/ResourceCheckerConfigCacheFactory.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ResourceCheckerInterface' => $vendorDir . '/symfony/config/ResourceCheckerInterface.php', @@ -82,8 +165,6 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\FileResource' => $vendorDir . '/symfony/config/Resource/FileResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\GlobResource' => $vendorDir . '/symfony/config/Resource/GlobResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ReflectionMethodHhvmWrapper' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ReflectionParameterHhvmWrapper' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ResourceInterface' => $vendorDir . '/symfony/config/Resource/ResourceInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceChecker.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceInterface.php', @@ -91,55 +172,69 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => $vendorDir . '/symfony/config/Util/Exception/XmlParsingException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Util\\XmlUtils' => $vendorDir . '/symfony/config/Util/XmlUtils.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Alias' => $vendorDir . '/symfony/dependency-injection/Alias.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\AbstractArgument' => $vendorDir . '/symfony/dependency-injection/Argument/AbstractArgument.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ArgumentInterface' => $vendorDir . '/symfony/dependency-injection/Argument/ArgumentInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\BoundArgument' => $vendorDir . '/symfony/dependency-injection/Argument/BoundArgument.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\IteratorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/IteratorArgument.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ReferenceSetArgumentTrait' => $vendorDir . '/symfony/dependency-injection/Argument/ReferenceSetArgumentTrait.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\RewindableGenerator' => $vendorDir . '/symfony/dependency-injection/Argument/RewindableGenerator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ServiceClosureArgument' => $vendorDir . '/symfony/dependency-injection/Argument/ServiceClosureArgument.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocator' => $vendorDir . '/symfony/dependency-injection/Argument/ServiceLocator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocatorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/ServiceLocatorArgument.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\TaggedIteratorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/TaggedIteratorArgument.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\AsTaggedItem' => $vendorDir . '/symfony/dependency-injection/Attribute/AsTaggedItem.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\Autoconfigure' => $vendorDir . '/symfony/dependency-injection/Attribute/Autoconfigure.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\AutoconfigureTag' => $vendorDir . '/symfony/dependency-injection/Attribute/AutoconfigureTag.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\TaggedIterator' => $vendorDir . '/symfony/dependency-injection/Attribute/TaggedIterator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\TaggedLocator' => $vendorDir . '/symfony/dependency-injection/Attribute/TaggedLocator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\Target' => $vendorDir . '/symfony/dependency-injection/Attribute/Target.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\When' => $vendorDir . '/symfony/dependency-injection/Attribute/When.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ChildDefinition' => $vendorDir . '/symfony/dependency-injection/ChildDefinition.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AbstractRecursivePass' => $vendorDir . '/symfony/dependency-injection/Compiler/AbstractRecursivePass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AliasDeprecatedPublicServicesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AliasDeprecatedPublicServicesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AnalyzeServiceReferencesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AttributeAutoconfigurationPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AttributeAutoconfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutoAliasServicePass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutoAliasServicePass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowireExceptionPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutowireExceptionPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowirePass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutowirePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowireRequiredMethodsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowireRequiredPropertiesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutowireRequiredPropertiesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckArgumentsValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckCircularReferencesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckExceptionOnInvalidReferenceBehaviorPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckReferenceValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckTypeDeclarationsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckTypeDeclarationsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\Compiler' => $vendorDir . '/symfony/dependency-injection/Compiler/Compiler.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface' => $vendorDir . '/symfony/dependency-injection/Compiler/CompilerPassInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\DecoratorServicePass' => $vendorDir . '/symfony/dependency-injection/Compiler/DecoratorServicePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\DefinitionErrorExceptionPass' => $vendorDir . '/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ExtensionCompilerPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ExtensionCompilerPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\FactoryReturnTypePass' => $vendorDir . '/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\InlineServiceDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\LoggingFormatter' => $vendorDir . '/symfony/dependency-injection/Compiler/LoggingFormatter.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationContainerBuilder' => $vendorDir . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationParameterBag' => $vendorDir . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationPass' => $vendorDir . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\PassConfig' => $vendorDir . '/symfony/dependency-injection/Compiler/PassConfig.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceTrait' => $vendorDir . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceUtil' => $vendorDir . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterAutoconfigureAttributesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterAutoconfigureAttributesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterEnvVarProcessorsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterReverseContainerPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterReverseContainerPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterServiceSubscribersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RemoveAbstractDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RemovePrivateAliasesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RemoveUnusedDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RepeatablePassInterface' => $vendorDir . '/symfony/dependency-injection/Compiler/RepeatablePassInterface.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RepeatedPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RepeatedPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ReplaceAliasByActualDefinitionPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveBindingsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveBindingsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveChildDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveClassPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveClassPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveDefinitionTemplatesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveDecoratorStackPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveDecoratorStackPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveEnvPlaceholdersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveFactoryClassPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveHotPathPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveHotPathPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInstanceofConditionalsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInvalidReferencesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveNamedArgumentsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveNoPreloadPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveNoPreloadPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveParameterPlaceHoldersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolvePrivatesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveReferencesToAliasesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php', @@ -149,7 +244,7 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraph' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphEdge' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphNode' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Config\\AutowireServiceResource' => $vendorDir . '/symfony/dependency-injection/Config/AutowireServiceResource.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ValidateEnvPlaceholdersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResource' => $vendorDir . '/symfony/dependency-injection/Config/ContainerParametersResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResourceChecker' => $vendorDir . '/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Container' => $vendorDir . '/symfony/dependency-injection/Container.php', @@ -158,13 +253,14 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ContainerBuilder' => $vendorDir . '/symfony/dependency-injection/ContainerBuilder.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ContainerInterface' => $vendorDir . '/symfony/dependency-injection/ContainerInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Definition' => $vendorDir . '/symfony/dependency-injection/Definition.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\DefinitionDecorator' => $vendorDir . '/symfony/dependency-injection/DefinitionDecorator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\Dumper' => $vendorDir . '/symfony/dependency-injection/Dumper/Dumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\DumperInterface' => $vendorDir . '/symfony/dependency-injection/Dumper/DumperInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\GraphvizDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/GraphvizDumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/PhpDumper.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\Preloader' => $vendorDir . '/symfony/dependency-injection/Dumper/Preloader.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\XmlDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/XmlDumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\YamlDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/YamlDumper.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\EnvVarLoaderInterface' => $vendorDir . '/symfony/dependency-injection/EnvVarLoaderInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\EnvVarProcessor' => $vendorDir . '/symfony/dependency-injection/EnvVarProcessor.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\EnvVarProcessorInterface' => $vendorDir . '/symfony/dependency-injection/EnvVarProcessorInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\AutowiringFailedException' => $vendorDir . '/symfony/dependency-injection/Exception/AutowiringFailedException.php', @@ -173,6 +269,7 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\EnvParameterException' => $vendorDir . '/symfony/dependency-injection/Exception/EnvParameterException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/dependency-injection/Exception/ExceptionInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/dependency-injection/Exception/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\InvalidParameterTypeException' => $vendorDir . '/symfony/dependency-injection/Exception/InvalidParameterTypeException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\LogicException' => $vendorDir . '/symfony/dependency-injection/Exception/LogicException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\OutOfBoundsException' => $vendorDir . '/symfony/dependency-injection/Exception/OutOfBoundsException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\ParameterCircularReferenceException' => $vendorDir . '/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php', @@ -195,8 +292,10 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractServiceConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AliasConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ClosureReferenceConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ClosureReferenceConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\DefaultsConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\EnvConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/EnvConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InlineServiceConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InstanceofConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ParametersConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php', @@ -232,23 +331,58 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\XmlFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/XmlFileLoader.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/YamlFileLoader.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Parameter' => $vendorDir . '/symfony/dependency-injection/Parameter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ContainerBag.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBagInterface' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\EnvPlaceholderParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\FrozenParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ParameterBag.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBagInterface' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Reference' => $vendorDir . '/symfony/dependency-injection/Reference.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ResettableContainerInterface' => $vendorDir . '/symfony/dependency-injection/ResettableContainerInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ReverseContainer' => $vendorDir . '/symfony/dependency-injection/ReverseContainer.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ServiceLocator' => $vendorDir . '/symfony/dependency-injection/ServiceLocator.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface' => $vendorDir . '/symfony/dependency-injection/ServiceSubscriberInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\TaggedContainerInterface' => $vendorDir . '/symfony/dependency-injection/TaggedContainerInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\TypedReference' => $vendorDir . '/symfony/dependency-injection/TypedReference.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Variable' => $vendorDir . '/symfony/dependency-injection/Variable.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Compiler' => $vendorDir . '/symfony/expression-language/Compiler.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Expression' => $vendorDir . '/symfony/expression-language/Expression.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ExpressionFunction' => $vendorDir . '/symfony/expression-language/ExpressionFunction.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface' => $vendorDir . '/symfony/expression-language/ExpressionFunctionProviderInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage' => $vendorDir . '/symfony/expression-language/ExpressionLanguage.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Lexer' => $vendorDir . '/symfony/expression-language/Lexer.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ArgumentsNode' => $vendorDir . '/symfony/expression-language/Node/ArgumentsNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ArrayNode' => $vendorDir . '/symfony/expression-language/Node/ArrayNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\BinaryNode' => $vendorDir . '/symfony/expression-language/Node/BinaryNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ConditionalNode' => $vendorDir . '/symfony/expression-language/Node/ConditionalNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ConstantNode' => $vendorDir . '/symfony/expression-language/Node/ConstantNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\FunctionNode' => $vendorDir . '/symfony/expression-language/Node/FunctionNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\GetAttrNode' => $vendorDir . '/symfony/expression-language/Node/GetAttrNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\NameNode' => $vendorDir . '/symfony/expression-language/Node/NameNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\Node' => $vendorDir . '/symfony/expression-language/Node/Node.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\UnaryNode' => $vendorDir . '/symfony/expression-language/Node/UnaryNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ParsedExpression' => $vendorDir . '/symfony/expression-language/ParsedExpression.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Parser' => $vendorDir . '/symfony/expression-language/Parser.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\SerializedParsedExpression' => $vendorDir . '/symfony/expression-language/SerializedParsedExpression.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\SyntaxError' => $vendorDir . '/symfony/expression-language/SyntaxError.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Token' => $vendorDir . '/symfony/expression-language/Token.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\TokenStream' => $vendorDir . '/symfony/expression-language/TokenStream.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/ExceptionInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/filesystem/Exception/FileNotFoundException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\IOException' => $vendorDir . '/symfony/filesystem/Exception/IOException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/IOExceptionInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/filesystem/Exception/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => $vendorDir . '/symfony/filesystem/Exception/RuntimeException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Filesystem' => $vendorDir . '/symfony/filesystem/Filesystem.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Path' => $vendorDir . '/symfony/filesystem/Path.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Exception\\ClassNotFoundException' => $vendorDir . '/symfony/var-exporter/Exception/ClassNotFoundException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/var-exporter/Exception/ExceptionInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Exception\\NotInstantiableTypeException' => $vendorDir . '/symfony/var-exporter/Exception/NotInstantiableTypeException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Instantiator' => $vendorDir . '/symfony/var-exporter/Instantiator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Exporter' => $vendorDir . '/symfony/var-exporter/Internal/Exporter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Hydrator' => $vendorDir . '/symfony/var-exporter/Internal/Hydrator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Reference' => $vendorDir . '/symfony/var-exporter/Internal/Reference.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Registry' => $vendorDir . '/symfony/var-exporter/Internal/Registry.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Values' => $vendorDir . '/symfony/var-exporter/Internal/Values.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\VarExporter' => $vendorDir . '/symfony/var-exporter/VarExporter.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Escaper.php', @@ -261,14 +395,26 @@ 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Tag\\TaggedValue' => $vendorDir . '/symfony/yaml/Tag/TaggedValue.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Unescaper' => $vendorDir . '/symfony/yaml/Unescaper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Yaml' => $vendorDir . '/symfony/yaml/Yaml.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\CacheInterface' => $vendorDir . '/symfony/cache-contracts/CacheInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\CacheTrait' => $vendorDir . '/symfony/cache-contracts/CacheTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\CallbackInterface' => $vendorDir . '/symfony/cache-contracts/CallbackInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\ItemInterface' => $vendorDir . '/symfony/cache-contracts/ItemInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\TagAwareCacheInterface' => $vendorDir . '/symfony/cache-contracts/TagAwareCacheInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Attribute\\Required' => $vendorDir . '/symfony/service-contracts/Attribute/Required.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => $vendorDir . '/symfony/service-contracts/Attribute/SubscribedService.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ResetInterface' => $vendorDir . '/symfony/service-contracts/ResetInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceLocatorTrait' => $vendorDir . '/symfony/service-contracts/ServiceLocatorTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceProviderInterface' => $vendorDir . '/symfony/service-contracts/ServiceProviderInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => $vendorDir . '/symfony/service-contracts/Test/ServiceLocatorTest.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Test\\ServiceLocatorTestCase' => $vendorDir . '/symfony/service-contracts/Test/ServiceLocatorTestCase.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php73\\Php73' => $vendorDir . '/symfony/polyfill-php73/Php73.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php80\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/PhpToken.php', + 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php81\\Php81' => $vendorDir . '/symfony/polyfill-php81/Php81.php', 'Wordlift\\Modules\\Common\\UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'Wordlift\\Modules\\Common\\ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', - 'Wordlift\\Modules\\Common\\cweagans\\Composer\\PatchEvent' => $vendorDir . '/cweagans/composer-patches/src/PatchEvent.php', - 'Wordlift\\Modules\\Common\\cweagans\\Composer\\PatchEvents' => $vendorDir . '/cweagans/composer-patches/src/PatchEvents.php', - 'Wordlift\\Modules\\Common\\cweagans\\Composer\\Patches' => $vendorDir . '/cweagans/composer-patches/src/Patches.php', ); diff --git a/src/modules/common/third-party/vendor/composer/autoload_files.php b/src/modules/common/third-party/vendor/composer/autoload_files.php new file mode 100644 index 0000000000..0fc75c9c4e --- /dev/null +++ b/src/modules/common/third-party/vendor/composer/autoload_files.php @@ -0,0 +1,22 @@ + $vendorDir . '/symfony/polyfill-php80/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', + '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', + 'c72349b1fe8d0deeedd3a52e8aa814d8' => $vendorDir . '/mockery/mockery/library/helpers.php', + 'ce9671a430e4846b44e1c68c7611f9f5' => $vendorDir . '/mockery/mockery/library/Mockery.php', + '9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php', + 'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php', + '051bafe20e2674435a162870efa2d2a7' => $vendorDir . '/brain/monkey/inc/api.php', + '7d3b315c4f303f2fc14aca642a738e50' => $vendorDir . '/yoast/phpunit-polyfills/phpunitpolyfills-autoload.php', +); diff --git a/src/modules/common/third-party/vendor/composer/autoload_real.php b/src/modules/common/third-party/vendor/composer/autoload_real.php index 2031cbea33..3be5690cae 100644 --- a/src/modules/common/third-party/vendor/composer/autoload_real.php +++ b/src/modules/common/third-party/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInitbd04ee84a816dcd5effd91d716ec9608 +class ComposerAutoloaderInitdfa501c6e5f181f14bf3ca6f0583434e { private static $loader; @@ -22,12 +22,12 @@ public static function getLoader() return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInitbd04ee84a816dcd5effd91d716ec9608', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitdfa501c6e5f181f14bf3ca6f0583434e', 'loadClassLoader'), true, true); self::$loader = $loader = new \Wordlift_Modules_Common_Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInitbd04ee84a816dcd5effd91d716ec9608', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitdfa501c6e5f181f14bf3ca6f0583434e', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Wordlift_Modules_Common_Composer\Autoload\ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608::getInitializer($loader)); + call_user_func(\Wordlift_Modules_Common_Composer\Autoload\ComposerStaticInitdfa501c6e5f181f14bf3ca6f0583434e::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); diff --git a/src/modules/common/third-party/vendor/composer/autoload_static.php b/src/modules/common/third-party/vendor/composer/autoload_static.php index ec596663d0..079b1351c5 100644 --- a/src/modules/common/third-party/vendor/composer/autoload_static.php +++ b/src/modules/common/third-party/vendor/composer/autoload_static.php @@ -4,18 +4,100 @@ namespace Wordlift_Modules_Common_Composer\Autoload; -class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 +class ComposerStaticInitdfa501c6e5f181f14bf3ca6f0583434e { public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'Wordlift\\Modules\\Common\\Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Wordlift\\Modules\\Common\\CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php', 'Wordlift\\Modules\\Common\\JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', 'Wordlift\\Modules\\Common\\McAskill\\Composer\\ExcludeFilePlugin' => __DIR__ . '/..' . '/mcaskill/composer-exclude-files/src/ExcludeFilePlugin.php', 'Wordlift\\Modules\\Common\\PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\CacheException' => __DIR__ . '/..' . '/psr/cache/src/CacheException.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\CacheItemInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\CacheItemPoolInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemPoolInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Cache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/cache/src/InvalidArgumentException.php', 'Wordlift\\Modules\\Common\\Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php', 'Wordlift\\Modules\\Common\\Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php', 'Wordlift\\Modules\\Common\\Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'Wordlift\\Modules\\Common\\Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php', + 'Wordlift\\Modules\\Common\\ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'Wordlift\\Modules\\Common\\Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\AbstractTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/AdapterInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ApcuAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ArrayAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ChainAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\CouchbaseBucketAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/CouchbaseBucketAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\CouchbaseCollectionAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/CouchbaseCollectionAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/DoctrineAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\DoctrineDbalAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/DoctrineDbalAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\FilesystemTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\LazyValue' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpFilesAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/MemcachedAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\NullAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/NullAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ParameterNormalizer' => __DIR__ . '/..' . '/symfony/cache/Adapter/ParameterNormalizer.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PdoAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpArrayAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpFilesAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ProxyAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\Psr16Adapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/Psr16Adapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\RedisTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapterInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TraceableAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TraceableAdapterEvent' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\CacheItem' => __DIR__ . '/..' . '/symfony/cache/CacheItem.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => __DIR__ . '/..' . '/symfony/cache/DataCollector/CacheDataCollector.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CacheCollectorPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CacheCollectorPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CachePoolClearerPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolClearerPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPrunerPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolPrunerPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\DoctrineProvider' => __DIR__ . '/..' . '/symfony/cache/DoctrineProvider.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Exception\\CacheException' => __DIR__ . '/..' . '/symfony/cache/Exception/CacheException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/cache/Exception/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/cache/Exception/LogicException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\LockRegistry' => __DIR__ . '/..' . '/symfony/cache/LockRegistry.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/DefaultMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\DeflateMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/DeflateMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\MarshallerInterface' => __DIR__ . '/..' . '/symfony/cache/Marshaller/MarshallerInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\SodiumMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/SodiumMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Marshaller\\TagAwareMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/TagAwareMarshaller.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Messenger\\EarlyExpirationDispatcher' => __DIR__ . '/..' . '/symfony/cache/Messenger/EarlyExpirationDispatcher.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Messenger\\EarlyExpirationHandler' => __DIR__ . '/..' . '/symfony/cache/Messenger/EarlyExpirationHandler.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Messenger\\EarlyExpirationMessage' => __DIR__ . '/..' . '/symfony/cache/Messenger/EarlyExpirationMessage.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\PruneableInterface' => __DIR__ . '/..' . '/symfony/cache/PruneableInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Psr16Cache' => __DIR__ . '/..' . '/symfony/cache/Psr16Cache.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\ResettableInterface' => __DIR__ . '/..' . '/symfony/cache/ResettableInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\AbstractAdapterTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/AbstractAdapterTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\ContractsTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ContractsTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemCommonTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\ProxyTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ProxyTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisClusterNodeProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisClusterNodeProxy.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisClusterProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisClusterProxy.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisProxy.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\Traits\\RedisTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ClassBuilder' => __DIR__ . '/..' . '/symfony/config/Builder/ClassBuilder.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ConfigBuilderGenerator' => __DIR__ . '/..' . '/symfony/config/Builder/ConfigBuilderGenerator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ConfigBuilderGeneratorInterface' => __DIR__ . '/..' . '/symfony/config/Builder/ConfigBuilderGeneratorInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\ConfigBuilderInterface' => __DIR__ . '/..' . '/symfony/config/Builder/ConfigBuilderInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\Method' => __DIR__ . '/..' . '/symfony/config/Builder/Method.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Builder\\Property' => __DIR__ . '/..' . '/symfony/config/Builder/Property.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ConfigCache' => __DIR__ . '/..' . '/symfony/config/ConfigCache.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactory.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactoryInterface.php', @@ -25,6 +107,7 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\BooleanNode' => __DIR__ . '/..' . '/symfony/config/Definition/BooleanNode.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\BuilderAwareInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BuilderAwareInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/EnumNodeDefinition.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ExprBuilder.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/FloatNodeDefinition.php', @@ -60,10 +143,9 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => __DIR__ . '/..' . '/symfony/config/Definition/PrototypedArrayNode.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\ScalarNode' => __DIR__ . '/..' . '/symfony/config/Definition/ScalarNode.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Definition\\VariableNode' => __DIR__ . '/..' . '/symfony/config/Definition/VariableNode.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\DependencyInjection\\ConfigCachePass' => __DIR__ . '/..' . '/symfony/config/DependencyInjection/ConfigCachePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderLoadException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLocatorFileNotFoundException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Exception\\LoaderLoadException' => __DIR__ . '/..' . '/symfony/config/Exception/LoaderLoadException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\FileLocator' => __DIR__ . '/..' . '/symfony/config/FileLocator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\FileLocatorInterface' => __DIR__ . '/..' . '/symfony/config/FileLocatorInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\DelegatingLoader' => __DIR__ . '/..' . '/symfony/config/Loader/DelegatingLoader.php', @@ -73,6 +155,7 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\LoaderResolver' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolver.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolverInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Loader\\ParamConfigurator' => __DIR__ . '/..' . '/symfony/config/Loader/ParamConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ResourceCheckerConfigCache' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCache.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCacheFactory.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\ResourceCheckerInterface' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerInterface.php', @@ -83,8 +166,6 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\FileResource' => __DIR__ . '/..' . '/symfony/config/Resource/FileResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\GlobResource' => __DIR__ . '/..' . '/symfony/config/Resource/GlobResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ReflectionMethodHhvmWrapper' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ReflectionParameterHhvmWrapper' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\ResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/ResourceInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceChecker.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceInterface.php', @@ -92,55 +173,69 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => __DIR__ . '/..' . '/symfony/config/Util/Exception/XmlParsingException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Config\\Util\\XmlUtils' => __DIR__ . '/..' . '/symfony/config/Util/XmlUtils.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Alias' => __DIR__ . '/..' . '/symfony/dependency-injection/Alias.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\AbstractArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/AbstractArgument.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ArgumentInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ArgumentInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\BoundArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/BoundArgument.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\IteratorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/IteratorArgument.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ReferenceSetArgumentTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ReferenceSetArgumentTrait.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\RewindableGenerator' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/RewindableGenerator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ServiceClosureArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ServiceClosureArgument.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocator' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ServiceLocator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocatorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ServiceLocatorArgument.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Argument\\TaggedIteratorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/TaggedIteratorArgument.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\AsTaggedItem' => __DIR__ . '/..' . '/symfony/dependency-injection/Attribute/AsTaggedItem.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\Autoconfigure' => __DIR__ . '/..' . '/symfony/dependency-injection/Attribute/Autoconfigure.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\AutoconfigureTag' => __DIR__ . '/..' . '/symfony/dependency-injection/Attribute/AutoconfigureTag.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\TaggedIterator' => __DIR__ . '/..' . '/symfony/dependency-injection/Attribute/TaggedIterator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\TaggedLocator' => __DIR__ . '/..' . '/symfony/dependency-injection/Attribute/TaggedLocator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\Target' => __DIR__ . '/..' . '/symfony/dependency-injection/Attribute/Target.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Attribute\\When' => __DIR__ . '/..' . '/symfony/dependency-injection/Attribute/When.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ChildDefinition' => __DIR__ . '/..' . '/symfony/dependency-injection/ChildDefinition.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AbstractRecursivePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AbstractRecursivePass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AliasDeprecatedPublicServicesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AliasDeprecatedPublicServicesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AnalyzeServiceReferencesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AttributeAutoconfigurationPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AttributeAutoconfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutoAliasServicePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutoAliasServicePass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowireExceptionPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutowireExceptionPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowirePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutowirePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowireRequiredMethodsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\AutowireRequiredPropertiesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutowireRequiredPropertiesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckArgumentsValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckCircularReferencesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckExceptionOnInvalidReferenceBehaviorPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckReferenceValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CheckTypeDeclarationsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckTypeDeclarationsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\Compiler' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/Compiler.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CompilerPassInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\DecoratorServicePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/DecoratorServicePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\DefinitionErrorExceptionPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ExtensionCompilerPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ExtensionCompilerPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\FactoryReturnTypePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\InlineServiceDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\LoggingFormatter' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/LoggingFormatter.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationContainerBuilder' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\PassConfig' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/PassConfig.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceUtil' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterAutoconfigureAttributesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterAutoconfigureAttributesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterEnvVarProcessorsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterReverseContainerPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterReverseContainerPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RegisterServiceSubscribersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RemoveAbstractDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RemovePrivateAliasesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RemoveUnusedDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RepeatablePassInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RepeatablePassInterface.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\RepeatedPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RepeatedPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ReplaceAliasByActualDefinitionPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveBindingsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveBindingsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveChildDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveClassPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveClassPass.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveDefinitionTemplatesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveDecoratorStackPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveDecoratorStackPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveEnvPlaceholdersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveFactoryClassPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveHotPathPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveHotPathPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInstanceofConditionalsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInvalidReferencesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveNamedArgumentsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveNoPreloadPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveNoPreloadPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveParameterPlaceHoldersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolvePrivatesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ResolveReferencesToAliasesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php', @@ -150,7 +245,7 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraph' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphEdge' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphNode' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Config\\AutowireServiceResource' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/AutowireServiceResource.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Compiler\\ValidateEnvPlaceholdersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResource' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/ContainerParametersResource.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResourceChecker' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Container' => __DIR__ . '/..' . '/symfony/dependency-injection/Container.php', @@ -159,13 +254,14 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ContainerBuilder' => __DIR__ . '/..' . '/symfony/dependency-injection/ContainerBuilder.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ContainerInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Definition' => __DIR__ . '/..' . '/symfony/dependency-injection/Definition.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\DefinitionDecorator' => __DIR__ . '/..' . '/symfony/dependency-injection/DefinitionDecorator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\Dumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/Dumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\DumperInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/DumperInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\GraphvizDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/GraphvizDumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/PhpDumper.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\Preloader' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/Preloader.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\XmlDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/XmlDumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Dumper\\YamlDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/YamlDumper.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\EnvVarLoaderInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarLoaderInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\EnvVarProcessor' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarProcessor.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\EnvVarProcessorInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarProcessorInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\AutowiringFailedException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/AutowiringFailedException.php', @@ -174,6 +270,7 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\EnvParameterException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/EnvParameterException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ExceptionInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\InvalidParameterTypeException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/InvalidParameterTypeException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/LogicException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/OutOfBoundsException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Exception\\ParameterCircularReferenceException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php', @@ -196,8 +293,10 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractServiceConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AliasConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ClosureReferenceConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ClosureReferenceConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\DefaultsConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\EnvConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/EnvConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InlineServiceConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InstanceofConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ParametersConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php', @@ -233,23 +332,58 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\XmlFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/XmlFileLoader.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/YamlFileLoader.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Parameter' => __DIR__ . '/..' . '/symfony/dependency-injection/Parameter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ContainerBag.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBagInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\EnvPlaceholderParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\FrozenParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ParameterBag.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBagInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Reference' => __DIR__ . '/..' . '/symfony/dependency-injection/Reference.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ResettableContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ResettableContainerInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ReverseContainer' => __DIR__ . '/..' . '/symfony/dependency-injection/ReverseContainer.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ServiceLocator' => __DIR__ . '/..' . '/symfony/dependency-injection/ServiceLocator.php', - 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ServiceSubscriberInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\TaggedContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/TaggedContainerInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\TypedReference' => __DIR__ . '/..' . '/symfony/dependency-injection/TypedReference.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\Variable' => __DIR__ . '/..' . '/symfony/dependency-injection/Variable.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Compiler' => __DIR__ . '/..' . '/symfony/expression-language/Compiler.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Expression' => __DIR__ . '/..' . '/symfony/expression-language/Expression.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ExpressionFunction' => __DIR__ . '/..' . '/symfony/expression-language/ExpressionFunction.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface' => __DIR__ . '/..' . '/symfony/expression-language/ExpressionFunctionProviderInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage' => __DIR__ . '/..' . '/symfony/expression-language/ExpressionLanguage.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Lexer' => __DIR__ . '/..' . '/symfony/expression-language/Lexer.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ArgumentsNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ArgumentsNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ArrayNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ArrayNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\BinaryNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/BinaryNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ConditionalNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ConditionalNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\ConstantNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ConstantNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\FunctionNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/FunctionNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\GetAttrNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/GetAttrNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\NameNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/NameNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\Node' => __DIR__ . '/..' . '/symfony/expression-language/Node/Node.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Node\\UnaryNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/UnaryNode.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\ParsedExpression' => __DIR__ . '/..' . '/symfony/expression-language/ParsedExpression.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Parser' => __DIR__ . '/..' . '/symfony/expression-language/Parser.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\SerializedParsedExpression' => __DIR__ . '/..' . '/symfony/expression-language/SerializedParsedExpression.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\SyntaxError' => __DIR__ . '/..' . '/symfony/expression-language/SyntaxError.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\Token' => __DIR__ . '/..' . '/symfony/expression-language/Token.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\TokenStream' => __DIR__ . '/..' . '/symfony/expression-language/TokenStream.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/ExceptionInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/FileNotFoundException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\IOException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOExceptionInterface.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/InvalidArgumentException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/RuntimeException.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Filesystem' => __DIR__ . '/..' . '/symfony/filesystem/Filesystem.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\Path' => __DIR__ . '/..' . '/symfony/filesystem/Path.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Exception\\ClassNotFoundException' => __DIR__ . '/..' . '/symfony/var-exporter/Exception/ClassNotFoundException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/var-exporter/Exception/ExceptionInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Exception\\NotInstantiableTypeException' => __DIR__ . '/..' . '/symfony/var-exporter/Exception/NotInstantiableTypeException.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Instantiator' => __DIR__ . '/..' . '/symfony/var-exporter/Instantiator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Exporter' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Exporter.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Hydrator' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Hydrator.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Reference' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Reference.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Registry' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Registry.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\Internal\\Values' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Values.php', + 'Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\VarExporter' => __DIR__ . '/..' . '/symfony/var-exporter/VarExporter.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Escaper' => __DIR__ . '/..' . '/symfony/yaml/Escaper.php', @@ -262,22 +396,34 @@ class ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Tag\\TaggedValue' => __DIR__ . '/..' . '/symfony/yaml/Tag/TaggedValue.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Unescaper' => __DIR__ . '/..' . '/symfony/yaml/Unescaper.php', 'Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\Yaml' => __DIR__ . '/..' . '/symfony/yaml/Yaml.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\CacheInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/CacheInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\CacheTrait' => __DIR__ . '/..' . '/symfony/cache-contracts/CacheTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\CallbackInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/CallbackInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\ItemInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/ItemInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\TagAwareCacheInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/TagAwareCacheInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Attribute\\Required' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/Required.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/SubscribedService.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ResetInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ResetInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceLocatorTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceLocatorTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceProviderInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceProviderInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberInterface.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberTrait.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => __DIR__ . '/..' . '/symfony/service-contracts/Test/ServiceLocatorTest.php', + 'Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\Test\\ServiceLocatorTestCase' => __DIR__ . '/..' . '/symfony/service-contracts/Test/ServiceLocatorTestCase.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php73\\Php73' => __DIR__ . '/..' . '/symfony/polyfill-php73/Php73.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php80\\Php80' => __DIR__ . '/..' . '/symfony/polyfill-php80/Php80.php', 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php80\\PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/PhpToken.php', + 'Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php81\\Php81' => __DIR__ . '/..' . '/symfony/polyfill-php81/Php81.php', 'Wordlift\\Modules\\Common\\UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'Wordlift\\Modules\\Common\\ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', - 'Wordlift\\Modules\\Common\\cweagans\\Composer\\PatchEvent' => __DIR__ . '/..' . '/cweagans/composer-patches/src/PatchEvent.php', - 'Wordlift\\Modules\\Common\\cweagans\\Composer\\PatchEvents' => __DIR__ . '/..' . '/cweagans/composer-patches/src/PatchEvents.php', - 'Wordlift\\Modules\\Common\\cweagans\\Composer\\Patches' => __DIR__ . '/..' . '/cweagans/composer-patches/src/Patches.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->classMap = ComposerStaticInitbd04ee84a816dcd5effd91d716ec9608::$classMap; + $loader->classMap = ComposerStaticInitdfa501c6e5f181f14bf3ca6f0583434e::$classMap; }, null, ClassLoader::class); } diff --git a/src/modules/common/third-party/vendor/cweagans/composer-patches/src/PatchEvent.php b/src/modules/common/third-party/vendor/cweagans/composer-patches/src/PatchEvent.php deleted file mode 100644 index ccc54380dd..0000000000 --- a/src/modules/common/third-party/vendor/cweagans/composer-patches/src/PatchEvent.php +++ /dev/null @@ -1,67 +0,0 @@ -package = $package; - $this->url = $url; - $this->description = $description; - } - /** - * Returns the package that is patched. - * - * @return PackageInterface - */ - public function getPackage() - { - return $this->package; - } - /** - * Returns the url of the patch. - * - * @return string - */ - public function getUrl() - { - return $this->url; - } - /** - * Returns the description of the patch. - * - * @return string - */ - public function getDescription() - { - return $this->description; - } -} diff --git a/src/modules/common/third-party/vendor/cweagans/composer-patches/src/PatchEvents.php b/src/modules/common/third-party/vendor/cweagans/composer-patches/src/PatchEvents.php deleted file mode 100644 index 384f5cdd20..0000000000 --- a/src/modules/common/third-party/vendor/cweagans/composer-patches/src/PatchEvents.php +++ /dev/null @@ -1,27 +0,0 @@ -composer = $composer; - $this->io = $io; - $this->eventDispatcher = $composer->getEventDispatcher(); - $this->executor = new ProcessExecutor($this->io); - $this->patches = array(); - $this->installedPatches = array(); - } - /** - * Returns an array of event names this subscriber wants to listen to. - */ - public static function getSubscribedEvents() - { - return array( - ScriptEvents::PRE_INSTALL_CMD => array('checkPatches'), - ScriptEvents::PRE_UPDATE_CMD => array('checkPatches'), - PackageEvents::PRE_PACKAGE_INSTALL => array('gatherPatches'), - PackageEvents::PRE_PACKAGE_UPDATE => array('gatherPatches'), - // The following is a higher weight for compatibility with - // https://github.com/AydinHassan/magento-core-composer-installer and more generally for compatibility with - // every Composer plugin which deploys downloaded packages to other locations. - // In such cases you want that those plugins deploy patched files so they have to run after - // the "composer-patches" plugin. - // @see: https://github.com/cweagans/composer-patches/pull/153 - PackageEvents::POST_PACKAGE_INSTALL => array('postInstall', 10), - PackageEvents::POST_PACKAGE_UPDATE => array('postInstall', 10), - ); - } - /** - * Before running composer install, - * @param Event $event - */ - public function checkPatches(Event $event) - { - if (!$this->isPatchingEnabled()) { - return; - } - try { - $repositoryManager = $this->composer->getRepositoryManager(); - $localRepository = $repositoryManager->getLocalRepository(); - $installationManager = $this->composer->getInstallationManager(); - $packages = $localRepository->getPackages(); - $extra = $this->composer->getPackage()->getExtra(); - $patches_ignore = isset($extra['patches-ignore']) ? $extra['patches-ignore'] : array(); - $tmp_patches = $this->grabPatches(); - foreach ($packages as $package) { - $extra = $package->getExtra(); - if (isset($extra['patches'])) { - if (isset($patches_ignore[$package->getName()])) { - foreach ($patches_ignore[$package->getName()] as $package_name => $patches) { - if (isset($extra['patches'][$package_name])) { - $extra['patches'][$package_name] = \array_diff($extra['patches'][$package_name], $patches); - } - } - } - $this->installedPatches[$package->getName()] = $extra['patches']; - } - $patches = isset($extra['patches']) ? $extra['patches'] : array(); - $tmp_patches = $this->arrayMergeRecursiveDistinct($tmp_patches, $patches); - } - if ($tmp_patches == \FALSE) { - $this->io->write('No patches supplied.'); - return; - } - // Remove packages for which the patch set has changed. - $promises = array(); - foreach ($packages as $package) { - if (!$package instanceof AliasPackage) { - $package_name = $package->getName(); - $extra = $package->getExtra(); - $has_patches = isset($tmp_patches[$package_name]); - $has_applied_patches = isset($extra['patches_applied']) && \count($extra['patches_applied']) > 0; - if ($has_patches && !$has_applied_patches || !$has_patches && $has_applied_patches || $has_patches && $has_applied_patches && $tmp_patches[$package_name] !== $extra['patches_applied']) { - $uninstallOperation = new UninstallOperation($package, 'Removing package so it can be re-installed and re-patched.'); - $this->io->write('Removing package ' . $package_name . ' so that it can be re-installed and re-patched.'); - $promises[] = $installationManager->uninstall($localRepository, $uninstallOperation); - } - } - } - $promises = \array_filter($promises); - if ($promises) { - $this->composer->getLoop()->wait($promises); - } - } catch (\LogicException $e) { - return; - } - } - /** - * Gather patches from dependencies and store them for later use. - * - * @param PackageEvent $event - */ - public function gatherPatches(PackageEvent $event) - { - // If we've already done this, then don't do it again. - if (isset($this->patches['_patchesGathered'])) { - $this->io->write('Patches already gathered. Skipping', \TRUE, IOInterface::VERBOSE); - return; - } elseif (!$this->isPatchingEnabled()) { - $this->io->write('Patching is disabled. Skipping.', \TRUE, IOInterface::VERBOSE); - return; - } - $this->patches = $this->grabPatches(); - if (empty($this->patches)) { - $this->io->write('No patches supplied.'); - } - $extra = $this->composer->getPackage()->getExtra(); - $patches_ignore = isset($extra['patches-ignore']) ? $extra['patches-ignore'] : array(); - // Now add all the patches from dependencies that will be installed. - $operations = $event->getOperations(); - $this->io->write('Gathering patches for dependencies. This might take a minute.'); - foreach ($operations as $operation) { - if ($operation instanceof InstallOperation || $operation instanceof UpdateOperation) { - $package = $this->getPackageFromOperation($operation); - $extra = $package->getExtra(); - if (isset($extra['patches'])) { - if (isset($patches_ignore[$package->getName()])) { - foreach ($patches_ignore[$package->getName()] as $package_name => $patches) { - if (isset($extra['patches'][$package_name])) { - $extra['patches'][$package_name] = \array_diff($extra['patches'][$package_name], $patches); - } - } - } - $this->patches = $this->arrayMergeRecursiveDistinct($this->patches, $extra['patches']); - } - // Unset installed patches for this package - if (isset($this->installedPatches[$package->getName()])) { - unset($this->installedPatches[$package->getName()]); - } - } - } - // Merge installed patches from dependencies that did not receive an update. - foreach ($this->installedPatches as $patches) { - $this->patches = $this->arrayMergeRecursiveDistinct($this->patches, $patches); - } - // If we're in verbose mode, list the projects we're going to patch. - if ($this->io->isVerbose()) { - foreach ($this->patches as $package => $patches) { - $number = \count($patches); - $this->io->write('Found ' . $number . ' patches for ' . $package . '.'); - } - } - // Make sure we don't gather patches again. Extra keys in $this->patches - // won't hurt anything, so we'll just stash it there. - $this->patches['_patchesGathered'] = \TRUE; - } - /** - * Get the patches from root composer or external file - * @return Patches - * @throws \Exception - */ - public function grabPatches() - { - // First, try to get the patches from the root composer.json. - $extra = $this->composer->getPackage()->getExtra(); - if (isset($extra['patches'])) { - $this->io->write('Gathering patches for root package.'); - $patches = $extra['patches']; - return $patches; - } elseif (isset($extra['patches-file'])) { - $this->io->write('Gathering patches from patch file.'); - $patches = \file_get_contents($extra['patches-file']); - $patches = \json_decode($patches, \TRUE); - $error = \json_last_error(); - if ($error != 0) { - switch ($error) { - case \JSON_ERROR_DEPTH: - $msg = ' - Maximum stack depth exceeded'; - break; - case \JSON_ERROR_STATE_MISMATCH: - $msg = ' - Underflow or the modes mismatch'; - break; - case \JSON_ERROR_CTRL_CHAR: - $msg = ' - Unexpected control character found'; - break; - case \JSON_ERROR_SYNTAX: - $msg = ' - Syntax error, malformed JSON'; - break; - case \JSON_ERROR_UTF8: - $msg = ' - Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - default: - $msg = ' - Unknown error'; - break; - } - throw new \Exception('There was an error in the supplied patches file:' . $msg); - } - if (isset($patches['patches'])) { - $patches = $patches['patches']; - return $patches; - } elseif (!$patches) { - throw new \Exception('There was an error in the supplied patch file'); - } - } else { - return array(); - } - } - /** - * @param PackageEvent $event - * @throws \Exception - */ - public function postInstall(PackageEvent $event) - { - // Check if we should exit in failure. - $extra = $this->composer->getPackage()->getExtra(); - $exitOnFailure = \getenv('COMPOSER_EXIT_ON_PATCH_FAILURE') || !empty($extra['composer-exit-on-patch-failure']); - $skipReporting = \getenv('COMPOSER_PATCHES_SKIP_REPORTING') || !empty($extra['composer-patches-skip-reporting']); - // Get the package object for the current operation. - $operation = $event->getOperation(); - /** @var PackageInterface $package */ - $package = $this->getPackageFromOperation($operation); - $package_name = $package->getName(); - if (!isset($this->patches[$package_name])) { - if ($this->io->isVerbose()) { - $this->io->write('No patches found for ' . $package_name . '.'); - } - return; - } - $this->io->write(' - Applying patches for ' . $package_name . ''); - // Get the install path from the package object. - $manager = $event->getComposer()->getInstallationManager(); - $install_path = $manager->getInstaller($package->getType())->getInstallPath($package); - // Set up a downloader. - $downloader = new RemoteFilesystem($this->io, $this->composer->getConfig()); - // Track applied patches in the package info in installed.json - $localRepository = $this->composer->getRepositoryManager()->getLocalRepository(); - $localPackage = $localRepository->findPackage($package_name, $package->getVersion()); - $extra = $localPackage->getExtra(); - $extra['patches_applied'] = array(); - foreach ($this->patches[$package_name] as $description => $url) { - $this->io->write(' ' . $url . ' (' . $description . ')'); - try { - $this->eventDispatcher->dispatch(NULL, new PatchEvent(PatchEvents::PRE_PATCH_APPLY, $package, $url, $description)); - $this->getAndApplyPatch($downloader, $install_path, $url, $package); - $this->eventDispatcher->dispatch(NULL, new PatchEvent(PatchEvents::POST_PATCH_APPLY, $package, $url, $description)); - $extra['patches_applied'][$description] = $url; - } catch (\Exception $e) { - $this->io->write(' Could not apply patch! Skipping. The error was: ' . $e->getMessage() . ''); - if ($exitOnFailure) { - throw new \Exception("Cannot apply patch {$description} ({$url})!"); - } - } - } - $localPackage->setExtra($extra); - $this->io->write(''); - if (\true !== $skipReporting) { - $this->writePatchReport($this->patches[$package_name], $install_path); - } - } - /** - * Get a Package object from an OperationInterface object. - * - * @param OperationInterface $operation - * @return PackageInterface - * @throws \Exception - */ - protected function getPackageFromOperation(OperationInterface $operation) - { - if ($operation instanceof InstallOperation) { - $package = $operation->getPackage(); - } elseif ($operation instanceof UpdateOperation) { - $package = $operation->getTargetPackage(); - } else { - throw new \Exception('Unknown operation: ' . \get_class($operation)); - } - return $package; - } - /** - * Apply a patch on code in the specified directory. - * - * @param RemoteFilesystem $downloader - * @param $install_path - * @param $patch_url - * @param PackageInterface $package - * @throws \Exception - */ - protected function getAndApplyPatch(RemoteFilesystem $downloader, $install_path, $patch_url, PackageInterface $package) - { - // Local patch file. - if (\file_exists($patch_url)) { - $filename = \realpath($patch_url); - } else { - // Generate random (but not cryptographically so) filename. - $filename = \uniqid(\sys_get_temp_dir() . '/') . ".patch"; - // Download file from remote filesystem to this location. - $hostname = \parse_url($patch_url, \PHP_URL_HOST); - try { - $downloader->copy($hostname, $patch_url, $filename, \false); - } catch (\Exception $e) { - // In case of an exception, retry once as the download might - // have failed due to intermittent network issues. - $downloader->copy($hostname, $patch_url, $filename, \false); - } - } - // The order here is intentional. p1 is most likely to apply with git apply. - // p0 is next likely. p2 is extremely unlikely, but for some special cases, - // it might be useful. p4 is useful for Magento 2 patches - $patch_levels = array('-p1', '-p0', '-p2', '-p4'); - // Check for specified patch level for this package. - $extra = $this->composer->getPackage()->getExtra(); - if (!empty($extra['patchLevel'][$package->getName()])) { - $patch_levels = array($extra['patchLevel'][$package->getName()]); - } - // Attempt to apply with git apply. - $patched = $this->applyPatchWithGit($install_path, $patch_levels, $filename); - // In some rare cases, git will fail to apply a patch, fallback to using - // the 'patch' command. - if (!$patched) { - foreach ($patch_levels as $patch_level) { - // --no-backup-if-mismatch here is a hack that fixes some - // differences between how patch works on windows and unix. - if ($patched = $this->executeCommand("patch %s --no-backup-if-mismatch -d %s < %s", $patch_level, $install_path, $filename)) { - break; - } - } - } - // Clean up the temporary patch file. - if (isset($hostname)) { - \unlink($filename); - } - // If the patch *still* isn't applied, then give up and throw an Exception. - // Otherwise, let the user know it worked. - if (!$patched) { - throw new \Exception("Cannot apply patch {$patch_url}"); - } - } - /** - * Checks if the root package enables patching. - * - * @return bool - * Whether patching is enabled. Defaults to TRUE. - */ - protected function isPatchingEnabled() - { - $extra = $this->composer->getPackage()->getExtra(); - if (empty($extra['patches']) && empty($extra['patches-ignore']) && !isset($extra['patches-file'])) { - // The root package has no patches of its own, so only allow patching if - // it has specifically opted in. - return isset($extra['enable-patching']) ? $extra['enable-patching'] : \FALSE; - } else { - return \TRUE; - } - } - /** - * Writes a patch report to the target directory. - * - * @param array $patches - * @param string $directory - */ - protected function writePatchReport($patches, $directory) - { - $output = "This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches)\n"; - $output .= "Patches applied to this directory:\n\n"; - foreach ($patches as $description => $url) { - $output .= $description . "\n"; - $output .= 'Source: ' . $url . "\n\n\n"; - } - \file_put_contents($directory . "/PATCHES.txt", $output); - } - /** - * Executes a shell command with escaping. - * - * @param string $cmd - * @return bool - */ - protected function executeCommand($cmd) - { - // Shell-escape all arguments except the command. - $args = \func_get_args(); - foreach ($args as $index => $arg) { - if ($index !== 0) { - $args[$index] = \escapeshellarg($arg); - } - } - // And replace the arguments. - $command = \call_user_func_array('sprintf', $args); - $output = ''; - if ($this->io->isVerbose()) { - $this->io->write('' . $command . ''); - $io = $this->io; - $output = function ($type, $data) use($io) { - if ($type == Process::ERR) { - $io->write('' . $data . ''); - } else { - $io->write('' . $data . ''); - } - }; - } - return $this->executor->execute($command, $output) == 0; - } - /** - * Recursively merge arrays without changing data types of values. - * - * Does not change the data types of the values in the arrays. Matching keys' - * values in the second array overwrite those in the first array, as is the - * case with array_merge. - * - * @param array $array1 - * The first array. - * @param array $array2 - * The second array. - * @return array - * The merged array. - * - * @see http://php.net/manual/en/function.array-merge-recursive.php#92195 - */ - protected function arrayMergeRecursiveDistinct(array $array1, array $array2) - { - $merged = $array1; - foreach ($array2 as $key => &$value) { - if (\is_array($value) && isset($merged[$key]) && \is_array($merged[$key])) { - $merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value); - } else { - $merged[$key] = $value; - } - } - return $merged; - } - /** - * Attempts to apply a patch with git apply. - * - * @param $install_path - * @param $patch_levels - * @param $filename - * - * @return bool - * TRUE if patch was applied, FALSE otherwise. - */ - protected function applyPatchWithGit($install_path, $patch_levels, $filename) - { - // Do not use git apply unless the install path is itself a git repo - // @see https://stackoverflow.com/a/27283285 - if (!\is_dir($install_path . '/.git')) { - return \FALSE; - } - $patched = \FALSE; - foreach ($patch_levels as $patch_level) { - if ($this->io->isVerbose()) { - $comment = 'Testing ability to patch with git apply.'; - $comment .= ' This command may produce errors that can be safely ignored.'; - $this->io->write('' . $comment . ''); - } - $checked = $this->executeCommand('git -C %s apply --check -v %s %s', $install_path, $patch_level, $filename); - $output = $this->executor->getErrorOutput(); - if (\substr($output, 0, 7) == 'Skipped') { - // Git will indicate success but silently skip patches in some scenarios. - // - // @see https://github.com/cweagans/composer-patches/pull/165 - $checked = \FALSE; - } - if ($checked) { - // Apply the first successful style. - $patched = $this->executeCommand('git -C %s apply %s %s', $install_path, $patch_level, $filename); - break; - } - } - return $patched; - } - /** - * Indicates if a package has been patched. - * - * @param \Composer\Package\PackageInterface $package - * The package to check. - * - * @return bool - * TRUE if the package has been patched. - */ - public static function isPackagePatched(PackageInterface $package) - { - return \array_key_exists('patches_applied', $package->getExtra()); - } - /** - * {@inheritDoc} - */ - public function deactivate(Composer $composer, IOInterface $io) - { - } - /** - * {@inheritDoc} - */ - public function uninstall(Composer $composer, IOInterface $io) - { - } -} diff --git a/src/modules/common/third-party/vendor/mcaskill/composer-exclude-files/composer.json b/src/modules/common/third-party/vendor/mcaskill/composer-exclude-files/composer.json new file mode 100644 index 0000000000..fbcb10e309 --- /dev/null +++ b/src/modules/common/third-party/vendor/mcaskill/composer-exclude-files/composer.json @@ -0,0 +1,50 @@ +{ + "type": "composer-plugin", + "name": "mcaskill\/composer-exclude-files", + "description": "Exclude files from autoload_files.php", + "license": "MIT", + "authors": [ + { + "name": "Chauncey McAskill", + "email": "chauncey@mcaskill.ca" + } + ], + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0", + "composer-plugin-api": "^1.0 || ^2.0" + }, + "require-dev": { + "composer\/composer": "^1.0 || ^2.0", + "symfony\/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\McAskill\\Composer\\": "src\/" + } + }, + "autoload-dev": { + "psr-4": { + "Wordlift\\Modules\\Common\\Tests\\McAskill\\Composer\\": "tests\/" + } + }, + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + }, + "class": "McAskill\\Composer\\ExcludeFilePlugin" + }, + "config": { + "platform": { + "php": "5.3.9" + }, + "platform-check": false + }, + "scripts": { + "test": "simple-phpunit" + }, + "scripts-descriptions": { + "test": "Run all tests" + }, + "minimum-stability": "dev", + "prefer-stable": true +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/mcaskill/composer-exclude-files/src/ExcludeFilePlugin.php b/src/modules/common/third-party/vendor/mcaskill/composer-exclude-files/src/ExcludeFilePlugin.php index 7afc5bd9b2..ed9c1023f7 100644 --- a/src/modules/common/third-party/vendor/mcaskill/composer-exclude-files/src/ExcludeFilePlugin.php +++ b/src/modules/common/third-party/vendor/mcaskill/composer-exclude-files/src/ExcludeFilePlugin.php @@ -106,7 +106,7 @@ public function parseAutoloads() */ private function filterAutoloads(array $packageMap, PackageInterface $mainPackage, array $excludedFiles) { - $excludedFiles = \array_flip($excludedFiles); + $excludedFiles = array_flip($excludedFiles); $type = self::INCLUDE_FILES_PROPERTY; foreach ($packageMap as $item) { list($package, $installPath) = $item; @@ -116,19 +116,19 @@ private function filterAutoloads(array $packageMap, PackageInterface $mainPackag } $autoload = $package->getAutoload(); // Skip misconfigured packages - if (!isset($autoload[$type]) || !\is_array($autoload[$type])) { + if (!isset($autoload[$type]) || !is_array($autoload[$type])) { continue; } if (null !== $package->getTargetDir()) { - $installPath = \substr($installPath, 0, -\strlen('/' . $package->getTargetDir())); + $installPath = substr($installPath, 0, -strlen('/' . $package->getTargetDir())); } foreach ($autoload[$type] as $key => $path) { - if ($package->getTargetDir() && !\is_readable($installPath . '/' . $path)) { + if ($package->getTargetDir() && !is_readable($installPath . '/' . $path)) { // add target-dir from file paths that don't have it $path = $package->getTargetDir() . '/' . $path; } $resolvedPath = $installPath . '/' . $path; - $resolvedPath = \strtr($resolvedPath, '\\', '/'); + $resolvedPath = strtr($resolvedPath, '\\', '/'); if (isset($excludedFiles[$resolvedPath])) { unset($autoload[$type][$key]); } @@ -146,7 +146,7 @@ private function getExcludedFiles(PackageInterface $package) { $type = self::EXCLUDE_FILES_PROPERTY; $extra = $package->getExtra(); - if (isset($extra[$type]) && \is_array($extra[$type])) { + if (isset($extra[$type]) && is_array($extra[$type])) { return $extra[$type]; } return array(); @@ -164,9 +164,9 @@ private function parseExcludedFiles(array $paths) } $filesystem = new Filesystem(); $config = $this->composer->getConfig(); - $vendorPath = $filesystem->normalizePath(\realpath(\realpath($config->get('vendor-dir')))); + $vendorPath = $filesystem->normalizePath(realpath(realpath($config->get('vendor-dir')))); foreach ($paths as &$path) { - $path = \preg_replace('{/+}', '/', \trim(\strtr($path, '\\', '/'), '/')); + $path = preg_replace('{/+}', '/', trim(strtr($path, '\\', '/'), '/')); $path = $vendorPath . '/' . $path; } return $paths; diff --git a/src/modules/common/third-party/vendor/psr/cache/composer.json b/src/modules/common/third-party/vendor/psr/cache/composer.json new file mode 100644 index 0000000000..1c3aec4272 --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/cache/composer.json @@ -0,0 +1,29 @@ +{ + "name": "psr\/cache", + "description": "Common interface for caching libraries", + "keywords": [ + "psr", + "psr-6", + "cache" + ], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http:\/\/www.php-fig.org\/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Psr\\Cache\\": "src\/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/psr/cache/src/CacheException.php b/src/modules/common/third-party/vendor/psr/cache/src/CacheException.php new file mode 100644 index 0000000000..2610143664 --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/cache/src/CacheException.php @@ -0,0 +1,10 @@ +=7.4.0" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Psr\\Container\\": "src\/" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/psr/container/src/ContainerExceptionInterface.php b/src/modules/common/third-party/vendor/psr/container/src/ContainerExceptionInterface.php index bce4c220f6..bba722a3a3 100644 --- a/src/modules/common/third-party/vendor/psr/container/src/ContainerExceptionInterface.php +++ b/src/modules/common/third-party/vendor/psr/container/src/ContainerExceptionInterface.php @@ -1,13 +1,11 @@ log(LogLevel::EMERGENCY, $message, $context); + } + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + /** + * Normal but significant events. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + /** + * Detailed debug information. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/src/modules/common/third-party/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/src/modules/common/third-party/vendor/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 0000000000..4bd08d346b --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/src/modules/common/third-party/vendor/psr/log/Psr/Log/LoggerInterface.php b/src/modules/common/third-party/vendor/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 0000000000..afbae88acd --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,117 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/src/modules/common/third-party/vendor/psr/log/Psr/Log/NullLogger.php b/src/modules/common/third-party/vendor/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 0000000000..30d0d7ef29 --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,30 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/src/modules/common/third-party/vendor/psr/log/Psr/Log/Test/DummyTest.php b/src/modules/common/third-party/vendor/psr/log/Psr/Log/Test/DummyTest.php new file mode 100644 index 0000000000..0a37b655d9 --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/log/Psr/Log/Test/DummyTest.php @@ -0,0 +1,18 @@ + ". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + abstract public function getLogs(); + public function testImplements() + { + $this->assertInstanceOf('Wordlift\Modules\Common\Psr\Log\LoggerInterface', $this->getLogger()); + } + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + $expected = array($level . ' message of level ' . $level . ' with context: Bob', $level . ' message of level ' . $level . ' with context: Bob'); + $this->assertEquals($expected, $this->getLogs()); + } + public function provideLevelsAndMessages() + { + return array(LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}')); + } + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('Wordlift\Modules\Common\Psr\Log\Test\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('Wordlift\Modules\Common\Psr\Log\Test\DummyTest', array('__toString')); + } + $dummy->expects($this->once())->method('__toString')->will($this->returnValue('DUMMY')); + $this->getLogger()->warning($dummy); + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); + } + public function testContextCanContainAnything() + { + $closed = fopen('php://memory', 'r'); + fclose($closed); + $context = array('bool' => \true, 'null' => null, 'string' => 'Foo', 'int' => 0, 'float' => 0.5, 'nested' => array('with object' => new DummyTest()), 'object' => new \DateTime(), 'resource' => fopen('php://memory', 'r'), 'closed' => $closed); + $this->getLogger()->warning('Crazy context data', $context); + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + $expected = array('warning Random message', 'critical Uncaught Exception!'); + $this->assertEquals($expected, $this->getLogs()); + } +} diff --git a/src/modules/common/third-party/vendor/psr/log/Psr/Log/Test/TestLogger.php b/src/modules/common/third-party/vendor/psr/log/Psr/Log/Test/TestLogger.php new file mode 100644 index 0000000000..0db2128c7a --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/log/Psr/Log/Test/TestLogger.php @@ -0,0 +1,132 @@ + $level, 'message' => $message, 'context' => $context]; + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + public function hasRecords($level) + { + return isset($this->recordsByLevel[$level]); + } + public function hasRecord($record, $level) + { + if (is_string($record)) { + $record = ['message' => $record]; + } + return $this->hasRecordThatPasses(function ($rec) use ($record) { + if ($rec['message'] !== $record['message']) { + return \false; + } + if (isset($record['context']) && $rec['context'] !== $record['context']) { + return \false; + } + return \true; + }, $level); + } + public function hasRecordThatContains($message, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($message) { + return strpos($rec['message'], $message) !== \false; + }, $level); + } + public function hasRecordThatMatches($regex, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($regex) { + return preg_match($regex, $rec['message']) > 0; + }, $level); + } + public function hasRecordThatPasses(callable $predicate, $level) + { + if (!isset($this->recordsByLevel[$level])) { + return \false; + } + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if (call_user_func($predicate, $rec, $i)) { + return \true; + } + } + return \false; + } + public function __call($method, $args) + { + if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . (('Records' !== $matches[3]) ? 'Record' : '') . $matches[3]; + $level = strtolower($matches[2]); + if (method_exists($this, $genericMethod)) { + $args[] = $level; + return call_user_func_array([$this, $genericMethod], $args); + } + } + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); + } + public function reset() + { + $this->records = []; + $this->recordsByLevel = []; + } +} diff --git a/src/modules/common/third-party/vendor/psr/log/composer.json b/src/modules/common/third-party/vendor/psr/log/composer.json new file mode 100644 index 0000000000..8c7f17a4a7 --- /dev/null +++ b/src/modules/common/third-party/vendor/psr/log/composer.json @@ -0,0 +1,30 @@ +{ + "name": "psr\/log", + "description": "Common interface for logging libraries", + "keywords": [ + "psr", + "psr-3", + "log" + ], + "homepage": "https:\/\/github.com\/php-fig\/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https:\/\/www.php-fig.org\/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Psr\\Log\\": "Psr\/Log\/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/scoper-autoload.php b/src/modules/common/third-party/vendor/scoper-autoload.php index 52fedd3e89..5fd661a34e 100644 --- a/src/modules/common/third-party/vendor/scoper-autoload.php +++ b/src/modules/common/third-party/vendor/scoper-autoload.php @@ -2,6 +2,113 @@ // scoper-autoload.php @generated by PhpScoper -$loader = require_once __DIR__.'/autoload.php'; +$loader = (static function () { + // Backup the autoloaded Composer files + $existingComposerAutoloadFiles = isset($GLOBALS['__composer_autoload_files']) ? $GLOBALS['__composer_autoload_files'] : []; + + $loader = require_once __DIR__.'/autoload.php'; + // Ensure InstalledVersions is available + $installedVersionsPath = __DIR__.'/composer/InstalledVersions.php'; + if (file_exists($installedVersionsPath)) require_once $installedVersionsPath; + + // Restore the backup and ensure the excluded files are properly marked as loaded + $GLOBALS['__composer_autoload_files'] = \array_merge( + $existingComposerAutoloadFiles, + \array_fill_keys([], true) + ); + + return $loader; +})(); + +// Class aliases. For more information see: +// https://github.com/humbug/php-scoper/blob/master/docs/further-reading.md#class-aliases +if (!function_exists('humbug_phpscoper_expose_class')) { + function humbug_phpscoper_expose_class($exposed, $prefixed) { + if (!class_exists($exposed, false) && !interface_exists($exposed, false) && !trait_exists($exposed, false)) { + spl_autoload_call($prefixed); + } + } +} +humbug_phpscoper_expose_class('JsonException', 'Wordlift\Modules\Common\JsonException'); +humbug_phpscoper_expose_class('PhpToken', 'Wordlift\Modules\Common\PhpToken'); +humbug_phpscoper_expose_class('ValueError', 'Wordlift\Modules\Common\ValueError'); +humbug_phpscoper_expose_class('Attribute', 'Wordlift\Modules\Common\Attribute'); +humbug_phpscoper_expose_class('UnhandledMatchError', 'Wordlift\Modules\Common\UnhandledMatchError'); +humbug_phpscoper_expose_class('Stringable', 'Wordlift\Modules\Common\Stringable'); +humbug_phpscoper_expose_class('ReturnTypeWillChange', 'Wordlift\Modules\Common\ReturnTypeWillChange'); +humbug_phpscoper_expose_class('CURLStringFile', 'Wordlift\Modules\Common\CURLStringFile'); + +// Function aliases. For more information see: +// https://github.com/humbug/php-scoper/blob/master/docs/further-reading.md#function-aliases +if (!function_exists('array_is_list')) { function array_is_list() { return \Wordlift\Modules\Common\array_is_list(...func_get_args()); } } +if (!function_exists('array_key_first')) { function array_key_first() { return \Wordlift\Modules\Common\array_key_first(...func_get_args()); } } +if (!function_exists('array_key_last')) { function array_key_last() { return \Wordlift\Modules\Common\array_key_last(...func_get_args()); } } +if (!function_exists('ctype_alnum')) { function ctype_alnum() { return \Wordlift\Modules\Common\ctype_alnum(...func_get_args()); } } +if (!function_exists('ctype_alpha')) { function ctype_alpha() { return \Wordlift\Modules\Common\ctype_alpha(...func_get_args()); } } +if (!function_exists('ctype_cntrl')) { function ctype_cntrl() { return \Wordlift\Modules\Common\ctype_cntrl(...func_get_args()); } } +if (!function_exists('ctype_digit')) { function ctype_digit() { return \Wordlift\Modules\Common\ctype_digit(...func_get_args()); } } +if (!function_exists('ctype_graph')) { function ctype_graph() { return \Wordlift\Modules\Common\ctype_graph(...func_get_args()); } } +if (!function_exists('ctype_lower')) { function ctype_lower() { return \Wordlift\Modules\Common\ctype_lower(...func_get_args()); } } +if (!function_exists('ctype_print')) { function ctype_print() { return \Wordlift\Modules\Common\ctype_print(...func_get_args()); } } +if (!function_exists('ctype_punct')) { function ctype_punct() { return \Wordlift\Modules\Common\ctype_punct(...func_get_args()); } } +if (!function_exists('ctype_space')) { function ctype_space() { return \Wordlift\Modules\Common\ctype_space(...func_get_args()); } } +if (!function_exists('ctype_upper')) { function ctype_upper() { return \Wordlift\Modules\Common\ctype_upper(...func_get_args()); } } +if (!function_exists('ctype_xdigit')) { function ctype_xdigit() { return \Wordlift\Modules\Common\ctype_xdigit(...func_get_args()); } } +if (!function_exists('enum_exists')) { function enum_exists() { return \Wordlift\Modules\Common\enum_exists(...func_get_args()); } } +if (!function_exists('fdiv')) { function fdiv() { return \Wordlift\Modules\Common\fdiv(...func_get_args()); } } +if (!function_exists('get_debug_type')) { function get_debug_type() { return \Wordlift\Modules\Common\get_debug_type(...func_get_args()); } } +if (!function_exists('get_resource_id')) { function get_resource_id() { return \Wordlift\Modules\Common\get_resource_id(...func_get_args()); } } +if (!function_exists('hrtime')) { function hrtime() { return \Wordlift\Modules\Common\hrtime(...func_get_args()); } } +if (!function_exists('includeIfExists')) { function includeIfExists() { return \Wordlift\Modules\Common\includeIfExists(...func_get_args()); } } +if (!function_exists('is_countable')) { function is_countable() { return \Wordlift\Modules\Common\is_countable(...func_get_args()); } } +if (!function_exists('mb_check_encoding')) { function mb_check_encoding() { return \Wordlift\Modules\Common\mb_check_encoding(...func_get_args()); } } +if (!function_exists('mb_chr')) { function mb_chr() { return \Wordlift\Modules\Common\mb_chr(...func_get_args()); } } +if (!function_exists('mb_convert_case')) { function mb_convert_case() { return \Wordlift\Modules\Common\mb_convert_case(...func_get_args()); } } +if (!function_exists('mb_convert_encoding')) { function mb_convert_encoding() { return \Wordlift\Modules\Common\mb_convert_encoding(...func_get_args()); } } +if (!function_exists('mb_convert_variables')) { function mb_convert_variables() { return \Wordlift\Modules\Common\mb_convert_variables(...func_get_args()); } } +if (!function_exists('mb_decode_mimeheader')) { function mb_decode_mimeheader() { return \Wordlift\Modules\Common\mb_decode_mimeheader(...func_get_args()); } } +if (!function_exists('mb_decode_numericentity')) { function mb_decode_numericentity() { return \Wordlift\Modules\Common\mb_decode_numericentity(...func_get_args()); } } +if (!function_exists('mb_detect_encoding')) { function mb_detect_encoding() { return \Wordlift\Modules\Common\mb_detect_encoding(...func_get_args()); } } +if (!function_exists('mb_detect_order')) { function mb_detect_order() { return \Wordlift\Modules\Common\mb_detect_order(...func_get_args()); } } +if (!function_exists('mb_encode_mimeheader')) { function mb_encode_mimeheader() { return \Wordlift\Modules\Common\mb_encode_mimeheader(...func_get_args()); } } +if (!function_exists('mb_encode_numericentity')) { function mb_encode_numericentity() { return \Wordlift\Modules\Common\mb_encode_numericentity(...func_get_args()); } } +if (!function_exists('mb_encoding_aliases')) { function mb_encoding_aliases() { return \Wordlift\Modules\Common\mb_encoding_aliases(...func_get_args()); } } +if (!function_exists('mb_get_info')) { function mb_get_info() { return \Wordlift\Modules\Common\mb_get_info(...func_get_args()); } } +if (!function_exists('mb_http_input')) { function mb_http_input() { return \Wordlift\Modules\Common\mb_http_input(...func_get_args()); } } +if (!function_exists('mb_http_output')) { function mb_http_output() { return \Wordlift\Modules\Common\mb_http_output(...func_get_args()); } } +if (!function_exists('mb_internal_encoding')) { function mb_internal_encoding() { return \Wordlift\Modules\Common\mb_internal_encoding(...func_get_args()); } } +if (!function_exists('mb_language')) { function mb_language() { return \Wordlift\Modules\Common\mb_language(...func_get_args()); } } +if (!function_exists('mb_lcfirst')) { function mb_lcfirst() { return \Wordlift\Modules\Common\mb_lcfirst(...func_get_args()); } } +if (!function_exists('mb_list_encodings')) { function mb_list_encodings() { return \Wordlift\Modules\Common\mb_list_encodings(...func_get_args()); } } +if (!function_exists('mb_ltrim')) { function mb_ltrim() { return \Wordlift\Modules\Common\mb_ltrim(...func_get_args()); } } +if (!function_exists('mb_ord')) { function mb_ord() { return \Wordlift\Modules\Common\mb_ord(...func_get_args()); } } +if (!function_exists('mb_output_handler')) { function mb_output_handler() { return \Wordlift\Modules\Common\mb_output_handler(...func_get_args()); } } +if (!function_exists('mb_parse_str')) { function mb_parse_str() { return \Wordlift\Modules\Common\mb_parse_str(...func_get_args()); } } +if (!function_exists('mb_rtrim')) { function mb_rtrim() { return \Wordlift\Modules\Common\mb_rtrim(...func_get_args()); } } +if (!function_exists('mb_scrub')) { function mb_scrub() { return \Wordlift\Modules\Common\mb_scrub(...func_get_args()); } } +if (!function_exists('mb_str_pad')) { function mb_str_pad() { return \Wordlift\Modules\Common\mb_str_pad(...func_get_args()); } } +if (!function_exists('mb_str_split')) { function mb_str_split() { return \Wordlift\Modules\Common\mb_str_split(...func_get_args()); } } +if (!function_exists('mb_stripos')) { function mb_stripos() { return \Wordlift\Modules\Common\mb_stripos(...func_get_args()); } } +if (!function_exists('mb_stristr')) { function mb_stristr() { return \Wordlift\Modules\Common\mb_stristr(...func_get_args()); } } +if (!function_exists('mb_strlen')) { function mb_strlen() { return \Wordlift\Modules\Common\mb_strlen(...func_get_args()); } } +if (!function_exists('mb_strpos')) { function mb_strpos() { return \Wordlift\Modules\Common\mb_strpos(...func_get_args()); } } +if (!function_exists('mb_strrchr')) { function mb_strrchr() { return \Wordlift\Modules\Common\mb_strrchr(...func_get_args()); } } +if (!function_exists('mb_strrichr')) { function mb_strrichr() { return \Wordlift\Modules\Common\mb_strrichr(...func_get_args()); } } +if (!function_exists('mb_strripos')) { function mb_strripos() { return \Wordlift\Modules\Common\mb_strripos(...func_get_args()); } } +if (!function_exists('mb_strrpos')) { function mb_strrpos() { return \Wordlift\Modules\Common\mb_strrpos(...func_get_args()); } } +if (!function_exists('mb_strstr')) { function mb_strstr() { return \Wordlift\Modules\Common\mb_strstr(...func_get_args()); } } +if (!function_exists('mb_strtolower')) { function mb_strtolower() { return \Wordlift\Modules\Common\mb_strtolower(...func_get_args()); } } +if (!function_exists('mb_strtoupper')) { function mb_strtoupper() { return \Wordlift\Modules\Common\mb_strtoupper(...func_get_args()); } } +if (!function_exists('mb_strwidth')) { function mb_strwidth() { return \Wordlift\Modules\Common\mb_strwidth(...func_get_args()); } } +if (!function_exists('mb_substitute_character')) { function mb_substitute_character() { return \Wordlift\Modules\Common\mb_substitute_character(...func_get_args()); } } +if (!function_exists('mb_substr')) { function mb_substr() { return \Wordlift\Modules\Common\mb_substr(...func_get_args()); } } +if (!function_exists('mb_substr_count')) { function mb_substr_count() { return \Wordlift\Modules\Common\mb_substr_count(...func_get_args()); } } +if (!function_exists('mb_trim')) { function mb_trim() { return \Wordlift\Modules\Common\mb_trim(...func_get_args()); } } +if (!function_exists('mb_ucfirst')) { function mb_ucfirst() { return \Wordlift\Modules\Common\mb_ucfirst(...func_get_args()); } } +if (!function_exists('preg_last_error_msg')) { function preg_last_error_msg() { return \Wordlift\Modules\Common\preg_last_error_msg(...func_get_args()); } } +if (!function_exists('str_contains')) { function str_contains() { return \Wordlift\Modules\Common\str_contains(...func_get_args()); } } +if (!function_exists('str_ends_with')) { function str_ends_with() { return \Wordlift\Modules\Common\str_ends_with(...func_get_args()); } } +if (!function_exists('str_starts_with')) { function str_starts_with() { return \Wordlift\Modules\Common\str_starts_with(...func_get_args()); } } +if (!function_exists('trigger_deprecation')) { function trigger_deprecation() { return \Wordlift\Modules\Common\trigger_deprecation(...func_get_args()); } } return $loader; diff --git a/src/modules/common/third-party/vendor/symfony/cache-contracts/CacheInterface.php b/src/modules/common/third-party/vendor/symfony/cache-contracts/CacheInterface.php new file mode 100644 index 0000000000..392dee9adc --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache-contracts/CacheInterface.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Cache; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Cache\InvalidArgumentException; +/** + * Covers most simple to advanced caching needs. + * + * @author Nicolas Grekas + */ +interface CacheInterface +{ + /** + * Fetches a value from the pool or computes it if not found. + * + * On cache misses, a callback is called that should return the missing value. + * This callback is given a PSR-6 CacheItemInterface instance corresponding to the + * requested key, that could be used e.g. for expiration control. It could also + * be an ItemInterface instance when its additional features are needed. + * + * @param string $key The key of the item to retrieve from the cache + * @param callable|CallbackInterface $callback Should return the computed value for the given key/item + * @param float|null $beta A float that, as it grows, controls the likeliness of triggering + * early expiration. 0 disables it, INF forces immediate expiration. + * The default (or providing null) is implementation dependent but should + * typically be 1.0, which should provide optimal stampede protection. + * See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration + * @param array &$metadata The metadata of the cached item {@see ItemInterface::getMetadata()} + * + * @return mixed + * + * @throws InvalidArgumentException When $key is not valid or when $beta is negative + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null); + /** + * Removes an item from the pool. + * + * @param string $key The key to delete + * + * @return bool True if the item was successfully removed, false if there was any error + * + * @throws InvalidArgumentException When $key is not valid + */ + public function delete(string $key): bool; +} diff --git a/src/modules/common/third-party/vendor/symfony/cache-contracts/CacheTrait.php b/src/modules/common/third-party/vendor/symfony/cache-contracts/CacheTrait.php new file mode 100644 index 0000000000..ff7be0a42e --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache-contracts/CacheTrait.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 Wordlift\Modules\Common\Symfony\Contracts\Cache; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Psr\Cache\InvalidArgumentException; +use Wordlift\Modules\Common\Psr\Log\LoggerInterface; +// Help opcache.preload discover always-needed symbols +class_exists(InvalidArgumentException::class); +/** + * An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes. + * + * @author Nicolas Grekas + */ +trait CacheTrait +{ + /** + * {@inheritdoc} + * + * @return mixed + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + return $this->doGet($this, $key, $callback, $beta, $metadata); + } + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + return $this->deleteItem($key); + } + private function doGet(CacheItemPoolInterface $pool, string $key, callable $callback, ?float $beta, ?array &$metadata = null, ?LoggerInterface $logger = null) + { + if (0 > $beta = $beta ?? 1.0) { + throw new class(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)) extends \InvalidArgumentException implements InvalidArgumentException + { + }; + } + $item = $pool->getItem($key); + $recompute = !$item->isHit() || \INF === $beta; + $metadata = ($item instanceof ItemInterface) ? $item->getMetadata() : []; + if (!$recompute && $metadata) { + $expiry = $metadata[ItemInterface::METADATA_EXPIRY] ?? \false; + $ctime = $metadata[ItemInterface::METADATA_CTIME] ?? \false; + if ($recompute = $ctime && $expiry && $expiry <= ($now = microtime(\true)) - $ctime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) { + // force applying defaultLifetime to expiry + $item->expiresAt(null); + $logger && $logger->info('Item "{key}" elected for early recomputation {delta}s before its expiration', ['key' => $key, 'delta' => sprintf('%.1f', $expiry - $now)]); + } + } + if ($recompute) { + $save = \true; + $item->set($callback($item, $save)); + if ($save) { + $pool->save($item); + } + } + return $item->get(); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache-contracts/CallbackInterface.php b/src/modules/common/third-party/vendor/symfony/cache-contracts/CallbackInterface.php new file mode 100644 index 0000000000..6d46f040c2 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache-contracts/CallbackInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Cache; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +/** + * Computes and returns the cached value of an item. + * + * @author Nicolas Grekas + */ +interface CallbackInterface +{ + /** + * @param CacheItemInterface|ItemInterface $item The item to compute the value for + * @param bool &$save Should be set to false when the value should not be saved in the pool + * + * @return mixed The computed value for the passed item + */ + public function __invoke(CacheItemInterface $item, bool &$save); +} diff --git a/src/modules/common/third-party/vendor/symfony/cache-contracts/ItemInterface.php b/src/modules/common/third-party/vendor/symfony/cache-contracts/ItemInterface.php new file mode 100644 index 0000000000..c6c0f4f920 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache-contracts/ItemInterface.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Cache; + +use Wordlift\Modules\Common\Psr\Cache\CacheException; +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Cache\InvalidArgumentException; +/** + * Augments PSR-6's CacheItemInterface with support for tags and metadata. + * + * @author Nicolas Grekas + */ +interface ItemInterface extends CacheItemInterface +{ + /** + * References the Unix timestamp stating when the item will expire. + */ + public const METADATA_EXPIRY = 'expiry'; + /** + * References the time the item took to be created, in milliseconds. + */ + public const METADATA_CTIME = 'ctime'; + /** + * References the list of tags that were assigned to the item, as string[]. + */ + public const METADATA_TAGS = 'tags'; + /** + * Reserved characters that cannot be used in a key or tag. + */ + public const RESERVED_CHARACTERS = '{}()/\@:'; + /** + * Adds a tag to a cache item. + * + * Tags are strings that follow the same validation rules as keys. + * + * @param string|string[] $tags A tag or array of tags + * + * @return $this + * + * @throws InvalidArgumentException When $tag is not valid + * @throws CacheException When the item comes from a pool that is not tag-aware + */ + public function tag($tags): self; + /** + * Returns a list of metadata info that were saved alongside with the cached value. + * + * See ItemInterface::METADATA_* consts for keys potentially found in the returned array. + */ + public function getMetadata(): array; +} diff --git a/src/modules/common/third-party/vendor/symfony/cache-contracts/TagAwareCacheInterface.php b/src/modules/common/third-party/vendor/symfony/cache-contracts/TagAwareCacheInterface.php new file mode 100644 index 0000000000..3932e29a69 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache-contracts/TagAwareCacheInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Cache; + +use Wordlift\Modules\Common\Psr\Cache\InvalidArgumentException; +/** + * Allows invalidating cached items using tags. + * + * @author Nicolas Grekas + */ +interface TagAwareCacheInterface extends CacheInterface +{ + /** + * Invalidates cached items using tags. + * + * When implemented on a PSR-6 pool, invalidation should not apply + * to deferred items. Instead, they should be committed as usual. + * This allows replacing old tagged values by new ones without + * race conditions. + * + * @param string[] $tags An array of tags to invalidate + * + * @return bool True on success + * + * @throws InvalidArgumentException When $tags is not valid + */ + public function invalidateTags(array $tags); +} diff --git a/src/modules/common/third-party/vendor/symfony/cache-contracts/composer.json b/src/modules/common/third-party/vendor/symfony/cache-contracts/composer.json new file mode 100644 index 0000000000..68212e3581 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache-contracts/composer.json @@ -0,0 +1,47 @@ +{ + "name": "symfony\/cache-contracts", + "type": "library", + "description": "Generic abstractions related to caching", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "psr\/cache": "^1.0|^2.0|^3.0" + }, + "suggest": { + "symfony\/cache-implementation": "" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Contracts\\Cache\\": "" + } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony\/contracts", + "url": "https:\/\/github.com\/symfony\/contracts" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/AbstractAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/AbstractAdapter.php new file mode 100644 index 0000000000..3be6fbbb63 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/AbstractAdapter.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Log\LoggerAwareInterface; +use Wordlift\Modules\Common\Psr\Log\LoggerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\AbstractAdapterTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ContractsTrait; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +/** + * @author Nicolas Grekas + */ +abstract class AbstractAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface +{ + use AbstractAdapterTrait; + use ContractsTrait; + /** + * @internal + */ + protected const NS_SEPARATOR = ':'; + private static $apcuSupported; + private static $phpFilesSupported; + protected function __construct(string $namespace = '', int $defaultLifetime = 0) + { + $this->namespace = ('' === $namespace) ? '' : (CacheItem::validateKey($namespace) . static::NS_SEPARATOR); + $this->defaultLifetime = $defaultLifetime; + 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)); + } + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $v = $value; + $item->isHit = $isHit; + // Detect wrapped values that encode for their expiry and creation duration + // For compactness, these values are packed in the key of an array using + // magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9d" === $k[0] && "\x00" === $k[5] && "_" === $k[9]) { + $item->value = $v[$k]; + $v = unpack('Ve/Nc', substr($k, 1, -1)); + $item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET; + $item->metadata[CacheItem::METADATA_CTIME] = $v['c']; + } + return $item; + }, null, CacheItem::class); + self::$mergeByLifetime ?? self::$mergeByLifetime = \Closure::bind(static function ($deferred, $namespace, &$expiredIds, $getId, $defaultLifetime) { + $byLifetime = []; + $now = microtime(\true); + $expiredIds = []; + foreach ($deferred as $key => $item) { + $key = (string) $key; + if (null === $item->expiry) { + $ttl = (0 < $defaultLifetime) ? $defaultLifetime : 0; + } elseif (!$item->expiry) { + $ttl = 0; + } elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) { + $expiredIds[] = $getId($key); + continue; + } + if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) { + unset($metadata[CacheItem::METADATA_TAGS]); + } + // For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators + $byLifetime[$ttl][$getId($key)] = $metadata ? ["\x9d" . pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]) . "_" => $item->value] : $item->value; + } + return $byLifetime; + }, null, CacheItem::class); + } + /** + * Returns the best possible adapter that your runtime supports. + * + * Using ApcuAdapter makes system caches compatible with read-only filesystems. + * + * @return AdapterInterface + */ + public static function createSystemCache(string $namespace, int $defaultLifetime, string $version, string $directory, ?LoggerInterface $logger = null) + { + $opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, \true); + if (null !== $logger) { + $opcache->setLogger($logger); + } + if (!self::$apcuSupported = self::$apcuSupported ?? ApcuAdapter::isSupported()) { + return $opcache; + } + if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true) && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) { + return $opcache; + } + $apcu = new ApcuAdapter($namespace, intdiv($defaultLifetime, 5), $version); + if (null !== $logger) { + $apcu->setLogger($logger); + } + return new ChainAdapter([$apcu, $opcache]); + } + public static function createConnection(string $dsn, array $options = []) + { + if (str_starts_with($dsn, 'redis:') || str_starts_with($dsn, 'rediss:')) { + return RedisAdapter::createConnection($dsn, $options); + } + if (str_starts_with($dsn, 'memcached:')) { + return MemcachedAdapter::createConnection($dsn, $options); + } + if (0 === strpos($dsn, 'couchbase:')) { + if (CouchbaseBucketAdapter::isSupported()) { + return CouchbaseBucketAdapter::createConnection($dsn, $options); + } + return CouchbaseCollectionAdapter::createConnection($dsn, $options); + } + throw new InvalidArgumentException('Unsupported DSN: it does not start with "redis[s]:", "memcached:" nor "couchbase:".'); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + $ok = \true; + $byLifetime = (self::$mergeByLifetime)($this->deferred, $this->namespace, $expiredIds, \Closure::fromCallable([$this, 'getId']), $this->defaultLifetime); + $retry = $this->deferred = []; + if ($expiredIds) { + try { + $this->doDelete($expiredIds); + } catch (\Exception $e) { + $ok = \false; + CacheItem::log($this->logger, 'Failed to delete expired items: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + } + foreach ($byLifetime as $lifetime => $values) { + try { + $e = $this->doSave($values, $lifetime); + } catch (\Exception $e) { + } + if (\true === $e || [] === $e) { + continue; + } + if (\is_array($e) || 1 === \count($values)) { + foreach (\is_array($e) ? $e : array_keys($values) as $id) { + $ok = \false; + $v = $values[$id]; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, ($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => ($e instanceof \Exception) ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } else { + foreach ($values as $id => $v) { + $retry[$lifetime][] = $id; + } + } + } + // When bulk-save failed, retry each item individually + foreach ($retry as $lifetime => $ids) { + foreach ($ids as $id) { + try { + $v = $byLifetime[$lifetime][$id]; + $e = $this->doSave([$id => $v], $lifetime); + } catch (\Exception $e) { + } + if (\true === $e || [] === $e) { + continue; + } + $ok = \false; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, ($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => ($e instanceof \Exception) ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } + return $ok; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php new file mode 100644 index 0000000000..27ef8097be --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Log\LoggerAwareInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\AbstractAdapterTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ContractsTrait; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\TagAwareCacheInterface; +/** + * Abstract for native TagAware adapters. + * + * To keep info on tags, the tags are both serialized as part of cache value and provided as tag ids + * to Adapters on operations when needed for storage to doSave(), doDelete() & doInvalidate(). + * + * @author Nicolas Grekas + * @author André Rømcke + * + * @internal + */ +abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, LoggerAwareInterface, ResettableInterface +{ + use AbstractAdapterTrait; + use ContractsTrait; + private const TAGS_PREFIX = "\x00tags\x00"; + protected function __construct(string $namespace = '', int $defaultLifetime = 0) + { + $this->namespace = ('' === $namespace) ? '' : (CacheItem::validateKey($namespace) . ':'); + $this->defaultLifetime = $defaultLifetime; + 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)); + } + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->isTaggable = \true; + // If structure does not match what we expect return item as is (no value and not a hit) + if (!\is_array($value) || !\array_key_exists('value', $value)) { + return $item; + } + $item->isHit = $isHit; + // Extract value, tags and meta data from the cache value + $item->value = $value['value']; + $item->metadata[CacheItem::METADATA_TAGS] = $value['tags'] ?? []; + if (isset($value['meta'])) { + // For compactness these values are packed, & expiry is offset to reduce size + $v = unpack('Ve/Nc', $value['meta']); + $item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET; + $item->metadata[CacheItem::METADATA_CTIME] = $v['c']; + } + return $item; + }, null, CacheItem::class); + self::$mergeByLifetime ?? self::$mergeByLifetime = \Closure::bind(static function ($deferred, &$expiredIds, $getId, $tagPrefix, $defaultLifetime) { + $byLifetime = []; + $now = microtime(\true); + $expiredIds = []; + foreach ($deferred as $key => $item) { + $key = (string) $key; + if (null === $item->expiry) { + $ttl = (0 < $defaultLifetime) ? $defaultLifetime : 0; + } elseif (!$item->expiry) { + $ttl = 0; + } elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) { + $expiredIds[] = $getId($key); + continue; + } + // Store Value and Tags on the cache value + if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) { + $value = ['value' => $item->value, 'tags' => $metadata[CacheItem::METADATA_TAGS]]; + unset($metadata[CacheItem::METADATA_TAGS]); + } else { + $value = ['value' => $item->value, 'tags' => []]; + } + if ($metadata) { + // For compactness, expiry and creation duration are packed, using magic numbers as separators + $value['meta'] = pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]); + } + // Extract tag changes, these should be removed from values in doSave() + $value['tag-operations'] = ['add' => [], 'remove' => []]; + $oldTags = $item->metadata[CacheItem::METADATA_TAGS] ?? []; + foreach (array_diff($value['tags'], $oldTags) as $addedTag) { + $value['tag-operations']['add'][] = $getId($tagPrefix . $addedTag); + } + foreach (array_diff($oldTags, $value['tags']) as $removedTag) { + $value['tag-operations']['remove'][] = $getId($tagPrefix . $removedTag); + } + $byLifetime[$ttl][$getId($key)] = $value; + $item->metadata = $item->newMetadata; + } + return $byLifetime; + }, null, CacheItem::class); + } + /** + * Persists several cache items immediately. + * + * @param array $values The values to cache, indexed by their cache identifier + * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning + * @param array[] $addTagData Hash where key is tag id, and array value is list of cache id's to add to tag + * @param array[] $removeTagData Hash where key is tag id, and array value is list of cache id's to remove to tag + * + * @return array The identifiers that failed to be cached or a boolean stating if caching succeeded or not + */ + abstract protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array; + /** + * Removes multiple items from the pool and their corresponding tags. + * + * @param array $ids An array of identifiers that should be removed from the pool + * + * @return bool + */ + abstract protected function doDelete(array $ids); + /** + * Removes relations between tags and deleted items. + * + * @param array $tagData Array of tag => key identifiers that should be removed from the pool + */ + abstract protected function doDeleteTagRelations(array $tagData): bool; + /** + * Invalidates cached items using tags. + * + * @param string[] $tagIds An array of tags to invalidate, key is tag and value is tag id + */ + abstract protected function doInvalidate(array $tagIds): bool; + /** + * Delete items and yields the tags they were bound to. + */ + protected function doDeleteYieldTags(array $ids): iterable + { + foreach ($this->doFetch($ids) as $id => $value) { + yield $id => (\is_array($value) && \is_array($value['tags'] ?? null)) ? $value['tags'] : []; + } + $this->doDelete($ids); + } + /** + * {@inheritdoc} + */ + public function commit(): bool + { + $ok = \true; + $byLifetime = (self::$mergeByLifetime)($this->deferred, $expiredIds, \Closure::fromCallable([$this, 'getId']), self::TAGS_PREFIX, $this->defaultLifetime); + $retry = $this->deferred = []; + if ($expiredIds) { + // Tags are not cleaned up in this case, however that is done on invalidateTags(). + try { + $this->doDelete($expiredIds); + } catch (\Exception $e) { + $ok = \false; + CacheItem::log($this->logger, 'Failed to delete expired items: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + } + foreach ($byLifetime as $lifetime => $values) { + try { + $values = $this->extractTagData($values, $addTagData, $removeTagData); + $e = $this->doSave($values, $lifetime, $addTagData, $removeTagData); + } catch (\Exception $e) { + } + if (\true === $e || [] === $e) { + continue; + } + if (\is_array($e) || 1 === \count($values)) { + foreach (\is_array($e) ? $e : array_keys($values) as $id) { + $ok = \false; + $v = $values[$id]; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, ($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => ($e instanceof \Exception) ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } else { + foreach ($values as $id => $v) { + $retry[$lifetime][] = $id; + } + } + } + // When bulk-save failed, retry each item individually + foreach ($retry as $lifetime => $ids) { + foreach ($ids as $id) { + try { + $v = $byLifetime[$lifetime][$id]; + $values = $this->extractTagData([$id => $v], $addTagData, $removeTagData); + $e = $this->doSave($values, $lifetime, $addTagData, $removeTagData); + } catch (\Exception $e) { + } + if (\true === $e || [] === $e) { + continue; + } + $ok = \false; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, ($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => ($e instanceof \Exception) ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } + return $ok; + } + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + if (!$keys) { + return \true; + } + $ok = \true; + $ids = []; + $tagData = []; + foreach ($keys as $key) { + $ids[$key] = $this->getId($key); + unset($this->deferred[$key]); + } + try { + foreach ($this->doDeleteYieldTags(array_values($ids)) as $id => $tags) { + foreach ($tags as $tag) { + $tagData[$this->getId(self::TAGS_PREFIX . $tag)][] = $id; + } + } + } catch (\Exception $e) { + $ok = \false; + } + try { + if ((!$tagData || $this->doDeleteTagRelations($tagData)) && $ok) { + return \true; + } + } catch (\Exception $e) { + } + // When bulk-delete failed, retry each item individually + foreach ($ids as $key => $id) { + try { + $e = null; + if ($this->doDelete([$id])) { + continue; + } + } catch (\Exception $e) { + } + $message = 'Failed to delete key "{key}"' . (($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $ok = \false; + } + return $ok; + } + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) + { + if (empty($tags)) { + return \false; + } + $tagIds = []; + foreach (array_unique($tags) as $tag) { + $tagIds[] = $this->getId(self::TAGS_PREFIX . $tag); + } + try { + if ($this->doInvalidate($tagIds)) { + return \true; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to invalidate tags: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + return \false; + } + /** + * Extracts tags operation data from $values set in mergeByLifetime, and returns values without it. + */ + private function extractTagData(array $values, ?array &$addTagData, ?array &$removeTagData): array + { + $addTagData = $removeTagData = []; + foreach ($values as $id => $value) { + foreach ($value['tag-operations']['add'] as $tag => $tagId) { + $addTagData[$tagId][] = $id; + } + foreach ($value['tag-operations']['remove'] as $tag => $tagId) { + $removeTagData[$tagId][] = $id; + } + unset($values[$id]['tag-operations']); + } + return $values; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/AdapterInterface.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/AdapterInterface.php new file mode 100644 index 0000000000..b23f30c321 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/AdapterInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +// Help opcache.preload discover always-needed symbols +class_exists(CacheItem::class); +/** + * Interface for adapters managing instances of Symfony's CacheItem. + * + * @author Kévin Dunglas + */ +interface AdapterInterface extends CacheItemPoolInterface +{ + /** + * {@inheritdoc} + * + * @return CacheItem + */ + public function getItem($key); + /** + * {@inheritdoc} + * + * @return \Traversable + */ + public function getItems(array $keys = []); + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = ''); +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/ApcuAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ApcuAdapter.php new file mode 100644 index 0000000000..3f27c9b320 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ApcuAdapter.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +/** + * @author Nicolas Grekas + */ +class ApcuAdapter extends AbstractAdapter +{ + private $marshaller; + /** + * @throws CacheException if APCu is not enabled + */ + public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $version = null, ?MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('APCu is not enabled.'); + } + if ('cli' === \PHP_SAPI) { + ini_set('apc.use_request_time', 0); + } + parent::__construct($namespace, $defaultLifetime); + if (null !== $version) { + CacheItem::validateKey($version); + if (!apcu_exists($version . '@' . $namespace)) { + $this->doClear($namespace); + apcu_add($version . '@' . $namespace, null); + } + } + $this->marshaller = $marshaller; + } + public static function isSupported() + { + return \function_exists('apcu_fetch') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN); + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__ . '::handleUnserializeCallback'); + try { + $values = []; + $ids = array_flip($ids); + foreach (apcu_fetch(array_keys($ids), $ok) ?: [] as $k => $v) { + if (!isset($ids[$k])) { + // work around https://github.com/krakjoe/apcu/issues/247 + $k = key($ids); + } + unset($ids[$k]); + if (null !== $v || $ok) { + $values[$k] = (null !== $this->marshaller) ? $this->marshaller->unmarshall($v) : $v; + } + } + return $values; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id) + { + return apcu_exists($id); + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + return (isset($namespace[0]) && class_exists(\APCUIterator::class, \false) && ('cli' !== \PHP_SAPI || filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) ? apcu_delete(new \APCUIterator(sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY)) : apcu_clear_cache(); + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + foreach ($ids as $id) { + apcu_delete($id); + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + if (null !== $this->marshaller && !$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + try { + if (\false === $failures = apcu_store($values, null, $lifetime)) { + $failures = $values; + } + return array_keys($failures); + } catch (\Throwable $e) { + if (1 === \count($values)) { + // Workaround https://github.com/krakjoe/apcu/issues/170 + apcu_delete(array_key_first($values)); + } + throw $e; + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/ArrayAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ArrayAdapter.php new file mode 100644 index 0000000000..4a76359190 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ArrayAdapter.php @@ -0,0 +1,346 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Log\LoggerAwareInterface; +use Wordlift\Modules\Common\Psr\Log\LoggerAwareTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +/** + * An in-memory cache storage. + * + * Acts as a least-recently-used (LRU) storage when configured with a maximum number of items. + * + * @author Nicolas Grekas + */ +class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface +{ + use LoggerAwareTrait; + private $storeSerialized; + private $values = []; + private $expiries = []; + private $defaultLifetime; + private $maxLifetime; + private $maxItems; + private static $createCacheItem; + /** + * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise + */ + public function __construct(int $defaultLifetime = 0, bool $storeSerialized = \true, float $maxLifetime = 0, int $maxItems = 0) + { + if (0 > $maxLifetime) { + throw new InvalidArgumentException(sprintf('Argument $maxLifetime must be positive, %F passed.', $maxLifetime)); + } + if (0 > $maxItems) { + throw new InvalidArgumentException(sprintf('Argument $maxItems must be a positive integer, %d passed.', $maxItems)); + } + $this->defaultLifetime = $defaultLifetime; + $this->storeSerialized = $storeSerialized; + $this->maxLifetime = $maxLifetime; + $this->maxItems = $maxItems; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + return $item; + }, null, CacheItem::class); + } + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + $item = $this->getItem($key); + $metadata = $item->getMetadata(); + // ArrayAdapter works in memory, we don't care about stampede protection + if (\INF === $beta || !$item->isHit()) { + $save = \true; + $item->set($callback($item, $save)); + if ($save) { + $this->save($item); + } + } + return $item->get(); + } + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + return $this->deleteItem($key); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(\true)) { + if ($this->maxItems) { + // Move the item last in the storage + $value = $this->values[$key]; + unset($this->values[$key]); + $this->values[$key] = $value; + } + return \true; + } + \assert('' !== CacheItem::validateKey($key)); + return isset($this->expiries[$key]) && !$this->deleteItem($key); + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + if (!$isHit = $this->hasItem($key)) { + $value = null; + if (!$this->maxItems) { + // Track misses in non-LRU mode only + $this->values[$key] = null; + } + } else { + $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key]; + } + return (self::$createCacheItem)($key, $value, $isHit); + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + \assert(self::validateKeys($keys)); + return $this->generateItems($keys, microtime(\true), self::$createCacheItem); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + \assert('' !== CacheItem::validateKey($key)); + unset($this->values[$key], $this->expiries[$key]); + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + foreach ($keys as $key) { + $this->deleteItem($key); + } + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return \false; + } + $item = (array) $item; + $key = $item["\x00*\x00key"]; + $value = $item["\x00*\x00value"]; + $expiry = $item["\x00*\x00expiry"]; + $now = microtime(\true); + if (null !== $expiry) { + if (!$expiry) { + $expiry = \PHP_INT_MAX; + } elseif ($expiry <= $now) { + $this->deleteItem($key); + return \true; + } + } + if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) { + return \false; + } + if (null === $expiry && 0 < $this->defaultLifetime) { + $expiry = $this->defaultLifetime; + $expiry = $now + (($expiry > ($this->maxLifetime ?: $expiry)) ? $this->maxLifetime : $expiry); + } elseif ($this->maxLifetime && (null === $expiry || $expiry > $now + $this->maxLifetime)) { + $expiry = $now + $this->maxLifetime; + } + if ($this->maxItems) { + unset($this->values[$key]); + // Iterate items and vacuum expired ones while we are at it + foreach ($this->values as $k => $v) { + if ($this->expiries[$k] > $now && \count($this->values) < $this->maxItems) { + break; + } + unset($this->values[$k], $this->expiries[$k]); + } + } + $this->values[$key] = $value; + $this->expiries[$key] = $expiry ?? \PHP_INT_MAX; + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->save($item); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + if ('' !== $prefix) { + $now = microtime(\true); + foreach ($this->values as $key => $value) { + if (!isset($this->expiries[$key]) || $this->expiries[$key] <= $now || 0 === strpos($key, $prefix)) { + unset($this->values[$key], $this->expiries[$key]); + } + } + if ($this->values) { + return \true; + } + } + $this->values = $this->expiries = []; + return \true; + } + /** + * Returns all cached values, with cache miss as null. + * + * @return array + */ + public function getValues() + { + if (!$this->storeSerialized) { + return $this->values; + } + $values = $this->values; + foreach ($values as $k => $v) { + if (null === $v || 'N;' === $v) { + continue; + } + if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) { + $values[$k] = serialize($v); + } + } + return $values; + } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->clear(); + } + private function generateItems(array $keys, float $now, \Closure $f): \Generator + { + foreach ($keys as $i => $key) { + if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) { + $value = null; + if (!$this->maxItems) { + // Track misses in non-LRU mode only + $this->values[$key] = null; + } + } else { + if ($this->maxItems) { + // Move the item last in the storage + $value = $this->values[$key]; + unset($this->values[$key]); + $this->values[$key] = $value; + } + $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key]; + } + unset($keys[$i]); + yield $key => $f($key, $value, $isHit); + } + foreach ($keys as $key) { + yield $key => $f($key, null, \false); + } + } + private function freeze($value, string $key) + { + if (null === $value) { + return 'N;'; + } + if (\is_string($value)) { + // Serialize strings if they could be confused with serialized objects or arrays + if ('N;' === $value || isset($value[2]) && ':' === $value[1]) { + return serialize($value); + } + } elseif (!\is_scalar($value)) { + try { + $serialized = serialize($value); + } catch (\Exception $e) { + unset($this->values[$key]); + $type = get_debug_type($value); + $message = sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage()); + CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + return; + } + // Keep value serialized if it contains any objects or any internal references + if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) { + return $serialized; + } + } + return $value; + } + private function unfreeze(string $key, bool &$isHit) + { + if ('N;' === $value = $this->values[$key]) { + return null; + } + if (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + $value = unserialize($value); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to unserialize key "{key}": ' . $e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $value = \false; + } + if (\false === $value) { + $value = null; + $isHit = \false; + if (!$this->maxItems) { + $this->values[$key] = null; + } + } + } + return $value; + } + private function validateKeys(array $keys): bool + { + foreach ($keys as $key) { + if (!\is_string($key) || !isset($this->expiries[$key])) { + CacheItem::validateKey($key); + } + } + return \true; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/ChainAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ChainAdapter.php new file mode 100644 index 0000000000..2e3cead96b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ChainAdapter.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ContractsTrait; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ResetInterface; +/** + * Chains several adapters together. + * + * Cached items are fetched from the first adapter having them in its data store. + * They are saved and deleted in all adapters at once. + * + * @author Kévin Dunglas + */ +class ChainAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + use ContractsTrait; + private $adapters = []; + private $adapterCount; + private $defaultLifetime; + private static $syncItem; + /** + * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items + * @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones + */ + public function __construct(array $adapters, int $defaultLifetime = 0) + { + if (!$adapters) { + throw new InvalidArgumentException('At least one adapter must be specified.'); + } + foreach ($adapters as $adapter) { + if (!$adapter instanceof CacheItemPoolInterface) { + throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', get_debug_type($adapter), CacheItemPoolInterface::class)); + } + if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true) && $adapter instanceof ApcuAdapter && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) { + continue; + // skip putting APCu in the chain when the backend is disabled + } + if ($adapter instanceof AdapterInterface) { + $this->adapters[] = $adapter; + } else { + $this->adapters[] = new ProxyAdapter($adapter); + } + } + $this->adapterCount = \count($this->adapters); + $this->defaultLifetime = $defaultLifetime; + self::$syncItem ?? self::$syncItem = \Closure::bind(static function ($sourceItem, $item, $defaultLifetime, $sourceMetadata = null) { + $sourceItem->isTaggable = \false; + $sourceMetadata = $sourceMetadata ?? $sourceItem->metadata; + unset($sourceMetadata[CacheItem::METADATA_TAGS]); + $item->value = $sourceItem->value; + $item->isHit = $sourceItem->isHit; + $item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata; + if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) { + $item->expiresAt(\DateTime::createFromFormat('U.u', sprintf('%.6F', $item->metadata[CacheItem::METADATA_EXPIRY]))); + } elseif (0 < $defaultLifetime) { + $item->expiresAfter($defaultLifetime); + } + return $item; + }, null, CacheItem::class); + } + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + $doSave = \true; + $callback = static function (CacheItem $item, bool &$save) use ($callback, &$doSave) { + $value = $callback($item, $save); + $doSave = $save; + return $value; + }; + $lastItem = null; + $i = 0; + $wrap = function (?CacheItem $item = null, bool &$save = \true) use ($key, $callback, $beta, &$wrap, &$i, &$doSave, &$lastItem, &$metadata) { + $adapter = $this->adapters[$i]; + if (isset($this->adapters[++$i])) { + $callback = $wrap; + $beta = (\INF === $beta) ? \INF : 0; + } + if ($adapter instanceof CacheInterface) { + $value = $adapter->get($key, $callback, $beta, $metadata); + } else { + $value = $this->doGet($adapter, $key, $callback, $beta, $metadata); + } + if (null !== $item) { + (self::$syncItem)($lastItem = $lastItem ?? $item, $item, $this->defaultLifetime, $metadata); + } + $save = $doSave; + return $value; + }; + return $wrap(); + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $syncItem = self::$syncItem; + $misses = []; + foreach ($this->adapters as $i => $adapter) { + $item = $adapter->getItem($key); + if ($item->isHit()) { + while (0 <= --$i) { + $this->adapters[$i]->save($syncItem($item, $misses[$i], $this->defaultLifetime)); + } + return $item; + } + $misses[$i] = $item; + } + return $item; + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + return $this->generateItems($this->adapters[0]->getItems($keys), 0); + } + private function generateItems(iterable $items, int $adapterIndex): \Generator + { + $missing = []; + $misses = []; + $nextAdapterIndex = $adapterIndex + 1; + $nextAdapter = $this->adapters[$nextAdapterIndex] ?? null; + foreach ($items as $k => $item) { + if (!$nextAdapter || $item->isHit()) { + yield $k => $item; + } else { + $missing[] = $k; + $misses[$k] = $item; + } + } + if ($missing) { + $syncItem = self::$syncItem; + $adapter = $this->adapters[$adapterIndex]; + $items = $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex); + foreach ($items as $k => $item) { + if ($item->isHit()) { + $adapter->save($syncItem($item, $misses[$k], $this->defaultLifetime)); + } + yield $k => $item; + } + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + foreach ($this->adapters as $adapter) { + if ($adapter->hasItem($key)) { + return \true; + } + } + return \false; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + $cleared = \true; + $i = $this->adapterCount; + while ($i--) { + if ($this->adapters[$i] instanceof AdapterInterface) { + $cleared = $this->adapters[$i]->clear($prefix) && $cleared; + } else { + $cleared = $this->adapters[$i]->clear() && $cleared; + } + } + return $cleared; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + $deleted = \true; + $i = $this->adapterCount; + while ($i--) { + $deleted = $this->adapters[$i]->deleteItem($key) && $deleted; + } + return $deleted; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + $deleted = \true; + $i = $this->adapterCount; + while ($i--) { + $deleted = $this->adapters[$i]->deleteItems($keys) && $deleted; + } + return $deleted; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + $saved = \true; + $i = $this->adapterCount; + while ($i--) { + $saved = $this->adapters[$i]->save($item) && $saved; + } + return $saved; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + $saved = \true; + $i = $this->adapterCount; + while ($i--) { + $saved = $this->adapters[$i]->saveDeferred($item) && $saved; + } + return $saved; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + $committed = \true; + $i = $this->adapterCount; + while ($i--) { + $committed = $this->adapters[$i]->commit() && $committed; + } + return $committed; + } + /** + * {@inheritdoc} + */ + public function prune() + { + $pruned = \true; + foreach ($this->adapters as $adapter) { + if ($adapter instanceof PruneableInterface) { + $pruned = $adapter->prune() && $pruned; + } + } + return $pruned; + } + /** + * {@inheritdoc} + */ + public function reset() + { + foreach ($this->adapters as $adapter) { + if ($adapter instanceof ResetInterface) { + $adapter->reset(); + } + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php new file mode 100644 index 0000000000..76b878cc62 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +/** + * @author Antonio Jose Cerezo Aranda + */ +class CouchbaseBucketAdapter extends AbstractAdapter +{ + private const THIRTY_DAYS_IN_SECONDS = 2592000; + private const MAX_KEY_LENGTH = 250; + private const KEY_NOT_FOUND = 13; + private const VALID_DSN_OPTIONS = ['operationTimeout', 'configTimeout', 'configNodeTimeout', 'n1qlTimeout', 'httpTimeout', 'configDelay', 'htconfigIdleTimeout', 'durabilityInterval', 'durabilityTimeout']; + private $bucket; + private $marshaller; + public function __construct(\Wordlift\Modules\Common\CouchbaseBucket $bucket, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.'); + } + $this->maxIdLength = static::MAX_KEY_LENGTH; + $this->bucket = $bucket; + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + /** + * @param array|string $servers + */ + public static function createConnection($servers, array $options = []): \Wordlift\Modules\Common\CouchbaseBucket + { + if (\is_string($servers)) { + $servers = [$servers]; + } elseif (!\is_array($servers)) { + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be array or string, "%s" given.', __METHOD__, get_debug_type($servers))); + } + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.'); + } + set_error_handler(function ($type, $msg, $file, $line) { + throw new \ErrorException($msg, 0, $type, $file, $line); + }); + $dsnPattern = '/^(?couchbase(?:s)?)\:\/\/(?:(?[^\:]+)\:(?[^\@]{6,})@)?' . '(?[^\:]+(?:\:\d+)?)(?:\/(?[^\?]+))(?:\?(?.*))?$/i'; + $newServers = []; + $protocol = 'couchbase'; + try { + $options = self::initOptions($options); + $username = $options['username']; + $password = $options['password']; + foreach ($servers as $dsn) { + if (0 !== strpos($dsn, 'couchbase:')) { + throw new InvalidArgumentException('Invalid Couchbase DSN: it does not start with "couchbase:".'); + } + preg_match($dsnPattern, $dsn, $matches); + $username = $matches['username'] ?: $username; + $password = $matches['password'] ?: $password; + $protocol = $matches['protocol'] ?: $protocol; + if (isset($matches['options'])) { + $optionsInDsn = self::getOptions($matches['options']); + foreach ($optionsInDsn as $parameter => $value) { + $options[$parameter] = $value; + } + } + $newServers[] = $matches['host']; + } + $connectionString = $protocol . '://' . implode(',', $newServers); + $client = new \Wordlift\Modules\Common\CouchbaseCluster($connectionString); + $client->authenticateAs($username, $password); + $bucket = $client->openBucket($matches['bucketName']); + unset($options['username'], $options['password']); + foreach ($options as $option => $value) { + if (!empty($value)) { + $bucket->{$option} = $value; + } + } + return $bucket; + } finally { + restore_error_handler(); + } + } + public static function isSupported(): bool + { + return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '2.6.0', '>=') && version_compare(phpversion('couchbase'), '3.0', '<'); + } + private static function getOptions(string $options): array + { + $results = []; + $optionsInArray = explode('&', $options); + foreach ($optionsInArray as $option) { + [$key, $value] = explode('=', $option); + if (\in_array($key, static::VALID_DSN_OPTIONS, \true)) { + $results[$key] = $value; + } + } + return $results; + } + private static function initOptions(array $options): array + { + $options['username'] = $options['username'] ?? ''; + $options['password'] = $options['password'] ?? ''; + $options['operationTimeout'] = $options['operationTimeout'] ?? 0; + $options['configTimeout'] = $options['configTimeout'] ?? 0; + $options['configNodeTimeout'] = $options['configNodeTimeout'] ?? 0; + $options['n1qlTimeout'] = $options['n1qlTimeout'] ?? 0; + $options['httpTimeout'] = $options['httpTimeout'] ?? 0; + $options['configDelay'] = $options['configDelay'] ?? 0; + $options['htconfigIdleTimeout'] = $options['htconfigIdleTimeout'] ?? 0; + $options['durabilityInterval'] = $options['durabilityInterval'] ?? 0; + $options['durabilityTimeout'] = $options['durabilityTimeout'] ?? 0; + return $options; + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $resultsCouchbase = $this->bucket->get($ids); + $results = []; + foreach ($resultsCouchbase as $key => $value) { + if (null !== $value->error) { + continue; + } + $results[$key] = $this->marshaller->unmarshall($value->value); + } + return $results; + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + return \false !== $this->bucket->get($id); + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + if ('' === $namespace) { + $this->bucket->manager()->flush(); + return \true; + } + return \false; + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $results = $this->bucket->remove(array_values($ids)); + foreach ($results as $key => $result) { + if (null !== $result->error && static::KEY_NOT_FOUND !== $result->error->getCode()) { + continue; + } + unset($results[$key]); + } + return 0 === \count($results); + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + $lifetime = $this->normalizeExpiry($lifetime); + $ko = []; + foreach ($values as $key => $value) { + $result = $this->bucket->upsert($key, $value, ['expiry' => $lifetime]); + if (null !== $result->error) { + $ko[$key] = $result; + } + } + return ([] === $ko) ? \true : $ko; + } + private function normalizeExpiry(int $expiry): int + { + if ($expiry && $expiry > static::THIRTY_DAYS_IN_SECONDS) { + $expiry += time(); + } + return $expiry; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php new file mode 100644 index 0000000000..df050bd577 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Couchbase\Bucket; +use Couchbase\Cluster; +use Couchbase\ClusterOptions; +use Couchbase\Collection; +use Couchbase\DocumentNotFoundException; +use Couchbase\UpsertOptions; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +/** + * @author Antonio Jose Cerezo Aranda + */ +class CouchbaseCollectionAdapter extends AbstractAdapter +{ + private const MAX_KEY_LENGTH = 250; + /** @var Collection */ + private $connection; + private $marshaller; + public function __construct(Collection $connection, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.'); + } + $this->maxIdLength = static::MAX_KEY_LENGTH; + $this->connection = $connection; + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + /** + * @param array|string $dsn + * + * @return Bucket|Collection + */ + public static function createConnection($dsn, array $options = []) + { + if (\is_string($dsn)) { + $dsn = [$dsn]; + } elseif (!\is_array($dsn)) { + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be array or string, "%s" given.', __METHOD__, get_debug_type($dsn))); + } + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.'); + } + set_error_handler(function ($type, $msg, $file, $line): bool { + throw new \ErrorException($msg, 0, $type, $file, $line); + }); + $dsnPattern = '/^(?couchbase(?:s)?)\:\/\/(?:(?[^\:]+)\:(?[^\@]{6,})@)?' . '(?[^\:]+(?:\:\d+)?)(?:\/(?[^\/\?]+))(?:(?:\/(?[^\/]+))' . '(?:\/(?[^\/\?]+)))?(?:\/)?(?:\?(?.*))?$/i'; + $newServers = []; + $protocol = 'couchbase'; + try { + $username = $options['username'] ?? ''; + $password = $options['password'] ?? ''; + foreach ($dsn as $server) { + if (0 !== strpos($server, 'couchbase:')) { + throw new InvalidArgumentException('Invalid Couchbase DSN: it does not start with "couchbase:".'); + } + preg_match($dsnPattern, $server, $matches); + $username = $matches['username'] ?: $username; + $password = $matches['password'] ?: $password; + $protocol = $matches['protocol'] ?: $protocol; + if (isset($matches['options'])) { + $optionsInDsn = self::getOptions($matches['options']); + foreach ($optionsInDsn as $parameter => $value) { + $options[$parameter] = $value; + } + } + $newServers[] = $matches['host']; + } + $option = isset($matches['options']) ? '?' . $matches['options'] : ''; + $connectionString = $protocol . '://' . implode(',', $newServers) . $option; + $clusterOptions = new ClusterOptions(); + $clusterOptions->credentials($username, $password); + $client = new Cluster($connectionString, $clusterOptions); + $bucket = $client->bucket($matches['bucketName']); + $collection = $bucket->defaultCollection(); + if (!empty($matches['scopeName'])) { + $scope = $bucket->scope($matches['scopeName']); + $collection = $scope->collection($matches['collectionName']); + } + return $collection; + } finally { + restore_error_handler(); + } + } + public static function isSupported(): bool + { + return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '3.0.5', '>=') && version_compare(phpversion('couchbase'), '4.0', '<'); + } + private static function getOptions(string $options): array + { + $results = []; + $optionsInArray = explode('&', $options); + foreach ($optionsInArray as $option) { + [$key, $value] = explode('=', $option); + $results[$key] = $value; + } + return $results; + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): array + { + $results = []; + foreach ($ids as $id) { + try { + $resultCouchbase = $this->connection->get($id); + } catch (DocumentNotFoundException $exception) { + continue; + } + $content = $resultCouchbase->value ?? $resultCouchbase->content(); + $results[$id] = $this->marshaller->unmarshall($content); + } + return $results; + } + /** + * {@inheritdoc} + */ + protected function doHave($id): bool + { + return $this->connection->exists($id)->exists(); + } + /** + * {@inheritdoc} + */ + protected function doClear($namespace): bool + { + return \false; + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $idsErrors = []; + foreach ($ids as $id) { + try { + $result = $this->connection->remove($id); + if (null === $result->mutationToken()) { + $idsErrors[] = $id; + } + } catch (DocumentNotFoundException $exception) { + } + } + return 0 === \count($idsErrors); + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + $upsertOptions = new UpsertOptions(); + $upsertOptions->expiry($lifetime); + $ko = []; + foreach ($values as $key => $value) { + try { + $this->connection->upsert($key, $value, $upsertOptions); + } catch (\Exception $exception) { + $ko[$key] = ''; + } + } + return ([] === $ko) ? \true : $ko; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/DoctrineAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/DoctrineAdapter.php new file mode 100644 index 0000000000..35ef3ec1d4 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/DoctrineAdapter.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Doctrine\Common\Cache\CacheProvider; +use Wordlift\Modules\Common\Doctrine\Common\Cache\Psr6\CacheAdapter; +/** + * @author Nicolas Grekas + * + * @deprecated Since Symfony 5.4, use Doctrine\Common\Cache\Psr6\CacheAdapter instead + */ +class DoctrineAdapter extends AbstractAdapter +{ + private $provider; + public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0) + { + trigger_deprecation('symfony/cache', '5.4', '"%s" is deprecated, use "%s" instead.', __CLASS__, CacheAdapter::class); + parent::__construct('', $defaultLifetime); + $this->provider = $provider; + $provider->setNamespace($namespace); + } + /** + * {@inheritdoc} + */ + public function reset() + { + parent::reset(); + $this->provider->setNamespace($this->provider->getNamespace()); + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class . '::handleUnserializeCallback'); + try { + return $this->provider->fetchMultiple($ids); + } catch (\Error $e) { + $trace = $e->getTrace(); + if (isset($trace[0]['function']) && !isset($trace[0]['class'])) { + switch ($trace[0]['function']) { + case 'unserialize': + case 'apcu_fetch': + case 'apc_fetch': + throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); + } + } + throw $e; + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id) + { + return $this->provider->contains($id); + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + $namespace = $this->provider->getNamespace(); + return isset($namespace[0]) ? $this->provider->deleteAll() : $this->provider->flushAll(); + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = \true; + foreach ($ids as $id) { + $ok = $this->provider->delete($id) && $ok; + } + return $ok; + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + return $this->provider->saveMultiple($values, $lifetime); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php new file mode 100644 index 0000000000..531e36070b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php @@ -0,0 +1,358 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Doctrine\DBAL\ArrayParameterType; +use Wordlift\Modules\Common\Doctrine\DBAL\Configuration; +use Wordlift\Modules\Common\Doctrine\DBAL\Connection; +use Wordlift\Modules\Common\Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Wordlift\Modules\Common\Doctrine\DBAL\DriverManager; +use Wordlift\Modules\Common\Doctrine\DBAL\Exception as DBALException; +use Wordlift\Modules\Common\Doctrine\DBAL\Exception\TableNotFoundException; +use Wordlift\Modules\Common\Doctrine\DBAL\ParameterType; +use Wordlift\Modules\Common\Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Wordlift\Modules\Common\Doctrine\DBAL\Schema\Schema; +use Wordlift\Modules\Common\Doctrine\DBAL\ServerVersionProvider; +use Wordlift\Modules\Common\Doctrine\DBAL\Tools\DsnParser; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +class DoctrineDbalAdapter extends AbstractAdapter implements PruneableInterface +{ + protected $maxIdLength = 255; + private $marshaller; + private $conn; + private $platformName; + private $serverVersion; + private $table = 'cache_items'; + private $idCol = 'item_id'; + private $dataCol = 'item_data'; + private $lifetimeCol = 'item_lifetime'; + private $timeCol = 'item_time'; + private $namespace; + /** + * You can either pass an existing database Doctrine DBAL Connection or + * a DSN string that will be used to connect to the database. + * + * The cache table is created automatically when possible. + * Otherwise, use the createTable() method. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_time] + * + * @param Connection|string $connOrDsn + * + * @throws InvalidArgumentException When namespace contains invalid characters + */ + public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], ?MarshallerInterface $marshaller = null) + { + if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0])); + } + if ($connOrDsn instanceof Connection) { + $this->conn = $connOrDsn; + } elseif (\is_string($connOrDsn)) { + if (!class_exists(DriverManager::class)) { + throw new InvalidArgumentException('Failed to parse DSN. Try running "composer require doctrine/dbal".'); + } + if (class_exists(DsnParser::class)) { + $params = (new DsnParser(['db2' => 'ibm_db2', 'mssql' => 'pdo_sqlsrv', 'mysql' => 'pdo_mysql', 'mysql2' => 'pdo_mysql', 'postgres' => 'pdo_pgsql', 'postgresql' => 'pdo_pgsql', 'pgsql' => 'pdo_pgsql', 'sqlite' => 'pdo_sqlite', 'sqlite3' => 'pdo_sqlite']))->parse($connOrDsn); + } else { + $params = ['url' => $connOrDsn]; + } + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + $this->conn = DriverManager::getConnection($params, $config); + } else { + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be "%s" or string, "%s" given.', __METHOD__, Connection::class, get_debug_type($connOrDsn))); + } + $this->table = $options['db_table'] ?? $this->table; + $this->idCol = $options['db_id_col'] ?? $this->idCol; + $this->dataCol = $options['db_data_col'] ?? $this->dataCol; + $this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol; + $this->timeCol = $options['db_time_col'] ?? $this->timeCol; + $this->namespace = $namespace; + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + parent::__construct($namespace, $defaultLifetime); + } + /** + * Creates the table to store cache items which can be called once for setup. + * + * Cache ID are saved in a column of maximum length 255. Cache data is + * saved in a BLOB. + * + * @throws DBALException When the table already exists + */ + public function createTable() + { + $schema = new Schema(); + $this->addTableToSchema($schema); + foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { + $this->conn->executeStatement($sql); + } + } + /** + * {@inheritdoc} + */ + public function configureSchema(Schema $schema, Connection $forConnection): void + { + // only update the schema for this connection + if ($forConnection !== $this->conn) { + return; + } + if ($schema->hasTable($this->table)) { + return; + } + $this->addTableToSchema($schema); + } + /** + * {@inheritdoc} + */ + public function prune(): bool + { + $deleteSql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= ?"; + $params = [time()]; + $paramTypes = [ParameterType::INTEGER]; + if ('' !== $this->namespace) { + $deleteSql .= " AND {$this->idCol} LIKE ?"; + $params[] = sprintf('%s%%', $this->namespace); + $paramTypes[] = ParameterType::STRING; + } + try { + $this->conn->executeStatement($deleteSql, $params, $paramTypes); + } catch (TableNotFoundException $e) { + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + $now = time(); + $expired = []; + $sql = "SELECT {$this->idCol}, CASE WHEN {$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > ? THEN {$this->dataCol} ELSE NULL END FROM {$this->table} WHERE {$this->idCol} IN (?)"; + $result = $this->conn->executeQuery($sql, [$now, $ids], [ParameterType::INTEGER, class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY])->iterateNumeric(); + foreach ($result as $row) { + if (null === $row[1]) { + $expired[] = $row[0]; + } else { + yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); + } + } + if ($expired) { + $sql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= ? AND {$this->idCol} IN (?)"; + $this->conn->executeStatement($sql, [$now, $expired], [ParameterType::INTEGER, class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY]); + } + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + $sql = "SELECT 1 FROM {$this->table} WHERE {$this->idCol} = ? AND ({$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > ?)"; + $result = $this->conn->executeQuery($sql, [$id, time()], [ParameterType::STRING, ParameterType::INTEGER]); + return (bool) $result->fetchOne(); + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + if ('' === $namespace) { + if ('sqlite' === $this->getPlatformName()) { + $sql = "DELETE FROM {$this->table}"; + } else { + $sql = "TRUNCATE TABLE {$this->table}"; + } + } else { + $sql = "DELETE FROM {$this->table} WHERE {$this->idCol} LIKE '{$namespace}%'"; + } + try { + $this->conn->executeStatement($sql); + } catch (TableNotFoundException $e) { + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $sql = "DELETE FROM {$this->table} WHERE {$this->idCol} IN (?)"; + try { + $this->conn->executeStatement($sql, [array_values($ids)], [class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY]); + } catch (TableNotFoundException $e) { + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + $platformName = $this->getPlatformName(); + $insertSql = "INSERT INTO {$this->table} ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?)"; + switch (\true) { + case 'mysql' === $platformName: + $sql = $insertSql . " ON DUPLICATE KEY UPDATE {$this->dataCol} = VALUES({$this->dataCol}), {$this->lifetimeCol} = VALUES({$this->lifetimeCol}), {$this->timeCol} = VALUES({$this->timeCol})"; + break; + case 'oci' === $platformName: + // DUAL is Oracle specific dummy table + $sql = "MERGE INTO {$this->table} USING DUAL ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?"; + break; + case 'sqlsrv' === $platformName && version_compare($this->getServerVersion(), '10', '>='): + // 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 + $sql = "MERGE INTO {$this->table} WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?;"; + break; + case 'sqlite' === $platformName: + $sql = 'INSERT OR REPLACE' . substr($insertSql, 6); + break; + case 'pgsql' === $platformName && version_compare($this->getServerVersion(), '9.5', '>='): + $sql = $insertSql . " ON CONFLICT ({$this->idCol}) DO UPDATE SET ({$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) = (EXCLUDED.{$this->dataCol}, EXCLUDED.{$this->lifetimeCol}, EXCLUDED.{$this->timeCol})"; + break; + default: + $platformName = null; + $sql = "UPDATE {$this->table} SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ? WHERE {$this->idCol} = ?"; + break; + } + $now = time(); + $lifetime = $lifetime ?: null; + try { + $stmt = $this->conn->prepare($sql); + } catch (TableNotFoundException $e) { + if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], \true)) { + $this->createTable(); + } + $stmt = $this->conn->prepare($sql); + } + if ('sqlsrv' === $platformName || 'oci' === $platformName) { + $bind = static function ($id, $data) use ($stmt) { + $stmt->bindValue(1, $id); + $stmt->bindValue(2, $id); + $stmt->bindValue(3, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(6, $data, ParameterType::LARGE_OBJECT); + }; + $stmt->bindValue(4, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(5, $now, ParameterType::INTEGER); + $stmt->bindValue(7, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(8, $now, ParameterType::INTEGER); + } elseif (null !== $platformName) { + $bind = static function ($id, $data) use ($stmt) { + $stmt->bindValue(1, $id); + $stmt->bindValue(2, $data, ParameterType::LARGE_OBJECT); + }; + $stmt->bindValue(3, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(4, $now, ParameterType::INTEGER); + } else { + $stmt->bindValue(2, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(3, $now, ParameterType::INTEGER); + $insertStmt = $this->conn->prepare($insertSql); + $insertStmt->bindValue(3, $lifetime, ParameterType::INTEGER); + $insertStmt->bindValue(4, $now, ParameterType::INTEGER); + $bind = static function ($id, $data) use ($stmt, $insertStmt) { + $stmt->bindValue(1, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(4, $id); + $insertStmt->bindValue(1, $id); + $insertStmt->bindValue(2, $data, ParameterType::LARGE_OBJECT); + }; + } + foreach ($values as $id => $data) { + $bind($id, $data); + try { + $rowCount = $stmt->executeStatement(); + } catch (TableNotFoundException $e) { + if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], \true)) { + $this->createTable(); + } + $rowCount = $stmt->executeStatement(); + } + if (null === $platformName && 0 === $rowCount) { + try { + $insertStmt->executeStatement(); + } catch (DBALException $e) { + // A concurrent write won, let it be + } + } + } + return $failed; + } + /** + * @internal + */ + protected function getId($key) + { + if ('pgsql' !== $this->getPlatformName()) { + return parent::getId($key); + } + if (str_contains($key, "\x00") || str_contains($key, '%') || !preg_match('//u', $key)) { + $key = rawurlencode($key); + } + return parent::getId($key); + } + private function getPlatformName(): string + { + if (isset($this->platformName)) { + return $this->platformName; + } + $platform = $this->conn->getDatabasePlatform(); + switch (\true) { + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\MySQLPlatform: + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\MySQL57Platform: + return $this->platformName = 'mysql'; + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\SqlitePlatform: + return $this->platformName = 'sqlite'; + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\PostgreSQLPlatform: + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\PostgreSQL94Platform: + return $this->platformName = 'pgsql'; + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\OraclePlatform: + return $this->platformName = 'oci'; + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\SQLServerPlatform: + case $platform instanceof \Wordlift\Modules\Common\Doctrine\DBAL\Platforms\SQLServer2012Platform: + return $this->platformName = 'sqlsrv'; + default: + return $this->platformName = \get_class($platform); + } + } + private function getServerVersion(): string + { + if (isset($this->serverVersion)) { + return $this->serverVersion; + } + if ($this->conn instanceof ServerVersionProvider || $this->conn instanceof ServerInfoAwareConnection) { + return $this->serverVersion = $this->conn->getServerVersion(); + } + // The condition should be removed once support for DBAL <3.3 is dropped + $conn = method_exists($this->conn, 'getNativeConnection') ? $this->conn->getNativeConnection() : $this->conn->getWrappedConnection(); + return $this->serverVersion = $conn->getAttribute(\PDO::ATTR_SERVER_VERSION); + } + private function addTableToSchema(Schema $schema): void + { + $types = ['mysql' => 'binary', 'sqlite' => 'text']; + $table = $schema->createTable($this->table); + $table->addColumn($this->idCol, $types[$this->getPlatformName()] ?? 'string', ['length' => 255]); + $table->addColumn($this->dataCol, 'blob', ['length' => 16777215]); + $table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => \true, 'notnull' => \false]); + $table->addColumn($this->timeCol, 'integer', ['unsigned' => \true]); + $table->setPrimaryKey([$this->idCol]); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/FilesystemAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/FilesystemAdapter.php new file mode 100644 index 0000000000..ce900d45fd --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/FilesystemAdapter.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 Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\FilesystemTrait; +class FilesystemAdapter extends AbstractAdapter implements PruneableInterface +{ + use FilesystemTrait; + public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $directory = null, ?MarshallerInterface $marshaller = null) + { + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php new file mode 100644 index 0000000000..6885d9af93 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php @@ -0,0 +1,205 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\TagAwareMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\FilesystemTrait; +/** + * Stores tag id <> cache id relationship as a symlink, and lookup on invalidation calls. + * + * @author Nicolas Grekas + * @author André Rømcke + */ +class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface +{ + use FilesystemTrait { + doClear as private doClearCache; + doSave as private doSaveCache; + } + /** + * Folder used for tag symlinks. + */ + private const TAG_FOLDER = 'tags'; + public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $directory = null, ?MarshallerInterface $marshaller = null) + { + $this->marshaller = new TagAwareMarshaller($marshaller); + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + $ok = $this->doClearCache($namespace); + if ('' !== $namespace) { + return $ok; + } + set_error_handler(static function () { + }); + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + try { + foreach ($this->scanHashDir($this->directory . self::TAG_FOLDER . \DIRECTORY_SEPARATOR) as $dir) { + if (rename($dir, $renamed = substr_replace($dir, bin2hex(random_bytes(4)), -8))) { + $dir = $renamed . \DIRECTORY_SEPARATOR; + } else { + $dir .= \DIRECTORY_SEPARATOR; + $renamed = null; + } + for ($i = 0; $i < 38; ++$i) { + if (!is_dir($dir . $chars[$i])) { + continue; + } + for ($j = 0; $j < 38; ++$j) { + if (!is_dir($d = $dir . $chars[$i] . \DIRECTORY_SEPARATOR . $chars[$j])) { + continue; + } + foreach (scandir($d, \SCANDIR_SORT_NONE) ?: [] as $link) { + if ('.' !== $link && '..' !== $link && (null !== $renamed || !realpath($d . \DIRECTORY_SEPARATOR . $link))) { + unlink($d . \DIRECTORY_SEPARATOR . $link); + } + } + (null === $renamed) ?: rmdir($d); + } + (null === $renamed) ?: rmdir($dir . $chars[$i]); + } + (null === $renamed) ?: rmdir($renamed); + } + } finally { + restore_error_handler(); + } + return $ok; + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array + { + $failed = $this->doSaveCache($values, $lifetime); + // Add Tags as symlinks + foreach ($addTagData as $tagId => $ids) { + $tagFolder = $this->getTagFolder($tagId); + foreach ($ids as $id) { + if ($failed && \in_array($id, $failed, \true)) { + continue; + } + $file = $this->getFile($id); + if (!@symlink($file, $tagLink = $this->getFile($id, \true, $tagFolder)) && !is_link($tagLink)) { + @unlink($file); + $failed[] = $id; + } + } + } + // Unlink removed Tags + foreach ($removeTagData as $tagId => $ids) { + $tagFolder = $this->getTagFolder($tagId); + foreach ($ids as $id) { + if ($failed && \in_array($id, $failed, \true)) { + continue; + } + @unlink($this->getFile($id, \false, $tagFolder)); + } + } + return $failed; + } + /** + * {@inheritdoc} + */ + protected function doDeleteYieldTags(array $ids): iterable + { + foreach ($ids as $id) { + $file = $this->getFile($id); + if (!is_file($file) || !$h = @fopen($file, 'r')) { + continue; + } + if ((\PHP_VERSION_ID >= 70300 || '\\' !== \DIRECTORY_SEPARATOR) && !@unlink($file)) { + fclose($h); + continue; + } + $meta = explode("\n", fread($h, 4096), 3)[2] ?? ''; + // detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (13 < \strlen($meta) && "\x9d" === $meta[0] && "\x00" === $meta[5] && "_" === $meta[9]) { + $meta[9] = "\x00"; + $tagLen = unpack('Nlen', $meta, 9)['len']; + $meta = substr($meta, 13, $tagLen); + if (0 < $tagLen -= \strlen($meta)) { + $meta .= fread($h, $tagLen); + } + try { + yield $id => ('' === $meta) ? [] : $this->marshaller->unmarshall($meta); + } catch (\Exception $e) { + yield $id => []; + } + } + fclose($h); + if (\PHP_VERSION_ID < 70300 && '\\' === \DIRECTORY_SEPARATOR) { + @unlink($file); + } + } + } + /** + * {@inheritdoc} + */ + protected function doDeleteTagRelations(array $tagData): bool + { + foreach ($tagData as $tagId => $idList) { + $tagFolder = $this->getTagFolder($tagId); + foreach ($idList as $id) { + @unlink($this->getFile($id, \false, $tagFolder)); + } + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doInvalidate(array $tagIds): bool + { + foreach ($tagIds as $tagId) { + if (!is_dir($tagFolder = $this->getTagFolder($tagId))) { + continue; + } + set_error_handler(static function () { + }); + try { + if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -9))) { + $tagFolder = $renamed . \DIRECTORY_SEPARATOR; + } else { + $renamed = null; + } + foreach ($this->scanHashDir($tagFolder) as $itemLink) { + unlink(realpath($itemLink) ?: $itemLink); + unlink($itemLink); + } + if (null === $renamed) { + continue; + } + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + for ($i = 0; $i < 38; ++$i) { + for ($j = 0; $j < 38; ++$j) { + rmdir($tagFolder . $chars[$i] . \DIRECTORY_SEPARATOR . $chars[$j]); + } + rmdir($tagFolder . $chars[$i]); + } + rmdir($renamed); + } finally { + restore_error_handler(); + } + } + return \true; + } + private function getTagFolder(string $tagId): string + { + return $this->getFile($tagId, \false, $this->directory . self::TAG_FOLDER . \DIRECTORY_SEPARATOR) . \DIRECTORY_SEPARATOR; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/MemcachedAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/MemcachedAdapter.php new file mode 100644 index 0000000000..482b2462cd --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/MemcachedAdapter.php @@ -0,0 +1,305 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +/** + * @author Rob Frawley 2nd + * @author Nicolas Grekas + */ +class MemcachedAdapter extends AbstractAdapter +{ + /** + * We are replacing characters that are illegal in Memcached keys with reserved characters from + * {@see \Symfony\Contracts\Cache\ItemInterface::RESERVED_CHARACTERS} that are legal in Memcached. + * Note: don’t use {@see \Symfony\Component\Cache\Adapter\AbstractAdapter::NS_SEPARATOR}. + */ + private const RESERVED_MEMCACHED = " \n\r\t\v\f\x00"; + private const RESERVED_PSR6 = '@()\{}/'; + protected $maxIdLength = 250; + private $marshaller; + private $client; + private $lazyClient; + /** + * Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged. + * Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that: + * - the Memcached::OPT_BINARY_PROTOCOL must be enabled + * (that's the default when using MemcachedAdapter::createConnection()); + * - tags eviction by Memcached's LRU algorithm will break by-tags invalidation; + * your Memcached memory should be large enough to never trigger LRU. + * + * Using a MemcachedAdapter as a pure items store is fine. + */ + public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('Memcached ' . ((\PHP_VERSION_ID >= 80100) ? '> 3.1.5' : '>= 2.2.0') . ' is required.'); + } + if ('Memcached' === \get_class($client)) { + $opt = $client->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + $this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY)); + $this->client = $client; + } else { + $this->lazyClient = $client; + } + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + public static function isSupported() + { + return \extension_loaded('memcached') && version_compare(phpversion('memcached'), (\PHP_VERSION_ID >= 80100) ? '3.1.6' : '2.2.0', '>='); + } + /** + * Creates a Memcached instance. + * + * By default, the binary protocol, no block, and libketama compatible options are enabled. + * + * Examples for servers: + * - 'memcached://user:pass@localhost?weight=33' + * - [['localhost', 11211, 33]] + * + * @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs + * + * @return \Memcached + * + * @throws \ErrorException When invalid options or servers are provided + */ + public static function createConnection($servers, array $options = []) + { + if (\is_string($servers)) { + $servers = [$servers]; + } elseif (!\is_array($servers)) { + throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, "%s" given.', get_debug_type($servers))); + } + if (!static::isSupported()) { + throw new CacheException('Memcached ' . ((\PHP_VERSION_ID >= 80100) ? '> 3.1.5' : '>= 2.2.0') . ' is required.'); + } + set_error_handler(function ($type, $msg, $file, $line) { + throw new \ErrorException($msg, 0, $type, $file, $line); + }); + try { + $client = new \Memcached($options['persistent_id'] ?? null); + $username = $options['username'] ?? null; + $password = $options['password'] ?? null; + // parse any DSN in $servers + foreach ($servers as $i => $dsn) { + if (\is_array($dsn)) { + continue; + } + if (!str_starts_with($dsn, 'memcached:')) { + throw new InvalidArgumentException('Invalid Memcached DSN: it does not start with "memcached:".'); + } + $params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) { + if (!empty($m[2])) { + [$username, $password] = explode(':', $m[2], 2) + [1 => null]; + $username = rawurldecode($username); + $password = (null !== $password) ? rawurldecode($password) : null; + } + return 'file:' . ($m[1] ?? ''); + }, $dsn); + if (\false === $params = parse_url($params)) { + throw new InvalidArgumentException('Invalid Memcached DSN.'); + } + $query = $hosts = []; + if (isset($params['query'])) { + parse_str($params['query'], $query); + if (isset($query['host'])) { + if (!\is_array($hosts = $query['host'])) { + throw new InvalidArgumentException('Invalid Memcached DSN: query parameter "host" must be an array.'); + } + foreach ($hosts as $host => $weight) { + if (\false === $port = strrpos($host, ':')) { + $hosts[$host] = [$host, 11211, (int) $weight]; + } else { + $hosts[$host] = [substr($host, 0, $port), (int) substr($host, 1 + $port), (int) $weight]; + } + } + $hosts = array_values($hosts); + unset($query['host']); + } + if ($hosts && !isset($params['host']) && !isset($params['path'])) { + unset($servers[$i]); + $servers = array_merge($servers, $hosts); + continue; + } + } + if (!isset($params['host']) && !isset($params['path'])) { + throw new InvalidArgumentException('Invalid Memcached DSN: missing host or path.'); + } + if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['weight'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } + $params += ['host' => $params['host'] ?? $params['path'], 'port' => isset($params['host']) ? 11211 : null, 'weight' => 0]; + if ($query) { + $params += $query; + $options = $query + $options; + } + $servers[$i] = [$params['host'], $params['port'], $params['weight']]; + if ($hosts) { + $servers = array_merge($servers, $hosts); + } + } + // set client's options + unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']); + $options = array_change_key_case($options, \CASE_UPPER); + $client->setOption(\Memcached::OPT_BINARY_PROTOCOL, \true); + $client->setOption(\Memcached::OPT_NO_BLOCK, \true); + $client->setOption(\Memcached::OPT_TCP_NODELAY, \true); + if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) { + $client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, \true); + } + foreach ($options as $name => $value) { + if (\is_int($name)) { + continue; + } + if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) { + $value = \constant('Memcached::' . $name . '_' . strtoupper($value)); + } + unset($options[$name]); + if (\defined('Memcached::OPT_' . $name)) { + $options[\constant('Memcached::OPT_' . $name)] = $value; + } + } + $client->setOptions($options + [\Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_PHP]); + // set client's servers, taking care of persistent connections + if (!$client->isPristine()) { + $oldServers = []; + foreach ($client->getServerList() as $server) { + $oldServers[] = [$server['host'], $server['port']]; + } + $newServers = []; + foreach ($servers as $server) { + if (1 < \count($server)) { + $server = array_values($server); + unset($server[2]); + $server[1] = (int) $server[1]; + } + $newServers[] = $server; + } + if ($oldServers !== $newServers) { + $client->resetServerList(); + $client->addServers($servers); + } + } else { + $client->addServers($servers); + } + if (null !== $username || null !== $password) { + if (!method_exists($client, 'setSaslAuthData')) { + trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.'); + } + $client->setSaslAuthData($username, $password); + } + return $client; + } finally { + restore_error_handler(); + } + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + if ($lifetime && $lifetime > 30 * 86400) { + $lifetime += time(); + } + $encodedValues = []; + foreach ($values as $key => $value) { + $encodedValues[self::encodeKey($key)] = $value; + } + return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)) ? $failed : \false; + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + try { + $encodedIds = array_map([__CLASS__, 'encodeKey'], $ids); + $encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds)); + $result = []; + foreach ($encodedResult as $key => $value) { + $result[self::decodeKey($key)] = $this->marshaller->unmarshall($value); + } + return $result; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); + } + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id) + { + return \false !== $this->getClient()->get(self::encodeKey($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode()); + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = \true; + $encodedIds = array_map([__CLASS__, 'encodeKey'], $ids); + foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) { + if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) { + $ok = \false; + } + } + return $ok; + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + return '' === $namespace && $this->getClient()->flush(); + } + private function checkResultCode($result) + { + $code = $this->client->getResultCode(); + if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) { + return $result; + } + throw new CacheException('MemcachedAdapter client error: ' . strtolower($this->client->getResultMessage())); + } + private function getClient(): \Memcached + { + if ($this->client) { + return $this->client; + } + $opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) { + throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix)); + } + return $this->client = $this->lazyClient; + } + private static function encodeKey(string $key): string + { + return strtr($key, self::RESERVED_MEMCACHED, self::RESERVED_PSR6); + } + private static function decodeKey(string $key): string + { + return strtr($key, self::RESERVED_PSR6, self::RESERVED_MEMCACHED); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/NullAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/NullAdapter.php new file mode 100644 index 0000000000..b889268f19 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/NullAdapter.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +/** + * @author Titouan Galopin + */ +class NullAdapter implements AdapterInterface, CacheInterface +{ + private static $createCacheItem; + public function __construct() + { + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(static function ($key) { + $item = new CacheItem(); + $item->key = $key; + $item->isHit = \false; + return $item; + }, null, CacheItem::class); + } + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + $save = \true; + return $callback((self::$createCacheItem)($key), $save); + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + return (self::$createCacheItem)($key); + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + return $this->generateItems($keys); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + return \false; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + return \true; + } + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + return $this->deleteItem($key); + } + private function generateItems(array $keys): \Generator + { + $f = self::$createCacheItem; + foreach ($keys as $key) { + yield $key => $f($key); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/ParameterNormalizer.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ParameterNormalizer.php new file mode 100644 index 0000000000..0981bf94a7 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ParameterNormalizer.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 Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +/** + * @author Lars Strojny + */ +final class ParameterNormalizer +{ + public static function normalizeDuration(string $duration): int + { + if (is_numeric($duration)) { + return $duration; + } + if (\false !== $time = strtotime($duration, 0)) { + return $time; + } + try { + return \DateTime::createFromFormat('U', 0)->add(new \DateInterval($duration))->getTimestamp(); + } catch (\Exception $e) { + throw new \InvalidArgumentException(sprintf('Cannot parse date interval "%s".', $duration), 0, $e); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/PdoAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/PdoAdapter.php new file mode 100644 index 0000000000..c0aae10bd1 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/PdoAdapter.php @@ -0,0 +1,527 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Doctrine\DBAL\Connection; +use Wordlift\Modules\Common\Doctrine\DBAL\Schema\Schema; +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Log\LoggerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +class PdoAdapter extends AbstractAdapter implements PruneableInterface +{ + protected $maxIdLength = 255; + private $marshaller; + private $conn; + private $dsn; + private $driver; + private $serverVersion; + private $table = 'cache_items'; + private $idCol = 'item_id'; + private $dataCol = 'item_data'; + private $lifetimeCol = 'item_lifetime'; + private $timeCol = 'item_time'; + private $username = null; + private $password = null; + private $connectionOptions = []; + private $namespace; + private $dbalAdapter; + /** + * You can either pass an existing database connection as PDO instance or + * a DSN string that will be used to lazy-connect to the database when the + * cache is actually used. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_time] + * * db_username: The username when lazy-connect [default: ''] + * * db_password: The password when lazy-connect [default: ''] + * * db_connection_options: An array of driver-specific connection options [default: []] + * + * @param \PDO|string $connOrDsn + * + * @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, string $namespace = '', int $defaultLifetime = 0, array $options = [], ?MarshallerInterface $marshaller = null) + { + if ($connOrDsn instanceof Connection || \is_string($connOrDsn) && str_contains($connOrDsn, '://')) { + trigger_deprecation('symfony/cache', '5.4', 'Usage of a DBAL Connection with "%s" is deprecated and will be removed in symfony 6.0. Use "%s" instead.', __CLASS__, DoctrineDbalAdapter::class); + $this->dbalAdapter = new DoctrineDbalAdapter($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller); + return; + } + if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0])); + } + if ($connOrDsn instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { + throw new InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __CLASS__)); + } + $this->conn = $connOrDsn; + } elseif (\is_string($connOrDsn)) { + $this->dsn = $connOrDsn; + } else { + throw new InvalidArgumentException(sprintf('"%s" requires PDO or Doctrine\DBAL\Connection instance or DSN string as first argument, "%s" given.', __CLASS__, get_debug_type($connOrDsn))); + } + $this->table = $options['db_table'] ?? $this->table; + $this->idCol = $options['db_id_col'] ?? $this->idCol; + $this->dataCol = $options['db_data_col'] ?? $this->dataCol; + $this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol; + $this->timeCol = $options['db_time_col'] ?? $this->timeCol; + $this->username = $options['db_username'] ?? $this->username; + $this->password = $options['db_password'] ?? $this->password; + $this->connectionOptions = $options['db_connection_options'] ?? $this->connectionOptions; + $this->namespace = $namespace; + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + parent::__construct($namespace, $defaultLifetime); + } + /** + * {@inheritDoc} + */ + public function getItem($key) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->getItem($key); + } + return parent::getItem($key); + } + /** + * {@inheritDoc} + */ + public function getItems(array $keys = []) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->getItems($keys); + } + return parent::getItems($keys); + } + /** + * {@inheritDoc} + */ + public function hasItem($key) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->hasItem($key); + } + return parent::hasItem($key); + } + /** + * {@inheritDoc} + */ + public function deleteItem($key) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->deleteItem($key); + } + return parent::deleteItem($key); + } + /** + * {@inheritDoc} + */ + public function deleteItems(array $keys) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->deleteItems($keys); + } + return parent::deleteItems($keys); + } + /** + * {@inheritDoc} + */ + public function clear(string $prefix = '') + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->clear($prefix); + } + return parent::clear($prefix); + } + /** + * {@inheritDoc} + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->get($key, $callback, $beta, $metadata); + } + return parent::get($key, $callback, $beta, $metadata); + } + /** + * {@inheritDoc} + */ + public function delete(string $key): bool + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->delete($key); + } + return parent::delete($key); + } + /** + * {@inheritDoc} + */ + public function save(CacheItemInterface $item) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->save($item); + } + return parent::save($item); + } + /** + * {@inheritDoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->saveDeferred($item); + } + return parent::saveDeferred($item); + } + /** + * {@inheritDoc} + */ + public function setLogger(LoggerInterface $logger): void + { + if (isset($this->dbalAdapter)) { + $this->dbalAdapter->setLogger($logger); + return; + } + parent::setLogger($logger); + } + /** + * {@inheritDoc} + */ + public function commit() + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->commit(); + } + return parent::commit(); + } + /** + * {@inheritDoc} + */ + public function reset() + { + if (isset($this->dbalAdapter)) { + $this->dbalAdapter->reset(); + return; + } + parent::reset(); + } + /** + * Creates the table to store cache items which can be called once for setup. + * + * Cache ID are saved in a column of maximum length 255. Cache data is + * saved in a BLOB. + * + * @throws \PDOException When the table already exists + * @throws \DomainException When an unsupported PDO driver is used + */ + public function createTable() + { + if (isset($this->dbalAdapter)) { + $this->dbalAdapter->createTable(); + return; + } + // connect if we are not yet + $conn = $this->getConnection(); + switch ($this->driver) { + case 'mysql': + // We use varbinary for the ID column because it prevents unwanted conversions: + // - character set conversions between server and client + // - trailing space removal + // - case-insensitivity + // - language processing like é == e + $sql = "CREATE TABLE {$this->table} ({$this->idCol} VARBINARY(255) NOT NULL PRIMARY KEY, {$this->dataCol} MEDIUMBLOB NOT NULL, {$this->lifetimeCol} INTEGER UNSIGNED, {$this->timeCol} INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB"; + break; + case 'sqlite': + $sql = "CREATE TABLE {$this->table} ({$this->idCol} TEXT NOT NULL PRIMARY KEY, {$this->dataCol} BLOB NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)"; + break; + case 'pgsql': + $sql = "CREATE TABLE {$this->table} ({$this->idCol} VARCHAR(255) NOT NULL PRIMARY KEY, {$this->dataCol} BYTEA NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)"; + break; + case 'oci': + $sql = "CREATE TABLE {$this->table} ({$this->idCol} VARCHAR2(255) NOT NULL PRIMARY KEY, {$this->dataCol} BLOB NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)"; + break; + case 'sqlsrv': + $sql = "CREATE TABLE {$this->table} ({$this->idCol} VARCHAR(255) NOT NULL PRIMARY KEY, {$this->dataCol} VARBINARY(MAX) NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)"; + break; + default: + throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver)); + } + $conn->exec($sql); + } + /** + * Adds the Table to the Schema if the adapter uses this Connection. + * + * @deprecated since symfony/cache 5.4 use DoctrineDbalAdapter instead + */ + public function configureSchema(Schema $schema, Connection $forConnection): void + { + if (isset($this->dbalAdapter)) { + $this->dbalAdapter->configureSchema($schema, $forConnection); + } + } + /** + * {@inheritdoc} + */ + public function prune() + { + if (isset($this->dbalAdapter)) { + return $this->dbalAdapter->prune(); + } + $deleteSql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= :time"; + if ('' !== $this->namespace) { + $deleteSql .= " AND {$this->idCol} LIKE :namespace"; + } + $connection = $this->getConnection(); + try { + $delete = $connection->prepare($deleteSql); + } catch (\PDOException $e) { + return \true; + } + $delete->bindValue(':time', time(), \PDO::PARAM_INT); + if ('' !== $this->namespace) { + $delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), \PDO::PARAM_STR); + } + try { + return $delete->execute(); + } catch (\PDOException $e) { + return \true; + } + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $connection = $this->getConnection(); + $now = time(); + $expired = []; + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "SELECT {$this->idCol}, CASE WHEN {$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > ? THEN {$this->dataCol} ELSE NULL END FROM {$this->table} WHERE {$this->idCol} IN ({$sql})"; + $stmt = $connection->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($ids as $id) { + $stmt->bindValue(++$i, $id); + } + $result = $stmt->execute(); + if (\is_object($result)) { + $result = $result->iterateNumeric(); + } else { + $stmt->setFetchMode(\PDO::FETCH_NUM); + $result = $stmt; + } + foreach ($result as $row) { + if (null === $row[1]) { + $expired[] = $row[0]; + } else { + yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); + } + } + if ($expired) { + $sql = str_pad('', (\count($expired) << 1) - 1, '?,'); + $sql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= ? AND {$this->idCol} IN ({$sql})"; + $stmt = $connection->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($expired as $id) { + $stmt->bindValue(++$i, $id); + } + $stmt->execute(); + } + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id) + { + $connection = $this->getConnection(); + $sql = "SELECT 1 FROM {$this->table} WHERE {$this->idCol} = :id AND ({$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > :time)"; + $stmt = $connection->prepare($sql); + $stmt->bindValue(':id', $id); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + return (bool) $stmt->fetchColumn(); + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + $conn = $this->getConnection(); + if ('' === $namespace) { + if ('sqlite' === $this->driver) { + $sql = "DELETE FROM {$this->table}"; + } else { + $sql = "TRUNCATE TABLE {$this->table}"; + } + } else { + $sql = "DELETE FROM {$this->table} WHERE {$this->idCol} LIKE '{$namespace}%'"; + } + try { + $conn->exec($sql); + } catch (\PDOException $e) { + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "DELETE FROM {$this->table} WHERE {$this->idCol} IN ({$sql})"; + try { + $stmt = $this->getConnection()->prepare($sql); + $stmt->execute(array_values($ids)); + } catch (\PDOException $e) { + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + $conn = $this->getConnection(); + $driver = $this->driver; + $insertSql = "INSERT INTO {$this->table} ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (:id, :data, :lifetime, :time)"; + switch (\true) { + case 'mysql' === $driver: + $sql = $insertSql . " ON DUPLICATE KEY UPDATE {$this->dataCol} = VALUES({$this->dataCol}), {$this->lifetimeCol} = VALUES({$this->lifetimeCol}), {$this->timeCol} = VALUES({$this->timeCol})"; + break; + case 'oci' === $driver: + // DUAL is Oracle specific dummy table + $sql = "MERGE INTO {$this->table} USING DUAL ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?"; + break; + case 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '10', '>='): + // 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 + $sql = "MERGE INTO {$this->table} WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?;"; + break; + case 'sqlite' === $driver: + $sql = 'INSERT OR REPLACE' . substr($insertSql, 6); + break; + case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='): + $sql = $insertSql . " ON CONFLICT ({$this->idCol}) DO UPDATE SET ({$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) = (EXCLUDED.{$this->dataCol}, EXCLUDED.{$this->lifetimeCol}, EXCLUDED.{$this->timeCol})"; + break; + default: + $driver = null; + $sql = "UPDATE {$this->table} SET {$this->dataCol} = :data, {$this->lifetimeCol} = :lifetime, {$this->timeCol} = :time WHERE {$this->idCol} = :id"; + break; + } + $now = time(); + $lifetime = $lifetime ?: null; + try { + $stmt = $conn->prepare($sql); + } catch (\PDOException $e) { + if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], \true))) { + $this->createTable(); + } + $stmt = $conn->prepare($sql); + } + // $id and $data are defined later in the loop. Binding is done by reference, values are read on execution. + if ('sqlsrv' === $driver || 'oci' === $driver) { + $stmt->bindParam(1, $id); + $stmt->bindParam(2, $id); + $stmt->bindParam(3, $data, \PDO::PARAM_LOB); + $stmt->bindValue(4, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(5, $now, \PDO::PARAM_INT); + $stmt->bindParam(6, $data, \PDO::PARAM_LOB); + $stmt->bindValue(7, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(8, $now, \PDO::PARAM_INT); + } else { + $stmt->bindParam(':id', $id); + $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + if (null === $driver) { + $insertStmt = $conn->prepare($insertSql); + $insertStmt->bindParam(':id', $id); + $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $insertStmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + foreach ($values as $id => $data) { + try { + $stmt->execute(); + } catch (\PDOException $e) { + if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], \true))) { + $this->createTable(); + } + $stmt->execute(); + } + if (null === $driver && !$stmt->rowCount()) { + try { + $insertStmt->execute(); + } catch (\PDOException $e) { + // A concurrent write won, let it be + } + } + } + return $failed; + } + /** + * @internal + */ + protected function getId($key) + { + if ('pgsql' !== $this->driver ?? ($this->getConnection() ? $this->driver : null)) { + return parent::getId($key); + } + if (str_contains($key, "\x00") || str_contains($key, '%') || !preg_match('//u', $key)) { + $key = rawurlencode($key); + } + return parent::getId($key); + } + private function getConnection(): \PDO + { + if (null === $this->conn) { + $this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions); + $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + if (null === $this->driver) { + $this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME); + } + return $this->conn; + } + private function getServerVersion(): string + { + if (null === $this->serverVersion) { + $this->serverVersion = $this->conn->getAttribute(\PDO::ATTR_SERVER_VERSION); + } + return $this->serverVersion; + } + private function isTableMissing(\PDOException $exception): bool + { + $driver = $this->driver; + [$sqlState, $code] = $exception->errorInfo ?? [null, $exception->getCode()]; + switch (\true) { + case 'pgsql' === $driver && '42P01' === $sqlState: + case 'sqlite' === $driver && str_contains($exception->getMessage(), 'no such table:'): + case 'oci' === $driver && 942 === $code: + case 'sqlsrv' === $driver && 208 === $code: + case 'mysql' === $driver && 1146 === $code: + return \true; + default: + return \false; + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/PhpArrayAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/PhpArrayAdapter.php new file mode 100644 index 0000000000..d681664b69 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/PhpArrayAdapter.php @@ -0,0 +1,372 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ContractsTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ProxyTrait; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\VarExporter; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +/** + * Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. + * Warmed up items are read-only and run-time discovered items are cached using a fallback adapter. + * + * @author Titouan Galopin + * @author Nicolas Grekas + */ +class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + use ContractsTrait; + use ProxyTrait; + private $file; + private $keys; + private $values; + private static $createCacheItem; + private static $valuesCache = []; + /** + * @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(string $file, AdapterInterface $fallbackPool) + { + $this->file = $file; + $this->pool = $fallbackPool; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + return $item; + }, null, CacheItem::class); + } + /** + * 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 A pool to fallback on when an item is not hit + * + * @return CacheItemPoolInterface + */ + public static function create(string $file, CacheItemPoolInterface $fallbackPool) + { + if (!$fallbackPool instanceof AdapterInterface) { + $fallbackPool = new ProxyAdapter($fallbackPool); + } + return new static($file, $fallbackPool); + } + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + if (null === $this->values) { + $this->initialize(); + } + if (!isset($this->keys[$key])) { + get_from_pool: + if ($this->pool instanceof CacheInterface) { + return $this->pool->get($key, $callback, $beta, $metadata); + } + return $this->doGet($this->pool, $key, $callback, $beta, $metadata); + } + $value = $this->values[$this->keys[$key]]; + if ('N;' === $value) { + return null; + } + try { + if ($value instanceof \Closure) { + return $value(); + } + } catch (\Throwable $e) { + unset($this->keys[$key]); + goto get_from_pool; + } + return $value; + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if (null === $this->values) { + $this->initialize(); + } + if (!isset($this->keys[$key])) { + return $this->pool->getItem($key); + } + $value = $this->values[$this->keys[$key]]; + $isHit = \true; + if ('N;' === $value) { + $value = null; + } elseif ($value instanceof \Closure) { + try { + $value = $value(); + } catch (\Throwable $e) { + $value = null; + $isHit = \false; + } + } + return (self::$createCacheItem)($key, $value, $isHit); + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + } + if (null === $this->values) { + $this->initialize(); + } + return $this->generateItems($keys); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if (null === $this->values) { + $this->initialize(); + } + return isset($this->keys[$key]) || $this->pool->hasItem($key); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if (null === $this->values) { + $this->initialize(); + } + return !isset($this->keys[$key]) && $this->pool->deleteItem($key); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + $deleted = \true; + $fallbackKeys = []; + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if (isset($this->keys[$key])) { + $deleted = \false; + } else { + $fallbackKeys[] = $key; + } + } + if (null === $this->values) { + $this->initialize(); + } + if ($fallbackKeys) { + $deleted = $this->pool->deleteItems($fallbackKeys) && $deleted; + } + return $deleted; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + if (null === $this->values) { + $this->initialize(); + } + return !isset($this->keys[$item->getKey()]) && $this->pool->save($item); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + if (null === $this->values) { + $this->initialize(); + } + return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + return $this->pool->commit(); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + $this->keys = $this->values = []; + $cleared = @unlink($this->file) || !file_exists($this->file); + unset(self::$valuesCache[$this->file]); + if ($this->pool instanceof AdapterInterface) { + return $this->pool->clear($prefix) && $cleared; + } + return $this->pool->clear() && $cleared; + } + /** + * Store an array of cached values. + * + * @param array $values The cached values + * + * @return string[] A list of classes to preload on PHP 7.4+ + */ + public function warmUp(array $values) + { + if (file_exists($this->file)) { + if (!is_file($this->file)) { + throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: "%s".', $this->file)); + } + if (!is_writable($this->file)) { + throw new InvalidArgumentException(sprintf('Cache file is not writable: "%s".', $this->file)); + } + } else { + $directory = \dirname($this->file); + if (!is_dir($directory) && !@mkdir($directory, 0777, \true)) { + throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: "%s".', $directory)); + } + if (!is_writable($directory)) { + throw new InvalidArgumentException(sprintf('Cache directory is not writable: "%s".', $directory)); + } + } + $preload = []; + $dumpedValues = ''; + $dumpedMap = []; + $dump = <<<'EOF' + $value) { + CacheItem::validateKey(\is_int($key) ? (string) $key : $key); + $isStaticValue = \true; + if (null === $value) { + $value = "'N;'"; + } elseif (\is_object($value) || \is_array($value)) { + try { + $value = VarExporter::export($value, $isStaticValue, $preload); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)), 0, $e); + } + } elseif (\is_string($value)) { + // Wrap "N;" in a closure to not confuse it with an encoded `null` + if ('N;' === $value) { + $isStaticValue = \false; + } + $value = var_export($value, \true); + } elseif (!\is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value))); + } else { + $value = var_export($value, \true); + } + if (!$isStaticValue) { + $value = str_replace("\n", "\n ", $value); + $value = "static function () {\n return {$value};\n}"; + } + $hash = hash('md5', $value); + if (null === $id = $dumpedMap[$hash] ?? null) { + $id = $dumpedMap[$hash] = \count($dumpedMap); + $dumpedValues .= "{$id} => {$value},\n"; + } + $dump .= var_export($key, \true) . " => {$id},\n"; + } + $dump .= "\n], [\n\n{$dumpedValues}\n]];\n"; + $tmpFile = uniqid($this->file, \true); + file_put_contents($tmpFile, $dump); + @chmod($tmpFile, 0666 & ~umask()); + unset($serialized, $value, $dump); + @rename($tmpFile, $this->file); + unset(self::$valuesCache[$this->file]); + $this->initialize(); + return $preload; + } + /** + * Load the cache file. + */ + private function initialize() + { + if (isset(self::$valuesCache[$this->file])) { + $values = self::$valuesCache[$this->file]; + } elseif (!is_file($this->file)) { + $this->keys = $this->values = []; + return; + } else { + $values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []]; + } + if (2 !== \count($values) || !isset($values[0], $values[1])) { + $this->keys = $this->values = []; + } else { + [$this->keys, $this->values] = $values; + } + } + private function generateItems(array $keys): \Generator + { + $f = self::$createCacheItem; + $fallbackKeys = []; + foreach ($keys as $key) { + if (isset($this->keys[$key])) { + $value = $this->values[$this->keys[$key]]; + if ('N;' === $value) { + yield $key => $f($key, null, \true); + } elseif ($value instanceof \Closure) { + try { + yield $key => $f($key, $value(), \true); + } catch (\Throwable $e) { + yield $key => $f($key, null, \false); + } + } else { + yield $key => $f($key, $value, \true); + } + } else { + $fallbackKeys[] = $key; + } + } + if ($fallbackKeys) { + yield from $this->pool->getItems($fallbackKeys); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/PhpFilesAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/PhpFilesAdapter.php new file mode 100644 index 0000000000..dfd6d4a6b7 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/PhpFilesAdapter.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\FilesystemCommonTrait; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\VarExporter; +/** + * @author Piotr Stankowski + * @author Nicolas Grekas + * @author Rob Frawley 2nd + */ +class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface +{ + use FilesystemCommonTrait { + doClear as private doCommonClear; + doDelete as private doCommonDelete; + } + private $includeHandler; + private $appendOnly; + private $values = []; + private $files = []; + private static $startTime; + private static $valuesCache = []; + /** + * @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire. + * Doing so is encouraged because it fits perfectly OPcache's memory model. + * + * @throws CacheException if OPcache is not enabled + */ + public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $directory = null, bool $appendOnly = \false) + { + $this->appendOnly = $appendOnly; + self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time(); + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + $this->includeHandler = static function ($type, $msg, $file, $line) { + throw new \ErrorException($msg, 0, $type, $file, $line); + }; + } + public static function isSupported() + { + self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time(); + return \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN)); + } + /** + * @return bool + */ + public function prune() + { + $time = time(); + $pruned = \true; + $getExpiry = \true; + set_error_handler($this->includeHandler); + try { + foreach ($this->scanHashDir($this->directory) as $file) { + try { + if (\is_array($expiresAt = include $file)) { + $expiresAt = $expiresAt[0]; + } + } catch (\ErrorException $e) { + $expiresAt = $time; + } + if ($time >= $expiresAt) { + $pruned = ($this->doUnlink($file) || !file_exists($file)) && $pruned; + } + } + } finally { + restore_error_handler(); + } + return $pruned; + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + if ($this->appendOnly) { + $now = 0; + $missingIds = []; + } else { + $now = time(); + $missingIds = $ids; + $ids = []; + } + $values = []; + begin: + $getExpiry = \false; + foreach ($ids as $id) { + if (null === $value = $this->values[$id] ?? null) { + $missingIds[] = $id; + } elseif ('N;' === $value) { + $values[$id] = null; + } elseif (!\is_object($value)) { + $values[$id] = $value; + } elseif (!$value instanceof LazyValue) { + $values[$id] = $value(); + } elseif (\false === $values[$id] = include $value->file) { + unset($values[$id], $this->values[$id]); + $missingIds[] = $id; + } + if (!$this->appendOnly) { + unset($this->values[$id]); + } + } + if (!$missingIds) { + return $values; + } + set_error_handler($this->includeHandler); + try { + $getExpiry = \true; + foreach ($missingIds as $k => $id) { + try { + $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id); + if (isset(self::$valuesCache[$file])) { + [$expiresAt, $this->values[$id]] = self::$valuesCache[$file]; + } elseif (\is_array($expiresAt = include $file)) { + if ($this->appendOnly) { + self::$valuesCache[$file] = $expiresAt; + } + [$expiresAt, $this->values[$id]] = $expiresAt; + } elseif ($now < $expiresAt) { + $this->values[$id] = new LazyValue($file); + } + if ($now >= $expiresAt) { + unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]); + } + } catch (\ErrorException $e) { + unset($missingIds[$k]); + } + } + } finally { + restore_error_handler(); + } + $ids = $missingIds; + $missingIds = []; + goto begin; + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id) + { + if ($this->appendOnly && isset($this->values[$id])) { + return \true; + } + set_error_handler($this->includeHandler); + try { + $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id); + $getExpiry = \true; + if (isset(self::$valuesCache[$file])) { + [$expiresAt, $value] = self::$valuesCache[$file]; + } elseif (\is_array($expiresAt = include $file)) { + if ($this->appendOnly) { + self::$valuesCache[$file] = $expiresAt; + } + [$expiresAt, $value] = $expiresAt; + } elseif ($this->appendOnly) { + $value = new LazyValue($file); + } + } catch (\ErrorException $e) { + return \false; + } finally { + restore_error_handler(); + } + if ($this->appendOnly) { + $now = 0; + $this->values[$id] = $value; + } else { + $now = time(); + } + return $now < $expiresAt; + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + $ok = \true; + $expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX'; + $allowCompile = self::isSupported(); + foreach ($values as $key => $value) { + unset($this->values[$key]); + $isStaticValue = \true; + if (null === $value) { + $value = "'N;'"; + } elseif (\is_object($value) || \is_array($value)) { + try { + $value = VarExporter::export($value, $isStaticValue); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)), 0, $e); + } + } elseif (\is_string($value)) { + // Wrap "N;" in a closure to not confuse it with an encoded `null` + if ('N;' === $value) { + $isStaticValue = \false; + } + $value = var_export($value, \true); + } elseif (!\is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value))); + } else { + $value = var_export($value, \true); + } + $encodedKey = rawurlencode($key); + if ($isStaticValue) { + $value = "return [{$expiry}, {$value}];"; + } elseif ($this->appendOnly) { + $value = "return [{$expiry}, static function () { return {$value}; }];"; + } else { + // We cannot use a closure here because of https://bugs.php.net/76982 + $value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value); + $value = "namespace Symfony\\Component\\VarExporter\\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};"; + } + $file = $this->files[$key] = $this->getFile($key, \true); + // Since OPcache only compiles files older than the script execution start, set the file's mtime in the past + $ok = $this->write($file, "directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s).', $this->directory)); + } + return $ok; + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + $this->values = []; + return $this->doCommonClear($namespace); + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + foreach ($ids as $id) { + unset($this->values[$id]); + } + return $this->doCommonDelete($ids); + } + protected function doUnlink(string $file) + { + unset(self::$valuesCache[$file]); + if (self::isSupported()) { + @opcache_invalidate($file, \true); + } + return @unlink($file); + } + private function getFileKey(string $file): string + { + if (!$h = @fopen($file, 'r')) { + return ''; + } + $encodedKey = substr(fgets($h), 8); + fclose($h); + return rawurldecode(rtrim($encodedKey)); + } +} +/** + * @internal + */ +class LazyValue +{ + public $file; + public function __construct(string $file) + { + $this->file = $file; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/ProxyAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ProxyAdapter.php new file mode 100644 index 0000000000..699c23b6cf --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/ProxyAdapter.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ContractsTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ProxyTrait; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +/** + * @author Nicolas Grekas + */ +class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + use ContractsTrait; + use ProxyTrait; + private $namespace = ''; + private $namespaceLen; + private $poolHash; + private $defaultLifetime; + private static $createCacheItem; + private static $setInnerItem; + public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0) + { + $this->pool = $pool; + $this->poolHash = $poolHash = spl_object_hash($pool); + if ('' !== $namespace) { + \assert('' !== CacheItem::validateKey($namespace)); + $this->namespace = $namespace; + } + $this->namespaceLen = \strlen($namespace); + $this->defaultLifetime = $defaultLifetime; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(static function ($key, $innerItem, $poolHash) { + $item = new CacheItem(); + $item->key = $key; + if (null === $innerItem) { + return $item; + } + $item->value = $v = $innerItem->get(); + $item->isHit = $innerItem->isHit(); + $item->innerItem = $innerItem; + $item->poolHash = $poolHash; + // Detect wrapped values that encode for their expiry and creation duration + // For compactness, these values are packed in the key of an array using + // magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9d" === $k[0] && "\x00" === $k[5] && "_" === $k[9]) { + $item->value = $v[$k]; + $v = unpack('Ve/Nc', substr($k, 1, -1)); + $item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET; + $item->metadata[CacheItem::METADATA_CTIME] = $v['c']; + } elseif ($innerItem instanceof CacheItem) { + $item->metadata = $innerItem->metadata; + } + $innerItem->set(null); + return $item; + }, null, CacheItem::class); + self::$setInnerItem ?? self::$setInnerItem = \Closure::bind( + /** + * @param array $item A CacheItem cast to (array); accessing protected properties requires adding the "\0*\0" PHP prefix + */ + static function (CacheItemInterface $innerItem, array $item) { + // Tags are stored separately, no need to account for them when considering this item's newly set metadata + if (isset(($metadata = $item["\x00*\x00newMetadata"])[CacheItem::METADATA_TAGS])) { + unset($metadata[CacheItem::METADATA_TAGS]); + } + if ($metadata) { + // For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators + $item["\x00*\x00value"] = ["\x9d" . pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]) . "_" => $item["\x00*\x00value"]]; + } + $innerItem->set($item["\x00*\x00value"]); + $innerItem->expiresAt((null !== $item["\x00*\x00expiry"]) ? \DateTime::createFromFormat('U.u', sprintf('%.6F', $item["\x00*\x00expiry"])) : null); + }, + null, + CacheItem::class + ); + } + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + if (!$this->pool instanceof CacheInterface) { + return $this->doGet($this, $key, $callback, $beta, $metadata); + } + return $this->pool->get($this->getId($key), function ($innerItem, bool &$save) use ($key, $callback) { + $item = (self::$createCacheItem)($key, $innerItem, $this->poolHash); + $item->set($value = $callback($item, $save)); + (self::$setInnerItem)($innerItem, (array) $item); + return $value; + }, $beta, $metadata); + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $item = $this->pool->getItem($this->getId($key)); + return (self::$createCacheItem)($key, $item, $this->poolHash); + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + return $this->generateItems($this->pool->getItems($keys)); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + return $this->pool->hasItem($this->getId($key)); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + if ($this->pool instanceof AdapterInterface) { + return $this->pool->clear($this->namespace . $prefix); + } + return $this->pool->clear(); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + return $this->pool->deleteItem($this->getId($key)); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + return $this->pool->deleteItems($keys); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + return $this->doSave($item, __FUNCTION__); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->doSave($item, __FUNCTION__); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + return $this->pool->commit(); + } + private function doSave(CacheItemInterface $item, string $method) + { + if (!$item instanceof CacheItem) { + return \false; + } + $item = (array) $item; + if (null === $item["\x00*\x00expiry"] && 0 < $this->defaultLifetime) { + $item["\x00*\x00expiry"] = microtime(\true) + $this->defaultLifetime; + } + if ($item["\x00*\x00poolHash"] === $this->poolHash && $item["\x00*\x00innerItem"]) { + $innerItem = $item["\x00*\x00innerItem"]; + } elseif ($this->pool instanceof AdapterInterface) { + // this is an optimization specific for AdapterInterface implementations + // so we can save a round-trip to the backend by just creating a new item + $innerItem = (self::$createCacheItem)($this->namespace . $item["\x00*\x00key"], null, $this->poolHash); + } else { + $innerItem = $this->pool->getItem($this->namespace . $item["\x00*\x00key"]); + } + (self::$setInnerItem)($innerItem, $item); + return $this->pool->{$method}($innerItem); + } + private function generateItems(iterable $items): \Generator + { + $f = self::$createCacheItem; + foreach ($items as $key => $item) { + if ($this->namespaceLen) { + $key = substr($key, $this->namespaceLen); + } + yield $key => $f($key, $item, $this->poolHash); + } + } + private function getId($key): string + { + \assert('' !== CacheItem::validateKey($key)); + return $this->namespace . $key; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/Psr16Adapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/Psr16Adapter.php new file mode 100644 index 0000000000..e2b98d2aaa --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/Psr16Adapter.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\SimpleCache\CacheInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ProxyTrait; +/** + * Turns a PSR-16 cache into a PSR-6 one. + * + * @author Nicolas Grekas + */ +class Psr16Adapter extends AbstractAdapter implements PruneableInterface, ResettableInterface +{ + use ProxyTrait; + /** + * @internal + */ + protected const NS_SEPARATOR = '_'; + private $miss; + public function __construct(CacheInterface $pool, string $namespace = '', int $defaultLifetime = 0) + { + parent::__construct($namespace, $defaultLifetime); + $this->pool = $pool; + $this->miss = new \stdClass(); + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) { + if ($this->miss !== $value) { + yield $key => $value; + } + } + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id) + { + return $this->pool->has($id); + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + return $this->pool->clear(); + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + return $this->pool->deleteMultiple($ids); + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + return $this->pool->setMultiple($values, (0 === $lifetime) ? null : $lifetime); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/RedisAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/RedisAdapter.php new file mode 100644 index 0000000000..0b36be329b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/RedisAdapter.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\RedisClusterProxy; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\RedisProxy; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\RedisTrait; +class RedisAdapter extends AbstractAdapter +{ + use RedisTrait; + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client + * @param string $namespace The default namespace + * @param int $defaultLifetime The default lifetime + */ + public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null) + { + $this->init($redis, $namespace, $defaultLifetime, $marshaller); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php new file mode 100644 index 0000000000..5b71fc409a --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php @@ -0,0 +1,278 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Predis\Connection\Aggregate\ClusterInterface; +use Wordlift\Modules\Common\Predis\Connection\Aggregate\PredisCluster; +use Wordlift\Modules\Common\Predis\Connection\Aggregate\ReplicationInterface; +use Wordlift\Modules\Common\Predis\Response\ErrorInterface; +use Wordlift\Modules\Common\Predis\Response\Status; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\LogicException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DeflateMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\TagAwareMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\RedisClusterProxy; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\RedisProxy; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\RedisTrait; +/** + * Stores tag id <> cache id relationship as a Redis Set. + * + * Set (tag relation info) is stored without expiry (non-volatile), while cache always gets an expiry (volatile) even + * if not set by caller. Thus if you configure redis with the right eviction policy you can be safe this tag <> cache + * relationship survives eviction (cache cleanup when Redis runs out of memory). + * + * Redis server 2.8+ with any `volatile-*` eviction policy, OR `noeviction` if you're sure memory will NEVER fill up + * + * Design limitations: + * - Max 4 billion cache keys per cache tag as limited by Redis Set datatype. + * E.g. If you use a "all" items tag for expiry instead of clear(), that limits you to 4 billion cache items also. + * + * @see https://redis.io/topics/lru-cache#eviction-policies Documentation for Redis eviction policies. + * @see https://redis.io/topics/data-types#sets Documentation for Redis Set datatype. + * + * @author Nicolas Grekas + * @author André Rømcke + */ +class RedisTagAwareAdapter extends AbstractTagAwareAdapter +{ + use RedisTrait; + /** + * On cache items without a lifetime set, we set it to 100 days. This is to make sure cache items are + * preferred to be evicted over tag Sets, if eviction policy is configured according to requirements. + */ + private const DEFAULT_CACHE_TTL = 8640000; + /** + * @var string|null detected eviction policy used on Redis server + */ + private $redisEvictionPolicy; + private $namespace; + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client + * @param string $namespace The default namespace + * @param int $defaultLifetime The default lifetime + */ + public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null) + { + if ($redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface && $redis->getConnection() instanceof ClusterInterface && !$redis->getConnection() instanceof PredisCluster) { + throw new InvalidArgumentException(sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, get_debug_type($redis->getConnection()))); + } + if (\defined('Redis::OPT_COMPRESSION') && ($redis instanceof \Redis || $redis instanceof \RedisArray || $redis instanceof \RedisCluster)) { + $compression = $redis->getOption(\Redis::OPT_COMPRESSION); + foreach (\is_array($compression) ? $compression : [$compression] as $c) { + if (\Redis::COMPRESSION_NONE !== $c) { + throw new InvalidArgumentException(sprintf('phpredis compression must be disabled when using "%s", use "%s" instead.', static::class, DeflateMarshaller::class)); + } + } + } + $this->init($redis, $namespace, $defaultLifetime, new TagAwareMarshaller($marshaller)); + $this->namespace = $namespace; + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime, array $addTagData = [], array $delTagData = []): array + { + $eviction = $this->getRedisEvictionPolicy(); + if ('noeviction' !== $eviction && !str_starts_with($eviction, 'volatile-')) { + throw new LogicException(sprintf('Redis maxmemory-policy setting "%s" is *not* supported by RedisTagAwareAdapter, use "noeviction" or "volatile-*" eviction policies.', $eviction)); + } + // serialize values + if (!$serialized = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + // While pipeline isn't supported on RedisCluster, other setups will at least benefit from doing this in one op + $results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData, $failed) { + // Store cache items, force a ttl if none is set, as there is no MSETEX we need to set each one + foreach ($serialized as $id => $value) { + yield 'setEx' => [$id, (0 >= $lifetime) ? self::DEFAULT_CACHE_TTL : $lifetime, $value]; + } + // Add and Remove Tags + foreach ($addTagData as $tagId => $ids) { + if (!$failed || $ids = array_diff($ids, $failed)) { + yield 'sAdd' => array_merge([$tagId], $ids); + } + } + foreach ($delTagData as $tagId => $ids) { + if (!$failed || $ids = array_diff($ids, $failed)) { + yield 'sRem' => array_merge([$tagId], $ids); + } + } + }); + foreach ($results as $id => $result) { + // Skip results of SADD/SREM operations, they'll be 1 or 0 depending on if set value already existed or not + if (is_numeric($result)) { + continue; + } + // setEx results + if (\true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) { + $failed[] = $id; + } + } + return $failed; + } + /** + * {@inheritdoc} + */ + protected function doDeleteYieldTags(array $ids): iterable + { + $lua = <<<'EOLUA' + local v = redis.call('GET', KEYS[1]) + local e = redis.pcall('UNLINK', KEYS[1]) + + if type(e) ~= 'number' then + redis.call('DEL', KEYS[1]) + end + + if not v or v:len() <= 13 or v:byte(1) ~= 0x9D or v:byte(6) ~= 0 or v:byte(10) ~= 0x5F then + return '' + end + + return v:sub(14, 13 + v:byte(13) + v:byte(12) * 256 + v:byte(11) * 65536) +EOLUA; + $results = $this->pipeline(function () use ($ids, $lua) { + foreach ($ids as $id) { + yield 'eval' => ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) ? [$lua, 1, $id] : [$lua, [$id], 1]; + } + }); + foreach ($results as $id => $result) { + if ($result instanceof \RedisException || $result instanceof ErrorInterface) { + CacheItem::log($this->logger, 'Failed to delete key "{key}": ' . $result->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $result]); + continue; + } + try { + yield $id => (!\is_string($result) || '' === $result) ? [] : $this->marshaller->unmarshall($result); + } catch (\Exception $e) { + yield $id => []; + } + } + } + /** + * {@inheritdoc} + */ + protected function doDeleteTagRelations(array $tagData): bool + { + $results = $this->pipeline(static function () use ($tagData) { + foreach ($tagData as $tagId => $idList) { + array_unshift($idList, $tagId); + yield 'sRem' => $idList; + } + }); + foreach ($results as $result) { + // no-op + } + return \true; + } + /** + * {@inheritdoc} + */ + protected function doInvalidate(array $tagIds): bool + { + // This script scans the set of items linked to tag: it empties the set + // and removes the linked items. When the set is still not empty after + // the scan, it means we're in cluster mode and that the linked items + // are on other nodes: we move the links to a temporary set and we + // garbage collect that set from the client side. + $lua = <<<'EOLUA' + redis.replicate_commands() + + local cursor = '0' + local id = KEYS[1] + repeat + local result = redis.call('SSCAN', id, cursor, 'COUNT', 5000); + cursor = result[1]; + local rems = {} + + for _, v in ipairs(result[2]) do + local ok, _ = pcall(redis.call, 'DEL', ARGV[1]..v) + if ok then + table.insert(rems, v) + end + end + if 0 < #rems then + redis.call('SREM', id, unpack(rems)) + end + until '0' == cursor; + + redis.call('SUNIONSTORE', '{'..id..'}'..id, id) + redis.call('DEL', id) + + return redis.call('SSCAN', '{'..id..'}'..id, '0', 'COUNT', 5000) +EOLUA; + $results = $this->pipeline(function () use ($tagIds, $lua) { + if ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) { + $prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : ''; + } elseif (\is_array($prefix = $this->redis->getOption(\Redis::OPT_PREFIX) ?? '')) { + $prefix = current($prefix); + } + foreach ($tagIds as $id) { + yield 'eval' => ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) ? [$lua, 1, $id, $prefix] : [$lua, [$id, $prefix], 1]; + } + }); + $lua = <<<'EOLUA' + redis.replicate_commands() + + local id = KEYS[1] + local cursor = table.remove(ARGV) + redis.call('SREM', '{'..id..'}'..id, unpack(ARGV)) + + return redis.call('SSCAN', '{'..id..'}'..id, cursor, 'COUNT', 5000) +EOLUA; + $success = \true; + foreach ($results as $id => $values) { + if ($values instanceof \RedisException || $values instanceof ErrorInterface) { + CacheItem::log($this->logger, 'Failed to invalidate key "{key}": ' . $values->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $values]); + $success = \false; + continue; + } + [$cursor, $ids] = $values; + while ($ids || '0' !== $cursor) { + $this->doDelete($ids); + $evalArgs = [$id, $cursor]; + array_splice($evalArgs, 1, 0, $ids); + if ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) { + array_unshift($evalArgs, $lua, 1); + } else { + $evalArgs = [$lua, $evalArgs, 1]; + } + $results = $this->pipeline(function () use ($evalArgs) { + yield 'eval' => $evalArgs; + }); + foreach ($results as [$cursor, $ids]) { + // no-op + } + } + } + return $success; + } + private function getRedisEvictionPolicy(): string + { + if (null !== $this->redisEvictionPolicy) { + return $this->redisEvictionPolicy; + } + $hosts = $this->getHosts(); + $host = reset($hosts); + if ($host instanceof \Wordlift\Modules\Common\Predis\Client && $host->getConnection() instanceof ReplicationInterface) { + // Predis supports info command only on the master in replication environments + $hosts = [$host->getClientFor('master')]; + } + foreach ($hosts as $host) { + $info = $host->info('Memory'); + if ($info instanceof ErrorInterface) { + continue; + } + $info = $info['Memory'] ?? $info; + return $this->redisEvictionPolicy = $info['maxmemory_policy']; + } + return $this->redisEvictionPolicy = ''; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/TagAwareAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TagAwareAdapter.php new file mode 100644 index 0000000000..4744011162 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TagAwareAdapter.php @@ -0,0 +1,350 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Cache\InvalidArgumentException; +use Wordlift\Modules\Common\Psr\Log\LoggerAwareInterface; +use Wordlift\Modules\Common\Psr\Log\LoggerAwareTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ContractsTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ProxyTrait; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\TagAwareCacheInterface; +/** + * @author Nicolas Grekas + */ +class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface, LoggerAwareInterface +{ + use ContractsTrait; + use LoggerAwareTrait; + use ProxyTrait; + public const TAGS_PREFIX = "\x00tags\x00"; + private $deferred = []; + private $tags; + private $knownTagVersions = []; + private $knownTagVersionsTtl; + private static $createCacheItem; + private static $setCacheItemTags; + private static $getTagsByKey; + private static $saveTags; + public function __construct(AdapterInterface $itemsPool, ?AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15) + { + $this->pool = $itemsPool; + $this->tags = $tagsPool ?: $itemsPool; + $this->knownTagVersionsTtl = $knownTagVersionsTtl; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(static function ($key, $value, CacheItem $protoItem) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->expiry = $protoItem->expiry; + $item->poolHash = $protoItem->poolHash; + return $item; + }, null, CacheItem::class); + self::$setCacheItemTags ?? self::$setCacheItemTags = \Closure::bind(static function (CacheItem $item, $key, array &$itemTags) { + $item->isTaggable = \true; + if (!$item->isHit) { + return $item; + } + if (isset($itemTags[$key])) { + foreach ($itemTags[$key] as $tag => $version) { + $item->metadata[CacheItem::METADATA_TAGS][$tag] = $tag; + } + unset($itemTags[$key]); + } else { + $item->value = null; + $item->isHit = \false; + } + return $item; + }, null, CacheItem::class); + self::$getTagsByKey ?? self::$getTagsByKey = \Closure::bind(static function ($deferred) { + $tagsByKey = []; + foreach ($deferred as $key => $item) { + $tagsByKey[$key] = $item->newMetadata[CacheItem::METADATA_TAGS] ?? []; + $item->metadata = $item->newMetadata; + } + return $tagsByKey; + }, null, CacheItem::class); + self::$saveTags ?? self::$saveTags = \Closure::bind(static function (AdapterInterface $tagsAdapter, array $tags) { + ksort($tags); + foreach ($tags as $v) { + $v->expiry = 0; + $tagsAdapter->saveDeferred($v); + } + return $tagsAdapter->commit(); + }, null, CacheItem::class); + } + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) + { + $ids = []; + foreach ($tags as $tag) { + \assert('' !== CacheItem::validateKey($tag)); + unset($this->knownTagVersions[$tag]); + $ids[] = $tag . static::TAGS_PREFIX; + } + return !$tags || $this->tags->deleteItems($ids); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + if (\is_string($key) && isset($this->deferred[$key])) { + $this->commit(); + } + if (!$this->pool->hasItem($key)) { + return \false; + } + $itemTags = $this->pool->getItem(static::TAGS_PREFIX . $key); + if (!$itemTags->isHit()) { + return \false; + } + if (!$itemTags = $itemTags->get()) { + return \true; + } + foreach ($this->getTagVersions([$itemTags]) as $tag => $version) { + if ($itemTags[$tag] !== $version) { + return \false; + } + } + return \true; + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + foreach ($this->getItems([$key]) as $item) { + return $item; + } + return null; + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + $tagKeys = []; + $commit = \false; + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $commit = $commit || isset($this->deferred[$key]); + $key = static::TAGS_PREFIX . $key; + $tagKeys[$key] = $key; + } + } + if ($commit) { + $this->commit(); + } + try { + $items = $this->pool->getItems($tagKeys + $keys); + } catch (InvalidArgumentException $e) { + $this->pool->getItems($keys); + // Should throw an exception + throw $e; + } + return $this->generateItems($items, $tagKeys); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + if ('' !== $prefix) { + foreach ($this->deferred as $key => $item) { + if (str_starts_with($key, $prefix)) { + unset($this->deferred[$key]); + } + } + } else { + $this->deferred = []; + } + if ($this->pool instanceof AdapterInterface) { + return $this->pool->clear($prefix); + } + return $this->pool->clear(); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + return $this->deleteItems([$key]); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $keys[] = static::TAGS_PREFIX . $key; + } + } + return $this->pool->deleteItems($keys); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return \false; + } + $this->deferred[$item->getKey()] = $item; + return $this->commit(); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return \false; + } + $this->deferred[$item->getKey()] = $item; + return \true; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + if (!$this->deferred) { + return \true; + } + $ok = \true; + foreach ($this->deferred as $key => $item) { + if (!$this->pool->saveDeferred($item)) { + unset($this->deferred[$key]); + $ok = \false; + } + } + $items = $this->deferred; + $tagsByKey = (self::$getTagsByKey)($items); + $this->deferred = []; + $tagVersions = $this->getTagVersions($tagsByKey); + $f = self::$createCacheItem; + foreach ($tagsByKey as $key => $tags) { + $this->pool->saveDeferred($f(static::TAGS_PREFIX . $key, array_intersect_key($tagVersions, $tags), $items[$key])); + } + return $this->pool->commit() && $ok; + } + /** + * @return array + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize ' . __CLASS__); + } + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__); + } + public function __destruct() + { + $this->commit(); + } + private function generateItems(iterable $items, array $tagKeys): \Generator + { + $bufferedItems = $itemTags = []; + $f = self::$setCacheItemTags; + foreach ($items as $key => $item) { + if (!$tagKeys) { + yield $key => $f($item, static::TAGS_PREFIX . $key, $itemTags); + continue; + } + if (!isset($tagKeys[$key])) { + $bufferedItems[$key] = $item; + continue; + } + unset($tagKeys[$key]); + if ($item->isHit()) { + $itemTags[$key] = $item->get() ?: []; + } + if (!$tagKeys) { + $tagVersions = $this->getTagVersions($itemTags); + foreach ($itemTags as $key => $tags) { + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] !== $version) { + unset($itemTags[$key]); + continue 2; + } + } + } + $tagVersions = $tagKeys = null; + foreach ($bufferedItems as $key => $item) { + yield $key => $f($item, static::TAGS_PREFIX . $key, $itemTags); + } + $bufferedItems = null; + } + } + } + private function getTagVersions(array $tagsByKey) + { + $tagVersions = []; + $fetchTagVersions = \false; + foreach ($tagsByKey as $tags) { + $tagVersions += $tags; + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] !== $version) { + unset($this->knownTagVersions[$tag]); + } + } + } + if (!$tagVersions) { + return []; + } + $now = microtime(\true); + $tags = []; + foreach ($tagVersions as $tag => $version) { + $tags[$tag . static::TAGS_PREFIX] = $tag; + if ($fetchTagVersions || ($this->knownTagVersions[$tag][1] ?? null) !== $version || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) { + // reuse previously fetched tag versions up to the ttl + $fetchTagVersions = \true; + } + } + if (!$fetchTagVersions) { + return $tagVersions; + } + $newTags = []; + $newVersion = null; + foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { + if (!$version->isHit()) { + $newTags[$tag] = $version->set($newVersion ?? $newVersion = random_int(\PHP_INT_MIN, \PHP_INT_MAX)); + } + $tagVersions[$tag = $tags[$tag]] = $version->get(); + $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; + } + if ($newTags) { + (self::$saveTags)($this->tags, $newTags); + } + return $tagVersions; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php new file mode 100644 index 0000000000..fb66ae621c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\InvalidArgumentException; +/** + * Interface for invalidating cached items using tags. + * + * @author Nicolas Grekas + */ +interface TagAwareAdapterInterface extends AdapterInterface +{ + /** + * Invalidates cached items using tags. + * + * @param string[] $tags An array of tags to invalidate + * + * @return bool + * + * @throws InvalidArgumentException When $tags is not valid + */ + public function invalidateTags(array $tags); +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/TraceableAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TraceableAdapter.php new file mode 100644 index 0000000000..8d741885e4 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TraceableAdapter.php @@ -0,0 +1,266 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\ResettableInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ResetInterface; +/** + * An adapter that collects data about all cache calls. + * + * @author Aaron Scherer + * @author Tobias Nyholm + * @author Nicolas Grekas + */ +class TraceableAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + protected $pool; + private $calls = []; + public function __construct(AdapterInterface $pool) + { + $this->pool = $pool; + } + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null) + { + if (!$this->pool instanceof CacheInterface) { + throw new \BadMethodCallException(sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".', get_debug_type($this->pool), CacheInterface::class)); + } + $isHit = \true; + $callback = function (CacheItem $item, bool &$save) use ($callback, &$isHit) { + $isHit = $item->isHit(); + return $callback($item, $save); + }; + $event = $this->start(__FUNCTION__); + try { + $value = $this->pool->get($key, $callback, $beta, $metadata); + $event->result[$key] = get_debug_type($value); + } finally { + $event->end = microtime(\true); + } + if ($isHit) { + ++$event->hits; + } else { + ++$event->misses; + } + return $value; + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $event = $this->start(__FUNCTION__); + try { + $item = $this->pool->getItem($key); + } finally { + $event->end = microtime(\true); + } + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + return $item; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->hasItem($key); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->save($item); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->saveDeferred($item); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + $event = $this->start(__FUNCTION__); + try { + $result = $this->pool->getItems($keys); + } finally { + $event->end = microtime(\true); + } + $f = function () use ($result, $event) { + $event->result = []; + foreach ($result as $key => $item) { + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + yield $key => $item; + } + }; + return $f(); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + $event = $this->start(__FUNCTION__); + try { + if ($this->pool instanceof AdapterInterface) { + return $event->result = $this->pool->clear($prefix); + } + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + $event = $this->start(__FUNCTION__); + $event->result['keys'] = $keys; + try { + return $event->result['result'] = $this->pool->deleteItems($keys); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function commit() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->commit(); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + */ + public function prune() + { + if (!$this->pool instanceof PruneableInterface) { + return \false; + } + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->prune(); + } finally { + $event->end = microtime(\true); + } + } + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResetInterface) { + $this->pool->reset(); + } + $this->clearCalls(); + } + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(\true); + } + } + public function getCalls() + { + return $this->calls; + } + public function clearCalls() + { + $this->calls = []; + } + protected function start(string $name) + { + $this->calls[] = $event = new TraceableAdapterEvent(); + $event->name = $name; + $event->start = microtime(\true); + return $event; + } +} +class TraceableAdapterEvent +{ + public $name; + public $start; + public $end; + public $result; + public $hits = 0; + public $misses = 0; +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php new file mode 100644 index 0000000000..fff64d6e15 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Adapter; + +use Wordlift\Modules\Common\Symfony\Contracts\Cache\TagAwareCacheInterface; +/** + * @author Robin Chalas + */ +class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface +{ + public function __construct(TagAwareAdapterInterface $pool) + { + parent::__construct($pool); + } + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->invalidateTags($tags); + } finally { + $event->end = microtime(\true); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/CacheItem.php b/src/modules/common/third-party/vendor/symfony/cache/CacheItem.php new file mode 100644 index 0000000000..85b232f35d --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/CacheItem.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache; + +use Wordlift\Modules\Common\Psr\Log\LoggerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\LogicException; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\ItemInterface; +/** + * @author Nicolas Grekas + */ +final class CacheItem implements ItemInterface +{ + private const METADATA_EXPIRY_OFFSET = 1527506807; + protected $key; + protected $value; + protected $isHit = \false; + protected $expiry; + protected $metadata = []; + protected $newMetadata = []; + protected $innerItem; + protected $poolHash; + protected $isTaggable = \false; + /** + * {@inheritdoc} + */ + public function getKey(): string + { + return $this->key; + } + /** + * {@inheritdoc} + * + * @return mixed + */ + public function get() + { + return $this->value; + } + /** + * {@inheritdoc} + */ + public function isHit(): bool + { + return $this->isHit; + } + /** + * {@inheritdoc} + * + * @return $this + */ + public function set($value): self + { + $this->value = $value; + return $this; + } + /** + * {@inheritdoc} + * + * @return $this + */ + public function expiresAt($expiration): self + { + if (null === $expiration) { + $this->expiry = null; + } elseif ($expiration instanceof \DateTimeInterface) { + $this->expiry = (float) $expiration->format('U.u'); + } else { + throw new InvalidArgumentException(sprintf('Expiration date must implement DateTimeInterface or be null, "%s" given.', get_debug_type($expiration))); + } + return $this; + } + /** + * {@inheritdoc} + * + * @return $this + */ + public function expiresAfter($time): self + { + if (null === $time) { + $this->expiry = null; + } elseif ($time instanceof \DateInterval) { + $this->expiry = microtime(\true) + \DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); + } elseif (\is_int($time)) { + $this->expiry = $time + microtime(\true); + } else { + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', get_debug_type($time))); + } + return $this; + } + /** + * {@inheritdoc} + */ + public function tag($tags): ItemInterface + { + if (!$this->isTaggable) { + throw new LogicException(sprintf('Cache item "%s" comes from a non tag-aware pool: you cannot tag it.', $this->key)); + } + if (!is_iterable($tags)) { + $tags = [$tags]; + } + foreach ($tags as $tag) { + if (!\is_string($tag) && !(\is_object($tag) && method_exists($tag, '__toString'))) { + throw new InvalidArgumentException(sprintf('Cache tag must be string or object that implements __toString(), "%s" given.', \is_object($tag) ? \get_class($tag) : \gettype($tag))); + } + $tag = (string) $tag; + if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) { + continue; + } + if ('' === $tag) { + throw new InvalidArgumentException('Cache tag length must be greater than zero.'); + } + if (\false !== strpbrk($tag, self::RESERVED_CHARACTERS)) { + throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters "%s".', $tag, self::RESERVED_CHARACTERS)); + } + $this->newMetadata[self::METADATA_TAGS][$tag] = $tag; + } + return $this; + } + /** + * {@inheritdoc} + */ + public function getMetadata(): array + { + return $this->metadata; + } + /** + * Validates a cache key according to PSR-6. + * + * @param mixed $key The key to validate + * + * @throws InvalidArgumentException When $key is not valid + */ + public static function validateKey($key): string + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if ('' === $key) { + throw new InvalidArgumentException('Cache key length must be greater than zero.'); + } + if (\false !== strpbrk($key, self::RESERVED_CHARACTERS)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS)); + } + return $key; + } + /** + * Internal logging helper. + * + * @internal + */ + public static function log(?LoggerInterface $logger, string $message, array $context = []) + { + if ($logger) { + $logger->warning($message, $context); + } else { + $replace = []; + foreach ($context as $k => $v) { + if (\is_scalar($v)) { + $replace['{' . $k . '}'] = $v; + } + } + @trigger_error(strtr($message, $replace), \E_USER_WARNING); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/DataCollector/CacheDataCollector.php b/src/modules/common/third-party/vendor/symfony/cache/DataCollector/CacheDataCollector.php new file mode 100644 index 0000000000..d0a32de8c2 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/DataCollector/CacheDataCollector.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\DataCollector; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\TraceableAdapter; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\TraceableAdapterEvent; +use Wordlift\Modules\Common\Symfony\Component\HttpFoundation\Request; +use Wordlift\Modules\Common\Symfony\Component\HttpFoundation\Response; +use Wordlift\Modules\Common\Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Wordlift\Modules\Common\Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; +/** + * @author Aaron Scherer + * @author Tobias Nyholm + * + * @final + */ +class CacheDataCollector extends DataCollector implements LateDataCollectorInterface +{ + /** + * @var TraceableAdapter[] + */ + private $instances = []; + public function addInstance(string $name, TraceableAdapter $instance) + { + $this->instances[$name] = $instance; + } + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, ?\Throwable $exception = null) + { + $empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []]; + $this->data = ['instances' => $empty, 'total' => $empty]; + foreach ($this->instances as $name => $instance) { + $this->data['instances']['calls'][$name] = $instance->getCalls(); + } + $this->data['instances']['statistics'] = $this->calculateStatistics(); + $this->data['total']['statistics'] = $this->calculateTotalStatistics(); + } + public function reset() + { + $this->data = []; + foreach ($this->instances as $instance) { + $instance->clearCalls(); + } + } + public function lateCollect() + { + $this->data['instances']['calls'] = $this->cloneVar($this->data['instances']['calls']); + } + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'cache'; + } + /** + * Method returns amount of logged Cache reads: "get" calls. + */ + public function getStatistics(): array + { + return $this->data['instances']['statistics']; + } + /** + * Method returns the statistic totals. + */ + public function getTotals(): array + { + return $this->data['total']['statistics']; + } + /** + * Method returns all logged Cache call objects. + * + * @return mixed + */ + public function getCalls() + { + return $this->data['instances']['calls']; + } + private function calculateStatistics(): array + { + $statistics = []; + foreach ($this->data['instances']['calls'] as $name => $calls) { + $statistics[$name] = ['calls' => 0, 'time' => 0, 'reads' => 0, 'writes' => 0, 'deletes' => 0, 'hits' => 0, 'misses' => 0]; + /** @var TraceableAdapterEvent $call */ + foreach ($calls as $call) { + ++$statistics[$name]['calls']; + $statistics[$name]['time'] += ($call->end ?? microtime(\true)) - $call->start; + if ('get' === $call->name) { + ++$statistics[$name]['reads']; + if ($call->hits) { + ++$statistics[$name]['hits']; + } else { + ++$statistics[$name]['misses']; + ++$statistics[$name]['writes']; + } + } elseif ('getItem' === $call->name) { + ++$statistics[$name]['reads']; + if ($call->hits) { + ++$statistics[$name]['hits']; + } else { + ++$statistics[$name]['misses']; + } + } elseif ('getItems' === $call->name) { + $statistics[$name]['reads'] += $call->hits + $call->misses; + $statistics[$name]['hits'] += $call->hits; + $statistics[$name]['misses'] += $call->misses; + } elseif ('hasItem' === $call->name) { + ++$statistics[$name]['reads']; + foreach ($call->result ?? [] as $result) { + ++$statistics[$name][$result ? 'hits' : 'misses']; + } + } elseif ('save' === $call->name) { + ++$statistics[$name]['writes']; + } elseif ('deleteItem' === $call->name) { + ++$statistics[$name]['deletes']; + } + } + if ($statistics[$name]['reads']) { + $statistics[$name]['hit_read_ratio'] = round(100 * $statistics[$name]['hits'] / $statistics[$name]['reads'], 2); + } else { + $statistics[$name]['hit_read_ratio'] = null; + } + } + return $statistics; + } + private function calculateTotalStatistics(): array + { + $statistics = $this->getStatistics(); + $totals = ['calls' => 0, 'time' => 0, 'reads' => 0, 'writes' => 0, 'deletes' => 0, 'hits' => 0, 'misses' => 0]; + foreach ($statistics as $name => $values) { + foreach ($totals as $key => $value) { + $totals[$key] += $statistics[$name][$key]; + } + } + if ($totals['reads']) { + $totals['hit_read_ratio'] = round(100 * $totals['hits'] / $totals['reads'], 2); + } else { + $totals['hit_read_ratio'] = null; + } + return $totals; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php new file mode 100644 index 0000000000..38bf229b30 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CacheCollectorPass.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 Wordlift\Modules\Common\Symfony\Component\Cache\DependencyInjection; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\TraceableAdapter; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ContainerBuilder; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Definition; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Reference; +/** + * Inject a data collector to all the cache services to be able to get detailed statistics. + * + * @author Tobias Nyholm + */ +class CacheCollectorPass implements CompilerPassInterface +{ + private $dataCollectorCacheId; + private $cachePoolTag; + private $cachePoolRecorderInnerSuffix; + public function __construct(string $dataCollectorCacheId = 'data_collector.cache', string $cachePoolTag = 'cache.pool', string $cachePoolRecorderInnerSuffix = '.recorder_inner') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + $this->dataCollectorCacheId = $dataCollectorCacheId; + $this->cachePoolTag = $cachePoolTag; + $this->cachePoolRecorderInnerSuffix = $cachePoolRecorderInnerSuffix; + } + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->dataCollectorCacheId)) { + return; + } + foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) { + $poolName = $attributes[0]['name'] ?? $id; + $this->addToCollector($id, $poolName, $container); + } + } + private function addToCollector(string $id, string $name, ContainerBuilder $container) + { + $definition = $container->getDefinition($id); + if ($definition->isAbstract()) { + return; + } + $collectorDefinition = $container->getDefinition($this->dataCollectorCacheId); + $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class); + $recorder->setTags($definition->getTags()); + if (!$definition->isPublic() || !$definition->isPrivate()) { + $recorder->setPublic($definition->isPublic()); + } + $recorder->setArguments([new Reference($innerId = $id . $this->cachePoolRecorderInnerSuffix)]); + foreach ($definition->getMethodCalls() as [$method, $args]) { + if ('setCallbackWrapper' !== $method || !$args[0] instanceof Definition || !($args[0]->getArguments()[2] ?? null) instanceof Definition) { + continue; + } + if ([new Reference($id), 'setCallbackWrapper'] == $args[0]->getArguments()[2]->getFactory()) { + $args[0]->getArguments()[2]->setFactory([new Reference($innerId), 'setCallbackWrapper']); + } + } + $definition->setTags([]); + $definition->setPublic(\false); + $container->setDefinition($innerId, $definition); + $container->setDefinition($id, $recorder); + // Tell the collector to add the new instance + $collectorDefinition->addMethodCall('addInstance', [$name, new Reference($id)]); + $collectorDefinition->setPublic(\false); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php new file mode 100644 index 0000000000..cdea85a9ee --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\DependencyInjection; + +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ContainerBuilder; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Reference; +/** + * @author Nicolas Grekas + */ +class CachePoolClearerPass implements CompilerPassInterface +{ + private $cachePoolClearerTag; + public function __construct(string $cachePoolClearerTag = 'cache.pool.clearer') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + $this->cachePoolClearerTag = $cachePoolClearerTag; + } + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $container->getParameterBag()->remove('cache.prefix.seed'); + foreach ($container->findTaggedServiceIds($this->cachePoolClearerTag) as $id => $attr) { + $clearer = $container->getDefinition($id); + $pools = []; + foreach ($clearer->getArgument(0) as $name => $ref) { + if ($container->hasDefinition($ref)) { + $pools[$name] = new Reference($ref); + } + } + $clearer->replaceArgument(0, $pools); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolPass.php b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolPass.php new file mode 100644 index 0000000000..27f99952b1 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolPass.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\DependencyInjection; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\AbstractAdapter; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\ArrayAdapter; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\ChainAdapter; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\NullAdapter; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\ParameterNormalizer; +use Wordlift\Modules\Common\Symfony\Component\Cache\Messenger\EarlyExpirationDispatcher; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ChildDefinition; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ContainerBuilder; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Definition; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Reference; +/** + * @author Nicolas Grekas + */ +class CachePoolPass implements CompilerPassInterface +{ + private $cachePoolTag; + private $kernelResetTag; + private $cacheClearerId; + private $cachePoolClearerTag; + private $cacheSystemClearerId; + private $cacheSystemClearerTag; + private $reverseContainerId; + private $reversibleTag; + private $messageHandlerId; + public function __construct(string $cachePoolTag = 'cache.pool', string $kernelResetTag = 'kernel.reset', string $cacheClearerId = 'cache.global_clearer', string $cachePoolClearerTag = 'cache.pool.clearer', string $cacheSystemClearerId = 'cache.system_clearer', string $cacheSystemClearerTag = 'kernel.cache_clearer', string $reverseContainerId = 'reverse_container', string $reversibleTag = 'container.reversible', string $messageHandlerId = 'cache.early_expiration_handler') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + $this->cachePoolTag = $cachePoolTag; + $this->kernelResetTag = $kernelResetTag; + $this->cacheClearerId = $cacheClearerId; + $this->cachePoolClearerTag = $cachePoolClearerTag; + $this->cacheSystemClearerId = $cacheSystemClearerId; + $this->cacheSystemClearerTag = $cacheSystemClearerTag; + $this->reverseContainerId = $reverseContainerId; + $this->reversibleTag = $reversibleTag; + $this->messageHandlerId = $messageHandlerId; + } + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if ($container->hasParameter('cache.prefix.seed')) { + $seed = $container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed')); + } else { + $seed = '_' . $container->getParameter('kernel.project_dir'); + $seed .= '.' . $container->getParameter('kernel.container_class'); + } + $needsMessageHandler = \false; + $allPools = []; + $clearers = []; + $attributes = ['provider', 'name', 'namespace', 'default_lifetime', 'early_expiration_message_bus', 'reset']; + foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) { + $adapter = $pool = $container->getDefinition($id); + if ($pool->isAbstract()) { + continue; + } + $class = $adapter->getClass(); + while ($adapter instanceof ChildDefinition) { + $adapter = $container->findDefinition($adapter->getParent()); + $class = $class ?: $adapter->getClass(); + if ($t = $adapter->getTag($this->cachePoolTag)) { + $tags[0] += $t[0]; + } + } + $name = $tags[0]['name'] ?? $id; + if (!isset($tags[0]['namespace'])) { + $namespaceSeed = $seed; + if (null !== $class) { + $namespaceSeed .= '.' . $class; + } + $tags[0]['namespace'] = $this->getNamespace($namespaceSeed, $name); + } + if (isset($tags[0]['clearer'])) { + $clearer = $tags[0]['clearer']; + while ($container->hasAlias($clearer)) { + $clearer = (string) $container->getAlias($clearer); + } + } else { + $clearer = null; + } + unset($tags[0]['clearer'], $tags[0]['name']); + if (isset($tags[0]['provider'])) { + $tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider'])); + } + if (ChainAdapter::class === $class) { + $adapters = []; + foreach ($adapter->getArgument(0) as $provider => $adapter) { + if ($adapter instanceof ChildDefinition) { + $chainedPool = $adapter; + } else { + $chainedPool = $adapter = new ChildDefinition($adapter); + } + $chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]]; + $chainedClass = ''; + while ($adapter instanceof ChildDefinition) { + $adapter = $container->findDefinition($adapter->getParent()); + $chainedClass = $chainedClass ?: $adapter->getClass(); + if ($t = $adapter->getTag($this->cachePoolTag)) { + $chainedTags[0] += $t[0]; + } + } + if (ChainAdapter::class === $chainedClass) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": chain of adapters cannot reference another chain, found "%s".', $id, $chainedPool->getParent())); + } + $i = 0; + if (isset($chainedTags[0]['provider'])) { + $chainedPool->replaceArgument($i++, new Reference(static::getServiceProvider($container, $chainedTags[0]['provider']))); + } + if (isset($tags[0]['namespace']) && !\in_array($adapter->getClass(), [ArrayAdapter::class, NullAdapter::class], \true)) { + $chainedPool->replaceArgument($i++, $tags[0]['namespace']); + } + if (isset($tags[0]['default_lifetime'])) { + $chainedPool->replaceArgument($i++, $tags[0]['default_lifetime']); + } + $adapters[] = $chainedPool; + } + $pool->replaceArgument(0, $adapters); + unset($tags[0]['provider'], $tags[0]['namespace']); + $i = 1; + } else { + $i = 0; + } + foreach ($attributes as $attr) { + if (!isset($tags[0][$attr])) { + // no-op + } elseif ('reset' === $attr) { + if ($tags[0][$attr]) { + $pool->addTag($this->kernelResetTag, ['method' => $tags[0][$attr]]); + } + } elseif ('early_expiration_message_bus' === $attr) { + $needsMessageHandler = \true; + $pool->addMethodCall('setCallbackWrapper', [(new Definition(EarlyExpirationDispatcher::class))->addArgument(new Reference($tags[0]['early_expiration_message_bus']))->addArgument(new Reference($this->reverseContainerId))->addArgument((new Definition('callable'))->setFactory([new Reference($id), 'setCallbackWrapper'])->addArgument(null))]); + $pool->addTag($this->reversibleTag); + } elseif ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class], \true)) { + $argument = $tags[0][$attr]; + if ('default_lifetime' === $attr && !is_numeric($argument)) { + $argument = (new Definition('int', [$argument]))->setFactory([ParameterNormalizer::class, 'normalizeDuration']); + } + $pool->replaceArgument($i++, $argument); + } + unset($tags[0][$attr]); + } + if (!empty($tags[0])) { + throw new InvalidArgumentException(sprintf('Invalid "%s" tag for service "%s": accepted attributes are "clearer", "provider", "name", "namespace", "default_lifetime", "early_expiration_message_bus" and "reset", found "%s".', $this->cachePoolTag, $id, implode('", "', array_keys($tags[0])))); + } + if (null !== $clearer) { + $clearers[$clearer][$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + } + $allPools[$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + } + if (!$needsMessageHandler) { + $container->removeDefinition($this->messageHandlerId); + } + $notAliasedCacheClearerId = $this->cacheClearerId; + while ($container->hasAlias($notAliasedCacheClearerId)) { + $notAliasedCacheClearerId = (string) $container->getAlias($notAliasedCacheClearerId); + } + if ($container->hasDefinition($notAliasedCacheClearerId)) { + $clearers[$notAliasedCacheClearerId] = $allPools; + } + foreach ($clearers as $id => $pools) { + $clearer = $container->getDefinition($id); + if ($clearer instanceof ChildDefinition) { + $clearer->replaceArgument(0, $pools); + } else { + $clearer->setArgument(0, $pools); + } + $clearer->addTag($this->cachePoolClearerTag); + if ($this->cacheSystemClearerId === $id) { + $clearer->addTag($this->cacheSystemClearerTag); + } + } + $allPoolsKeys = array_keys($allPools); + if ($container->hasDefinition('console.command.cache_pool_list')) { + $container->getDefinition('console.command.cache_pool_list')->replaceArgument(0, $allPoolsKeys); + } + if ($container->hasDefinition('console.command.cache_pool_clear')) { + $container->getDefinition('console.command.cache_pool_clear')->addArgument($allPoolsKeys); + } + if ($container->hasDefinition('console.command.cache_pool_delete')) { + $container->getDefinition('console.command.cache_pool_delete')->addArgument($allPoolsKeys); + } + } + private function getNamespace(string $seed, string $id) + { + return substr(str_replace('/', '-', base64_encode(hash('sha256', $id . $seed, \true))), 0, 10); + } + /** + * @internal + */ + public static function getServiceProvider(ContainerBuilder $container, string $name) + { + $container->resolveEnvPlaceholders($name, null, $usedEnvs); + if ($usedEnvs || preg_match('#^[a-z]++:#', $name)) { + $dsn = $name; + if (!$container->hasDefinition($name = '.cache_connection.' . ContainerBuilder::hash($dsn))) { + $definition = new Definition(AbstractAdapter::class); + $definition->setPublic(\false); + $definition->setFactory([AbstractAdapter::class, 'createConnection']); + $definition->setArguments([$dsn, ['lazy' => \true]]); + $container->setDefinition($name, $definition); + } + } + return $name; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php new file mode 100644 index 0000000000..964d200e50 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\DependencyInjection; + +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ContainerBuilder; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Reference; +/** + * @author Rob Frawley 2nd + */ +class CachePoolPrunerPass implements CompilerPassInterface +{ + private $cacheCommandServiceId; + private $cachePoolTag; + public function __construct(string $cacheCommandServiceId = 'console.command.cache_pool_prune', string $cachePoolTag = 'cache.pool') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + $this->cacheCommandServiceId = $cacheCommandServiceId; + $this->cachePoolTag = $cachePoolTag; + } + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->cacheCommandServiceId)) { + return; + } + $services = []; + foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) { + $class = $container->getParameterBag()->resolveValue($container->getDefinition($id)->getClass()); + if (!$reflection = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if ($reflection->implementsInterface(PruneableInterface::class)) { + $services[$id] = new Reference($id); + } + } + $container->getDefinition($this->cacheCommandServiceId)->replaceArgument(0, new IteratorArgument($services)); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/DoctrineProvider.php b/src/modules/common/third-party/vendor/symfony/cache/DoctrineProvider.php new file mode 100644 index 0000000000..c86e1aaa35 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/DoctrineProvider.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache; + +use Wordlift\Modules\Common\Doctrine\Common\Cache\CacheProvider; +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ResetInterface; +if (!class_exists(CacheProvider::class)) { + return; +} +/** + * @author Nicolas Grekas + * + * @deprecated Use Doctrine\Common\Cache\Psr6\DoctrineProvider instead + */ +class DoctrineProvider extends CacheProvider implements PruneableInterface, ResettableInterface +{ + private $pool; + public function __construct(CacheItemPoolInterface $pool) + { + trigger_deprecation('symfony/cache', '5.4', '"%s" is deprecated, use "Doctrine\Common\Cache\Psr6\DoctrineProvider" instead.', __CLASS__); + $this->pool = $pool; + } + /** + * {@inheritdoc} + */ + public function prune() + { + return $this->pool instanceof PruneableInterface && $this->pool->prune(); + } + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResetInterface) { + $this->pool->reset(); + } + $this->setNamespace($this->getNamespace()); + } + /** + * {@inheritdoc} + * + * @return mixed + */ + protected function doFetch($id) + { + $item = $this->pool->getItem(rawurlencode($id)); + return $item->isHit() ? $item->get() : \false; + } + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doContains($id) + { + return $this->pool->hasItem(rawurlencode($id)); + } + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $item = $this->pool->getItem(rawurlencode($id)); + if (0 < $lifeTime) { + $item->expiresAfter($lifeTime); + } + return $this->pool->save($item->set($data)); + } + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doDelete($id) + { + return $this->pool->deleteItem(rawurlencode($id)); + } + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doFlush() + { + return $this->pool->clear(); + } + /** + * {@inheritdoc} + * + * @return array|null + */ + protected function doGetStats() + { + return null; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Exception/CacheException.php b/src/modules/common/third-party/vendor/symfony/cache/Exception/CacheException.php new file mode 100644 index 0000000000..6f8fc8084b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Exception/CacheException.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 Wordlift\Modules\Common\Symfony\Component\Cache\Exception; + +use Wordlift\Modules\Common\Psr\Cache\CacheException as Psr6CacheInterface; +use Wordlift\Modules\Common\Psr\SimpleCache\CacheException as SimpleCacheInterface; +if (interface_exists(SimpleCacheInterface::class)) { + class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface + { + } +} else { + class CacheException extends \Exception implements Psr6CacheInterface + { + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Exception/InvalidArgumentException.php b/src/modules/common/third-party/vendor/symfony/cache/Exception/InvalidArgumentException.php new file mode 100644 index 0000000000..c89501e6cd --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Exception/InvalidArgumentException.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 Wordlift\Modules\Common\Symfony\Component\Cache\Exception; + +use Wordlift\Modules\Common\Psr\Cache\InvalidArgumentException as Psr6CacheInterface; +use Wordlift\Modules\Common\Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface; +if (interface_exists(SimpleCacheInterface::class)) { + class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface + { + } +} else { + class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface + { + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Exception/LogicException.php b/src/modules/common/third-party/vendor/symfony/cache/Exception/LogicException.php new file mode 100644 index 0000000000..193043b2ea --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Exception/LogicException.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 Wordlift\Modules\Common\Symfony\Component\Cache\Exception; + +use Wordlift\Modules\Common\Psr\Cache\CacheException as Psr6CacheInterface; +use Wordlift\Modules\Common\Psr\SimpleCache\CacheException as SimpleCacheInterface; +if (interface_exists(SimpleCacheInterface::class)) { + class LogicException extends \LogicException implements Psr6CacheInterface, SimpleCacheInterface + { + } +} else { + class LogicException extends \LogicException implements Psr6CacheInterface + { + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/LockRegistry.php b/src/modules/common/third-party/vendor/symfony/cache/LockRegistry.php new file mode 100644 index 0000000000..b3f248a17e --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/LockRegistry.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache; + +use Wordlift\Modules\Common\Psr\Log\LoggerInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\ItemInterface; +/** + * LockRegistry is used internally by existing adapters to protect against cache stampede. + * + * It does so by wrapping the computation of items in a pool of locks. + * Foreach each apps, there can be at most 20 concurrent processes that + * compute items at the same time and only one per cache-key. + * + * @author Nicolas Grekas + */ +final class LockRegistry +{ + private static $openedFiles = []; + private static $lockedFiles; + private static $signalingException; + private static $signalingCallback; + /** + * The number of items in this list controls the max number of concurrent processes. + */ + private static $files = [__DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'AbstractAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'AbstractTagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'AdapterInterface.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ApcuAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ArrayAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ChainAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'CouchbaseBucketAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'CouchbaseCollectionAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'DoctrineAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'DoctrineDbalAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'FilesystemAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'FilesystemTagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'MemcachedAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'NullAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ParameterNormalizer.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'PdoAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'PhpArrayAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'PhpFilesAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ProxyAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'Psr16Adapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'RedisAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'RedisTagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TagAwareAdapterInterface.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TraceableAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TraceableTagAwareAdapter.php']; + /** + * Defines a set of existing files that will be used as keys to acquire locks. + * + * @return array The previously defined set of files + */ + public static function setFiles(array $files): array + { + $previousFiles = self::$files; + self::$files = $files; + foreach (self::$openedFiles as $file) { + if ($file) { + flock($file, \LOCK_UN); + fclose($file); + } + } + self::$openedFiles = self::$lockedFiles = []; + return $previousFiles; + } + public static function compute(callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, ?\Closure $setMetadata = null, ?LoggerInterface $logger = null) + { + if ('\\' === \DIRECTORY_SEPARATOR && null === self::$lockedFiles) { + // disable locking on Windows by default + self::$files = self::$lockedFiles = []; + } + $key = self::$files ? abs(crc32($item->getKey())) % \count(self::$files) : -1; + if ($key < 0 || self::$lockedFiles || !$lock = self::open($key)) { + return $callback($item, $save); + } + self::$signalingException ?? self::$signalingException = unserialize("O:9:\"Exception\":1:{s:16:\"\x00Exception\x00trace\";a:0:{}}"); + self::$signalingCallback ?? self::$signalingCallback = function () { + throw self::$signalingException; + }; + while (\true) { + try { + $locked = \false; + // race to get the lock in non-blocking mode + $locked = flock($lock, \LOCK_EX | \LOCK_NB, $wouldBlock); + if ($locked || !$wouldBlock) { + $logger && $logger->info(sprintf('Lock %s, now computing item "{key}"', $locked ? 'acquired' : 'not supported'), ['key' => $item->getKey()]); + self::$lockedFiles[$key] = \true; + $value = $callback($item, $save); + if ($save) { + if ($setMetadata) { + $setMetadata($item); + } + $pool->save($item->set($value)); + $save = \false; + } + return $value; + } + // if we failed the race, retry locking in blocking mode to wait for the winner + $logger && $logger->info('Item "{key}" is locked, waiting for it to be released', ['key' => $item->getKey()]); + flock($lock, \LOCK_SH); + } finally { + flock($lock, \LOCK_UN); + unset(self::$lockedFiles[$key]); + } + try { + $value = $pool->get($item->getKey(), self::$signalingCallback, 0); + $logger && $logger->info('Item "{key}" retrieved after lock was released', ['key' => $item->getKey()]); + $save = \false; + return $value; + } catch (\Exception $e) { + if (self::$signalingException !== $e) { + throw $e; + } + $logger && $logger->info('Item "{key}" not found while lock was released, now retrying', ['key' => $item->getKey()]); + } + } + return null; + } + private static function open(int $key) + { + if (null !== $h = self::$openedFiles[$key] ?? null) { + return $h; + } + set_error_handler(function () { + }); + try { + $h = fopen(self::$files[$key], 'r+'); + } finally { + restore_error_handler(); + } + return self::$openedFiles[$key] = $h ?: @fopen(self::$files[$key], 'r'); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Marshaller/DefaultMarshaller.php b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/DefaultMarshaller.php new file mode 100644 index 0000000000..38040a1387 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/DefaultMarshaller.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +/** + * Serializes/unserializes values using igbinary_serialize() if available, serialize() otherwise. + * + * @author Nicolas Grekas + */ +class DefaultMarshaller implements MarshallerInterface +{ + private $useIgbinarySerialize = \true; + private $throwOnSerializationFailure; + public function __construct(?bool $useIgbinarySerialize = null, bool $throwOnSerializationFailure = \false) + { + if (null === $useIgbinarySerialize) { + $useIgbinarySerialize = \extension_loaded('igbinary') && (\PHP_VERSION_ID < 70400 || version_compare('3.1.6', phpversion('igbinary'), '<=')); + } elseif ($useIgbinarySerialize && (!\extension_loaded('igbinary') || \PHP_VERSION_ID >= 70400 && version_compare('3.1.6', phpversion('igbinary'), '>'))) { + throw new CacheException((\extension_loaded('igbinary') && \PHP_VERSION_ID >= 70400) ? 'Please upgrade the "igbinary" PHP extension to v3.1.6 or higher.' : 'The "igbinary" PHP extension is not loaded.'); + } + $this->useIgbinarySerialize = $useIgbinarySerialize; + $this->throwOnSerializationFailure = $throwOnSerializationFailure; + } + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + $serialized = $failed = []; + foreach ($values as $id => $value) { + try { + if ($this->useIgbinarySerialize) { + $serialized[$id] = igbinary_serialize($value); + } else { + $serialized[$id] = serialize($value); + } + } catch (\Exception $e) { + if ($this->throwOnSerializationFailure) { + throw new \ValueError($e->getMessage(), 0, $e); + } + $failed[] = $id; + } + } + return $serialized; + } + /** + * {@inheritdoc} + */ + public function unmarshall(string $value) + { + if ('b:0;' === $value) { + return \false; + } + if ('N;' === $value) { + return null; + } + static $igbinaryNull; + if ($value === ($igbinaryNull ?? $igbinaryNull = \extension_loaded('igbinary') ? igbinary_serialize(null) : \false)) { + return null; + } + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__ . '::handleUnserializeCallback'); + try { + if (':' === ($value[1] ?? ':')) { + if (\false !== $value = unserialize($value)) { + return $value; + } + } elseif (\false === $igbinaryNull) { + throw new \RuntimeException('Failed to unserialize values, did you forget to install the "igbinary" extension?'); + } elseif (null !== $value = igbinary_unserialize($value)) { + return $value; + } + throw new \DomainException(error_get_last() ? error_get_last()['message'] : 'Failed to unserialize values.'); + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + /** + * @internal + */ + public static function handleUnserializeCallback(string $class) + { + throw new \DomainException('Class not found: ' . $class); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Marshaller/DeflateMarshaller.php b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/DeflateMarshaller.php new file mode 100644 index 0000000000..41a298ed27 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/DeflateMarshaller.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +/** + * Compresses values using gzdeflate(). + * + * @author Nicolas Grekas + */ +class DeflateMarshaller implements MarshallerInterface +{ + private $marshaller; + public function __construct(MarshallerInterface $marshaller) + { + if (!\function_exists('gzdeflate') && !\function_exists('Wordlift\Modules\Common\gzdeflate')) { + throw new CacheException('The "zlib" PHP extension is not loaded.'); + } + $this->marshaller = $marshaller; + } + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + return array_map('gzdeflate', $this->marshaller->marshall($values, $failed)); + } + /** + * {@inheritdoc} + */ + public function unmarshall(string $value) + { + if (\false !== $inflatedValue = @gzinflate($value)) { + $value = $inflatedValue; + } + return $this->marshaller->unmarshall($value); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Marshaller/MarshallerInterface.php b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/MarshallerInterface.php new file mode 100644 index 0000000000..96634205d7 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/MarshallerInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller; + +/** + * Serializes/unserializes PHP values. + * + * Implementations of this interface MUST deal with errors carefully. They MUST + * also deal with forward and backward compatibility at the storage format level. + * + * @author Nicolas Grekas + */ +interface MarshallerInterface +{ + /** + * Serializes a list of values. + * + * When serialization fails for a specific value, no exception should be + * thrown. Instead, its key should be listed in $failed. + */ + public function marshall(array $values, ?array &$failed): array; + /** + * Unserializes a single value and throws an exception if anything goes wrong. + * + * @return mixed + * + * @throws \Exception Whenever unserialization fails + */ + public function unmarshall(string $value); +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Marshaller/SodiumMarshaller.php b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/SodiumMarshaller.php new file mode 100644 index 0000000000..779e3f53da --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/SodiumMarshaller.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 Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +/** + * Encrypt/decrypt values using Libsodium. + * + * @author Ahmed TAILOULOUTE + */ +class SodiumMarshaller implements MarshallerInterface +{ + private $marshaller; + private $decryptionKeys; + /** + * @param string[] $decryptionKeys The key at index "0" is required and is used to decrypt and encrypt values; + * more rotating keys can be provided to decrypt values; + * each key must be generated using sodium_crypto_box_keypair() + */ + public function __construct(array $decryptionKeys, ?MarshallerInterface $marshaller = null) + { + if (!self::isSupported()) { + throw new CacheException('The "sodium" PHP extension is not loaded.'); + } + if (!isset($decryptionKeys[0])) { + throw new InvalidArgumentException('At least one decryption key must be provided at index "0".'); + } + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + $this->decryptionKeys = $decryptionKeys; + } + public static function isSupported(): bool + { + return \function_exists('sodium_crypto_box_seal'); + } + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + $encryptionKey = sodium_crypto_box_publickey($this->decryptionKeys[0]); + $encryptedValues = []; + foreach ($this->marshaller->marshall($values, $failed) as $k => $v) { + $encryptedValues[$k] = sodium_crypto_box_seal($v, $encryptionKey); + } + return $encryptedValues; + } + /** + * {@inheritdoc} + */ + public function unmarshall(string $value) + { + foreach ($this->decryptionKeys as $k) { + if (\false !== $decryptedValue = @sodium_crypto_box_seal_open($value, $k)) { + $value = $decryptedValue; + break; + } + } + return $this->marshaller->unmarshall($value); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Marshaller/TagAwareMarshaller.php b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/TagAwareMarshaller.php new file mode 100644 index 0000000000..a7b676a0f8 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Marshaller/TagAwareMarshaller.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller; + +/** + * A marshaller optimized for data structures generated by AbstractTagAwareAdapter. + * + * @author Nicolas Grekas + */ +class TagAwareMarshaller implements MarshallerInterface +{ + private $marshaller; + public function __construct(?MarshallerInterface $marshaller = null) + { + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + $failed = $notSerialized = $serialized = []; + foreach ($values as $id => $value) { + if (\is_array($value) && \is_array($value['tags'] ?? null) && \array_key_exists('value', $value) && \count($value) === 2 + (\is_string($value['meta'] ?? null) && 8 === \strlen($value['meta']))) { + // if the value is an array with keys "tags", "value" and "meta", use a compact serialization format + // magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F allow detecting this format quickly in unmarshall() + $v = $this->marshaller->marshall($value, $f); + if ($f) { + $f = []; + $failed[] = $id; + } else { + if ([] === $value['tags']) { + $v['tags'] = ''; + } + $serialized[$id] = "\x9d" . ($value['meta'] ?? "\x00\x00\x00\x00\x00\x00\x00\x00") . pack('N', \strlen($v['tags'])) . $v['tags'] . $v['value']; + $serialized[$id][9] = "_"; + } + } else { + // other arbitrary values are serialized using the decorated marshaller below + $notSerialized[$id] = $value; + } + } + if ($notSerialized) { + $serialized += $this->marshaller->marshall($notSerialized, $f); + $failed = array_merge($failed, $f); + } + return $serialized; + } + /** + * {@inheritdoc} + */ + public function unmarshall(string $value) + { + // detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (13 >= \strlen($value) || "\x9d" !== $value[0] || "\x00" !== $value[5] || "_" !== $value[9]) { + return $this->marshaller->unmarshall($value); + } + // data consists of value, tags and metadata which we need to unpack + $meta = substr($value, 1, 12); + $meta[8] = "\x00"; + $tagLen = unpack('Nlen', $meta, 8)['len']; + $meta = substr($meta, 0, 8); + return ['value' => $this->marshaller->unmarshall(substr($value, 13 + $tagLen)), 'tags' => $tagLen ? $this->marshaller->unmarshall(substr($value, 13, $tagLen)) : [], 'meta' => ("\x00\x00\x00\x00\x00\x00\x00\x00" === $meta) ? null : $meta]; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationDispatcher.php b/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationDispatcher.php new file mode 100644 index 0000000000..2f841d5fcf --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationDispatcher.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Messenger; + +use Wordlift\Modules\Common\Psr\Log\LoggerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\AdapterInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ReverseContainer; +use Wordlift\Modules\Common\Symfony\Component\Messenger\MessageBusInterface; +use Wordlift\Modules\Common\Symfony\Component\Messenger\Stamp\HandledStamp; +/** + * Sends the computation of cached values to a message bus. + */ +class EarlyExpirationDispatcher +{ + private $bus; + private $reverseContainer; + private $callbackWrapper; + public function __construct(MessageBusInterface $bus, ReverseContainer $reverseContainer, ?callable $callbackWrapper = null) + { + $this->bus = $bus; + $this->reverseContainer = $reverseContainer; + $this->callbackWrapper = $callbackWrapper; + } + public function __invoke(callable $callback, CacheItem $item, bool &$save, AdapterInterface $pool, \Closure $setMetadata, ?LoggerInterface $logger = null) + { + if (!$item->isHit() || null === $message = EarlyExpirationMessage::create($this->reverseContainer, $callback, $item, $pool)) { + // The item is stale or the callback cannot be reversed: we must compute the value now + $logger && $logger->info('Computing item "{key}" online: ' . ($item->isHit() ? 'callback cannot be reversed' : 'item is stale'), ['key' => $item->getKey()]); + return (null !== $this->callbackWrapper) ? ($this->callbackWrapper)($callback, $item, $save, $pool, $setMetadata, $logger) : $callback($item, $save); + } + $envelope = $this->bus->dispatch($message); + if ($logger) { + if ($envelope->last(HandledStamp::class)) { + $logger->info('Item "{key}" was computed online', ['key' => $item->getKey()]); + } else { + $logger->info('Item "{key}" sent for recomputation', ['key' => $item->getKey()]); + } + } + // The item's value is not stale, no need to write it to the backend + $save = \false; + return $message->getItem()->get() ?? $item->get(); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php b/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php new file mode 100644 index 0000000000..d147c40532 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationHandler.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 Wordlift\Modules\Common\Symfony\Component\Cache\Messenger; + +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ReverseContainer; +use Wordlift\Modules\Common\Symfony\Component\Messenger\Handler\MessageHandlerInterface; +/** + * Computes cached values sent to a message bus. + */ +class EarlyExpirationHandler implements MessageHandlerInterface +{ + private $reverseContainer; + private $processedNonces = []; + public function __construct(ReverseContainer $reverseContainer) + { + $this->reverseContainer = $reverseContainer; + } + public function __invoke(EarlyExpirationMessage $message) + { + $item = $message->getItem(); + $metadata = $item->getMetadata(); + $expiry = $metadata[CacheItem::METADATA_EXPIRY] ?? 0; + $ctime = $metadata[CacheItem::METADATA_CTIME] ?? 0; + if ($expiry && $ctime) { + // skip duplicate or expired messages + $processingNonce = [$expiry, $ctime]; + $pool = $message->getPool(); + $key = $item->getKey(); + if (($this->processedNonces[$pool][$key] ?? null) === $processingNonce) { + return; + } + if (microtime(\true) >= $expiry) { + return; + } + $this->processedNonces[$pool] = [$key => $processingNonce] + ($this->processedNonces[$pool] ?? []); + if (\count($this->processedNonces[$pool]) > 100) { + array_pop($this->processedNonces[$pool]); + } + } + static $setMetadata; + $setMetadata ?? $setMetadata = \Closure::bind(function (CacheItem $item, float $startTime) { + if ($item->expiry > $endTime = microtime(\true)) { + $item->newMetadata[CacheItem::METADATA_EXPIRY] = $item->expiry; + $item->newMetadata[CacheItem::METADATA_CTIME] = (int) ceil(1000 * ($endTime - $startTime)); + } + }, null, CacheItem::class); + $startTime = microtime(\true); + $pool = $message->findPool($this->reverseContainer); + $callback = $message->findCallback($this->reverseContainer); + $save = \true; + $value = $callback($item, $save); + $setMetadata($item, $startTime); + $pool->save($item->set($value)); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationMessage.php b/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationMessage.php new file mode 100644 index 0000000000..6d80042601 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Messenger/EarlyExpirationMessage.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Messenger; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\AdapterInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ReverseContainer; +/** + * Conveys a cached value that needs to be computed. + */ +final class EarlyExpirationMessage +{ + private $item; + private $pool; + private $callback; + public static function create(ReverseContainer $reverseContainer, callable $callback, CacheItem $item, AdapterInterface $pool): ?self + { + try { + $item = clone $item; + $item->set(null); + } catch (\Exception $e) { + return null; + } + $pool = $reverseContainer->getId($pool); + if (\is_object($callback)) { + if (null === $id = $reverseContainer->getId($callback)) { + return null; + } + $callback = '@' . $id; + } elseif (!\is_array($callback)) { + $callback = (string) $callback; + } elseif (!\is_object($callback[0])) { + $callback = [(string) $callback[0], (string) $callback[1]]; + } else { + if (null === $id = $reverseContainer->getId($callback[0])) { + return null; + } + $callback = ['@' . $id, (string) $callback[1]]; + } + return new self($item, $pool, $callback); + } + public function getItem(): CacheItem + { + return $this->item; + } + public function getPool(): string + { + return $this->pool; + } + public function getCallback() + { + return $this->callback; + } + public function findPool(ReverseContainer $reverseContainer): AdapterInterface + { + return $reverseContainer->getService($this->pool); + } + public function findCallback(ReverseContainer $reverseContainer): callable + { + if (\is_string($callback = $this->callback)) { + return ('@' === $callback[0]) ? $reverseContainer->getService(substr($callback, 1)) : $callback; + } + if ('@' === $callback[0][0]) { + $callback[0] = $reverseContainer->getService(substr($callback[0], 1)); + } + return $callback; + } + private function __construct(CacheItem $item, string $pool, $callback) + { + $this->item = $item; + $this->pool = $pool; + $this->callback = $callback; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/PruneableInterface.php b/src/modules/common/third-party/vendor/symfony/cache/PruneableInterface.php new file mode 100644 index 0000000000..84e48b56c1 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/PruneableInterface.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 Wordlift\Modules\Common\Symfony\Component\Cache; + +/** + * Interface extends psr-6 and psr-16 caches to allow for pruning (deletion) of all expired cache items. + */ +interface PruneableInterface +{ + /** + * @return bool + */ + public function prune(); +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Psr16Cache.php b/src/modules/common/third-party/vendor/symfony/cache/Psr16Cache.php new file mode 100644 index 0000000000..174e8ae397 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Psr16Cache.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache; + +use Wordlift\Modules\Common\Psr\Cache\CacheException as Psr6CacheException; +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Psr\SimpleCache\CacheException as SimpleCacheException; +use Wordlift\Modules\Common\Psr\SimpleCache\CacheInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\AdapterInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Traits\ProxyTrait; +if (null !== (new \ReflectionMethod(CacheInterface::class, 'get'))->getReturnType()) { + throw new \LogicException('psr/simple-cache 3.0+ is not compatible with this version of symfony/cache. Please upgrade symfony/cache to 6.0+ or downgrade psr/simple-cache to 1.x or 2.x.'); +} +/** + * Turns a PSR-6 cache into a PSR-16 one. + * + * @author Nicolas Grekas + */ +class Psr16Cache implements CacheInterface, PruneableInterface, ResettableInterface +{ + use ProxyTrait; + private const METADATA_EXPIRY_OFFSET = 1527506807; + private $createCacheItem; + private $cacheItemPrototype; + public function __construct(CacheItemPoolInterface $pool) + { + $this->pool = $pool; + if (!$pool instanceof AdapterInterface) { + return; + } + $cacheItemPrototype =& $this->cacheItemPrototype; + $createCacheItem = \Closure::bind(static function ($key, $value, $allowInt = \false) use (&$cacheItemPrototype) { + $item = clone $cacheItemPrototype; + $item->poolHash = $item->innerItem = null; + if ($allowInt && \is_int($key)) { + $item->key = (string) $key; + } else { + \assert('' !== CacheItem::validateKey($key)); + $item->key = $key; + } + $item->value = $value; + $item->isHit = \false; + return $item; + }, null, CacheItem::class); + $this->createCacheItem = function ($key, $value, $allowInt = \false) use ($createCacheItem) { + if (null === $this->cacheItemPrototype) { + $this->get(($allowInt && \is_int($key)) ? (string) $key : $key); + } + $this->createCacheItem = $createCacheItem; + return $createCacheItem($key, null, $allowInt)->set($value); + }; + } + /** + * {@inheritdoc} + * + * @return mixed + */ + public function get($key, $default = null) + { + try { + $item = $this->pool->getItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + if (null === $this->cacheItemPrototype) { + $this->cacheItemPrototype = clone $item; + $this->cacheItemPrototype->set(null); + } + return $item->isHit() ? $item->get() : $default; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function set($key, $value, $ttl = null) + { + try { + if (null !== $f = $this->createCacheItem) { + $item = $f($key, $value); + } else { + $item = $this->pool->getItem($key)->set($value); + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + return $this->pool->save($item); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function delete($key) + { + try { + return $this->pool->deleteItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear() + { + return $this->pool->clear(); + } + /** + * {@inheritdoc} + * + * @return iterable + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, \false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', get_debug_type($keys))); + } + try { + $items = $this->pool->getItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $values = []; + if (!$this->pool instanceof AdapterInterface) { + foreach ($items as $key => $item) { + $values[$key] = $item->isHit() ? $item->get() : $default; + } + return $values; + } + foreach ($items as $key => $item) { + if (!$item->isHit()) { + $values[$key] = $default; + continue; + } + $values[$key] = $item->get(); + if (!$metadata = $item->getMetadata()) { + continue; + } + unset($metadata[CacheItem::METADATA_TAGS]); + if ($metadata) { + $values[$key] = ["\x9d" . pack('VN', (int) (0.1 + $metadata[CacheItem::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[CacheItem::METADATA_CTIME]) . "_" => $values[$key]]; + } + } + return $values; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function setMultiple($values, $ttl = null) + { + $valuesIsArray = \is_array($values); + if (!$valuesIsArray && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given.', get_debug_type($values))); + } + $items = []; + try { + if (null !== $f = $this->createCacheItem) { + $valuesIsArray = \false; + foreach ($values as $key => $value) { + $items[$key] = $f($key, $value, \true); + } + } elseif ($valuesIsArray) { + $items = []; + foreach ($values as $key => $value) { + $items[] = (string) $key; + } + $items = $this->pool->getItems($items); + } else { + foreach ($values as $key => $value) { + if (\is_int($key)) { + $key = (string) $key; + } + $items[$key] = $this->pool->getItem($key)->set($value); + } + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $ok = \true; + foreach ($items as $key => $item) { + if ($valuesIsArray) { + $item->set($values[$key]); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + $ok = $this->pool->saveDeferred($item) && $ok; + } + return $this->pool->commit() && $ok; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, \false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', get_debug_type($keys))); + } + try { + return $this->pool->deleteItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function has($key) + { + try { + return $this->pool->hasItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/ResettableInterface.php b/src/modules/common/third-party/vendor/symfony/cache/ResettableInterface.php new file mode 100644 index 0000000000..f7103f51cf --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/ResettableInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache; + +use Wordlift\Modules\Common\Symfony\Contracts\Service\ResetInterface; +/** + * Resets a pool's local state. + */ +interface ResettableInterface extends ResetInterface +{ +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/AbstractAdapterTrait.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/AbstractAdapterTrait.php new file mode 100644 index 0000000000..633a23e837 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/AbstractAdapterTrait.php @@ -0,0 +1,372 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemInterface; +use Wordlift\Modules\Common\Psr\Log\LoggerAwareTrait; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +/** + * @author Nicolas Grekas + * + * @internal + */ +trait AbstractAdapterTrait +{ + use LoggerAwareTrait; + /** + * @var \Closure needs to be set by class, signature is function(string , mixed , bool ) + */ + private static $createCacheItem; + /** + * @var \Closure needs to be set by class, signature is function(array , string , array <&expiredIds>) + */ + private static $mergeByLifetime; + private $namespace = ''; + private $defaultLifetime; + private $namespaceVersion = ''; + private $versioningIsEnabled = \false; + private $deferred = []; + private $ids = []; + /** + * @var int|null The maximum length to enforce for identifiers or null when no limit applies + */ + protected $maxIdLength; + /** + * Fetches several cache items. + * + * @param array $ids The cache identifiers to fetch + * + * @return array|\Traversable + */ + abstract protected function doFetch(array $ids); + /** + * Confirms if the cache contains specified cache item. + * + * @param string $id The identifier for which to check existence + * + * @return bool + */ + abstract protected function doHave(string $id); + /** + * Deletes all items in the pool. + * + * @param string $namespace The prefix used for all identifiers managed by this pool + * + * @return bool + */ + abstract protected function doClear(string $namespace); + /** + * Removes multiple items from the pool. + * + * @param array $ids An array of identifiers that should be removed from the pool + * + * @return bool + */ + abstract protected function doDelete(array $ids); + /** + * Persists several cache items immediately. + * + * @param array $values The values to cache, indexed by their cache identifier + * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning + * + * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not + */ + abstract protected function doSave(array $values, int $lifetime); + /** + * {@inheritdoc} + * + * @return bool + */ + public function hasItem($key) + { + $id = $this->getId($key); + if (isset($this->deferred[$key])) { + $this->commit(); + } + try { + return $this->doHave($id); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached: ' . $e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + return \false; + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function clear(string $prefix = '') + { + $this->deferred = []; + if ($cleared = $this->versioningIsEnabled) { + if ('' === $namespaceVersionToClear = $this->namespaceVersion) { + foreach ($this->doFetch([static::NS_SEPARATOR . $this->namespace]) as $v) { + $namespaceVersionToClear = $v; + } + } + $namespaceToClear = $this->namespace . $namespaceVersionToClear; + $namespaceVersion = self::formatNamespaceVersion(mt_rand()); + try { + $e = $this->doSave([static::NS_SEPARATOR . $this->namespace => $namespaceVersion], 0); + } catch (\Exception $e) { + } + if (\true !== $e && [] !== $e) { + $cleared = \false; + $message = 'Failed to save the new namespace' . (($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['exception' => ($e instanceof \Exception) ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } else { + $this->namespaceVersion = $namespaceVersion; + $this->ids = []; + } + } else { + $namespaceToClear = $this->namespace . $prefix; + } + try { + return $this->doClear($namespaceToClear) || $cleared; + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to clear the cache: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + return \false; + } + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItem($key) + { + return $this->deleteItems([$key]); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function deleteItems(array $keys) + { + $ids = []; + foreach ($keys as $key) { + $ids[$key] = $this->getId($key); + unset($this->deferred[$key]); + } + try { + if ($this->doDelete($ids)) { + return \true; + } + } catch (\Exception $e) { + } + $ok = \true; + // When bulk-delete failed, retry each item individually + foreach ($ids as $key => $id) { + try { + $e = null; + if ($this->doDelete([$id])) { + continue; + } + } catch (\Exception $e) { + } + $message = 'Failed to delete key "{key}"' . (($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $ok = \false; + } + return $ok; + } + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $id = $this->getId($key); + if (isset($this->deferred[$key])) { + $this->commit(); + } + $isHit = \false; + $value = null; + try { + foreach ($this->doFetch([$id]) as $value) { + $isHit = \true; + } + return (self::$createCacheItem)($key, $value, $isHit); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch key "{key}": ' . $e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + return (self::$createCacheItem)($key, null, \false); + } + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + $ids = []; + $commit = \false; + foreach ($keys as $key) { + $ids[] = $this->getId($key); + $commit = $commit || isset($this->deferred[$key]); + } + if ($commit) { + $this->commit(); + } + try { + $items = $this->doFetch($ids); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch items: ' . $e->getMessage(), ['keys' => $keys, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $items = []; + } + $ids = array_combine($ids, $keys); + return $this->generateItems($items, $ids); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return \false; + } + $this->deferred[$item->getKey()] = $item; + return $this->commit(); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function saveDeferred(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return \false; + } + $this->deferred[$item->getKey()] = $item; + return \true; + } + /** + * Enables/disables versioning of items. + * + * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed, + * but old keys may need garbage collection and extra round-trips to the back-end are required. + * + * Calling this method also clears the memoized namespace version and thus forces a resynchronization of it. + * + * @return bool the previous state of versioning + */ + public function enableVersioning(bool $enable = \true) + { + $wasEnabled = $this->versioningIsEnabled; + $this->versioningIsEnabled = $enable; + $this->namespaceVersion = ''; + $this->ids = []; + return $wasEnabled; + } + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->deferred) { + $this->commit(); + } + $this->namespaceVersion = ''; + $this->ids = []; + } + /** + * @return array + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize ' . __CLASS__); + } + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__); + } + public function __destruct() + { + if ($this->deferred) { + $this->commit(); + } + } + private function generateItems(iterable $items, array &$keys): \Generator + { + $f = self::$createCacheItem; + try { + foreach ($items as $id => $value) { + if (!isset($keys[$id])) { + throw new InvalidArgumentException(sprintf('Could not match value id "%s" to keys "%s".', $id, implode('", "', $keys))); + } + $key = $keys[$id]; + unset($keys[$id]); + yield $key => $f($key, $value, \true); + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch items: ' . $e->getMessage(), ['keys' => array_values($keys), 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + foreach ($keys as $key) { + yield $key => $f($key, null, \false); + } + } + /** + * @internal + */ + protected function getId($key) + { + if ($this->versioningIsEnabled && '' === $this->namespaceVersion) { + $this->ids = []; + $this->namespaceVersion = '1' . static::NS_SEPARATOR; + try { + foreach ($this->doFetch([static::NS_SEPARATOR . $this->namespace]) as $v) { + $this->namespaceVersion = $v; + } + $e = \true; + if ('1' . static::NS_SEPARATOR === $this->namespaceVersion) { + $this->namespaceVersion = self::formatNamespaceVersion(time()); + $e = $this->doSave([static::NS_SEPARATOR . $this->namespace => $this->namespaceVersion], 0); + } + } catch (\Exception $e) { + } + if (\true !== $e && [] !== $e) { + $message = 'Failed to save the new namespace' . (($e instanceof \Exception) ? ': ' . $e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['exception' => ($e instanceof \Exception) ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } + if (\is_string($key) && isset($this->ids[$key])) { + return $this->namespace . $this->namespaceVersion . $this->ids[$key]; + } + \assert('' !== CacheItem::validateKey($key)); + $this->ids[$key] = $key; + if (\count($this->ids) > 1000) { + $this->ids = \array_slice($this->ids, 500, null, \true); + // stop memory leak if there are many keys + } + if (null === $this->maxIdLength) { + return $this->namespace . $this->namespaceVersion . $key; + } + if (\strlen($id = $this->namespace . $this->namespaceVersion . $key) > $this->maxIdLength) { + // Use MD5 to favor speed over security, which is not an issue here + $this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, \true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 2)); + $id = $this->namespace . $this->namespaceVersion . $id; + } + return $id; + } + /** + * @internal + */ + public static function handleUnserializeCallback(string $class) + { + throw new \DomainException('Class not found: ' . $class); + } + private static function formatNamespaceVersion(int $value): string + { + return strtr(substr_replace(base64_encode(pack('V', $value)), static::NS_SEPARATOR, 5), '/', '_'); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/ContractsTrait.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/ContractsTrait.php new file mode 100644 index 0000000000..7e22fb9fc3 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/ContractsTrait.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +use Wordlift\Modules\Common\Psr\Log\LoggerInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\AdapterInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\CacheItem; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\LockRegistry; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\CacheTrait; +use Wordlift\Modules\Common\Symfony\Contracts\Cache\ItemInterface; +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ContractsTrait +{ + use CacheTrait { + doGet as private contractsGet; + } + private $callbackWrapper; + private $computing = []; + /** + * Wraps the callback passed to ->get() in a callable. + * + * @return callable the previous callback wrapper + */ + public function setCallbackWrapper(?callable $callbackWrapper): callable + { + if (!isset($this->callbackWrapper)) { + $this->callbackWrapper = \Closure::fromCallable([LockRegistry::class, 'compute']); + if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true)) { + $this->setCallbackWrapper(null); + } + } + $previousWrapper = $this->callbackWrapper; + $this->callbackWrapper = $callbackWrapper ?? static function (callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata, ?LoggerInterface $logger) { + return $callback($item, $save); + }; + return $previousWrapper; + } + private function doGet(AdapterInterface $pool, string $key, callable $callback, ?float $beta, ?array &$metadata = null) + { + if (0 > $beta = $beta ?? 1.0) { + throw new InvalidArgumentException(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)); + } + static $setMetadata; + $setMetadata ?? $setMetadata = \Closure::bind(static function (CacheItem $item, float $startTime, ?array &$metadata) { + if ($item->expiry > $endTime = microtime(\true)) { + $item->newMetadata[CacheItem::METADATA_EXPIRY] = $metadata[CacheItem::METADATA_EXPIRY] = $item->expiry; + $item->newMetadata[CacheItem::METADATA_CTIME] = $metadata[CacheItem::METADATA_CTIME] = (int) ceil(1000 * ($endTime - $startTime)); + } else { + unset($metadata[CacheItem::METADATA_EXPIRY], $metadata[CacheItem::METADATA_CTIME]); + } + }, null, CacheItem::class); + return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata, &$metadata, $key) { + // don't wrap nor save recursive calls + if (isset($this->computing[$key])) { + $value = $callback($item, $save); + $save = \false; + return $value; + } + $this->computing[$key] = $key; + $startTime = microtime(\true); + if (!isset($this->callbackWrapper)) { + $this->setCallbackWrapper($this->setCallbackWrapper(null)); + } + try { + $value = ($this->callbackWrapper)($callback, $item, $save, $pool, function (CacheItem $item) use ($setMetadata, $startTime, &$metadata) { + $setMetadata($item, $startTime, $metadata); + }, $this->logger ?? null); + $setMetadata($item, $startTime, $metadata); + return $value; + } finally { + unset($this->computing[$key]); + } + }, $beta, $metadata, $this->logger ?? null); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/FilesystemCommonTrait.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/FilesystemCommonTrait.php new file mode 100644 index 0000000000..d5ab61cd25 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/FilesystemCommonTrait.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +/** + * @author Nicolas Grekas + * + * @internal + */ +trait FilesystemCommonTrait +{ + private $directory; + private $tmp; + private function init(string $namespace, ?string $directory) + { + if (!isset($directory[0])) { + $directory = sys_get_temp_dir() . \DIRECTORY_SEPARATOR . 'symfony-cache'; + } else { + $directory = realpath($directory) ?: $directory; + } + if (isset($namespace[0])) { + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + $directory .= \DIRECTORY_SEPARATOR . $namespace; + } else { + $directory .= \DIRECTORY_SEPARATOR . '@'; + } + if (!is_dir($directory)) { + @mkdir($directory, 0777, \true); + } + $directory .= \DIRECTORY_SEPARATOR; + // On Windows the whole path is limited to 258 chars + if ('\\' === \DIRECTORY_SEPARATOR && \strlen($directory) > 234) { + throw new InvalidArgumentException(sprintf('Cache directory too long (%s).', $directory)); + } + $this->directory = $directory; + } + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace) + { + $ok = \true; + foreach ($this->scanHashDir($this->directory) as $file) { + if ('' !== $namespace && !str_starts_with($this->getFileKey($file), $namespace)) { + continue; + } + $ok = ($this->doUnlink($file) || !file_exists($file)) && $ok; + } + return $ok; + } + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = \true; + foreach ($ids as $id) { + $file = $this->getFile($id); + $ok = (!is_file($file) || $this->doUnlink($file) || !file_exists($file)) && $ok; + } + return $ok; + } + protected function doUnlink(string $file) + { + return @unlink($file); + } + private function write(string $file, string $data, ?int $expiresAt = null) + { + $unlink = \false; + set_error_handler(__CLASS__ . '::throwError'); + try { + if (null === $this->tmp) { + $this->tmp = $this->directory . bin2hex(random_bytes(6)); + } + try { + $h = fopen($this->tmp, 'x'); + } catch (\ErrorException $e) { + if (!str_contains($e->getMessage(), 'File exists')) { + throw $e; + } + $this->tmp = $this->directory . bin2hex(random_bytes(6)); + $h = fopen($this->tmp, 'x'); + } + fwrite($h, $data); + fclose($h); + $unlink = \true; + if (null !== $expiresAt) { + touch($this->tmp, $expiresAt ?: (time() + 31556952)); + // 1 year in seconds + } + if ('\\' === \DIRECTORY_SEPARATOR) { + $success = copy($this->tmp, $file); + $unlink = \true; + } else { + $success = rename($this->tmp, $file); + $unlink = !$success; + } + return $success; + } finally { + restore_error_handler(); + if ($unlink) { + @unlink($this->tmp); + } + } + } + private function getFile(string $id, bool $mkdir = \false, ?string $directory = null) + { + // Use MD5 to favor speed over security, which is not an issue here + $hash = str_replace('/', '-', base64_encode(hash('md5', static::class . $id, \true))); + $dir = ($directory ?? $this->directory) . strtoupper($hash[0] . \DIRECTORY_SEPARATOR . $hash[1] . \DIRECTORY_SEPARATOR); + if ($mkdir && !is_dir($dir)) { + @mkdir($dir, 0777, \true); + } + return $dir . substr($hash, 2, 20); + } + private function getFileKey(string $file): string + { + return ''; + } + private function scanHashDir(string $directory): \Generator + { + if (!is_dir($directory)) { + return; + } + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + for ($i = 0; $i < 38; ++$i) { + if (!is_dir($directory . $chars[$i])) { + continue; + } + for ($j = 0; $j < 38; ++$j) { + if (!is_dir($dir = $directory . $chars[$i] . \DIRECTORY_SEPARATOR . $chars[$j])) { + continue; + } + foreach ((@scandir($dir, \SCANDIR_SORT_NONE)) ?: [] as $file) { + if ('.' !== $file && '..' !== $file) { + yield $dir . \DIRECTORY_SEPARATOR . $file; + } + } + } + } + } + /** + * @internal + */ + public static function throwError(int $type, string $message, string $file, int $line) + { + throw new \ErrorException($message, 0, $type, $file, $line); + } + /** + * @return array + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize ' . __CLASS__); + } + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__); + } + public function __destruct() + { + if (method_exists(parent::class, '__destruct')) { + parent::__destruct(); + } + if (null !== $this->tmp && is_file($this->tmp)) { + unlink($this->tmp); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/FilesystemTrait.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/FilesystemTrait.php new file mode 100644 index 0000000000..1add54dabc --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/FilesystemTrait.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +/** + * @author Nicolas Grekas + * @author Rob Frawley 2nd + * + * @internal + */ +trait FilesystemTrait +{ + use FilesystemCommonTrait; + private $marshaller; + /** + * @return bool + */ + public function prune() + { + $time = time(); + $pruned = \true; + foreach ($this->scanHashDir($this->directory) as $file) { + if (!$h = @fopen($file, 'r')) { + continue; + } + if (($expiresAt = (int) fgets($h)) && $time >= $expiresAt) { + fclose($h); + $pruned = (@unlink($file) || !file_exists($file)) && $pruned; + } else { + fclose($h); + } + } + return $pruned; + } + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $values = []; + $now = time(); + foreach ($ids as $id) { + $file = $this->getFile($id); + if (!is_file($file) || !$h = @fopen($file, 'r')) { + continue; + } + if (($expiresAt = (int) fgets($h)) && $now >= $expiresAt) { + fclose($h); + @unlink($file); + } else { + $i = rawurldecode(rtrim(fgets($h))); + $value = stream_get_contents($h); + fclose($h); + if ($i === $id) { + $values[$id] = $this->marshaller->unmarshall($value); + } + } + } + return $values; + } + /** + * {@inheritdoc} + */ + protected function doHave(string $id) + { + $file = $this->getFile($id); + return is_file($file) && (@filemtime($file) > time() || $this->doFetch([$id])); + } + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime) + { + $expiresAt = $lifetime ? time() + $lifetime : 0; + $values = $this->marshaller->marshall($values, $failed); + foreach ($values as $id => $value) { + if (!$this->write($this->getFile($id, \true), $expiresAt . "\n" . rawurlencode($id) . "\n" . $value, $expiresAt)) { + $failed[] = $id; + } + } + if ($failed && !is_writable($this->directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s).', $this->directory)); + } + return $failed; + } + private function getFileKey(string $file): string + { + if (!$h = @fopen($file, 'r')) { + return ''; + } + fgets($h); + // expiry + $encodedKey = fgets($h); + fclose($h); + return rawurldecode(rtrim($encodedKey)); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/ProxyTrait.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/ProxyTrait.php new file mode 100644 index 0000000000..3c68bf2152 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/ProxyTrait.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 Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +use Wordlift\Modules\Common\Symfony\Component\Cache\PruneableInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ResetInterface; +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ProxyTrait +{ + private $pool; + /** + * {@inheritdoc} + */ + public function prune() + { + return $this->pool instanceof PruneableInterface && $this->pool->prune(); + } + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResetInterface) { + $this->pool->reset(); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisClusterNodeProxy.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisClusterNodeProxy.php new file mode 100644 index 0000000000..f0f15b108a --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisClusterNodeProxy.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 Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +/** + * This file acts as a wrapper to the \RedisCluster implementation so it can accept the same type of calls as + * individual \Redis objects. + * + * Calls are made to individual nodes via: RedisCluster->{method}($host, ...args)' + * according to https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#directed-node-commands + * + * @author Jack Thomas + * + * @internal + */ +class RedisClusterNodeProxy +{ + private $host; + private $redis; + /** + * @param \RedisCluster|RedisClusterProxy $redis + */ + public function __construct(array $host, $redis) + { + $this->host = $host; + $this->redis = $redis; + } + public function __call(string $method, array $args) + { + return $this->redis->{$method}($this->host, ...$args); + } + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + return $this->redis->scan($iIterator, $this->host, $strPattern, $iCount); + } + public function getOption($name) + { + return $this->redis->getOption($name); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisClusterProxy.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisClusterProxy.php new file mode 100644 index 0000000000..13478f5d5e --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisClusterProxy.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 Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +/** + * @author Alessandro Chitolina + * + * @internal + */ +class RedisClusterProxy +{ + private $redis; + private $initializer; + public function __construct(\Closure $initializer) + { + $this->initializer = $initializer; + } + public function __call(string $method, array $args) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + return $this->redis->{$method}(...$args); + } + public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount); + } + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + return $this->redis->scan($iIterator, $strPattern, $iCount); + } + public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount); + } + public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisProxy.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisProxy.php new file mode 100644 index 0000000000..9575277a80 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisProxy.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class RedisProxy +{ + private $redis; + private $initializer; + private $ready = \false; + public function __construct(\Redis $redis, \Closure $initializer) + { + $this->redis = $redis; + $this->initializer = $initializer; + } + public function __call(string $method, array $args) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + return $this->redis->{$method}(...$args); + } + public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount); + } + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + return $this->redis->scan($iIterator, $strPattern, $iCount); + } + public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount); + } + public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisTrait.php b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisTrait.php new file mode 100644 index 0000000000..7c6a96e454 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/Traits/RedisTrait.php @@ -0,0 +1,553 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Cache\Traits; + +use Wordlift\Modules\Common\Predis\Command\Redis\UNLINK; +use Wordlift\Modules\Common\Predis\Connection\Aggregate\ClusterInterface; +use Wordlift\Modules\Common\Predis\Connection\Aggregate\RedisCluster; +use Wordlift\Modules\Common\Predis\Connection\Aggregate\ReplicationInterface; +use Wordlift\Modules\Common\Predis\Connection\Cluster\ClusterInterface as Predis2ClusterInterface; +use Wordlift\Modules\Common\Predis\Connection\Cluster\RedisCluster as Predis2RedisCluster; +use Wordlift\Modules\Common\Predis\Response\ErrorInterface; +use Wordlift\Modules\Common\Predis\Response\Status; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\CacheException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Wordlift\Modules\Common\Symfony\Component\Cache\Marshaller\MarshallerInterface; +/** + * @author Aurimas Niekis + * @author Nicolas Grekas + * + * @internal + */ +trait RedisTrait +{ + private static $defaultConnectionOptions = ['class' => null, 'persistent' => 0, 'persistent_id' => null, 'timeout' => 30, 'read_timeout' => 0, 'retry_interval' => 0, 'tcp_keepalive' => 0, 'lazy' => null, 'redis_cluster' => \false, 'redis_sentinel' => null, 'dbindex' => 0, 'failover' => 'none', 'ssl' => null]; + private $redis; + private $marshaller; + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis + */ + private function init($redis, string $namespace, int $defaultLifetime, ?MarshallerInterface $marshaller) + { + parent::__construct($namespace, $defaultLifetime); + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + if (!$redis instanceof \Redis && !$redis instanceof \RedisArray && !$redis instanceof \RedisCluster && !$redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface && !$redis instanceof RedisProxy && !$redis instanceof RedisClusterProxy) { + throw new InvalidArgumentException(sprintf('"%s()" expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, "%s" given.', __METHOD__, get_debug_type($redis))); + } + if ($redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface && $redis->getOptions()->exceptions) { + $options = clone $redis->getOptions(); + \Closure::bind(function () { + $this->options['exceptions'] = \false; + }, $options, $options)(); + $redis = new $redis($redis->getConnection(), $options); + } + $this->redis = $redis; + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + /** + * Creates a Redis connection using a DSN configuration. + * + * Example DSN: + * - redis://localhost + * - redis://example.com:1234 + * - redis://secret@example.com/13 + * - redis:///var/run/redis.sock + * - redis://secret@/var/run/redis.sock/13 + * + * @param array $options See self::$defaultConnectionOptions + * + * @return \Redis|\RedisArray|\RedisCluster|RedisClusterProxy|RedisProxy|\Predis\ClientInterface According to the "class" option + * + * @throws InvalidArgumentException when the DSN is invalid + */ + public static function createConnection(string $dsn, array $options = []) + { + if (str_starts_with($dsn, 'redis:')) { + $scheme = 'redis'; + } elseif (str_starts_with($dsn, 'rediss:')) { + $scheme = 'rediss'; + } else { + throw new InvalidArgumentException('Invalid Redis DSN: it does not start with "redis[s]:".'); + } + if (!\extension_loaded('redis') && !class_exists(\Wordlift\Modules\Common\Predis\Client::class)) { + throw new CacheException('Cannot find the "redis" extension nor the "predis/predis" package.'); + } + $params = preg_replace_callback('#^' . $scheme . ':(//)?(?:(?:[^:@]*+:)?([^@]*+)@)?#', function ($m) use (&$auth) { + if (isset($m[2])) { + $auth = rawurldecode($m[2]); + if ('' === $auth) { + $auth = null; + } + } + return 'file:' . ($m[1] ?? ''); + }, $dsn); + if (\false === $params = parse_url($params)) { + throw new InvalidArgumentException('Invalid Redis DSN.'); + } + $query = $hosts = []; + $tls = 'rediss' === $scheme; + $tcpScheme = $tls ? 'tls' : 'tcp'; + if (isset($params['query'])) { + parse_str($params['query'], $query); + if (isset($query['host'])) { + if (!\is_array($hosts = $query['host'])) { + throw new InvalidArgumentException('Invalid Redis DSN: query parameter "host" must be an array.'); + } + foreach ($hosts as $host => $parameters) { + if (\is_string($parameters)) { + parse_str($parameters, $parameters); + } + if (\false === $i = strrpos($host, ':')) { + $hosts[$host] = ['scheme' => $tcpScheme, 'host' => $host, 'port' => 6379] + $parameters; + } elseif ($port = (int) substr($host, 1 + $i)) { + $hosts[$host] = ['scheme' => $tcpScheme, 'host' => substr($host, 0, $i), 'port' => $port] + $parameters; + } else { + $hosts[$host] = ['scheme' => 'unix', 'path' => substr($host, 0, $i)] + $parameters; + } + } + $hosts = array_values($hosts); + } + } + if (isset($params['host']) || isset($params['path'])) { + if (!isset($params['dbindex']) && isset($params['path'])) { + if (preg_match('#/(\d+)?$#', $params['path'], $m)) { + $params['dbindex'] = $m[1] ?? $query['dbindex'] ?? '0'; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } elseif (isset($params['host'])) { + throw new InvalidArgumentException('Invalid Redis DSN: parameter "dbindex" must be a number.'); + } + } + if (isset($params['host'])) { + array_unshift($hosts, ['scheme' => $tcpScheme, 'host' => $params['host'], 'port' => $params['port'] ?? 6379]); + } else { + array_unshift($hosts, ['scheme' => 'unix', 'path' => $params['path']]); + } + } + if (!$hosts) { + throw new InvalidArgumentException('Invalid Redis DSN: missing host.'); + } + if (isset($params['dbindex'], $query['dbindex']) && $params['dbindex'] !== $query['dbindex']) { + throw new InvalidArgumentException('Invalid Redis DSN: path and query "dbindex" parameters mismatch.'); + } + $params += $query + $options + self::$defaultConnectionOptions; + if (isset($params['redis_sentinel']) && !class_exists(\Wordlift\Modules\Common\Predis\Client::class) && !class_exists(\RedisSentinel::class)) { + throw new CacheException('Redis Sentinel support requires the "predis/predis" package or the "redis" extension v5.2 or higher.'); + } + if (isset($params['lazy'])) { + $params['lazy'] = filter_var($params['lazy'], \FILTER_VALIDATE_BOOLEAN); + } + $params['redis_cluster'] = filter_var($params['redis_cluster'], \FILTER_VALIDATE_BOOLEAN); + if ($params['redis_cluster'] && isset($params['redis_sentinel'])) { + throw new InvalidArgumentException('Cannot use both "redis_cluster" and "redis_sentinel" at the same time.'); + } + if (null === $params['class'] && \extension_loaded('redis')) { + $class = $params['redis_cluster'] ? \RedisCluster::class : ((1 < \count($hosts) && !isset($params['redis_sentinel'])) ? \RedisArray::class : \Redis::class); + } else { + $class = $params['class'] ?? \Wordlift\Modules\Common\Predis\Client::class; + if (isset($params['redis_sentinel']) && !is_a($class, \Wordlift\Modules\Common\Predis\Client::class, \true) && !class_exists(\RedisSentinel::class)) { + throw new CacheException(sprintf('Cannot use Redis Sentinel: class "%s" does not extend "Predis\Client" and ext-redis >= 5.2 not found.', $class)); + } + } + if (is_a($class, \Redis::class, \true)) { + $connect = ($params['persistent'] || $params['persistent_id']) ? 'pconnect' : 'connect'; + $redis = new $class(); + $initializer = static function ($redis) use ($connect, $params, $auth, $hosts, $tls) { + $hostIndex = 0; + do { + $host = $hosts[$hostIndex]['host'] ?? $hosts[$hostIndex]['path']; + $port = $hosts[$hostIndex]['port'] ?? 0; + $passAuth = \defined('Redis::OPT_NULL_MULTIBULK_AS_NULL') && isset($params['auth']); + $address = \false; + if (isset($hosts[$hostIndex]['host']) && $tls) { + $host = 'tls://' . $host; + } + if (!isset($params['redis_sentinel'])) { + break; + } + if (version_compare(phpversion('redis'), '6.0.0', '>=')) { + $options = ['host' => $host, 'port' => $port, 'connectTimeout' => (float) $params['timeout'], 'persistent' => $params['persistent_id'], 'retryInterval' => (int) $params['retry_interval'], 'readTimeout' => (float) $params['read_timeout']]; + if ($passAuth) { + $options['auth'] = $params['auth']; + } + $sentinel = new \RedisSentinel($options); + } else { + $extra = $passAuth ? [$params['auth']] : []; + $sentinel = new \RedisSentinel($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...$extra); + } + try { + if ($address = $sentinel->getMasterAddrByName($params['redis_sentinel'])) { + [$host, $port] = $address; + } + } catch (\RedisException $e) { + } + } while (++$hostIndex < \count($hosts) && !$address); + if (isset($params['redis_sentinel']) && !$address) { + throw new InvalidArgumentException(sprintf('Failed to retrieve master information from sentinel "%s".', $params['redis_sentinel'])); + } + try { + $extra = ['stream' => $params['ssl'] ?? null]; + $booleanStreamOptions = ['allow_self_signed', 'capture_peer_cert', 'capture_peer_cert_chain', 'disable_compression', 'SNI_enabled', 'verify_peer', 'verify_peer_name']; + foreach ($extra['stream'] ?? [] as $streamOption => $value) { + if (\in_array($streamOption, $booleanStreamOptions, \true) && \is_string($value)) { + $extra['stream'][$streamOption] = filter_var($value, \FILTER_VALIDATE_BOOL); + } + } + if (isset($params['auth'])) { + $extra['auth'] = $params['auth']; + } + @$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...\defined('Redis::SCAN_PREFIX') ? [$extra] : []); + set_error_handler(function ($type, $msg) use (&$error) { + $error = $msg; + }); + try { + $isConnected = $redis->isConnected(); + } finally { + restore_error_handler(); + } + if (!$isConnected) { + $error = preg_match('/^Redis::p?connect\(\): (.*)/', $error ?? $redis->getLastError() ?? '', $error) ? sprintf(' (%s)', $error[1]) : ''; + throw new InvalidArgumentException('Redis connection failed: ' . $error . '.'); + } + if (null !== $auth && !$redis->auth($auth) || ($params['dbindex'] || 'pconnect' === $connect && '0' !== \ini_get('redis.pconnect.pooling_enabled')) && !$redis->select($params['dbindex'])) { + $e = preg_replace('/^ERR /', '', $redis->getLastError()); + throw new InvalidArgumentException('Redis connection failed: ' . $e . '.'); + } + if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) { + $redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']); + } + } catch (\RedisException $e) { + throw new InvalidArgumentException('Redis connection failed: ' . $e->getMessage()); + } + return \true; + }; + if ($params['lazy']) { + $redis = new RedisProxy($redis, $initializer); + } else { + $initializer($redis); + } + } elseif (is_a($class, \RedisArray::class, \true)) { + foreach ($hosts as $i => $host) { + switch ($host['scheme']) { + case 'tcp': + $hosts[$i] = $host['host'] . ':' . $host['port']; + break; + case 'tls': + $hosts[$i] = 'tls://' . $host['host'] . ':' . $host['port']; + break; + default: + $hosts[$i] = $host['path']; + } + } + $params['lazy_connect'] = $params['lazy'] ?? \true; + $params['connect_timeout'] = $params['timeout']; + try { + $redis = new $class($hosts, $params); + } catch (\RedisClusterException $e) { + throw new InvalidArgumentException('Redis connection failed: ' . $e->getMessage()); + } + if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) { + $redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']); + } + } elseif (is_a($class, \RedisCluster::class, \true)) { + $initializer = static function () use ($class, $params, $hosts) { + foreach ($hosts as $i => $host) { + switch ($host['scheme']) { + case 'tcp': + $hosts[$i] = $host['host'] . ':' . $host['port']; + break; + case 'tls': + $hosts[$i] = 'tls://' . $host['host'] . ':' . $host['port']; + break; + default: + $hosts[$i] = $host['path']; + } + } + try { + $redis = new $class(null, $hosts, $params['timeout'], $params['read_timeout'], (bool) $params['persistent'], $params['auth'] ?? '', ...\defined('Redis::SCAN_PREFIX') ? [$params['ssl'] ?? null] : []); + } catch (\RedisClusterException $e) { + throw new InvalidArgumentException('Redis connection failed: ' . $e->getMessage()); + } + if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) { + $redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']); + } + switch ($params['failover']) { + case 'error': + $redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_ERROR); + break; + case 'distribute': + $redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_DISTRIBUTE); + break; + case 'slaves': + $redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES); + break; + } + return $redis; + }; + $redis = $params['lazy'] ? new RedisClusterProxy($initializer) : $initializer(); + } elseif (is_a($class, \Wordlift\Modules\Common\Predis\ClientInterface::class, \true)) { + if ($params['redis_cluster']) { + $params['cluster'] = 'redis'; + } elseif (isset($params['redis_sentinel'])) { + $params['replication'] = 'sentinel'; + $params['service'] = $params['redis_sentinel']; + } + $params += ['parameters' => []]; + $params['parameters'] += ['persistent' => $params['persistent'], 'timeout' => $params['timeout'], 'read_write_timeout' => $params['read_timeout'], 'tcp_nodelay' => \true]; + if ($params['dbindex']) { + $params['parameters']['database'] = $params['dbindex']; + } + if (null !== $auth) { + $params['parameters']['password'] = $auth; + } + if (isset($params['ssl'])) { + foreach ($hosts as $i => $host) { + if (!isset($host['ssl'])) { + $hosts[$i]['ssl'] = $params['ssl']; + } + } + } + if (1 === \count($hosts) && !($params['redis_cluster'] || $params['redis_sentinel'])) { + $hosts = $hosts[0]; + } elseif (\in_array($params['failover'], ['slaves', 'distribute'], \true) && !isset($params['replication'])) { + $params['replication'] = \true; + $hosts[0] += ['alias' => 'master']; + } + $params['exceptions'] = \false; + $redis = new $class($hosts, array_diff_key($params, self::$defaultConnectionOptions)); + if (isset($params['redis_sentinel'])) { + $redis->getConnection()->setSentinelTimeout($params['timeout']); + } + } elseif (class_exists($class, \false)) { + throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis", "RedisArray", "RedisCluster" nor "Predis\ClientInterface".', $class)); + } else { + throw new InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + return $redis; + } + protected function doFetch(array $ids) + { + if (!$ids) { + return []; + } + $result = []; + if ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface && ($this->redis->getConnection() instanceof ClusterInterface || $this->redis->getConnection() instanceof Predis2ClusterInterface)) { + $values = $this->pipeline(function () use ($ids) { + foreach ($ids as $id) { + yield 'get' => [$id]; + } + }); + } else { + $values = $this->redis->mget($ids); + if (!\is_array($values) || \count($values) !== \count($ids)) { + return []; + } + $values = array_combine($ids, $values); + } + foreach ($values as $id => $v) { + if ($v) { + $result[$id] = $this->marshaller->unmarshall($v); + } + } + return $result; + } + protected function doHave(string $id) + { + return (bool) $this->redis->exists($id); + } + protected function doClear(string $namespace) + { + if ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) { + $prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : ''; + $prefixLen = \strlen($prefix ?? ''); + } + $cleared = \true; + $hosts = $this->getHosts(); + $host = reset($hosts); + if ($host instanceof \Wordlift\Modules\Common\Predis\Client && $host->getConnection() instanceof ReplicationInterface) { + // Predis supports info command only on the master in replication environments + $hosts = [$host->getClientFor('master')]; + } + foreach ($hosts as $host) { + if (!isset($namespace[0])) { + $cleared = $host->flushDb() && $cleared; + continue; + } + $info = $host->info('Server'); + $info = (!$info instanceof ErrorInterface) ? $info['Server'] ?? $info : ['redis_version' => '2.0']; + if (!$host instanceof \Wordlift\Modules\Common\Predis\ClientInterface) { + $prefix = (\defined('Redis::SCAN_PREFIX') && \Redis::SCAN_PREFIX & $host->getOption(\Redis::OPT_SCAN)) ? '' : $host->getOption(\Redis::OPT_PREFIX); + $prefixLen = \strlen($host->getOption(\Redis::OPT_PREFIX) ?? ''); + } + $pattern = $prefix . $namespace . '*'; + if (!version_compare($info['redis_version'], '2.8', '>=')) { + // As documented in Redis documentation (http://redis.io/commands/keys) using KEYS + // can hang your server when it is executed against large databases (millions of items). + // Whenever you hit this scale, you should really consider upgrading to Redis 2.8 or above. + $unlink = version_compare($info['redis_version'], '4.0', '>=') ? 'UNLINK' : 'DEL'; + $args = ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) ? [0, $pattern] : [[$pattern], 0]; + $cleared = $host->eval("local keys=redis.call('KEYS',ARGV[1]) for i=1,#keys,5000 do redis.call('{$unlink}',unpack(keys,i,math.min(i+4999,#keys))) end return 1", $args[0], $args[1]) && $cleared; + continue; + } + $cursor = null; + do { + $keys = ($host instanceof \Wordlift\Modules\Common\Predis\ClientInterface) ? $host->scan($cursor, 'MATCH', $pattern, 'COUNT', 1000) : $host->scan($cursor, $pattern, 1000); + if (isset($keys[1]) && \is_array($keys[1])) { + $cursor = $keys[0]; + $keys = $keys[1]; + } + if ($keys) { + if ($prefixLen) { + foreach ($keys as $i => $key) { + $keys[$i] = substr($key, $prefixLen); + } + } + $this->doDelete($keys); + } + } while ($cursor); + } + return $cleared; + } + protected function doDelete(array $ids) + { + if (!$ids) { + return \true; + } + if ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface && ($this->redis->getConnection() instanceof ClusterInterface || $this->redis->getConnection() instanceof Predis2ClusterInterface)) { + static $del; + $del = $del ?? (class_exists(UNLINK::class) ? 'unlink' : 'del'); + $this->pipeline(function () use ($ids, $del) { + foreach ($ids as $id) { + yield $del => [$id]; + } + })->rewind(); + } else { + static $unlink = \true; + if ($unlink) { + try { + $unlink = \false !== $this->redis->unlink($ids); + } catch (\Throwable $e) { + $unlink = \false; + } + } + if (!$unlink) { + $this->redis->del($ids); + } + } + return \true; + } + protected function doSave(array $values, int $lifetime) + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + $results = $this->pipeline(function () use ($values, $lifetime) { + foreach ($values as $id => $value) { + if (0 >= $lifetime) { + yield 'set' => [$id, $value]; + } else { + yield 'setEx' => [$id, $lifetime, $value]; + } + } + }); + foreach ($results as $id => $result) { + if (\true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) { + $failed[] = $id; + } + } + return $failed; + } + private function pipeline(\Closure $generator, ?object $redis = null): \Generator + { + $ids = []; + $redis = $redis ?? $this->redis; + if ($redis instanceof RedisClusterProxy || $redis instanceof \RedisCluster || $redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface && ($redis->getConnection() instanceof RedisCluster || $redis->getConnection() instanceof Predis2RedisCluster)) { + // phpredis & predis don't support pipelining with RedisCluster + // see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining + // see https://github.com/nrk/predis/issues/267#issuecomment-123781423 + $results = []; + foreach ($generator() as $command => $args) { + $results[] = $redis->{$command}(...$args); + $ids[] = ('eval' === $command) ? ($redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) ? $args[2] : $args[1][0] : $args[0]; + } + } elseif ($redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) { + $results = $redis->pipeline(static function ($redis) use ($generator, &$ids) { + foreach ($generator() as $command => $args) { + $redis->{$command}(...$args); + $ids[] = ('eval' === $command) ? $args[2] : $args[0]; + } + }); + } elseif ($redis instanceof \RedisArray) { + $connections = $results = $ids = []; + foreach ($generator() as $command => $args) { + $id = ('eval' === $command) ? $args[1][0] : $args[0]; + if (!isset($connections[$h = $redis->_target($id)])) { + $connections[$h] = [$redis->_instance($h), -1]; + $connections[$h][0]->multi(\Redis::PIPELINE); + } + $connections[$h][0]->{$command}(...$args); + $results[] = [$h, ++$connections[$h][1]]; + $ids[] = $id; + } + foreach ($connections as $h => $c) { + $connections[$h] = $c[0]->exec(); + } + foreach ($results as $k => [$h, $c]) { + $results[$k] = $connections[$h][$c]; + } + } else { + $redis->multi(\Redis::PIPELINE); + foreach ($generator() as $command => $args) { + $redis->{$command}(...$args); + $ids[] = ('eval' === $command) ? $args[1][0] : $args[0]; + } + $results = $redis->exec(); + } + if (!$redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface && 'eval' === $command && $redis->getLastError()) { + $e = new \RedisException($redis->getLastError()); + $results = array_map(function ($v) use ($e) { + return (\false === $v) ? $e : $v; + }, (array) $results); + } + if (\is_bool($results)) { + return; + } + foreach ($ids as $k => $id) { + yield $id => $results[$k]; + } + } + private function getHosts(): array + { + $hosts = [$this->redis]; + if ($this->redis instanceof \Wordlift\Modules\Common\Predis\ClientInterface) { + $connection = $this->redis->getConnection(); + if (($connection instanceof ClusterInterface || $connection instanceof Predis2ClusterInterface) && $connection instanceof \Traversable) { + $hosts = []; + foreach ($connection as $c) { + $hosts[] = new \Wordlift\Modules\Common\Predis\Client($c); + } + } + } elseif ($this->redis instanceof \RedisArray) { + $hosts = []; + foreach ($this->redis->_hosts() as $host) { + $hosts[] = $this->redis->_instance($host); + } + } elseif ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster) { + $hosts = []; + foreach ($this->redis->_masters() as $host) { + $hosts[] = new RedisClusterNodeProxy($host, $this->redis); + } + } + return $hosts; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/cache/composer.json b/src/modules/common/third-party/vendor/symfony/cache/composer.json new file mode 100644 index 0000000000..5676d942f0 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/cache/composer.json @@ -0,0 +1,65 @@ +{ + "name": "symfony\/cache", + "type": "library", + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "keywords": [ + "caching", + "psr6" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "provide": { + "psr\/cache-implementation": "1.0|2.0", + "psr\/simple-cache-implementation": "1.0|2.0", + "symfony\/cache-implementation": "1.0|2.0" + }, + "require": { + "php": ">=7.2.5", + "psr\/cache": "^1.0|^2.0", + "psr\/log": "^1.1|^2|^3", + "symfony\/cache-contracts": "^1.1.7|^2", + "symfony\/deprecation-contracts": "^2.1|^3", + "symfony\/polyfill-php73": "^1.9", + "symfony\/polyfill-php80": "^1.16", + "symfony\/service-contracts": "^1.1|^2|^3", + "symfony\/var-exporter": "^4.4|^5.0|^6.0" + }, + "require-dev": { + "cache\/integration-tests": "dev-master", + "doctrine\/cache": "^1.6|^2.0", + "doctrine\/dbal": "^2.13.1|^3|^4", + "predis\/predis": "^1.1|^2.0", + "psr\/simple-cache": "^1.0|^2.0", + "symfony\/config": "^4.4|^5.0|^6.0", + "symfony\/dependency-injection": "^4.4|^5.0|^6.0", + "symfony\/filesystem": "^4.4|^5.0|^6.0", + "symfony\/http-kernel": "^4.4|^5.0|^6.0", + "symfony\/messenger": "^4.4|^5.0|^6.0", + "symfony\/var-dumper": "^4.4|^5.0|^6.0" + }, + "conflict": { + "doctrine\/dbal": "<2.13.1", + "symfony\/dependency-injection": "<4.4", + "symfony\/http-kernel": "<4.4", + "symfony\/var-dumper": "<4.4" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "\/Tests\/" + ] + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/config/Builder/ClassBuilder.php b/src/modules/common/third-party/vendor/symfony/config/Builder/ClassBuilder.php new file mode 100644 index 0000000000..b6be8b5853 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/config/Builder/ClassBuilder.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Config\Builder; + +/** + * Build PHP classes to generate config. + * + * @internal + * + * @author Tobias Nyholm + */ +class ClassBuilder +{ + /** @var string */ + private $namespace; + /** @var string */ + private $name; + /** @var Property[] */ + private $properties = []; + /** @var Method[] */ + private $methods = []; + private $require = []; + private $use = []; + private $implements = []; + private $allowExtraKeys = \false; + public function __construct(string $namespace, string $name) + { + $this->namespace = $namespace; + $this->name = ucfirst($this->camelCase($name)) . 'Config'; + } + public function getDirectory(): string + { + return str_replace('\\', \DIRECTORY_SEPARATOR, $this->namespace); + } + public function getFilename(): string + { + return $this->name . '.php'; + } + public function build(): string + { + $rootPath = explode(\DIRECTORY_SEPARATOR, $this->getDirectory()); + $require = ''; + foreach ($this->require as $class) { + // figure out relative path. + $path = explode(\DIRECTORY_SEPARATOR, $class->getDirectory()); + $path[] = $class->getFilename(); + foreach ($rootPath as $key => $value) { + if ($path[$key] !== $value) { + break; + } + unset($path[$key]); + } + $require .= sprintf('require_once __DIR__.\DIRECTORY_SEPARATOR.\'%s\';', implode('\'.\DIRECTORY_SEPARATOR.\'', $path)) . "\n"; + } + $use = $require ? "\n" : ''; + foreach (array_keys($this->use) as $statement) { + $use .= sprintf('use %s;', $statement) . "\n"; + } + $implements = ([] === $this->implements) ? '' : ('implements ' . implode(', ', $this->implements)); + $body = ''; + foreach ($this->properties as $property) { + $body .= ' ' . $property->getContent() . "\n"; + } + foreach ($this->methods as $method) { + $lines = explode("\n", $method->getContent()); + foreach ($lines as $line) { + $body .= ($line ? ' ' . $line : '') . "\n"; + } + } + $content = strtr(' $this->namespace, 'REQUIRE' => $require, 'USE' => $use, 'CLASS' => $this->getName(), 'IMPLEMENTS' => $implements, 'BODY' => $body]); + return $content; + } + public function addRequire(self $class): void + { + $this->require[] = $class; + } + public function addUse(string $class): void + { + $this->use[$class] = \true; + } + public function addImplements(string $interface): void + { + $this->implements[] = '\\' . ltrim($interface, '\\'); + } + public function addMethod(string $name, string $body, array $params = []): void + { + $this->methods[] = new Method(strtr($body, ['NAME' => $this->camelCase($name)] + $params)); + } + public function addProperty(string $name, ?string $classType = null, ?string $defaultValue = null): Property + { + $property = new Property($name, ('_' !== $name[0]) ? $this->camelCase($name) : $name); + if (null !== $classType) { + $property->setType($classType); + } + $this->properties[] = $property; + $defaultValue = (null !== $defaultValue) ? sprintf(' = %s', $defaultValue) : ''; + $property->setContent(sprintf('private $%s%s;', $property->getName(), $defaultValue)); + return $property; + } + public function getProperties(): array + { + return $this->properties; + } + private function camelCase(string $input): string + { + $output = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $input)))); + return preg_replace('#\W#', '', $output); + } + public function getName(): string + { + return $this->name; + } + public function getNamespace(): string + { + return $this->namespace; + } + public function getFqcn(): string + { + return '\\' . $this->namespace . '\\' . $this->name; + } + public function setAllowExtraKeys(bool $allowExtraKeys): void + { + $this->allowExtraKeys = $allowExtraKeys; + } + public function shouldAllowExtraKeys(): bool + { + return $this->allowExtraKeys; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderGenerator.php b/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderGenerator.php new file mode 100644 index 0000000000..1c8bd726ae --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderGenerator.php @@ -0,0 +1,476 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Config\Builder; + +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\ArrayNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\BooleanNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\ConfigurationInterface; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\EnumNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\FloatNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\IntegerNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\NodeInterface; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\PrototypedArrayNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\ScalarNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\VariableNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Loader\ParamConfigurator; +/** + * Generate ConfigBuilders to help create valid config. + * + * @author Tobias Nyholm + */ +class ConfigBuilderGenerator implements ConfigBuilderGeneratorInterface +{ + /** + * @var ClassBuilder[] + */ + private $classes; + private $outputDir; + public function __construct(string $outputDir) + { + $this->outputDir = $outputDir; + } + /** + * @return \Closure that will return the root config class + */ + public function build(ConfigurationInterface $configuration): \Closure + { + $this->classes = []; + $rootNode = $configuration->getConfigTreeBuilder()->buildTree(); + $rootClass = new ClassBuilder('Wordlift\Modules\Common\Symfony\Config', $rootNode->getName()); + $path = $this->getFullPath($rootClass); + if (!is_file($path)) { + // Generate the class if the file not exists + $this->classes[] = $rootClass; + $this->buildNode($rootNode, $rootClass, $this->getSubNamespace($rootClass)); + $rootClass->addImplements(ConfigBuilderInterface::class); + $rootClass->addMethod('getExtensionAlias', ' +public function NAME(): string +{ + return \'ALIAS\'; +}', ['ALIAS' => $rootNode->getPath()]); + $this->writeClasses(); + } + $loader = \Closure::fromCallable(function () use ($path, $rootClass) { + require_once $path; + $className = $rootClass->getFqcn(); + return new $className(); + }); + return $loader; + } + private function getFullPath(ClassBuilder $class): string + { + $directory = $this->outputDir . \DIRECTORY_SEPARATOR . $class->getDirectory(); + if (!is_dir($directory)) { + @mkdir($directory, 0777, \true); + } + return $directory . \DIRECTORY_SEPARATOR . $class->getFilename(); + } + private function writeClasses(): void + { + foreach ($this->classes as $class) { + $this->buildConstructor($class); + $this->buildToArray($class); + if ($class->getProperties()) { + $class->addProperty('_usedProperties', null, '[]'); + } + $this->buildSetExtraKey($class); + file_put_contents($this->getFullPath($class), $class->build()); + } + $this->classes = []; + } + private function buildNode(NodeInterface $node, ClassBuilder $class, string $namespace): void + { + if (!$node instanceof ArrayNode) { + throw new \LogicException('The node was expected to be an ArrayNode. This Configuration includes an edge case not supported yet.'); + } + foreach ($node->getChildren() as $child) { + switch (\true) { + case $child instanceof ScalarNode: + $this->handleScalarNode($child, $class); + break; + case $child instanceof PrototypedArrayNode: + $this->handlePrototypedArrayNode($child, $class, $namespace); + break; + case $child instanceof VariableNode: + $this->handleVariableNode($child, $class); + break; + case $child instanceof ArrayNode: + $this->handleArrayNode($child, $class, $namespace); + break; + default: + throw new \RuntimeException(sprintf('Unknown node "%s".', \get_class($child))); + } + } + } + private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $namespace): void + { + $childClass = new ClassBuilder($namespace, $node->getName()); + $childClass->setAllowExtraKeys($node->shouldIgnoreExtraKeys()); + $class->addRequire($childClass); + $this->classes[] = $childClass; + $hasNormalizationClosures = $this->hasNormalizationClosures($node); + $property = $class->addProperty($node->getName(), $this->getType($childClass->getFqcn(), $hasNormalizationClosures)); + $body = $hasNormalizationClosures ? ' +/** + * @return CLASS|$this + */ +public function NAME($value = []) +{ + if (!\is_array($value)) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; + } + + if (!$this->PROPERTY instanceof CLASS) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = new CLASS($value); + } elseif (0 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY; +}' : ' +public function NAME(array $value = []): CLASS +{ + if (null === $this->PROPERTY) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = new CLASS($value); + } elseif (0 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY; +}'; + $class->addUse(InvalidConfigurationException::class); + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + $this->buildNode($node, $childClass, $this->getSubNamespace($childClass)); + } + private function handleVariableNode(VariableNode $node, ClassBuilder $class): void + { + $comment = $this->getComment($node); + $property = $class->addProperty($node->getName()); + $class->addUse(ParamConfigurator::class); + $body = ' +/** +COMMENT * @return $this + */ +public function NAME($valueDEFAULT): self +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; +}'; + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment, 'DEFAULT' => $node->hasDefaultValue() ? ' = ' . var_export($node->getDefaultValue(), \true) : '']); + } + private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuilder $class, string $namespace): void + { + $name = $this->getSingularName($node); + $prototype = $node->getPrototype(); + $methodName = $name; + $parameterType = $this->getParameterType($prototype); + if (null !== $parameterType || $prototype instanceof ScalarNode) { + $class->addUse(ParamConfigurator::class); + $property = $class->addProperty($node->getName()); + if (null === $key = $node->getKeyAttribute()) { + // This is an array of values; don't use singular name + $body = ' +/** + * @param ParamConfigurator|list $value + * @return $this + */ +public function NAME($value): self +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; +}'; + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'TYPE' => ('' === $parameterType) ? 'mixed' : $parameterType]); + } else { + $body = ' +/** + * @param ParamConfigurator|TYPE $value + * @return $this + */ +public function NAME(string $VAR, $VALUE): self +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = $VALUE; + + return $this; +}'; + $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'TYPE' => ('' === $parameterType) ? 'mixed' : $parameterType, 'VAR' => ('' === $key) ? 'key' : $key, 'VALUE' => ('value' === $key) ? 'data' : 'value']); + } + return; + } + $childClass = new ClassBuilder($namespace, $name); + if ($prototype instanceof ArrayNode) { + $childClass->setAllowExtraKeys($prototype->shouldIgnoreExtraKeys()); + } + $class->addRequire($childClass); + $this->classes[] = $childClass; + $hasNormalizationClosures = $this->hasNormalizationClosures($node) || $this->hasNormalizationClosures($prototype); + $property = $class->addProperty($node->getName(), $this->getType($childClass->getFqcn() . '[]', $hasNormalizationClosures)); + if (null === $key = $node->getKeyAttribute()) { + $body = $hasNormalizationClosures ? ' +/** + * @return CLASS|$this + */ +public function NAME($value = []) +{ + $this->_usedProperties[\'PROPERTY\'] = true; + if (!\is_array($value)) { + $this->PROPERTY[] = $value; + + return $this; + } + + return $this->PROPERTY[] = new CLASS($value); +}' : ' +public function NAME(array $value = []): CLASS +{ + $this->_usedProperties[\'PROPERTY\'] = true; + + return $this->PROPERTY[] = new CLASS($value); +}'; + $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + } else { + $body = $hasNormalizationClosures ? ' +/** + * @return CLASS|$this + */ +public function NAME(string $VAR, $VALUE = []) +{ + if (!\is_array($VALUE)) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = $VALUE; + + return $this; + } + + if (!isset($this->PROPERTY[$VAR]) || !$this->PROPERTY[$VAR] instanceof CLASS) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = new CLASS($VALUE); + } elseif (1 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY[$VAR]; +}' : ' +public function NAME(string $VAR, array $VALUE = []): CLASS +{ + if (!isset($this->PROPERTY[$VAR])) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = new CLASS($VALUE); + } elseif (1 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY[$VAR]; +}'; + $class->addUse(InvalidConfigurationException::class); + $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn(), 'VAR' => ('' === $key) ? 'key' : $key, 'VALUE' => ('value' === $key) ? 'data' : 'value']); + } + $this->buildNode($prototype, $childClass, $namespace . '\\' . $childClass->getName()); + } + private function handleScalarNode(ScalarNode $node, ClassBuilder $class): void + { + $comment = $this->getComment($node); + $property = $class->addProperty($node->getName()); + $class->addUse(ParamConfigurator::class); + $body = ' +/** +COMMENT * @return $this + */ +public function NAME($value): self +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; +}'; + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]); + } + private function getParameterType(NodeInterface $node): ?string + { + if ($node instanceof BooleanNode) { + return 'bool'; + } + if ($node instanceof IntegerNode) { + return 'int'; + } + if ($node instanceof FloatNode) { + return 'float'; + } + if ($node instanceof EnumNode) { + return ''; + } + if ($node instanceof PrototypedArrayNode && $node->getPrototype() instanceof ScalarNode) { + // This is just an array of variables + return 'array'; + } + if ($node instanceof VariableNode) { + // mixed + return ''; + } + return null; + } + private function getComment(VariableNode $node): string + { + $comment = ''; + if ('' !== $info = (string) $node->getInfo()) { + $comment .= ' * ' . $info . "\n"; + } + foreach ((array) ($node->getExample() ?? []) as $example) { + $comment .= ' * @example ' . $example . "\n"; + } + if ('' !== $default = $node->getDefaultValue()) { + $comment .= ' * @default ' . ((null === $default) ? 'null' : var_export($default, \true)) . "\n"; + } + if ($node instanceof EnumNode) { + $comment .= sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_map(function ($a) { + return var_export($a, \true); + }, $node->getValues()))) . "\n"; + } else { + $parameterType = $this->getParameterType($node); + if (null === $parameterType || '' === $parameterType) { + $parameterType = 'mixed'; + } + $comment .= ' * @param ParamConfigurator|' . $parameterType . ' $value' . "\n"; + } + if ($node->isDeprecated()) { + $comment .= ' * @deprecated ' . $node->getDeprecation($node->getName(), $node->getParent()->getName())['message'] . "\n"; + } + return $comment; + } + /** + * Pick a good singular name. + */ + private function getSingularName(PrototypedArrayNode $node): string + { + $name = $node->getName(); + if ('s' !== substr($name, -1)) { + return $name; + } + $parent = $node->getParent(); + $mappings = ($parent instanceof ArrayNode) ? $parent->getXmlRemappings() : []; + foreach ($mappings as $map) { + if ($map[1] === $name) { + $name = $map[0]; + break; + } + } + return $name; + } + private function buildToArray(ClassBuilder $class): void + { + $body = '$output = [];'; + foreach ($class->getProperties() as $p) { + $code = '$this->PROPERTY'; + if (null !== $p->getType()) { + if ($p->isArray()) { + $code = $p->areScalarsAllowed() ? 'array_map(function ($v) { return $v instanceof CLASS ? $v->toArray() : $v; }, $this->PROPERTY)' : 'array_map(function ($v) { return $v->toArray(); }, $this->PROPERTY)'; + } else { + $code = $p->areScalarsAllowed() ? '$this->PROPERTY instanceof CLASS ? $this->PROPERTY->toArray() : $this->PROPERTY' : '$this->PROPERTY->toArray()'; + } + } + $body .= strtr(' + if (isset($this->_usedProperties[\'PROPERTY\'])) { + $output[\'ORG_NAME\'] = ' . $code . '; + }', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName(), 'CLASS' => $p->getType()]); + } + $extraKeys = $class->shouldAllowExtraKeys() ? ' + $this->_extraKeys' : ''; + $class->addMethod('toArray', ' +public function NAME(): array +{ + ' . $body . ' + + return $output' . $extraKeys . '; +}'); + } + private function buildConstructor(ClassBuilder $class): void + { + $body = ''; + foreach ($class->getProperties() as $p) { + $code = '$value[\'ORG_NAME\']'; + if (null !== $p->getType()) { + if ($p->isArray()) { + $code = $p->areScalarsAllowed() ? 'array_map(function ($v) { return \is_array($v) ? new ' . $p->getType() . '($v) : $v; }, $value[\'ORG_NAME\'])' : ('array_map(function ($v) { return new ' . $p->getType() . '($v); }, $value[\'ORG_NAME\'])'); + } else { + $code = $p->areScalarsAllowed() ? '\is_array($value[\'ORG_NAME\']) ? new ' . $p->getType() . '($value[\'ORG_NAME\']) : $value[\'ORG_NAME\']' : ('new ' . $p->getType() . '($value[\'ORG_NAME\'])'); + } + } + $body .= strtr(' + if (array_key_exists(\'ORG_NAME\', $value)) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = ' . $code . '; + unset($value[\'ORG_NAME\']); + } +', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName()]); + } + if ($class->shouldAllowExtraKeys()) { + $body .= ' + $this->_extraKeys = $value; +'; + } else { + $body .= ' + if ([] !== $value) { + throw new InvalidConfigurationException(sprintf(\'The following keys are not supported by "%s": \', __CLASS__).implode(\', \', array_keys($value))); + }'; + $class->addUse(InvalidConfigurationException::class); + } + $class->addMethod('__construct', ' +public function __construct(array $value = []) +{' . $body . ' +}'); + } + private function buildSetExtraKey(ClassBuilder $class): void + { + if (!$class->shouldAllowExtraKeys()) { + return; + } + $class->addUse(ParamConfigurator::class); + $class->addProperty('_extraKeys'); + $class->addMethod('set', ' +/** + * @param ParamConfigurator|mixed $value + * @return $this + */ +public function NAME(string $key, $value): self +{ + $this->_extraKeys[$key] = $value; + + return $this; +}'); + } + private function getSubNamespace(ClassBuilder $rootClass): string + { + return sprintf('%s\%s', $rootClass->getNamespace(), substr($rootClass->getName(), 0, -6)); + } + private function hasNormalizationClosures(NodeInterface $node): bool + { + try { + $r = new \ReflectionProperty($node, 'normalizationClosures'); + } catch (\ReflectionException $e) { + return \false; + } + $r->setAccessible(\true); + return [] !== $r->getValue($node); + } + private function getType(string $classType, bool $hasNormalizationClosures): string + { + return $classType . ($hasNormalizationClosures ? '|scalar' : ''); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php b/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php new file mode 100644 index 0000000000..35210b3d14 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Config\Builder; + +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\ConfigurationInterface; +/** + * Generates ConfigBuilders to help create valid config. + * + * @author Tobias Nyholm + */ +interface ConfigBuilderGeneratorInterface +{ + /** + * @return \Closure that will return the root config class + */ + public function build(ConfigurationInterface $configuration): \Closure; +} diff --git a/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderInterface.php b/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderInterface.php new file mode 100644 index 0000000000..aeb8ad4638 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/config/Builder/ConfigBuilderInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Config\Builder; + +/** + * A ConfigBuilder provides helper methods to build a large complex array. + * + * @author Tobias Nyholm + */ +interface ConfigBuilderInterface +{ + /** + * Gets all configuration represented as an array. + */ + public function toArray(): array; + /** + * Gets the alias for the extension which config we are building. + */ + public function getExtensionAlias(): string; +} diff --git a/src/modules/common/third-party/vendor/symfony/config/Builder/Method.php b/src/modules/common/third-party/vendor/symfony/config/Builder/Method.php new file mode 100644 index 0000000000..2c26258e1a --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/config/Builder/Method.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Config\Builder; + +/** + * Represents a method when building classes. + * + * @internal + * + * @author Tobias Nyholm + */ +class Method +{ + private $content; + public function __construct(string $content) + { + $this->content = $content; + } + public function getContent(): string + { + return $this->content; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/config/Builder/Property.php b/src/modules/common/third-party/vendor/symfony/config/Builder/Property.php new file mode 100644 index 0000000000..3742a23058 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/config/Builder/Property.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Config\Builder; + +/** + * Represents a property when building classes. + * + * @internal + * + * @author Tobias Nyholm + */ +class Property +{ + private $name; + private $originalName; + private $array = \false; + private $scalarsAllowed = \false; + private $type = null; + private $content; + public function __construct(string $originalName, string $name) + { + $this->name = $name; + $this->originalName = $originalName; + } + public function getName(): string + { + return $this->name; + } + public function getOriginalName(): string + { + return $this->originalName; + } + public function setType(string $type): void + { + $this->array = \false; + $this->type = $type; + if ('|scalar' === substr($type, -7)) { + $this->scalarsAllowed = \true; + $this->type = $type = substr($type, 0, -7); + } + if ('[]' === substr($type, -2)) { + $this->array = \true; + $this->type = substr($type, 0, -2); + } + } + public function getType(): ?string + { + return $this->type; + } + public function getContent(): ?string + { + return $this->content; + } + public function setContent(string $content): void + { + $this->content = $content; + } + public function isArray(): bool + { + return $this->array; + } + public function areScalarsAllowed(): bool + { + return $this->scalarsAllowed; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/config/ConfigCache.php b/src/modules/common/third-party/vendor/symfony/config/ConfigCache.php index 4c5b836025..b8813ab1e0 100644 --- a/src/modules/common/third-party/vendor/symfony/config/ConfigCache.php +++ b/src/modules/common/third-party/vendor/symfony/config/ConfigCache.php @@ -28,9 +28,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 = []; if (\true === $this->debug) { $checkers = [new SelfCheckingResourceChecker()]; @@ -43,11 +43,11 @@ public function __construct($file, $debug) * This implementation always returns true when debug is off and the * cache file exists. * - * @return bool true if the cache is fresh, false otherwise + * @return bool */ public function isFresh() { - if (!$this->debug && \is_file($this->getPath())) { + if (!$this->debug && is_file($this->getPath())) { return \true; } return parent::isFresh(); diff --git a/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactory.php b/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactory.php index 907787db1f..320db0657a 100644 --- a/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactory.php +++ b/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactory.php @@ -25,21 +25,18 @@ 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; } /** * {@inheritdoc} */ - public function cache($file, $callback) + public function cache(string $file, callable $callback) { - if (!\is_callable($callback)) { - throw new \InvalidArgumentException(\sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback))); - } $cache = new ConfigCache($file, $this->debug); if (!$cache->isFresh()) { - \call_user_func($callback, $cache); + $callback($cache); } return $cache; } diff --git a/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactoryInterface.php b/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactoryInterface.php index a9a4511600..db0784662c 100644 --- a/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactoryInterface.php +++ b/src/modules/common/third-party/vendor/symfony/config/ConfigCacheFactoryInterface.php @@ -25,7 +25,7 @@ interface ConfigCacheFactoryInterface * @param string $file The absolute cache file path * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback * - * @return ConfigCacheInterface The cache instance + * @return ConfigCacheInterface */ - public function cache($file, $callable); + public function cache(string $file, callable $callable); } diff --git a/src/modules/common/third-party/vendor/symfony/config/ConfigCacheInterface.php b/src/modules/common/third-party/vendor/symfony/config/ConfigCacheInterface.php index 8d33b5f587..63cef69db7 100644 --- a/src/modules/common/third-party/vendor/symfony/config/ConfigCacheInterface.php +++ b/src/modules/common/third-party/vendor/symfony/config/ConfigCacheInterface.php @@ -21,7 +21,7 @@ interface ConfigCacheInterface /** * Gets the cache file path. * - * @return string The cache file path + * @return string */ public function getPath(); /** @@ -29,7 +29,7 @@ public function getPath(); * * This check should take the metadata passed to the write() method into consideration. * - * @return bool Whether the cache is still fresh + * @return bool */ public function isFresh(); /** @@ -41,5 +41,5 @@ public function isFresh(); * * @throws \RuntimeException When the cache file cannot be written */ - public function write($content, array $metadata = null); + public function write(string $content, ?array $metadata = null); } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/ArrayNode.php b/src/modules/common/third-party/vendor/symfony/config/Definition/ArrayNode.php index 0363598f7e..a9ccd19c5f 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/ArrayNode.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/ArrayNode.php @@ -29,9 +29,9 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface protected $ignoreExtraKeys = \false; protected $removeExtraKeys = \true; protected $normalizeKeys = \true; - public function setNormalizeKeys($normalizeKeys) + public function setNormalizeKeys(bool $normalizeKeys) { - $this->normalizeKeys = (bool) $normalizeKeys; + $this->normalizeKeys = $normalizeKeys; } /** * {@inheritdoc} @@ -49,7 +49,7 @@ protected function preNormalize($value) } $normalized = []; foreach ($value as $k => $v) { - if (\false !== \strpos($k, '-') && \false === \strpos($k, '_') && !\array_key_exists($normalizedKey = \str_replace('-', '_', $k), $value)) { + if (str_contains($k, '-') && !str_contains($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { $normalized[$normalizedKey] = $v; } else { $normalized[$k] = $v; @@ -60,7 +60,7 @@ protected function preNormalize($value) /** * Retrieves the children of this node. * - * @return array The children + * @return array */ public function getChildren() { @@ -87,39 +87,31 @@ public function getXmlRemappings() /** * Sets whether to add default values for this array if it has not been * defined in any of the configuration files. - * - * @param bool $boolean */ - public function setAddIfNotSet($boolean) + public function setAddIfNotSet(bool $boolean) { - $this->addIfNotSet = (bool) $boolean; + $this->addIfNotSet = $boolean; } /** * Sets whether false is allowed as value indicating that the array should be unset. - * - * @param bool $allow */ - public function setAllowFalse($allow) + public function setAllowFalse(bool $allow) { - $this->allowFalse = (bool) $allow; + $this->allowFalse = $allow; } /** * Sets whether new keys can be defined in subsequent configurations. - * - * @param bool $allow */ - public function setAllowNewKeys($allow) + public function setAllowNewKeys(bool $allow) { - $this->allowNewKeys = (bool) $allow; + $this->allowNewKeys = $allow; } /** * Sets if deep merging should occur. - * - * @param bool $boolean */ - public function setPerformDeepMerging($boolean) + public function setPerformDeepMerging(bool $boolean) { - $this->performDeepMerging = (bool) $boolean; + $this->performDeepMerging = $boolean; } /** * Whether extra keys should just be ignored without an exception. @@ -127,15 +119,22 @@ public function setPerformDeepMerging($boolean) * @param bool $boolean To allow extra keys * @param bool $remove To remove extra keys */ - public function setIgnoreExtraKeys($boolean, $remove = \true) + public function setIgnoreExtraKeys(bool $boolean, bool $remove = \true) { - $this->ignoreExtraKeys = (bool) $boolean; + $this->ignoreExtraKeys = $boolean; $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; } + /** + * Returns true when extra keys should be ignored without an exception. + */ + public function shouldIgnoreExtraKeys(): bool + { + return $this->ignoreExtraKeys; + } /** * {@inheritdoc} */ - public function setName($name) + public function setName(string $name) { $this->name = $name; } @@ -152,7 +151,7 @@ public function hasDefaultValue() public function getDefaultValue() { if (!$this->hasDefaultValue()) { - throw new \RuntimeException(\sprintf('The node at path "%s" has no default value.', $this->getPath())); + throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); } $defaults = []; foreach ($this->children as $name => $child) { @@ -171,20 +170,16 @@ public function getDefaultValue() public function addChild(NodeInterface $node) { $name = $node->getName(); - if (!\strlen($name)) { + if ('' === $name) { throw new \InvalidArgumentException('Child nodes must be named.'); } if (isset($this->children[$name])) { - throw new \InvalidArgumentException(\sprintf('A child node named "%s" already exists.', $name)); + throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name)); } $this->children[$name] = $node; } /** - * Finalizes the value of this node. - * - * @param mixed $value - * - * @return mixed The finalised value + * {@inheritdoc} * * @throws UnsetKeyException * @throws InvalidConfigurationException if the node doesn't have enough children @@ -192,12 +187,18 @@ public function addChild(NodeInterface $node) protected function finalizeValue($value) { if (\false === $value) { - throw new UnsetKeyException(\sprintf('Unsetting key for path "%s", value: "%s".', $this->getPath(), \json_encode($value))); + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s.', $this->getPath(), json_encode($value))); } foreach ($this->children as $name => $child) { if (!\array_key_exists($name, $value)) { if ($child->isRequired()) { - $ex = new InvalidConfigurationException(\sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath())); + $message = sprintf('The child config "%s" under "%s" must be configured', $name, $this->getPath()); + if ($child->getInfo()) { + $message .= sprintf(': %s', $child->getInfo()); + } else { + $message .= '.'; + } + $ex = new InvalidConfigurationException($message); $ex->setPath($this->getPath()); throw $ex; } @@ -207,7 +208,8 @@ protected function finalizeValue($value) continue; } if ($child->isDeprecated()) { - @\trigger_error($child->getDeprecationMessage($name, $this->getPath()), \E_USER_DEPRECATED); + $deprecation = $child->getDeprecation($name, $this->getPath()); + trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } try { $value[$name] = $child->finalize($value[$name]); @@ -218,16 +220,12 @@ protected function finalizeValue($value) return $value; } /** - * Validates the type of the value. - * - * @param mixed $value - * - * @throws InvalidTypeException + * {@inheritdoc} */ protected function validateType($value) { if (!\is_array($value) && (!$this->allowFalse || \false !== $value)) { - $ex = new InvalidTypeException(\sprintf('Invalid type for path "%s". Expected array, but got %s', $this->getPath(), \gettype($value))); + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "array", but got "%s"', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } @@ -236,11 +234,7 @@ protected function validateType($value) } } /** - * Normalizes the value. - * - * @param mixed $value The value to normalize - * - * @return mixed The normalized value + * {@inheritdoc} * * @throws InvalidConfigurationException */ @@ -264,7 +258,27 @@ protected function normalizeValue($value) } // if extra fields are present, throw exception if (\count($value) && !$this->ignoreExtraKeys) { - $ex = new InvalidConfigurationException(\sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', \implode(', ', \array_keys($value)), $this->getPath())); + $proposals = array_keys($this->children); + sort($proposals); + $guesses = []; + foreach (array_keys($value) as $subject) { + $minScore = \INF; + foreach ($proposals as $proposal) { + $distance = levenshtein($subject, $proposal); + if ($distance <= $minScore && $distance < 3) { + $guesses[$proposal] = $distance; + $minScore = $distance; + } + } + } + $msg = sprintf('Unrecognized option%s "%s" under "%s"', (1 === \count($value)) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); + if (\count($guesses)) { + asort($guesses); + $msg .= sprintf('. Did you mean "%s"?', implode('", "', array_keys($guesses))); + } else { + $msg .= sprintf('. Available option%s %s "%s".', (1 === \count($proposals)) ? '' : 's', (1 === \count($proposals)) ? 'is' : 'are', implode('", "', $proposals)); + } + $ex = new InvalidConfigurationException($msg); $ex->setPath($this->getPath()); throw $ex; } @@ -273,13 +287,11 @@ protected function normalizeValue($value) /** * Remaps multiple singular values to a single plural value. * - * @param array $value The source values - * - * @return array The remapped values + * @return array */ - protected function remapXml($value) + protected function remapXml(array $value) { - foreach ($this->xmlRemappings as list($singular, $plural)) { + foreach ($this->xmlRemappings as [$singular, $plural]) { if (!isset($value[$singular])) { continue; } @@ -289,12 +301,7 @@ protected function remapXml($value) return $value; } /** - * Merges values together. - * - * @param mixed $leftSide The left side to merge - * @param mixed $rightSide The right side to merge - * - * @return mixed The merged values + * {@inheritdoc} * * @throws InvalidConfigurationException * @throws \RuntimeException @@ -313,7 +320,7 @@ protected function mergeValues($leftSide, $rightSide) // no conflict if (!\array_key_exists($k, $leftSide)) { if (!$this->allowNewKeys) { - $ex = new InvalidConfigurationException(\sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); $ex->setPath($this->getPath()); throw $ex; } @@ -321,10 +328,21 @@ protected function mergeValues($leftSide, $rightSide) continue; } if (!isset($this->children[$k])) { - throw new \RuntimeException('merge() expects a normalized config array.'); + if (!$this->ignoreExtraKeys || $this->removeExtraKeys) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + $leftSide[$k] = $v; + continue; } $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); } return $leftSide; } + /** + * {@inheritdoc} + */ + protected function allowPlaceholders(): bool + { + return \false; + } } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/BaseNode.php b/src/modules/common/third-party/vendor/symfony/config/Definition/BaseNode.php index 55853d4cae..61cb6c7a75 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/BaseNode.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/BaseNode.php @@ -14,6 +14,7 @@ use Wordlift\Modules\Common\Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\Exception\UnsetKeyException; /** * The base node class. * @@ -21,51 +22,84 @@ */ abstract class BaseNode implements NodeInterface { + public const DEFAULT_PATH_SEPARATOR = '.'; + private static $placeholderUniquePrefixes = []; + private static $placeholders = []; protected $name; protected $parent; protected $normalizationClosures = []; protected $finalValidationClosures = []; protected $allowOverwrite = \true; protected $required = \false; - protected $deprecationMessage = null; + protected $deprecation = []; protected $equivalentValues = []; protected $attributes = []; + protected $pathSeparator; + private $handlingPlaceholder; /** - * @param string|null $name The name of the node - * @param NodeInterface|null $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 = (string) $name, '.')) { - throw new \InvalidArgumentException('The name must not contain ".".'); + if (str_contains($name = (string) $name, $pathSeparator)) { + throw new \InvalidArgumentException('The name must not contain ".' . $pathSeparator . '".'); } $this->name = $name; $this->parent = $parent; + $this->pathSeparator = $pathSeparator; } /** - * @param string $key + * Register possible (dummy) values for a dynamic placeholder value. + * + * Matching configuration values will be processed with a provided value, one by one. After a provided value is + * successfully processed the configuration value is returned as is, thus preserving the placeholder. + * + * @internal */ - public function setAttribute($key, $value) + public static function setPlaceholder(string $placeholder, array $values): void { - $this->attributes[$key] = $value; + if (!$values) { + throw new \InvalidArgumentException('At least one value must be provided.'); + } + self::$placeholders[$placeholder] = $values; } /** - * @param string $key + * Adds a common prefix for dynamic placeholder values. * - * @return mixed + * Matching configuration values will be skipped from being processed and are returned as is, thus preserving the + * placeholder. An exact match provided by {@see setPlaceholder()} might take precedence. + * + * @internal */ - public function getAttribute($key, $default = null) + public static function setPlaceholderUniquePrefix(string $prefix): void { - return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; + self::$placeholderUniquePrefixes[] = $prefix; } /** - * @param string $key + * Resets all current placeholders available. * + * @internal + */ + public static function resetPlaceholders(): void + { + self::$placeholderUniquePrefixes = []; + self::$placeholders = []; + } + public function setAttribute(string $key, $value) + { + $this->attributes[$key] = $value; + } + /** + * @return mixed + */ + public function getAttribute(string $key, $default = null) + { + return $this->attributes[$key] ?? $default; + } + /** * @return bool */ - public function hasAttribute($key) + public function hasAttribute(string $key) { return isset($this->attributes[$key]); } @@ -80,26 +114,21 @@ public function setAttributes(array $attributes) { $this->attributes = $attributes; } - /** - * @param string $key - */ - public function removeAttribute($key) + public function removeAttribute(string $key) { unset($this->attributes[$key]); } /** * Sets an info message. - * - * @param string $info */ - public function setInfo($info) + public function setInfo(string $info) { $this->setAttribute('info', $info); } /** * Returns info message. * - * @return string|null The info text + * @return string|null */ public function getInfo() { @@ -117,7 +146,7 @@ public function setExample($example) /** * Retrieves the example configuration for this node. * - * @return string|array|null The example + * @return string|array|null */ public function getExample() { @@ -135,33 +164,46 @@ public function addEquivalentValue($originalValue, $equivalentValue) } /** * Set this node as required. - * - * @param bool $boolean Required node */ - public function setRequired($boolean) + public function setRequired(bool $boolean) { - $this->required = (bool) $boolean; + $this->required = $boolean; } /** * Sets this node as deprecated. * - * You can use %node% and %path% placeholders in your message to display, - * respectively, the node name and its complete path. + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message the deprecation message to use * - * @param string|null $message Deprecated message + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path */ - public function setDeprecated($message) + public function setDeprecated(?string $package) { - $this->deprecationMessage = $message; + $args = \func_get_args(); + if (\func_num_args() < 2) { + trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + if (!isset($args[0])) { + trigger_deprecation('symfony/config', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); + $this->deprecation = []; + return; + } + $message = (string) $args[0]; + $package = $version = ''; + } else { + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); + } + $this->deprecation = ['package' => $package, 'version' => $version, 'message' => $message]; } /** * Sets if this node can be overridden. - * - * @param bool $allow */ - public function setAllowOverwrite($allow) + public function setAllowOverwrite(bool $allow) { - $this->allowOverwrite = (bool) $allow; + $this->allowOverwrite = $allow; } /** * Sets the closures used for normalization. @@ -195,7 +237,7 @@ public function isRequired() */ public function isDeprecated() { - return null !== $this->deprecationMessage; + return (bool) $this->deprecation; } /** * Returns the deprecated message. @@ -204,10 +246,21 @@ public function isDeprecated() * @param string $path the path of the node * * @return string + * + * @deprecated since Symfony 5.1, use "getDeprecation()" instead. + */ + public function getDeprecationMessage(string $node, string $path) + { + trigger_deprecation('symfony/config', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); + return $this->getDeprecation($node, $path)['message']; + } + /** + * @param string $node The configuration node name + * @param string $path The path of the node */ - public function getDeprecationMessage($node, $path) + public function getDeprecation(string $node, string $path): array { - return \strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]); + return ['package' => $this->deprecation['package'] ?? '', 'version' => $this->deprecation['version'] ?? '', 'message' => strtr($this->deprecation['message'] ?? '', ['%node%' => $node, '%path%' => $path])]; } /** * {@inheritdoc} @@ -221,34 +274,67 @@ 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; } /** * {@inheritdoc} */ - public final function merge($leftSide, $rightSide) + final public function merge($leftSide, $rightSide) { if (!$this->allowOverwrite) { - throw new ForbiddenOverwriteException(\sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); + throw new ForbiddenOverwriteException(sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); + } + if ($leftSide !== $leftPlaceholders = self::resolvePlaceholderValue($leftSide)) { + foreach ($leftPlaceholders as $leftPlaceholder) { + $this->handlingPlaceholder = $leftSide; + try { + $this->merge($leftPlaceholder, $rightSide); + } finally { + $this->handlingPlaceholder = null; + } + } + return $rightSide; } - $this->validateType($leftSide); - $this->validateType($rightSide); + if ($rightSide !== $rightPlaceholders = self::resolvePlaceholderValue($rightSide)) { + foreach ($rightPlaceholders as $rightPlaceholder) { + $this->handlingPlaceholder = $rightSide; + try { + $this->merge($leftSide, $rightPlaceholder); + } finally { + $this->handlingPlaceholder = null; + } + } + return $rightSide; + } + $this->doValidateType($leftSide); + $this->doValidateType($rightSide); return $this->mergeValues($leftSide, $rightSide); } /** * {@inheritdoc} */ - public final function normalize($value) + final public function normalize($value) { $value = $this->preNormalize($value); // run custom normalization closures foreach ($this->normalizationClosures as $closure) { $value = $closure($value); } + // resolve placeholder value + if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { + foreach ($placeholders as $placeholder) { + $this->handlingPlaceholder = $value; + try { + $this->normalize($placeholder); + } finally { + $this->handlingPlaceholder = null; + } + } + return $value; + } // replace value with their equivalent foreach ($this->equivalentValues as $data) { if ($data[0] === $value) { @@ -256,7 +342,7 @@ public final function normalize($value) } } // validate type - $this->validateType($value); + $this->doValidateType($value); // normalize value return $this->normalizeValue($value); } @@ -265,7 +351,7 @@ public final function normalize($value) * * @param mixed $value * - * @return mixed The normalized array value + * @return mixed */ protected function preNormalize($value) { @@ -283,9 +369,20 @@ public function getParent() /** * {@inheritdoc} */ - public final function finalize($value) + final public function finalize($value) { - $this->validateType($value); + if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { + foreach ($placeholders as $placeholder) { + $this->handlingPlaceholder = $value; + try { + $this->finalize($placeholder); + } finally { + $this->handlingPlaceholder = null; + } + } + return $value; + } + $this->doValidateType($value); $value = $this->finalizeValue($value); // Perform validation on the final value if a closure has been set. // The closure is also allowed to return another value. @@ -293,9 +390,12 @@ public final function finalize($value) try { $value = $closure($value); } catch (Exception $e) { + if ($e instanceof UnsetKeyException && null !== $this->handlingPlaceholder) { + continue; + } throw $e; } catch (\Exception $e) { - throw new InvalidConfigurationException(\sprintf('Invalid configuration for path "%s": ', $this->getPath()) . $e->getMessage(), $e->getCode(), $e); + throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": ', $this->getPath()) . $e->getMessage(), $e->getCode(), $e); } } return $value; @@ -307,30 +407,88 @@ public final function finalize($value) * * @throws InvalidTypeException when the value is invalid */ - protected abstract function validateType($value); + abstract protected function validateType($value); /** * Normalizes the value. * * @param mixed $value The value to normalize * - * @return mixed The normalized value + * @return mixed */ - protected abstract function normalizeValue($value); + abstract protected function normalizeValue($value); /** * Merges two values together. * * @param mixed $leftSide * @param mixed $rightSide * - * @return mixed The merged value + * @return mixed */ - protected abstract function mergeValues($leftSide, $rightSide); + abstract protected function mergeValues($leftSide, $rightSide); /** * Finalizes a value. * * @param mixed $value The value to finalize * - * @return mixed The finalized value + * @return mixed + */ + abstract protected function finalizeValue($value); + /** + * Tests if placeholder values are allowed for this node. + */ + protected function allowPlaceholders(): bool + { + return \true; + } + /** + * Tests if a placeholder is being handled currently. + */ + protected function isHandlingPlaceholder(): bool + { + return null !== $this->handlingPlaceholder; + } + /** + * Gets allowed dynamic types for this node. */ - protected abstract function finalizeValue($value); + protected function getValidPlaceholderTypes(): array + { + return []; + } + private static function resolvePlaceholderValue($value) + { + if (\is_string($value)) { + if (isset(self::$placeholders[$value])) { + return self::$placeholders[$value]; + } + foreach (self::$placeholderUniquePrefixes as $placeholderUniquePrefix) { + if (str_starts_with($value, $placeholderUniquePrefix)) { + return []; + } + } + } + return $value; + } + private function doValidateType($value): void + { + if (null !== $this->handlingPlaceholder && !$this->allowPlaceholders()) { + $e = new InvalidTypeException(sprintf('A dynamic value is not compatible with a "%s" node type at path "%s".', static::class, $this->getPath())); + $e->setPath($this->getPath()); + throw $e; + } + if (null === $this->handlingPlaceholder || null === $value) { + $this->validateType($value); + return; + } + $knownTypes = array_keys(self::$placeholders[$this->handlingPlaceholder]); + $validTypes = $this->getValidPlaceholderTypes(); + if ($validTypes && array_diff($knownTypes, $validTypes)) { + $e = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected %s, but got %s.', $this->getPath(), (1 === \count($validTypes)) ? '"' . reset($validTypes) . '"' : ('one of "' . implode('", "', $validTypes) . '"'), (1 === \count($knownTypes)) ? '"' . reset($knownTypes) . '"' : ('one of "' . implode('", "', $knownTypes) . '"'))); + if ($hint = $this->getInfo()) { + $e->addHint($hint); + } + $e->setPath($this->getPath()); + throw $e; + } + $this->validateType($value); + } } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/BooleanNode.php b/src/modules/common/third-party/vendor/symfony/config/Definition/BooleanNode.php index be157b3f74..2624479107 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/BooleanNode.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/BooleanNode.php @@ -24,7 +24,7 @@ class BooleanNode extends ScalarNode protected function validateType($value) { if (!\is_bool($value)) { - $ex = new InvalidTypeException(\sprintf('Invalid type for path "%s". Expected boolean, but got %s.', $this->getPath(), \gettype($value))); + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "bool", but got "%s".', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } @@ -40,4 +40,11 @@ protected function isValueEmpty($value) // a boolean value cannot be empty return \false; } + /** + * {@inheritdoc} + */ + protected function getValidPlaceholderTypes(): array + { + return ['bool']; + } } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php index 8ce70ae940..1ed8c72dd9 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php @@ -36,7 +36,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); $this->nullEquivalent = []; @@ -59,11 +59,9 @@ public function children() /** * Sets a prototype for child nodes. * - * @param string $type The type of node - * * @return NodeDefinition */ - public function prototype($type) + public function prototype(string $type) { return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); } @@ -171,12 +169,12 @@ public function disallowNewKeysInSubsequentConfigs() /** * Sets a normalization rule for XML configurations. * - * @param string $singular The key to remap - * @param string $plural The plural of the key for irregular plurals + * @param string $singular The key to remap + * @param string|null $plural The plural of the key for irregular plurals * * @return $this */ - public function fixXmlConfig($singular, $plural = null) + public function fixXmlConfig(string $singular, ?string $plural = null) { $this->normalization()->remap($singular, $plural); return $this; @@ -209,7 +207,7 @@ public function fixXmlConfig($singular, $plural = null) * * @return $this */ - public function useAttributeAsKey($name, $removeKeyItem = \true) + public function useAttributeAsKey(string $name, bool $removeKeyItem = \true) { $this->key = $name; $this->removeKeyItem = $removeKeyItem; @@ -218,11 +216,9 @@ public function useAttributeAsKey($name, $removeKeyItem = \true) /** * Sets whether the node can be unset. * - * @param bool $allow - * * @return $this */ - public function canBeUnset($allow = \true) + public function canBeUnset(bool $allow = \true) { $this->merge()->allowUnset($allow); return $this; @@ -244,8 +240,8 @@ public function canBeUnset($allow = \true) */ public function canBeEnabled() { - $this->addDefaultsIfNotSet()->treatFalseLike(['enabled' => \false])->treatTrueLike(['enabled' => \true])->treatNullLike(['enabled' => \true])->beforeNormalization()->ifArray()->then(function ($v) { - $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : \true; + $this->addDefaultsIfNotSet()->treatFalseLike(['enabled' => \false])->treatTrueLike(['enabled' => \true])->treatNullLike(['enabled' => \true])->beforeNormalization()->ifArray()->then(function (array $v) { + $v['enabled'] = $v['enabled'] ?? \true; return $v; })->end()->children()->booleanNode('enabled')->defaultFalse(); return $this; @@ -285,22 +281,20 @@ public function performNoDeepMerging() * * @return $this */ - public function ignoreExtraKeys($remove = \true) + public function ignoreExtraKeys(bool $remove = \true) { $this->ignoreExtraKeys = \true; $this->removeExtraKeys = $remove; return $this; } /** - * Sets key normalization. - * - * @param bool $bool Whether to enable key normalization + * Sets whether to enable key normalization. * * @return $this */ - public function normalizeKeys($bool) + public function normalizeKeys(bool $bool) { - $this->normalizeKeys = (bool) $bool; + $this->normalizeKeys = $bool; return $this; } /** @@ -314,7 +308,7 @@ public function append(NodeDefinition $node) /** * Returns a node builder to be used to add children and prototype. * - * @return NodeBuilder The node builder + * @return NodeBuilder */ protected function getNodeBuilder() { @@ -329,7 +323,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); $node->setAddIfNotSet($this->addDefaults); foreach ($this->children as $child) { @@ -337,18 +331,18 @@ 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); if (null !== $this->key) { $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); } if ($this->default) { + if (!\is_array($this->defaultValue)) { + throw new \InvalidArgumentException(sprintf('%s: the default value of an array node has to be an array.', $node->getPath())); + } $node->setDefaultValue($this->defaultValue); } if (\false !== $this->addDefaultChildren) { @@ -366,9 +360,11 @@ protected function createNode() $node->addEquivalentValue(\false, $this->falseEquivalent); $node->setPerformDeepMerging($this->performDeepMerging); $node->setRequired($this->required); - $node->setDeprecated($this->deprecationMessage); $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); $node->setNormalizeKeys($this->normalizeKeys); + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } if (null !== $this->normalization) { $node->setNormalizationClosures($this->normalization->before); $node->setXmlRemappings($this->normalization->remappings); @@ -391,19 +387,19 @@ protected function validateConcreteNode(ArrayNode $node) { $path = $node->getPath(); if (null !== $this->key) { - throw new InvalidDefinitionException(\sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s".', $path)); } 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) { - throw new InvalidDefinitionException(\sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s".', $path)); } if ($this->default) { - throw new InvalidDefinitionException(\sprintf('->defaultValue() is not applicable to concrete nodes at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s".', $path)); } if (\false !== $this->addDefaultChildren) { - throw new InvalidDefinitionException(\sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s".', $path)); } } /** @@ -415,18 +411,41 @@ protected function validatePrototypeNode(PrototypedArrayNode $node) { $path = $node->getPath(); if ($this->addDefaults) { - throw new InvalidDefinitionException(\sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s".', $path)); } if (\false !== $this->addDefaultChildren) { if ($this->default) { - throw new InvalidDefinitionException(\sprintf('A default value and default children might not be used together at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s".', $path)); } if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { - throw new InvalidDefinitionException(\sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s".', $path)); } if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) { - throw new InvalidDefinitionException(\sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s".', $path)); + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s".', $path)); } } } + /** + * @return NodeDefinition[] + */ + public function getChildNodeDefinitions() + { + return $this->children; + } + /** + * Finds a node defined by the given $nodePath. + * + * @param string $nodePath The path of the node to find. e.g "doctrine.orm.mappings" + */ + public function find(string $nodePath): NodeDefinition + { + $firstPathSegment = (\false === $pathSeparatorPos = strpos($nodePath, $this->pathSeparator)) ? $nodePath : substr($nodePath, 0, $pathSeparatorPos); + if (null === $node = $this->children[$firstPathSegment] ?? null) { + throw new \RuntimeException(sprintf('Node with name "%s" does not exist in the current node "%s".', $firstPathSegment, $this->name)); + } + if (\false === $pathSeparatorPos) { + return $node; + } + return $node->find(substr($nodePath, $pathSeparatorPos + \strlen($this->pathSeparator))); + } } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php index f1745a9338..f749fdb059 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php @@ -22,7 +22,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); $this->nullEquivalent = \true; @@ -30,11 +30,11 @@ public function __construct($name, NodeParentInterface $parent = null) /** * Instantiate a Node. * - * @return BooleanNode The node + * @return BooleanNode */ protected function instantiateNode() { - return new BooleanNode($this->name, $this->parent); + return new BooleanNode($this->name, $this->parent, $this->pathSeparator); } /** * {@inheritdoc} diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php new file mode 100644 index 0000000000..32a3890fbb --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.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 Wordlift\Modules\Common\Symfony\Component\Config\Definition\Builder; + +/** + * An interface that can be implemented by nodes which build other nodes. + * + * @author Roland Franssen + */ +interface BuilderAwareInterface +{ + /** + * Sets a custom children builder. + */ + public function setBuilder(NodeBuilder $builder); +} diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php index 5e31b3e326..2d47793a3d 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php @@ -24,7 +24,7 @@ class EnumNodeDefinition extends ScalarNodeDefinition */ public function values(array $values) { - $values = \array_unique($values); + $values = array_unique($values); if (empty($values)) { throw new \InvalidArgumentException('->values() must be called with at least one value.'); } @@ -34,7 +34,7 @@ public function values(array $values) /** * Instantiate a Node. * - * @return EnumNode The node + * @return EnumNode * * @throws \RuntimeException */ @@ -43,6 +43,6 @@ protected function instantiateNode() if (null === $this->values) { 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/modules/common/third-party/vendor/symfony/config/Definition/Builder/ExprBuilder.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ExprBuilder.php index 27b2ccd5b0..c3ad6a3324 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ExprBuilder.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ExprBuilder.php @@ -31,9 +31,9 @@ public function __construct(NodeDefinition $node) * * @return $this */ - public function always(\Closure $then = null) + public function always(?\Closure $then = null) { - $this->ifPart = function ($v) { + $this->ifPart = function () { return \true; }; if (null !== $then) { @@ -48,7 +48,7 @@ public function always(\Closure $then = null) * * @return $this */ - public function ifTrue(\Closure $closure = null) + public function ifTrue(?\Closure $closure = null) { if (null === $closure) { $closure = function ($v) { @@ -85,7 +85,7 @@ public function ifNull() /** * Tests if the value is empty. * - * @return ExprBuilder + * @return $this */ public function ifEmpty() { @@ -113,7 +113,7 @@ public function ifArray() */ public function ifInArray(array $array) { - $this->ifPart = function ($v) use($array) { + $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, \true); }; return $this; @@ -125,7 +125,7 @@ public function ifInArray(array $array) */ public function ifNotInArray(array $array) { - $this->ifPart = function ($v) use($array) { + $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, \true); }; return $this; @@ -162,7 +162,7 @@ public function then(\Closure $closure) */ public function thenEmptyArray() { - $this->thenPart = function ($v) { + $this->thenPart = function () { return []; }; return $this; @@ -172,16 +172,14 @@ public function thenEmptyArray() * * if you want to add the value of the node in your message just use a %s placeholder. * - * @param string $message - * * @return $this * * @throws \InvalidArgumentException */ - public function thenInvalid($message) + public function thenInvalid(string $message) { - $this->thenPart = function ($v) use($message) { - throw new \InvalidArgumentException(\sprintf($message, \json_encode($v))); + $this->thenPart = function ($v) use ($message) { + throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; return $this; } @@ -194,7 +192,7 @@ public function thenInvalid($message) */ public function thenUnset() { - $this->thenPart = function ($v) { + $this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); }; return $this; @@ -229,7 +227,7 @@ public static function buildExpressions(array $expressions) if ($expr instanceof self) { $if = $expr->ifPart; $then = $expr->thenPart; - $expressions[$k] = function ($v) use($if, $then) { + $expressions[$k] = function ($v) use ($if, $then) { return $if($v) ? $then($v) : $v; }; } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php index e8f1da0f3e..a972a69db8 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php @@ -21,10 +21,10 @@ class FloatNodeDefinition extends NumericNodeDefinition /** * Instantiates a Node. * - * @return FloatNode The node + * @return FloatNode */ 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/modules/common/third-party/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php index 289766f1cb..9440e1ea20 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php @@ -21,10 +21,10 @@ class IntegerNodeDefinition extends NumericNodeDefinition /** * Instantiates a Node. * - * @return IntegerNode The node + * @return IntegerNode */ 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/modules/common/third-party/vendor/symfony/config/Definition/Builder/MergeBuilder.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/MergeBuilder.php index 5fab1dd4c4..e1b69b83ed 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/MergeBuilder.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/MergeBuilder.php @@ -27,11 +27,9 @@ public function __construct(NodeDefinition $node) /** * Sets whether the node can be unset. * - * @param bool $allow - * * @return $this */ - public function allowUnset($allow = \true) + public function allowUnset(bool $allow = \true) { $this->allowFalse = $allow; return $this; @@ -39,11 +37,9 @@ public function allowUnset($allow = \true) /** * Sets whether the node can be overwritten. * - * @param bool $deny Whether the overwriting is forbidden or not - * * @return $this */ - public function denyOverwrite($deny = \true) + public function denyOverwrite(bool $deny = \true) { $this->allowOverwrite = !$deny; return $this; diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeBuilder.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeBuilder.php index a8c6c49a9d..757b188bf1 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeBuilder.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeBuilder.php @@ -28,7 +28,7 @@ public function __construct() * * @return $this */ - public function setParent(ParentNodeDefinitionInterface $parent = null) + public function setParent(?ParentNodeDefinitionInterface $parent = null) { $this->parent = $parent; return $this; @@ -36,84 +36,70 @@ public function setParent(ParentNodeDefinitionInterface $parent = null) /** * Creates a child array node. * - * @param string $name The name of the node - * - * @return ArrayNodeDefinition The child node + * @return ArrayNodeDefinition */ - public function arrayNode($name) + public function arrayNode(string $name) { return $this->node($name, 'array'); } /** * Creates a child scalar node. * - * @param string $name The name of the node - * - * @return ScalarNodeDefinition The child node + * @return ScalarNodeDefinition */ - public function scalarNode($name) + public function scalarNode(string $name) { return $this->node($name, 'scalar'); } /** * Creates a child Boolean node. * - * @param string $name The name of the node - * - * @return BooleanNodeDefinition The child node + * @return BooleanNodeDefinition */ - public function booleanNode($name) + public function booleanNode(string $name) { return $this->node($name, 'boolean'); } /** * Creates a child integer node. * - * @param string $name The name of the node - * - * @return IntegerNodeDefinition The child node + * @return IntegerNodeDefinition */ - public function integerNode($name) + public function integerNode(string $name) { return $this->node($name, 'integer'); } /** * Creates a child float node. * - * @param string $name The name of the node - * - * @return FloatNodeDefinition The child node + * @return FloatNodeDefinition */ - public function floatNode($name) + public function floatNode(string $name) { return $this->node($name, 'float'); } /** * Creates a child EnumNode. * - * @param string $name - * * @return EnumNodeDefinition */ - public function enumNode($name) + public function enumNode(string $name) { return $this->node($name, 'enum'); } /** * Creates a child variable node. * - * @param string $name The name of the node - * - * @return VariableNodeDefinition The builder of the child node + * @return VariableNodeDefinition */ - public function variableNode($name) + public function variableNode(string $name) { return $this->node($name, 'variable'); } /** * Returns the parent node. * - * @return NodeDefinition&ParentNodeDefinitionInterface The parent node + * @return NodeDefinition&ParentNodeDefinitionInterface */ public function end() { @@ -122,15 +108,12 @@ public function end() /** * Creates a child node. * - * @param string|null $name The name of the node - * @param string $type The type of the node - * - * @return NodeDefinition The child node + * @return NodeDefinition * * @throws \RuntimeException When the node type is not registered * @throws \RuntimeException When the node class is not found */ - public function node($name, $type) + public function node(?string $name, string $type) { $class = $this->getNodeClass($type); $node = new $class($name); @@ -154,7 +137,7 @@ public function node($name, $type) */ public function append(NodeDefinition $node) { - if ($node instanceof ParentNodeDefinitionInterface) { + if ($node instanceof BuilderAwareInterface) { $builder = clone $this; $builder->setParent(null); $node->setBuilder($builder); @@ -174,30 +157,28 @@ public function append(NodeDefinition $node) * * @return $this */ - public function setNodeClass($type, $class) + public function setNodeClass(string $type, string $class) { - $this->nodeMapping[\strtolower($type)] = $class; + $this->nodeMapping[strtolower($type)] = $class; return $this; } /** * Returns the class name of the node definition. * - * @param string $type The node type - * - * @return string The node definition class name + * @return string * * @throws \RuntimeException When the node type is not registered * @throws \RuntimeException When the node class is not found */ - protected function getNodeClass($type) + protected function getNodeClass(string $type) { - $type = \strtolower($type); + $type = strtolower($type); if (!isset($this->nodeMapping[$type])) { - throw new \RuntimeException(\sprintf('The node type "%s" is not registered.', $type)); + throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type)); } $class = $this->nodeMapping[$type]; - if (!\class_exists($class)) { - throw new \RuntimeException(\sprintf('The node class "%s" does not exist.', $class)); + if (!class_exists($class)) { + throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class)); } return $class; } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeDefinition.php index 5c0e75bcc6..496b7c6352 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NodeDefinition.php @@ -10,6 +10,7 @@ */ namespace Wordlift\Modules\Common\Symfony\Component\Config\Definition\Builder; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\BaseNode; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\NodeInterface; /** @@ -25,19 +26,16 @@ abstract class NodeDefinition implements NodeParentInterface protected $defaultValue; protected $default = \false; protected $required = \false; - protected $deprecationMessage = null; + protected $deprecation = []; protected $merge; protected $allowEmptyValue = \true; protected $nullEquivalent; protected $trueEquivalent = \true; protected $falseEquivalent = \false; + protected $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR; protected $parent; protected $attributes = []; - /** - * @param string|null $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; @@ -55,11 +53,9 @@ public function setParent(NodeParentInterface $parent) /** * Sets info message. * - * @param string $info The info text - * * @return $this */ - public function info($info) + public function info(string $info) { return $this->attribute('info', $info); } @@ -77,12 +73,11 @@ public function example($example) /** * Sets an attribute on the node. * - * @param string $key - * @param mixed $value + * @param mixed $value * * @return $this */ - public function attribute($key, $value) + public function attribute(string $key, $value) { $this->attributes[$key] = $value; return $this; @@ -90,7 +85,7 @@ public function attribute($key, $value) /** * Returns the parent node. * - * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null The builder of the parent node + * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null */ public function end() { @@ -99,11 +94,9 @@ public function end() /** * Creates the node. * - * @param bool $forceRootNode Whether to force this node as the root node - * * @return NodeInterface */ - public function getNode($forceRootNode = \false) + public function getNode(bool $forceRootNode = \false) { if ($forceRootNode) { $this->parent = null; @@ -115,7 +108,9 @@ public function getNode($forceRootNode = \false) $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); } $node = $this->createNode(); - $node->setAttributes($this->attributes); + if ($node instanceof BaseNode) { + $node->setAttributes($this->attributes); + } return $node; } /** @@ -144,16 +139,28 @@ public function isRequired() /** * Sets the node as deprecated. * - * You can use %node% and %path% placeholders in your message to display, - * respectively, the node name and its complete path. + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message the deprecation message to use * - * @param string $message Deprecation message + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path * * @return $this */ - public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.') + public function setDeprecated() { - $this->deprecationMessage = $message; + $args = \func_get_args(); + if (\func_num_args() < 2) { + trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + $message = $args[0] ?? 'The child node "%node%" at path "%path%" is deprecated.'; + $package = $version = ''; + } else { + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); + } + $this->deprecation = ['package' => $package, 'version' => $version, 'message' => $message]; return $this; } /** @@ -254,11 +261,9 @@ public function validate() /** * Sets whether the node can be overwritten. * - * @param bool $deny Whether the overwriting is forbidden or not - * * @return $this */ - public function cannotBeOverwritten($deny = \true) + public function cannotBeOverwritten(bool $deny = \true) { $this->merge()->denyOverwrite($deny); return $this; @@ -302,9 +307,24 @@ protected function normalization() /** * Instantiate and configure the node according to this definition. * - * @return NodeInterface The node instance + * @return NodeInterface * * @throws InvalidDefinitionException When the definition is invalid */ - protected abstract function createNode(); + abstract protected function createNode(); + /** + * Set PathSeparator to use. + * + * @return $this + */ + public function setPathSeparator(string $separator) + { + if ($this instanceof ParentNodeDefinitionInterface) { + foreach ($this->getChildNodeDefinitions() as $child) { + $child->setPathSeparator($separator); + } + } + $this->pathSeparator = $separator; + return $this; + } } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php index 9e904e7328..6e740539d1 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php @@ -27,14 +27,14 @@ public function __construct(NodeDefinition $node) /** * Registers a key to remap to its plural form. * - * @param string $key The key to remap - * @param string $plural The plural of the key in case of irregular plural + * @param string $key The key to remap + * @param string|null $plural The plural of the key in case of irregular plural * * @return $this */ - public function remap($key, $plural = null) + public function remap(string $key, ?string $plural = null) { - $this->remappings[] = [$key, null === $plural ? $key . 's' : $plural]; + $this->remappings[] = [$key, (null === $plural) ? $key . 's' : $plural]; return $this; } /** @@ -42,7 +42,7 @@ public function remap($key, $plural = null) * * @return ExprBuilder|$this */ - public function before(\Closure $closure = null) + public function before(?\Closure $closure = null) { if (null !== $closure) { $this->before[] = $closure; diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php index cc9ba8f77d..fae38afd0b 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php @@ -23,7 +23,7 @@ abstract class NumericNodeDefinition extends ScalarNodeDefinition /** * Ensures that the value is smaller than the given reference. * - * @param mixed $max + * @param int|float $max * * @return $this * @@ -32,7 +32,7 @@ abstract class NumericNodeDefinition extends ScalarNodeDefinition public function max($max) { if (isset($this->min) && $this->min > $max) { - throw new \InvalidArgumentException(\sprintf('You cannot define a max(%s) as you already have a min(%s).', $max, $this->min)); + throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s).', $max, $this->min)); } $this->max = $max; return $this; @@ -40,7 +40,7 @@ public function max($max) /** * Ensures that the value is bigger than the given reference. * - * @param mixed $min + * @param int|float $min * * @return $this * @@ -49,7 +49,7 @@ public function max($max) public function min($min) { if (isset($this->max) && $this->max < $min) { - throw new \InvalidArgumentException(\sprintf('You cannot define a min(%s) as you already have a max(%s).', $min, $this->max)); + throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s).', $min, $this->max)); } $this->min = $min; return $this; diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php index a7f18c2281..7243473eed 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -15,7 +15,7 @@ * * @author Victor Berchet */ -interface ParentNodeDefinitionInterface +interface ParentNodeDefinitionInterface extends BuilderAwareInterface { /** * Returns a builder to add children nodes. @@ -40,7 +40,9 @@ public function children(); */ public function append(NodeDefinition $node); /** - * Sets a custom children builder. + * Gets the child node definitions. + * + * @return NodeDefinition[] */ - public function setBuilder(NodeBuilder $builder); + public function getChildNodeDefinitions(); } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php index 46538e477d..dc07390ab5 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php @@ -21,10 +21,10 @@ class ScalarNodeDefinition extends VariableNodeDefinition /** * Instantiate a Node. * - * @return ScalarNode The node + * @return ScalarNode */ protected function instantiateNode() { - return new ScalarNode($this->name, $this->parent); + return new ScalarNode($this->name, $this->parent, $this->pathSeparator); } } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/TreeBuilder.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/TreeBuilder.php index 88978c0846..8fac4fa1d1 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/TreeBuilder.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/TreeBuilder.php @@ -20,25 +20,17 @@ class TreeBuilder implements NodeParentInterface { protected $tree; protected $root; + public function __construct(string $name, string $type = 'array', ?NodeBuilder $builder = null) + { + $builder = $builder ?? new NodeBuilder(); + $this->root = $builder->node($name, $type)->setParent($this); + } /** - * @deprecated since 3.4. To be removed in 4.0 - */ - protected $builder; - /** - * Creates the root node. - * - * @param string $name The name of the root node - * @param string $type The type of the root node - * @param NodeBuilder $builder A custom node builder instance - * - * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') - * - * @throws \RuntimeException When the node type is not supported + * @return NodeDefinition|ArrayNodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') */ - public function root($name, $type = 'array', NodeBuilder $builder = null) + public function getRootNode(): NodeDefinition { - $builder = $builder ?: new NodeBuilder(); - return $this->root = $builder->node($name, $type)->setParent($this); + return $this->root; } /** * Builds the tree. @@ -49,12 +41,15 @@ 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.'); - } if (null !== $this->tree) { return $this->tree; } return $this->tree = $this->root->getNode(\true); } + public function setPathSeparator(string $separator) + { + // unset last built as changing path separator changes all nodes + $this->tree = null; + $this->root->setPathSeparator($separator); + } } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ValidationBuilder.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ValidationBuilder.php index 2831ba998c..9084a9b13e 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ValidationBuilder.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/ValidationBuilder.php @@ -28,7 +28,7 @@ public function __construct(NodeDefinition $node) * * @return ExprBuilder|$this */ - public function rule(\Closure $closure = null) + public function rule(?\Closure $closure = null) { if (null !== $closure) { $this->rules[] = $closure; diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php index 0d46b0212e..9b43955478 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php @@ -21,11 +21,11 @@ class VariableNodeDefinition extends NodeDefinition /** * Instantiate a Node. * - * @return VariableNode The node + * @return VariableNode */ protected function instantiateNode() { - return new VariableNode($this->name, $this->parent); + return new VariableNode($this->name, $this->parent, $this->pathSeparator); } /** * {@inheritdoc} @@ -47,7 +47,9 @@ protected function createNode() $node->addEquivalentValue(\true, $this->trueEquivalent); $node->addEquivalentValue(\false, $this->falseEquivalent); $node->setRequired($this->required); - $node->setDeprecated($this->deprecationMessage); + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } if (null !== $this->validation) { $node->setFinalValidationClosures($this->validation->rules); } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/ConfigurationInterface.php b/src/modules/common/third-party/vendor/symfony/config/Definition/ConfigurationInterface.php index 54a69fd4d0..290357b14c 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/ConfigurationInterface.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/ConfigurationInterface.php @@ -10,6 +10,7 @@ */ namespace Wordlift\Modules\Common\Symfony\Component\Config\Definition; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\Builder\TreeBuilder; /** * Configuration interface. * @@ -20,7 +21,7 @@ interface ConfigurationInterface /** * Generates the configuration tree builder. * - * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + * @return TreeBuilder */ public function getConfigTreeBuilder(); } diff --git a/src/modules/common/third-party/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php b/src/modules/common/third-party/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php index 02c1a8f3ec..3e95003f14 100644 --- a/src/modules/common/third-party/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php +++ b/src/modules/common/third-party/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php @@ -11,23 +11,24 @@ namespace Wordlift\Modules\Common\Symfony\Component\Config\Definition\Dumper; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\ArrayNode; +use Wordlift\Modules\Common\Symfony\Component\Config\Definition\BaseNode; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\ConfigurationInterface; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\EnumNode; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\NodeInterface; use Wordlift\Modules\Common\Symfony\Component\Config\Definition\PrototypedArrayNode; /** - * Dumps a XML reference configuration for the given configuration/node instance. + * Dumps an XML reference configuration for the given configuration/node instance. * * @author Wouter J */ class XmlReferenceDumper { private $reference; - public function dump(ConfigurationInterface $configuration, $namespace = null) + public function dump(ConfigurationInterface $configuration, ?string $namespace = null) { return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); } - public function dumpNode(NodeInterface $node, $namespace = null) + public function dumpNode(NodeInterface $node, ?string $namespace = null) { $this->reference = ''; $this->writeNode($node, 0, \true, $namespace); @@ -35,26 +36,21 @@ public function dumpNode(NodeInterface $node, $namespace = null) $this->reference = null; return $ref; } - /** - * @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); // xml remapping if ($node->getParent()) { - $remapping = \array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use($rootName) { + $remapping = array_filter($node->getParent()->getXmlRemappings(), function (array $mapping) use ($rootName) { return $rootName === $mapping[1]; }); if (\count($remapping)) { - list($singular) = \current($remapping); + [$singular] = current($remapping); $rootName = $singular; } } - $rootName = \str_replace('_', '-', $rootName); + $rootName = str_replace('_', '-', $rootName); $rootAttributes = []; $rootAttributeComments = []; $rootChildren = []; @@ -75,77 +71,76 @@ private function writeNode(NodeInterface $node, $depth = 0, $root = \false, $nam if (null !== $prototype->getInfo()) { $info .= ': ' . $prototype->getInfo(); } - \array_unshift($rootComments, $info); + array_unshift($rootComments, $info); if ($key = $node->getKeyAttribute()) { - $rootAttributes[$key] = \str_replace('-', ' ', $rootName) . ' ' . $key; + $rootAttributes[$key] = str_replace('-', ' ', $rootName) . ' ' . $key; } if ($prototype instanceof PrototypedArrayNode) { - $prototype->setName($key); + $prototype->setName($key ?? ''); $children = [$key => $prototype]; } elseif ($prototype instanceof ArrayNode) { $children = $prototype->getChildren(); + } else if ($prototype->hasDefaultValue()) { + $prototypeValue = $prototype->getDefaultValue(); } else { - if ($prototype->hasDefaultValue()) { - $prototypeValue = $prototype->getDefaultValue(); - } else { - switch (\get_class($prototype)) { - case 'Symfony\\Component\\Config\\Definition\\ScalarNode': - $prototypeValue = 'scalar value'; - break; - case 'Symfony\\Component\\Config\\Definition\\FloatNode': - case 'Symfony\\Component\\Config\\Definition\\IntegerNode': - $prototypeValue = 'numeric value'; - break; - case 'Symfony\\Component\\Config\\Definition\\BooleanNode': - $prototypeValue = 'true|false'; - break; - case 'Symfony\\Component\\Config\\Definition\\EnumNode': - $prototypeValue = \implode('|', \array_map('json_encode', $prototype->getValues())); - break; - default: - $prototypeValue = 'value'; - } + switch (\get_class($prototype)) { + case 'Symfony\Component\Config\Definition\ScalarNode': + $prototypeValue = 'scalar value'; + break; + case 'Symfony\Component\Config\Definition\FloatNode': + case 'Symfony\Component\Config\Definition\IntegerNode': + $prototypeValue = 'numeric value'; + break; + case 'Symfony\Component\Config\Definition\BooleanNode': + $prototypeValue = 'true|false'; + break; + case 'Symfony\Component\Config\Definition\EnumNode': + $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues())); + break; + default: + $prototypeValue = 'value'; } } } // get attributes and elements foreach ($children as $child) { - if (!$child instanceof ArrayNode) { - // get attributes - // metadata - $name = \str_replace('_', '-', $child->getName()); - $value = '%%%%not_defined%%%%'; - // use a string which isn't used in the normal world - // comments - $comments = []; - if ($info = $child->getInfo()) { - $comments[] = $info; - } - if ($example = $child->getExample()) { - $comments[] = 'Example: ' . $example; - } - if ($child->isRequired()) { - $comments[] = 'Required'; - } - if ($child->isDeprecated()) { - $comments[] = \sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath())); - } - if ($child instanceof EnumNode) { - $comments[] = 'One of ' . \implode('; ', \array_map('json_encode', $child->getValues())); - } - if (\count($comments)) { - $rootAttributeComments[$name] = \implode(";\n", $comments); - } - // default values - if ($child->hasDefaultValue()) { - $value = $child->getDefaultValue(); - } - // append attribute - $rootAttributes[$name] = $value; - } else { + if ($child instanceof ArrayNode) { // get elements $rootChildren[] = $child; + continue; } + // get attributes + // metadata + $name = str_replace('_', '-', $child->getName()); + $value = '%%%%not_defined%%%%'; + // use a string which isn't used in the normal world + // comments + $comments = []; + if ($child instanceof BaseNode && $info = $child->getInfo()) { + $comments[] = $info; + } + if ($child instanceof BaseNode && $example = $child->getExample()) { + $comments[] = 'Example: ' . (\is_array($example) ? implode(', ', $example) : $example); + } + if ($child->isRequired()) { + $comments[] = 'Required'; + } + if ($child instanceof BaseNode && $child->isDeprecated()) { + $deprecation = $child->getDeprecation($child->getName(), $node->getPath()); + $comments[] = sprintf('Deprecated (%s)', (($deprecation['package'] || $deprecation['version']) ? "Since {$deprecation['package']} {$deprecation['version']}: " : '') . $deprecation['message']); + } + if ($child instanceof EnumNode) { + $comments[] = 'One of ' . implode('; ', array_map('json_encode', $child->getValues())); + } + if (\count($comments)) { + $rootAttributeComments[$name] = implode(";\n", $comments); + } + // default values + if ($child->hasDefaultValue()) { + $value = $child->getDefaultValue(); + } + // append attribute + $rootAttributes[$name] = $value; } } // render comments @@ -159,9 +154,9 @@ private function writeNode(NodeInterface $node, $depth = 0, $root = \false, $nam if (\count($rootAttributeComments)) { foreach ($rootAttributeComments as $attrName => $comment) { $commentDepth = $depth + 4 + \strlen($attrName) + 2; - $commentLines = \explode("\n", $comment); + $commentLines = explode("\n", $comment); $multiline = \count($commentLines) > 1; - $comment = \implode(\PHP_EOL . \str_repeat(' ', $commentDepth), $commentLines); + $comment = implode(\PHP_EOL . str_repeat(' ', $commentDepth), $commentLines); if ($multiline) { $this->writeLine(' + + + + @@ -207,7 +254,6 @@ - @@ -221,6 +267,7 @@ + @@ -233,8 +280,10 @@ - + + + @@ -242,6 +291,7 @@ + @@ -249,18 +299,32 @@ + + + + + + + + + + + + + + @@ -273,6 +337,14 @@ + + + + + + + + diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/Parameter.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/Parameter.php index ba81f6beea..ddb4be505e 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/Parameter.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/Parameter.php @@ -18,18 +18,15 @@ class Parameter { private $id; - /** - * @param string $id The parameter key - */ - public function __construct($id) + public function __construct(string $id) { $this->id = $id; } /** - * @return string The parameter key + * @return string */ public function __toString() { - return (string) $this->id; + return $this->id; } } diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ContainerBag.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ContainerBag.php new file mode 100644 index 0000000000..d949a1f253 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ContainerBag.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ParameterBag; + +use Wordlift\Modules\Common\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} + * + * @return array|bool|string|int|float|\UnitEnum|null + */ + public function get(string $name) + { + return $this->container->getParameter($name); + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function has(string $name) + { + return $this->container->hasParameter($name); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php new file mode 100644 index 0000000000..6ce9066b52 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\DependencyInjection\ParameterBag; + +use Wordlift\Modules\Common\Psr\Container\ContainerInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +/** + * ContainerBagInterface is the interface implemented by objects that manage service container parameters. + * + * @author Nicolas Grekas + */ +interface ContainerBagInterface extends ContainerInterface +{ + /** + * Gets the service container parameters. + * + * @return array + */ + 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/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php index daa978ab61..334676cf87 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php @@ -17,37 +17,57 @@ */ class EnvPlaceholderParameterBag extends ParameterBag { + private $envPlaceholderUniquePrefix; private $envPlaceholders = []; + private $unusedEnvPlaceholders = []; private $providedTypes = []; + private static $counter = 0; /** * {@inheritdoc} */ - public function get($name) + public function get(string $name) { - if (0 === \strpos($name, 'env(') && ')' === \substr($name, -1) && 'env()' !== $name) { - $env = \substr($name, 4, -1); + if (str_starts_with($name, 'env(') && str_ends_with($name, ')') && 'env()' !== $name) { + $env = substr($name, 4, -1); if (isset($this->envPlaceholders[$env])) { foreach ($this->envPlaceholders[$env] as $placeholder) { return $placeholder; // return first result } } - if (!\preg_match('/^(?:\\w++:)*+\\w++$/', $env)) { - throw new InvalidArgumentException(\sprintf('Invalid "%s" name: only "word" characters are allowed.', $name)); - } - if ($this->has($name)) { - $defaultValue = parent::get($name); - if (null !== $defaultValue && !\is_scalar($defaultValue)) { - throw new RuntimeException(\sprintf('The default value of an env() parameter must be scalar or null, but "%s" given to "%s".', \gettype($defaultValue), $name)); + if (isset($this->unusedEnvPlaceholders[$env])) { + foreach ($this->unusedEnvPlaceholders[$env] as $placeholder) { + return $placeholder; + // return first result } } - $uniqueName = \md5($name . \uniqid(\mt_rand(), \true)); - $placeholder = \sprintf('env_%s_%s', \str_replace(':', '_', $env), $uniqueName); + if (!preg_match('/^(?:[-.\w]*+:)*+\w++$/', $env)) { + throw new InvalidArgumentException(sprintf('Invalid %s name: only "word" characters are allowed.', $name)); + } + if ($this->has($name) && null !== ($defaultValue = parent::get($name)) && !\is_string($defaultValue)) { + throw new RuntimeException(sprintf('The default value of an env() parameter must be a string or null, but "%s" given to "%s".', get_debug_type($defaultValue), $name)); + } + $uniqueName = md5($name . '_' . self::$counter++); + $placeholder = sprintf('%s_%s_%s', $this->getEnvPlaceholderUniquePrefix(), strtr($env, ':-.', '___'), $uniqueName); $this->envPlaceholders[$env][$placeholder] = $placeholder; return $placeholder; } return parent::get($name); } + /** + * Gets the common env placeholder prefix for env vars created by this bag. + */ + public function getEnvPlaceholderUniquePrefix(): string + { + if (null === $this->envPlaceholderUniquePrefix) { + $reproducibleEntropy = unserialize(serialize($this->parameters)); + array_walk_recursive($reproducibleEntropy, function (&$v) { + $v = null; + }); + $this->envPlaceholderUniquePrefix = 'env_' . substr(md5(serialize($reproducibleEntropy)), -16); + } + return $this->envPlaceholderUniquePrefix; + } /** * Returns the map of env vars used in the resolved parameter values to their placeholders. * @@ -57,6 +77,14 @@ public function getEnvPlaceholders() { return $this->envPlaceholders; } + public function getUnusedEnvPlaceholders(): array + { + return $this->unusedEnvPlaceholders; + } + public function clearUnusedEnvPlaceholders() + { + $this->unusedEnvPlaceholders = []; + } /** * Merges the env placeholders of another EnvPlaceholderParameterBag. */ @@ -68,6 +96,12 @@ public function mergeEnvPlaceholders(self $bag) $this->envPlaceholders[$env] += $placeholders; } } + if ($newUnusedPlaceholders = $bag->getUnusedEnvPlaceholders()) { + $this->unusedEnvPlaceholders += $newUnusedPlaceholders; + foreach ($newUnusedPlaceholders as $env => $placeholders) { + $this->unusedEnvPlaceholders[$env] += $placeholders; + } + } } /** * Maps env prefixes to their corresponding PHP types. @@ -95,13 +129,8 @@ public function resolve() } parent::resolve(); foreach ($this->envPlaceholders as $env => $placeholders) { - if (!$this->has($name = "env({$env})")) { - continue; - } - if (\is_numeric($default = $this->parameters[$name])) { - $this->parameters[$name] = (string) $default; - } elseif (null !== $default && !\is_scalar($default)) { - throw new RuntimeException(\sprintf('The default value of env parameter "%s" must be scalar or null, "%s" given.', $env, \gettype($default))); + if ($this->has($name = "env({$env})") && null !== ($default = $this->parameters[$name]) && !\is_string($default)) { + throw new RuntimeException(sprintf('The default value of env parameter "%s" must be a string or null, "%s" given.', $env, get_debug_type($default))); } } } diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php index 50a38ea026..aebfe99679 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php @@ -48,14 +48,14 @@ public function add(array $parameters) /** * {@inheritdoc} */ - public function set($name, $value) + public function set(string $name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } /** * {@inheritdoc} */ - public function remove($name) + public function remove(string $name) { throw new LogicException('Impossible to call remove() on a frozen ParameterBag.'); } diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php index beae00ed16..2bcc722a10 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php @@ -22,25 +22,19 @@ class ParameterBag implements ParameterBagInterface { protected $parameters = []; protected $resolved = \false; - private $normalizedNames = []; - /** - * @param array $parameters An array of parameters - */ public function __construct(array $parameters = []) { $this->add($parameters); } /** - * Clears all parameters. + * {@inheritdoc} */ public function clear() { $this->parameters = []; } /** - * Adds parameters to the service container parameters. - * - * @param array $parameters An array of parameters + * {@inheritdoc} */ public function add(array $parameters) { @@ -58,24 +52,23 @@ public function all() /** * {@inheritdoc} */ - public function get($name) + public function get(string $name) { - $name = $this->normalizeName($name); if (!\array_key_exists($name, $this->parameters)) { if (!$name) { throw new ParameterNotFoundException($name); } $alternatives = []; foreach ($this->parameters as $key => $parameterValue) { - $lev = \levenshtein($name, $key); - if ($lev <= \strlen($name) / 3 || \false !== \strpos($key, $name)) { + $lev = levenshtein($name, $key); + if ($lev <= \strlen($name) / 3 || str_contains($key, $name)) { $alternatives[] = $key; } } $nonNestedAlternative = null; - if (!\count($alternatives) && \false !== \strpos($name, '.')) { - $namePartsLength = \array_map('strlen', \explode('.', $name)); - $key = \substr($name, 0, -1 * (1 + \array_pop($namePartsLength))); + if (!\count($alternatives) && str_contains($name, '.')) { + $namePartsLength = array_map('strlen', explode('.', $name)); + $key = substr($name, 0, -1 * (1 + array_pop($namePartsLength))); while (\count($namePartsLength)) { if ($this->has($key)) { if (\is_array($this->get($key))) { @@ -83,7 +76,7 @@ public function get($name) } break; } - $key = \substr($key, 0, -1 * (1 + \array_pop($namePartsLength))); + $key = substr($key, 0, -1 * (1 + array_pop($namePartsLength))); } } throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative); @@ -91,30 +84,25 @@ public function get($name) return $this->parameters[$name]; } /** - * Sets a service container parameter. - * - * @param string $name The parameter name - * @param mixed $value The parameter value + * {@inheritdoc} */ - public function set($name, $value) + public function set(string $name, $value) { - $this->parameters[$this->normalizeName($name)] = $value; + $this->parameters[$name] = $value; } /** * {@inheritdoc} */ - public function has($name) + public function has(string $name) { - return \array_key_exists($this->normalizeName($name), $this->parameters); + return \array_key_exists($name, $this->parameters); } /** - * Removes a parameter. - * - * @param string $name The parameter name + * {@inheritdoc} */ - public function remove($name) + public function remove(string $name) { - unset($this->parameters[$this->normalizeName($name)]); + unset($this->parameters[$name]); } /** * {@inheritdoc} @@ -143,7 +131,7 @@ public function resolve() * @param mixed $value A value * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) * - * @return mixed The resolved value + * @return mixed * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected @@ -166,47 +154,42 @@ public function resolveValue($value, array $resolving = []) /** * Resolves parameters inside a string. * - * @param string $value The string to resolve - * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) + * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) * - * @return mixed The resolved string + * @return mixed * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected * @throws RuntimeException when a given parameter has a type problem */ - public function resolveString($value, array $resolving = []) + public function resolveString(string $value, array $resolving = []) { // we do this to deal with non string values (Boolean, integer, ...) // as the preg_replace_callback throw an exception when trying // a non-string in a parameter value - if (\preg_match('/^%([^%\\s]+)%$/', $value, $match)) { + if (preg_match('/^%([^%\s]+)%$/', $value, $match)) { $key = $match[1]; - $lcKey = \strtolower($key); - // strtolower() to be removed in 4.0 - if (isset($resolving[$lcKey])) { - throw new ParameterCircularReferenceException(\array_keys($resolving)); + 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); } - return \preg_replace_callback('/%%|%([^%\\s]+)%/', function ($match) use($resolving, $value) { + return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($resolving, $value) { // skip %% if (!isset($match[1])) { return '%%'; } $key = $match[1]; - $lcKey = \strtolower($key); - // strtolower() to be removed in 4.0 - if (isset($resolving[$lcKey])) { - throw new ParameterCircularReferenceException(\array_keys($resolving)); + if (isset($resolving[$key])) { + throw new ParameterCircularReferenceException(array_keys($resolving)); } $resolved = $this->get($key); - if (!\is_string($resolved) && !\is_numeric($resolved)) { - throw new RuntimeException(\sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type "%s" inside string value "%s".', $key, \gettype($resolved), $value)); + if (!\is_string($resolved) && !is_numeric($resolved)) { + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type "%s" inside string value "%s".', $key, get_debug_type($resolved), $value)); } $resolved = (string) $resolved; - $resolving[$lcKey] = \true; + $resolving[$key] = \true; return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving); }, $value); } @@ -220,7 +203,7 @@ public function isResolved() public function escapeValue($value) { if (\is_string($value)) { - return \str_replace('%', '%%', $value); + return str_replace('%', '%%', $value); } if (\is_array($value)) { $result = []; @@ -237,7 +220,7 @@ public function escapeValue($value) public function unescapeValue($value) { if (\is_string($value)) { - return \str_replace('%%', '%', $value); + return str_replace('%%', '%', $value); } if (\is_array($value)) { $result = []; @@ -248,16 +231,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/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php index 5ea9e7d2ef..2708e55256 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php @@ -13,7 +13,7 @@ use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\LogicException; use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; /** - * ParameterBagInterface. + * ParameterBagInterface is the interface implemented by objects that manage service container parameters. * * @author Fabien Potencier */ @@ -22,56 +22,47 @@ interface ParameterBagInterface /** * Clears all parameters. * - * @throws LogicException if the ParameterBagInterface can not be cleared + * @throws LogicException if the ParameterBagInterface cannot be cleared */ public function clear(); /** * Adds parameters to the service container parameters. * - * @param array $parameters An array of parameters - * - * @throws LogicException if the parameter can not be added + * @throws LogicException if the parameter cannot be added */ public function add(array $parameters); /** * Gets the service container parameters. * - * @return array An array of parameters + * @return array */ public function all(); /** * Gets a service container parameter. * - * @param string $name The parameter name - * - * @return mixed The parameter value + * @return array|bool|string|int|float|\UnitEnum|null * * @throws ParameterNotFoundException if the parameter is not defined */ - public function get($name); + public function get(string $name); /** * Removes a parameter. - * - * @param string $name The parameter name */ - public function remove($name); + public function remove(string $name); /** * Sets a service container parameter. * - * @param string $name The parameter name - * @param mixed $value The parameter value + * @param array|bool|string|int|float|\UnitEnum|null $value The parameter value * - * @throws LogicException if the parameter can not be set + * @throws LogicException if the parameter cannot be set */ - public function set($name, $value); + public function set(string $name, $value); /** * Returns true if a parameter name is defined. * - * @param string $name The parameter name - * - * @return bool true if the parameter name is defined, false otherwise + * @return bool */ - public function has($name); + public function has(string $name); /** * Replaces parameter placeholders (%name%) by their values for all parameters. */ diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/Reference.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/Reference.php index fefc94bc6f..7bad181e0e 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/Reference.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/Reference.php @@ -19,19 +19,13 @@ 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; } /** - * @return string The service identifier + * @return string */ public function __toString() { diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ResettableContainerInterface.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ResettableContainerInterface.php deleted file mode 100644 index 7b7e6c4ed1..0000000000 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/ResettableContainerInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Wordlift\Modules\Common\Symfony\Component\DependencyInjection; - -/** - * ResettableContainerInterface defines additional resetting functionality - * for containers, allowing to release shared services when the container is - * not needed anymore. - * - * @author Christophe Coevoet - */ -interface ResettableContainerInterface extends ContainerInterface -{ - /** - * Resets shared services from the container. - * - * The container is not intended to be used again after being reset in a normal workflow. This method is - * meant as a way to release references for ref-counting. - * A subsequent call to ContainerInterface::get will recreate a new instance of the shared service. - */ - public function reset(); -} diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ReverseContainer.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ReverseContainer.php new file mode 100644 index 0000000000..95f7af4448 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ReverseContainer.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\DependencyInjection; + +use Wordlift\Modules\Common\Psr\Container\ContainerInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +/** + * Turns public and "container.reversible" services back to their ids. + * + * @author Nicolas Grekas + */ +final class ReverseContainer +{ + private $serviceContainer; + private $reversibleLocator; + private $tagName; + private $getServiceId; + public function __construct(Container $serviceContainer, ContainerInterface $reversibleLocator, string $tagName = 'container.reversible') + { + $this->serviceContainer = $serviceContainer; + $this->reversibleLocator = $reversibleLocator; + $this->tagName = $tagName; + $this->getServiceId = \Closure::bind(function (object $service): ?string { + return (array_search($service, $this->services, \true) ?: array_search($service, $this->privates, \true)) ?: null; + }, $serviceContainer, Container::class); + } + /** + * Returns the id of the passed object when it exists as a service. + * + * To be reversible, services need to be either public or be tagged with "container.reversible". + */ + public function getId(object $service): ?string + { + if ($this->serviceContainer === $service) { + return 'service_container'; + } + if (null === $id = ($this->getServiceId)($service)) { + return null; + } + if ($this->serviceContainer->has($id) || $this->reversibleLocator->has($id)) { + return $id; + } + return null; + } + /** + * @throws ServiceNotFoundException When the service is not reversible + */ + public function getService(string $id): object + { + if ($this->reversibleLocator->has($id)) { + return $this->reversibleLocator->get($id); + } + if (isset($this->serviceContainer->getRemovedIds()[$id])) { + throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is private and cannot be accessed by reference. You should either make it public, or tag it as "%s".', $id, $this->tagName)); + } + return $this->serviceContainer->get($id); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ServiceLocator.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/ServiceLocator.php index 440311747a..550356bf7c 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/ServiceLocator.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/ServiceLocator.php @@ -10,78 +10,76 @@ */ namespace Wordlift\Modules\Common\Symfony\Component\DependencyInjection; -use Wordlift\Modules\Common\Psr\Container\ContainerInterface as PsrContainerInterface; +use Wordlift\Modules\Common\Psr\Container\ContainerExceptionInterface; +use Wordlift\Modules\Common\Psr\Container\NotFoundExceptionInterface; +use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\RuntimeException; use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Wordlift\Modules\Common\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ServiceLocatorTrait; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ServiceProviderInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ServiceSubscriberInterface; /** * @author Robin Chalas * @author Nicolas Grekas */ -class ServiceLocator implements PsrContainerInterface +class ServiceLocator implements ServiceProviderInterface { - private $factories; - private $loading = []; + use ServiceLocatorTrait { + get as private doGet; + } private $externalId; private $container; - /** - * @param callable[] $factories - */ - public function __construct(array $factories) - { - $this->factories = $factories; - } - /** - * {@inheritdoc} - */ - public function has($id) - { - return isset($this->factories[$id]); - } /** * {@inheritdoc} + * + * @return mixed */ - public function get($id) + public function get(string $id) { - if (!isset($this->factories[$id])) { - throw new ServiceNotFoundException($id, \end($this->loading) ?: null, null, [], $this->createServiceNotFoundMessage($id)); - } - if (isset($this->loading[$id])) { - $ids = \array_values($this->loading); - $ids = \array_slice($this->loading, \array_search($id, $ids)); - $ids[] = $id; - throw new ServiceCircularReferenceException($id, $ids); + if (!$this->externalId) { + return $this->doGet($id); } - $this->loading[$id] = $id; try { - return $this->factories[$id](); - } finally { - unset($this->loading[$id]); + return $this->doGet($id); + } catch (RuntimeException $e) { + $what = sprintf('service "%s" required by "%s"', $id, $this->externalId); + $message = preg_replace('/service "\.service_locator\.[^"]++"/', $what, $e->getMessage()); + if ($e->getMessage() === $message) { + $message = sprintf('Cannot resolve %s: %s', $what, $message); + } + $r = new \ReflectionProperty($e, 'message'); + $r->setAccessible(\true); + $r->setValue($e, $message); + throw $e; } } - public function __invoke($id) + public function __invoke(string $id) { return isset($this->factories[$id]) ? $this->get($id) : null; } /** * @internal + * + * @return static */ - public function withContext($externalId, Container $container) + public function withContext(string $externalId, Container $container): self { $locator = clone $this; $locator->externalId = $externalId; $locator->container = $container; return $locator; } - private function createServiceNotFoundMessage($id) + private function createNotFoundException(string $id): NotFoundExceptionInterface { if ($this->loading) { - return \sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', \end($this->loading), $id, $this->formatAlternatives()); + $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives()); + return new ServiceNotFoundException($id, end($this->loading) ?: null, null, [], $msg); } - $class = \debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 3); - $class = isset($class[2]['object']) ? \get_class($class[2]['object']) : null; + $class = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 4); + $class = isset($class[3]['object']) ? \get_class($class[3]['object']) : null; $externalId = $this->externalId ?: $class; $msg = []; - $msg[] = \sprintf('Service "%s" not found:', $id); + $msg[] = sprintf('Service "%s" not found:', $id); if (!$this->container) { $class = null; } elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) { @@ -92,36 +90,40 @@ private function createServiceNotFoundMessage($id) $class = null; } catch (ServiceNotFoundException $e) { if ($e->getAlternatives()) { - $msg[] = \sprintf('did you mean %s? Anyway,', $this->formatAlternatives($e->getAlternatives(), 'or')); + $msg[] = sprintf('did you mean %s? Anyway,', $this->formatAlternatives($e->getAlternatives(), 'or')); } else { $class = null; } } } if ($externalId) { - $msg[] = \sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives()); + $msg[] = sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives()); } else { - $msg[] = \sprintf('the current service locator %s', $this->formatAlternatives()); + $msg[] = sprintf('the current service locator %s', $this->formatAlternatives()); } if (!$class) { // no-op - } elseif (\is_subclass_of($class, ServiceSubscriberInterface::class)) { - $msg[] = \sprintf('Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', \preg_replace('/([^\\\\]++\\\\)++/', '', $class)); + } elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) { + $msg[] = sprintf('Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class)); } else { $msg[] = 'Try using dependency injection instead.'; } - return \implode(' ', $msg); + return new ServiceNotFoundException($id, end($this->loading) ?: null, null, [], implode(' ', $msg)); + } + private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface + { + return new ServiceCircularReferenceException($id, $path); } - private function formatAlternatives(array $alternatives = null, $separator = 'and') + private function formatAlternatives(?array $alternatives = null, string $separator = 'and'): string { $format = '"%s"%s'; if (null === $alternatives) { - if (!($alternatives = \array_keys($this->factories))) { + if (!$alternatives = array_keys($this->factories)) { return 'is empty...'; } - $format = \sprintf('only knows about the %s service%s.', $format, 1 < \count($alternatives) ? 's' : ''); + $format = sprintf('only knows about the %s service%s.', $format, (1 < \count($alternatives)) ? 's' : ''); } - $last = \array_pop($alternatives); - return \sprintf($format, $alternatives ? \implode('", "', $alternatives) : $last, $alternatives ? \sprintf(' %s "%s"', $separator, $last) : ''); + $last = array_pop($alternatives); + return sprintf($format, $alternatives ? implode('", "', $alternatives) : $last, $alternatives ? sprintf(' %s "%s"', $separator, $last) : ''); } } diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/TaggedContainerInterface.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/TaggedContainerInterface.php index f962fc0004..26d9ed0a18 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/TaggedContainerInterface.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/TaggedContainerInterface.php @@ -22,7 +22,7 @@ interface TaggedContainerInterface extends ContainerInterface * * @param string $name The tag name * - * @return array An array of tags + * @return array */ - public function findTaggedServiceIds($name); + public function findTaggedServiceIds(string $name); } diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/TypedReference.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/TypedReference.php index a624acf3b7..7ca1641531 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/TypedReference.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/TypedReference.php @@ -18,29 +18,25 @@ class TypedReference extends Reference { private $type; - private $requiringClass; + private $name; /** - * @param string $id The service identifier - * @param string $type The PHP type of the identified service - * @param string $requiringClass The class of the service that requires the referenced type - * @param int $invalidBehavior The behavior when the service does not exist + * @param string $id The service identifier + * @param string $type The PHP type of the identified service + * @param int $invalidBehavior The behavior when the service does not exist + * @param string|null $name The name of the argument targeting the service */ - public function __construct($id, $type, $requiringClass = '', $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + public function __construct(string $id, string $type, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, ?string $name = null) { + $this->name = ($type === $id) ? $name : null; parent::__construct($id, $invalidBehavior); $this->type = $type; - $this->requiringClass = $requiringClass; } public function getType() { return $this->type; } - public function getRequiringClass() + public function getName(): ?string { - return $this->requiringClass; - } - public function canBeAutoregistered() - { - return $this->requiringClass && \false !== ($i = \strpos($this->type, '\\')) && 0 === \strncasecmp($this->type, $this->requiringClass, 1 + $i); + return $this->name; } } diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/Variable.php b/src/modules/common/third-party/vendor/symfony/dependency-injection/Variable.php index 8ff822b288..1e71b78ed3 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/Variable.php +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/Variable.php @@ -26,13 +26,13 @@ class Variable { private $name; - /** - * @param string $name - */ - public function __construct($name) + public function __construct(string $name) { $this->name = $name; } + /** + * @return string + */ public function __toString() { return $this->name; diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/composer.json b/src/modules/common/third-party/vendor/symfony/dependency-injection/composer.json new file mode 100644 index 0000000000..a5f9701f5b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/dependency-injection/composer.json @@ -0,0 +1,58 @@ +{ + "name": "symfony\/dependency-injection", + "type": "library", + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "keywords": [], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "psr\/container": "^1.1.1", + "symfony\/deprecation-contracts": "^2.1|^3", + "symfony\/polyfill-php80": "^1.16", + "symfony\/polyfill-php81": "^1.22", + "symfony\/service-contracts": "^1.1.6|^2" + }, + "require-dev": { + "symfony\/yaml": "^4.4.26|^5.0|^6.0", + "symfony\/config": "^5.3|^6.0", + "symfony\/expression-language": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony\/yaml": "", + "symfony\/config": "", + "symfony\/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony\/expression-language": "For using expressions in service container configuration", + "symfony\/proxy-manager-bridge": "Generate service proxies to lazy load them" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony\/config": "<5.3", + "symfony\/finder": "<4.4", + "symfony\/proxy-manager-bridge": "<4.4", + "symfony\/yaml": "<4.4.26" + }, + "provide": { + "psr\/container-implementation": "1.0", + "symfony\/service-implementation": "1.0|2.0" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "\/Tests\/" + ] + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/deprecation-contracts/composer.json b/src/modules/common/third-party/vendor/symfony/deprecation-contracts/composer.json new file mode 100644 index 0000000000..ab39026cd6 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/deprecation-contracts/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony\/deprecation-contracts", + "type": "library", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony\/contracts", + "url": "https:\/\/github.com\/symfony\/contracts" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/deprecation-contracts/function.php b/src/modules/common/third-party/vendor/symfony/deprecation-contracts/function.php new file mode 100644 index 0000000000..f8aed24e10 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/deprecation-contracts/function.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!\function_exists('Wordlift\Modules\Common\trigger_deprecation')) { + /** + * Triggers a silenced deprecation notice. + * + * @param string $package The name of the Composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The message of the deprecation + * @param mixed ...$args Values to insert in the message using printf() formatting + * + * @author Nicolas Grekas + */ + function trigger_deprecation(string $package, string $version, string $message, ...$args): void + { + @\trigger_error((($package || $version) ? "Since {$package} {$version}: " : '') . ($args ? \vsprintf($message, $args) : $message), \E_USER_DEPRECATED); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Compiler.php b/src/modules/common/third-party/vendor/symfony/expression-language/Compiler.php new file mode 100644 index 0000000000..eb7543092c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Compiler.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +use Wordlift\Modules\Common\Symfony\Contracts\Service\ResetInterface; +/** + * Compiles a node to PHP code. + * + * @author Fabien Potencier + */ +class Compiler implements ResetInterface +{ + private $source; + private $functions; + public function __construct(array $functions) + { + $this->functions = $functions; + } + public function getFunction(string $name) + { + return $this->functions[$name]; + } + /** + * Gets the current PHP code after compilation. + * + * @return string + */ + public function getSource() + { + return $this->source; + } + /** + * @return $this + */ + public function reset() + { + $this->source = ''; + return $this; + } + /** + * Compiles a node. + * + * @return $this + */ + public function compile(Node\Node $node) + { + $node->compile($this); + return $this; + } + public function subcompile(Node\Node $node) + { + $current = $this->source; + $this->source = ''; + $node->compile($this); + $source = $this->source; + $this->source = $current; + return $source; + } + /** + * Adds a raw string to the compiled code. + * + * @return $this + */ + public function raw(string $string) + { + $this->source .= $string; + return $this; + } + /** + * Adds a quoted string to the compiled code. + * + * @return $this + */ + public function string(string $value) + { + $this->source .= sprintf('"%s"', addcslashes($value, "\x00\t\"\$\\")); + return $this; + } + /** + * Returns a PHP representation of a given value. + * + * @param mixed $value The value to convert + * + * @return $this + */ + public function repr($value) + { + if (\is_int($value) || \is_float($value)) { + if (\false !== $locale = setlocale(\LC_NUMERIC, 0)) { + setlocale(\LC_NUMERIC, 'C'); + } + $this->raw($value); + if (\false !== $locale) { + setlocale(\LC_NUMERIC, $locale); + } + } elseif (null === $value) { + $this->raw('null'); + } elseif (\is_bool($value)) { + $this->raw($value ? 'true' : 'false'); + } elseif (\is_array($value)) { + $this->raw('['); + $first = \true; + foreach ($value as $key => $value) { + if (!$first) { + $this->raw(', '); + } + $first = \false; + $this->repr($key); + $this->raw(' => '); + $this->repr($value); + } + $this->raw(']'); + } else { + $this->string($value); + } + return $this; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Expression.php b/src/modules/common/third-party/vendor/symfony/expression-language/Expression.php new file mode 100644 index 0000000000..d88cf6649f --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Expression.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * Represents an expression. + * + * @author Fabien Potencier + */ +class Expression +{ + protected $expression; + public function __construct(string $expression) + { + $this->expression = $expression; + } + /** + * Gets the expression. + * + * @return string + */ + public function __toString() + { + return $this->expression; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionFunction.php b/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionFunction.php new file mode 100644 index 0000000000..768b93b81b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionFunction.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * Represents a function that can be used in an expression. + * + * A function is defined by two PHP callables. The callables are used + * by the language to compile and/or evaluate the function. + * + * The "compiler" function is used at compilation time and must return a + * PHP representation of the function call (it receives the function + * arguments as arguments). + * + * The "evaluator" function is used for expression evaluation and must return + * the value of the function call based on the values defined for the + * expression (it receives the values as a first argument and the function + * arguments as remaining arguments). + * + * @author Fabien Potencier + */ +class ExpressionFunction +{ + private $name; + private $compiler; + private $evaluator; + /** + * @param string $name The function name + * @param callable $compiler A callable able to compile the function + * @param callable $evaluator A callable able to evaluate the function + */ + public function __construct(string $name, callable $compiler, callable $evaluator) + { + $this->name = $name; + $this->compiler = ($compiler instanceof \Closure) ? $compiler : \Closure::fromCallable($compiler); + $this->evaluator = ($evaluator instanceof \Closure) ? $evaluator : \Closure::fromCallable($evaluator); + } + /** + * @return string + */ + public function getName() + { + return $this->name; + } + /** + * @return \Closure + */ + public function getCompiler() + { + return $this->compiler; + } + /** + * @return \Closure + */ + public function getEvaluator() + { + return $this->evaluator; + } + /** + * Creates an ExpressionFunction from a PHP function name. + * + * @param string|null $expressionFunctionName The expression function name (default: same than the PHP function name) + * + * @return self + * + * @throws \InvalidArgumentException if given PHP function name does not exist + * @throws \InvalidArgumentException if given PHP function name is in namespace + * and expression function name is not defined + */ + public static function fromPhp(string $phpFunctionName, ?string $expressionFunctionName = null) + { + $phpFunctionName = ltrim($phpFunctionName, '\\'); + if (!\function_exists($phpFunctionName)) { + throw new \InvalidArgumentException(sprintf('PHP function "%s" does not exist.', $phpFunctionName)); + } + $parts = explode('\\', $phpFunctionName); + if (!$expressionFunctionName && \count($parts) > 1) { + throw new \InvalidArgumentException(sprintf('An expression function name must be defined when PHP function "%s" is namespaced.', $phpFunctionName)); + } + $compiler = function (...$args) use ($phpFunctionName) { + return sprintf('\%s(%s)', $phpFunctionName, implode(', ', $args)); + }; + $evaluator = function ($p, ...$args) use ($phpFunctionName) { + return $phpFunctionName(...$args); + }; + return new self($expressionFunctionName ?: end($parts), $compiler, $evaluator); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionFunctionProviderInterface.php b/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionFunctionProviderInterface.php new file mode 100644 index 0000000000..0017bb0c8c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionFunctionProviderInterface.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 Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * @author Fabien Potencier + */ +interface ExpressionFunctionProviderInterface +{ + /** + * @return ExpressionFunction[] + */ + public function getFunctions(); +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionLanguage.php b/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionLanguage.php new file mode 100644 index 0000000000..79368162b6 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/ExpressionLanguage.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +use Wordlift\Modules\Common\Psr\Cache\CacheItemPoolInterface; +use Wordlift\Modules\Common\Symfony\Component\Cache\Adapter\ArrayAdapter; +// Help opcache.preload discover always-needed symbols +class_exists(ParsedExpression::class); +/** + * Allows to compile and evaluate expressions written in your own DSL. + * + * @author Fabien Potencier + */ +class ExpressionLanguage +{ + private $cache; + private $lexer; + private $parser; + private $compiler; + protected $functions = []; + /** + * @param ExpressionFunctionProviderInterface[] $providers + */ + public function __construct(?CacheItemPoolInterface $cache = null, array $providers = []) + { + $this->cache = $cache ?? new ArrayAdapter(); + $this->registerFunctions(); + foreach ($providers as $provider) { + $this->registerProvider($provider); + } + } + /** + * Compiles an expression source code. + * + * @param Expression|string $expression The expression to compile + * + * @return string + */ + public function compile($expression, array $names = []) + { + return $this->getCompiler()->compile($this->parse($expression, $names)->getNodes())->getSource(); + } + /** + * Evaluate an expression. + * + * @param Expression|string $expression The expression to compile + * + * @return mixed + */ + public function evaluate($expression, array $values = []) + { + return $this->parse($expression, array_keys($values))->getNodes()->evaluate($this->functions, $values); + } + /** + * Parses an expression. + * + * @param Expression|string $expression The expression to parse + * + * @return ParsedExpression + */ + public function parse($expression, array $names) + { + if ($expression instanceof ParsedExpression) { + return $expression; + } + asort($names); + $cacheKeyItems = []; + foreach ($names as $nameKey => $name) { + $cacheKeyItems[] = \is_int($nameKey) ? $name : ($nameKey . ':' . $name); + } + $cacheItem = $this->cache->getItem(rawurlencode($expression . '//' . implode('|', $cacheKeyItems))); + if (null === $parsedExpression = $cacheItem->get()) { + $nodes = $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names); + $parsedExpression = new ParsedExpression((string) $expression, $nodes); + $cacheItem->set($parsedExpression); + $this->cache->save($cacheItem); + } + return $parsedExpression; + } + /** + * Validates the syntax of an expression. + * + * @param Expression|string $expression The expression to validate + * @param array|null $names The list of acceptable variable names in the expression, or null to accept any names + * + * @throws SyntaxError When the passed expression is invalid + */ + public function lint($expression, ?array $names): void + { + if ($expression instanceof ParsedExpression) { + return; + } + $this->getParser()->lint($this->getLexer()->tokenize((string) $expression), $names); + } + /** + * Registers a function. + * + * @param callable $compiler A callable able to compile the function + * @param callable $evaluator A callable able to evaluate the function + * + * @throws \LogicException when registering a function after calling evaluate(), compile() or parse() + * + * @see ExpressionFunction + */ + public function register(string $name, callable $compiler, callable $evaluator) + { + if (null !== $this->parser) { + throw new \LogicException('Registering functions after calling evaluate(), compile() or parse() is not supported.'); + } + $this->functions[$name] = ['compiler' => $compiler, 'evaluator' => $evaluator]; + } + public function addFunction(ExpressionFunction $function) + { + $this->register($function->getName(), $function->getCompiler(), $function->getEvaluator()); + } + public function registerProvider(ExpressionFunctionProviderInterface $provider) + { + foreach ($provider->getFunctions() as $function) { + $this->addFunction($function); + } + } + protected function registerFunctions() + { + $this->addFunction(ExpressionFunction::fromPhp('constant')); + } + private function getLexer(): Lexer + { + if (null === $this->lexer) { + $this->lexer = new Lexer(); + } + return $this->lexer; + } + private function getParser(): Parser + { + if (null === $this->parser) { + $this->parser = new Parser($this->functions); + } + return $this->parser; + } + private function getCompiler(): Compiler + { + if (null === $this->compiler) { + $this->compiler = new Compiler($this->functions); + } + return $this->compiler->reset(); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Lexer.php b/src/modules/common/third-party/vendor/symfony/expression-language/Lexer.php new file mode 100644 index 0000000000..9b9b59a17f --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Lexer.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * Lexes an expression. + * + * @author Fabien Potencier + */ +class Lexer +{ + /** + * Tokenizes an expression. + * + * @return TokenStream + * + * @throws SyntaxError + */ + public function tokenize(string $expression) + { + $expression = str_replace(["\r", "\n", "\t", "\v", "\f"], ' ', $expression); + $cursor = 0; + $tokens = []; + $brackets = []; + $end = \strlen($expression); + while ($cursor < $end) { + if (' ' == $expression[$cursor]) { + ++$cursor; + continue; + } + if (preg_match('/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A', $expression, $match, 0, $cursor)) { + // numbers + $number = (float) $match[0]; + // floats + if (preg_match('/^[0-9]+$/', $match[0]) && $number <= \PHP_INT_MAX) { + $number = (int) $match[0]; + // integers lower than the maximum + } + $tokens[] = new Token(Token::NUMBER_TYPE, $number, $cursor + 1); + $cursor += \strlen($match[0]); + } elseif (\false !== strpos('([{', $expression[$cursor])) { + // opening bracket + $brackets[] = [$expression[$cursor], $cursor]; + $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); + ++$cursor; + } elseif (\false !== strpos(')]}', $expression[$cursor])) { + // closing bracket + if (empty($brackets)) { + throw new SyntaxError(sprintf('Unexpected "%s".', $expression[$cursor]), $cursor, $expression); + } + [$expect, $cur] = array_pop($brackets); + if ($expression[$cursor] != strtr($expect, '([{', ')]}')) { + throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $cur, $expression); + } + $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); + ++$cursor; + } elseif (preg_match('/"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As', $expression, $match, 0, $cursor)) { + // strings + $tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1); + $cursor += \strlen($match[0]); + } elseif (preg_match('/(?<=^|[\s(])not in(?=[\s(])|\!\=\=|(?<=^|[\s(])not(?=[\s(])|(?<=^|[\s(])and(?=[\s(])|\=\=\=|\>\=|(?<=^|[\s(])or(?=[\s(])|\<\=|\*\*|\.\.|(?<=^|[\s(])in(?=[\s(])|&&|\|\||(?<=^|[\s(])matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) { + // operators + $tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1); + $cursor += \strlen($match[0]); + } elseif (\false !== strpos('.,?:', $expression[$cursor])) { + // punctuation + $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); + ++$cursor; + } elseif (preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $expression, $match, 0, $cursor)) { + // names + $tokens[] = new Token(Token::NAME_TYPE, $match[0], $cursor + 1); + $cursor += \strlen($match[0]); + } else { + // unlexable + throw new SyntaxError(sprintf('Unexpected character "%s".', $expression[$cursor]), $cursor, $expression); + } + } + $tokens[] = new Token(Token::EOF_TYPE, null, $cursor + 1); + if (!empty($brackets)) { + [$expect, $cur] = array_pop($brackets); + throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $cur, $expression); + } + return new TokenStream($tokens, $expression); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/ArgumentsNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ArgumentsNode.php new file mode 100644 index 0000000000..5d1eae5efc --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ArgumentsNode.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class ArgumentsNode extends ArrayNode +{ + public function compile(Compiler $compiler) + { + $this->compileArguments($compiler, \false); + } + public function toArray() + { + $array = []; + foreach ($this->getKeyValuePairs() as $pair) { + $array[] = $pair['value']; + $array[] = ', '; + } + array_pop($array); + return $array; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/ArrayNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ArrayNode.php new file mode 100644 index 0000000000..2d4e846ec0 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ArrayNode.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class ArrayNode extends Node +{ + protected $index; + public function __construct() + { + $this->index = -1; + } + public function addElement(Node $value, ?Node $key = null) + { + if (null === $key) { + $key = new ConstantNode(++$this->index); + } + array_push($this->nodes, $key, $value); + } + /** + * Compiles the node to PHP. + */ + public function compile(Compiler $compiler) + { + $compiler->raw('['); + $this->compileArguments($compiler); + $compiler->raw(']'); + } + public function evaluate(array $functions, array $values) + { + $result = []; + foreach ($this->getKeyValuePairs() as $pair) { + $result[$pair['key']->evaluate($functions, $values)] = $pair['value']->evaluate($functions, $values); + } + return $result; + } + public function toArray() + { + $value = []; + foreach ($this->getKeyValuePairs() as $pair) { + $value[$pair['key']->attributes['value']] = $pair['value']; + } + $array = []; + if ($this->isHash($value)) { + foreach ($value as $k => $v) { + $array[] = ', '; + $array[] = new ConstantNode($k); + $array[] = ': '; + $array[] = $v; + } + $array[0] = '{'; + $array[] = '}'; + } else { + foreach ($value as $v) { + $array[] = ', '; + $array[] = $v; + } + $array[0] = '['; + $array[] = ']'; + } + return $array; + } + protected function getKeyValuePairs() + { + $pairs = []; + foreach (array_chunk($this->nodes, 2) as $pair) { + $pairs[] = ['key' => $pair[0], 'value' => $pair[1]]; + } + return $pairs; + } + protected function compileArguments(Compiler $compiler, bool $withKeys = \true) + { + $first = \true; + foreach ($this->getKeyValuePairs() as $pair) { + if (!$first) { + $compiler->raw(', '); + } + $first = \false; + if ($withKeys) { + $compiler->compile($pair['key'])->raw(' => '); + } + $compiler->compile($pair['value']); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/BinaryNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/BinaryNode.php new file mode 100644 index 0000000000..f8327e6add --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/BinaryNode.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\SyntaxError; +/** + * @author Fabien Potencier + * + * @internal + */ +class BinaryNode extends Node +{ + private const OPERATORS = ['~' => '.', 'and' => '&&', 'or' => '||']; + private const FUNCTIONS = ['**' => 'pow', '..' => 'range', 'in' => 'in_array', 'not in' => '!in_array']; + public function __construct(string $operator, Node $left, Node $right) + { + parent::__construct(['left' => $left, 'right' => $right], ['operator' => $operator]); + } + public function compile(Compiler $compiler) + { + $operator = $this->attributes['operator']; + if ('matches' == $operator) { + if ($this->nodes['right'] instanceof ConstantNode) { + $this->evaluateMatches($this->nodes['right']->evaluate([], []), ''); + } + $compiler->raw('(static function ($regexp, $str) { set_error_handler(function ($t, $m) use ($regexp, $str) { throw new \Symfony\Component\ExpressionLanguage\SyntaxError(sprintf(\'Regexp "%s" passed to "matches" is not valid\', $regexp).substr($m, 12)); }); try { return preg_match($regexp, (string) $str); } finally { restore_error_handler(); } })(')->compile($this->nodes['right'])->raw(', ')->compile($this->nodes['left'])->raw(')'); + return; + } + if (isset(self::FUNCTIONS[$operator])) { + $compiler->raw(sprintf('%s(', self::FUNCTIONS[$operator]))->compile($this->nodes['left'])->raw(', ')->compile($this->nodes['right'])->raw(')'); + return; + } + if (isset(self::OPERATORS[$operator])) { + $operator = self::OPERATORS[$operator]; + } + $compiler->raw('(')->compile($this->nodes['left'])->raw(' ')->raw($operator)->raw(' ')->compile($this->nodes['right'])->raw(')'); + } + public function evaluate(array $functions, array $values) + { + $operator = $this->attributes['operator']; + $left = $this->nodes['left']->evaluate($functions, $values); + if (isset(self::FUNCTIONS[$operator])) { + $right = $this->nodes['right']->evaluate($functions, $values); + if ('not in' === $operator) { + return !\in_array($left, $right); + } + $f = self::FUNCTIONS[$operator]; + return $f($left, $right); + } + switch ($operator) { + case 'or': + case '||': + return $left || $this->nodes['right']->evaluate($functions, $values); + case 'and': + case '&&': + return $left && $this->nodes['right']->evaluate($functions, $values); + } + $right = $this->nodes['right']->evaluate($functions, $values); + switch ($operator) { + case '|': + return $left | $right; + case '^': + return $left ^ $right; + case '&': + return $left & $right; + case '==': + return $left == $right; + case '===': + return $left === $right; + case '!=': + return $left != $right; + case '!==': + return $left !== $right; + case '<': + return $left < $right; + case '>': + return $left > $right; + case '>=': + return $left >= $right; + case '<=': + return $left <= $right; + case 'not in': + return !\in_array($left, $right); + case 'in': + return \in_array($left, $right); + case '+': + return $left + $right; + case '-': + return $left - $right; + case '~': + return $left . $right; + case '*': + return $left * $right; + case '/': + if (0 == $right) { + throw new \DivisionByZeroError('Division by zero.'); + } + return $left / $right; + case '%': + if (0 == $right) { + throw new \DivisionByZeroError('Modulo by zero.'); + } + return $left % $right; + case 'matches': + return $this->evaluateMatches($right, $left); + } + } + public function toArray() + { + return ['(', $this->nodes['left'], ' ' . $this->attributes['operator'] . ' ', $this->nodes['right'], ')']; + } + private function evaluateMatches(string $regexp, ?string $str): int + { + set_error_handler(function ($t, $m) use ($regexp) { + throw new SyntaxError(sprintf('Regexp "%s" passed to "matches" is not valid', $regexp) . substr($m, 12)); + }); + try { + return preg_match($regexp, (string) $str); + } finally { + restore_error_handler(); + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/ConditionalNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ConditionalNode.php new file mode 100644 index 0000000000..c39512d800 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ConditionalNode.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class ConditionalNode extends Node +{ + public function __construct(Node $expr1, Node $expr2, Node $expr3) + { + parent::__construct(['expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3]); + } + public function compile(Compiler $compiler) + { + $compiler->raw('((')->compile($this->nodes['expr1'])->raw(') ? (')->compile($this->nodes['expr2'])->raw(') : (')->compile($this->nodes['expr3'])->raw('))'); + } + public function evaluate(array $functions, array $values) + { + if ($this->nodes['expr1']->evaluate($functions, $values)) { + return $this->nodes['expr2']->evaluate($functions, $values); + } + return $this->nodes['expr3']->evaluate($functions, $values); + } + public function toArray() + { + return ['(', $this->nodes['expr1'], ' ? ', $this->nodes['expr2'], ' : ', $this->nodes['expr3'], ')']; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/ConstantNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ConstantNode.php new file mode 100644 index 0000000000..f9d2d856a8 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/ConstantNode.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class ConstantNode extends Node +{ + private $isIdentifier; + public function __construct($value, bool $isIdentifier = \false) + { + $this->isIdentifier = $isIdentifier; + parent::__construct([], ['value' => $value]); + } + public function compile(Compiler $compiler) + { + $compiler->repr($this->attributes['value']); + } + public function evaluate(array $functions, array $values) + { + return $this->attributes['value']; + } + public function toArray() + { + $array = []; + $value = $this->attributes['value']; + if ($this->isIdentifier) { + $array[] = $value; + } elseif (\true === $value) { + $array[] = 'true'; + } elseif (\false === $value) { + $array[] = 'false'; + } elseif (null === $value) { + $array[] = 'null'; + } elseif (is_numeric($value)) { + $array[] = $value; + } elseif (!\is_array($value)) { + $array[] = $this->dumpString($value); + } elseif ($this->isHash($value)) { + foreach ($value as $k => $v) { + $array[] = ', '; + $array[] = new self($k); + $array[] = ': '; + $array[] = new self($v); + } + $array[0] = '{'; + $array[] = '}'; + } else { + foreach ($value as $v) { + $array[] = ', '; + $array[] = new self($v); + } + $array[0] = '['; + $array[] = ']'; + } + return $array; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/FunctionNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/FunctionNode.php new file mode 100644 index 0000000000..a89701df6c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/FunctionNode.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class FunctionNode extends Node +{ + public function __construct(string $name, Node $arguments) + { + parent::__construct(['arguments' => $arguments], ['name' => $name]); + } + public function compile(Compiler $compiler) + { + $arguments = []; + foreach ($this->nodes['arguments']->nodes as $node) { + $arguments[] = $compiler->subcompile($node); + } + $function = $compiler->getFunction($this->attributes['name']); + $compiler->raw($function['compiler'](...$arguments)); + } + public function evaluate(array $functions, array $values) + { + $arguments = [$values]; + foreach ($this->nodes['arguments']->nodes as $node) { + $arguments[] = $node->evaluate($functions, $values); + } + return $functions[$this->attributes['name']]['evaluator'](...$arguments); + } + public function toArray() + { + $array = []; + $array[] = $this->attributes['name']; + foreach ($this->nodes['arguments']->nodes as $node) { + $array[] = ', '; + $array[] = $node; + } + $array[1] = '('; + $array[] = ')'; + return $array; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/GetAttrNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/GetAttrNode.php new file mode 100644 index 0000000000..d7b67698b2 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/GetAttrNode.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class GetAttrNode extends Node +{ + public const PROPERTY_CALL = 1; + public const METHOD_CALL = 2; + public const ARRAY_CALL = 3; + public function __construct(Node $node, Node $attribute, ArrayNode $arguments, int $type) + { + parent::__construct(['node' => $node, 'attribute' => $attribute, 'arguments' => $arguments], ['type' => $type]); + } + public function compile(Compiler $compiler) + { + switch ($this->attributes['type']) { + case self::PROPERTY_CALL: + $compiler->compile($this->nodes['node'])->raw('->')->raw($this->nodes['attribute']->attributes['value']); + break; + case self::METHOD_CALL: + $compiler->compile($this->nodes['node'])->raw('->')->raw($this->nodes['attribute']->attributes['value'])->raw('(')->compile($this->nodes['arguments'])->raw(')'); + break; + case self::ARRAY_CALL: + $compiler->compile($this->nodes['node'])->raw('[')->compile($this->nodes['attribute'])->raw(']'); + break; + } + } + public function evaluate(array $functions, array $values) + { + switch ($this->attributes['type']) { + case self::PROPERTY_CALL: + $obj = $this->nodes['node']->evaluate($functions, $values); + if (!\is_object($obj)) { + throw new \RuntimeException(sprintf('Unable to get property "%s" of non-object "%s".', $this->nodes['attribute']->dump(), $this->nodes['node']->dump())); + } + $property = $this->nodes['attribute']->attributes['value']; + return $obj->{$property}; + case self::METHOD_CALL: + $obj = $this->nodes['node']->evaluate($functions, $values); + if (!\is_object($obj)) { + throw new \RuntimeException(sprintf('Unable to call method "%s" of non-object "%s".', $this->nodes['attribute']->dump(), $this->nodes['node']->dump())); + } + if (!\is_callable($toCall = [$obj, $this->nodes['attribute']->attributes['value']])) { + throw new \RuntimeException(sprintf('Unable to call method "%s" of object "%s".', $this->nodes['attribute']->attributes['value'], get_debug_type($obj))); + } + return $toCall(...array_values($this->nodes['arguments']->evaluate($functions, $values))); + case self::ARRAY_CALL: + $array = $this->nodes['node']->evaluate($functions, $values); + if (!\is_array($array) && !$array instanceof \ArrayAccess) { + throw new \RuntimeException(sprintf('Unable to get an item of non-array "%s".', $this->nodes['node']->dump())); + } + return $array[$this->nodes['attribute']->evaluate($functions, $values)]; + } + } + public function toArray() + { + switch ($this->attributes['type']) { + case self::PROPERTY_CALL: + return [$this->nodes['node'], '.', $this->nodes['attribute']]; + case self::METHOD_CALL: + return [$this->nodes['node'], '.', $this->nodes['attribute'], '(', $this->nodes['arguments'], ')']; + case self::ARRAY_CALL: + return [$this->nodes['node'], '[', $this->nodes['attribute'], ']']; + } + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/NameNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/NameNode.php new file mode 100644 index 0000000000..6475395895 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/NameNode.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class NameNode extends Node +{ + public function __construct(string $name) + { + parent::__construct([], ['name' => $name]); + } + public function compile(Compiler $compiler) + { + $compiler->raw('$' . $this->attributes['name']); + } + public function evaluate(array $functions, array $values) + { + return $values[$this->attributes['name']]; + } + public function toArray() + { + return [$this->attributes['name']]; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/Node.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/Node.php new file mode 100644 index 0000000000..da8bb6363f --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/Node.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * Represents a node in the AST. + * + * @author Fabien Potencier + */ +class Node +{ + public $nodes = []; + public $attributes = []; + /** + * @param array $nodes An array of nodes + * @param array $attributes An array of attributes + */ + public function __construct(array $nodes = [], array $attributes = []) + { + $this->nodes = $nodes; + $this->attributes = $attributes; + } + /** + * @return string + */ + public function __toString() + { + $attributes = []; + foreach ($this->attributes as $name => $value) { + $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, \true))); + } + $repr = [str_replace('Symfony\Component\ExpressionLanguage\Node\\', '', static::class) . '(' . implode(', ', $attributes)]; + if (\count($this->nodes)) { + foreach ($this->nodes as $node) { + foreach (explode("\n", (string) $node) as $line) { + $repr[] = ' ' . $line; + } + } + $repr[] = ')'; + } else { + $repr[0] .= ')'; + } + return implode("\n", $repr); + } + public function compile(Compiler $compiler) + { + foreach ($this->nodes as $node) { + $node->compile($compiler); + } + } + public function evaluate(array $functions, array $values) + { + $results = []; + foreach ($this->nodes as $node) { + $results[] = $node->evaluate($functions, $values); + } + return $results; + } + public function toArray() + { + throw new \BadMethodCallException(sprintf('Dumping a "%s" instance is not supported yet.', static::class)); + } + public function dump() + { + $dump = ''; + foreach ($this->toArray() as $v) { + $dump .= \is_scalar($v) ? $v : $v->dump(); + } + return $dump; + } + protected function dumpString(string $value) + { + return sprintf('"%s"', addcslashes($value, "\x00\t\"\\")); + } + protected function isHash(array $value) + { + $expectedKey = 0; + foreach ($value as $key => $val) { + if ($key !== $expectedKey++) { + return \true; + } + } + return \false; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Node/UnaryNode.php b/src/modules/common/third-party/vendor/symfony/expression-language/Node/UnaryNode.php new file mode 100644 index 0000000000..3370cfd1d3 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Node/UnaryNode.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Compiler; +/** + * @author Fabien Potencier + * + * @internal + */ +class UnaryNode extends Node +{ + private const OPERATORS = ['!' => '!', 'not' => '!', '+' => '+', '-' => '-']; + public function __construct(string $operator, Node $node) + { + parent::__construct(['node' => $node], ['operator' => $operator]); + } + public function compile(Compiler $compiler) + { + $compiler->raw('(')->raw(self::OPERATORS[$this->attributes['operator']])->compile($this->nodes['node'])->raw(')'); + } + public function evaluate(array $functions, array $values) + { + $value = $this->nodes['node']->evaluate($functions, $values); + switch ($this->attributes['operator']) { + case 'not': + case '!': + return !$value; + case '-': + return -$value; + } + return $value; + } + public function toArray(): array + { + return ['(', $this->attributes['operator'] . ' ', $this->nodes['node'], ')']; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/ParsedExpression.php b/src/modules/common/third-party/vendor/symfony/expression-language/ParsedExpression.php new file mode 100644 index 0000000000..a0eeab1868 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/ParsedExpression.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +use Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage\Node\Node; +/** + * Represents an already parsed expression. + * + * @author Fabien Potencier + */ +class ParsedExpression extends Expression +{ + private $nodes; + public function __construct(string $expression, Node $nodes) + { + parent::__construct($expression); + $this->nodes = $nodes; + } + public function getNodes() + { + return $this->nodes; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Parser.php b/src/modules/common/third-party/vendor/symfony/expression-language/Parser.php new file mode 100644 index 0000000000..a75076eff7 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Parser.php @@ -0,0 +1,302 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * Parsers a token stream. + * + * This parser implements a "Precedence climbing" algorithm. + * + * @see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm + * @see http://en.wikipedia.org/wiki/Operator-precedence_parser + * + * @author Fabien Potencier + */ +class Parser +{ + public const OPERATOR_LEFT = 1; + public const OPERATOR_RIGHT = 2; + private $stream; + private $unaryOperators; + private $binaryOperators; + private $functions; + private $names; + private $lint; + public function __construct(array $functions) + { + $this->functions = $functions; + $this->unaryOperators = ['not' => ['precedence' => 50], '!' => ['precedence' => 50], '-' => ['precedence' => 500], '+' => ['precedence' => 500]]; + $this->binaryOperators = ['or' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT], '||' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT], 'and' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT], '&&' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT], '|' => ['precedence' => 16, 'associativity' => self::OPERATOR_LEFT], '^' => ['precedence' => 17, 'associativity' => self::OPERATOR_LEFT], '&' => ['precedence' => 18, 'associativity' => self::OPERATOR_LEFT], '==' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '===' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '!=' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '!==' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '<' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '>' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '>=' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '<=' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], 'not in' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], 'in' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], 'matches' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT], '..' => ['precedence' => 25, 'associativity' => self::OPERATOR_LEFT], '+' => ['precedence' => 30, 'associativity' => self::OPERATOR_LEFT], '-' => ['precedence' => 30, 'associativity' => self::OPERATOR_LEFT], '~' => ['precedence' => 40, 'associativity' => self::OPERATOR_LEFT], '*' => ['precedence' => 60, 'associativity' => self::OPERATOR_LEFT], '/' => ['precedence' => 60, 'associativity' => self::OPERATOR_LEFT], '%' => ['precedence' => 60, 'associativity' => self::OPERATOR_LEFT], '**' => ['precedence' => 200, 'associativity' => self::OPERATOR_RIGHT]]; + } + /** + * Converts a token stream to a node tree. + * + * The valid names is an array where the values + * are the names that the user can use in an expression. + * + * If the variable name in the compiled PHP code must be + * different, define it as the key. + * + * For instance, ['this' => 'container'] means that the + * variable 'container' can be used in the expression + * but the compiled code will use 'this'. + * + * @return Node\Node + * + * @throws SyntaxError + */ + public function parse(TokenStream $stream, array $names = []) + { + $this->lint = \false; + return $this->doParse($stream, $names); + } + /** + * Validates the syntax of an expression. + * + * The syntax of the passed expression will be checked, but not parsed. + * If you want to skip checking dynamic variable names, pass `null` instead of the array. + * + * @throws SyntaxError When the passed expression is invalid + */ + public function lint(TokenStream $stream, ?array $names = []): void + { + $this->lint = \true; + $this->doParse($stream, $names); + } + /** + * @throws SyntaxError + */ + private function doParse(TokenStream $stream, ?array $names = []): Node\Node + { + $this->stream = $stream; + $this->names = $names; + $node = $this->parseExpression(); + if (!$stream->isEOF()) { + throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', $stream->current->type, $stream->current->value), $stream->current->cursor, $stream->getExpression()); + } + $this->stream = null; + $this->names = null; + return $node; + } + public function parseExpression(int $precedence = 0) + { + $expr = $this->getPrimary(); + $token = $this->stream->current; + while ($token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->value]) && $this->binaryOperators[$token->value]['precedence'] >= $precedence) { + $op = $this->binaryOperators[$token->value]; + $this->stream->next(); + $expr1 = $this->parseExpression((self::OPERATOR_LEFT === $op['associativity']) ? $op['precedence'] + 1 : $op['precedence']); + $expr = new Node\BinaryNode($token->value, $expr, $expr1); + $token = $this->stream->current; + } + if (0 === $precedence) { + return $this->parseConditionalExpression($expr); + } + return $expr; + } + protected function getPrimary() + { + $token = $this->stream->current; + if ($token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->value])) { + $operator = $this->unaryOperators[$token->value]; + $this->stream->next(); + $expr = $this->parseExpression($operator['precedence']); + return $this->parsePostfixExpression(new Node\UnaryNode($token->value, $expr)); + } + if ($token->test(Token::PUNCTUATION_TYPE, '(')) { + $this->stream->next(); + $expr = $this->parseExpression(); + $this->stream->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); + return $this->parsePostfixExpression($expr); + } + return $this->parsePrimaryExpression(); + } + protected function parseConditionalExpression(Node\Node $expr) + { + while ($this->stream->current->test(Token::PUNCTUATION_TYPE, '?')) { + $this->stream->next(); + if (!$this->stream->current->test(Token::PUNCTUATION_TYPE, ':')) { + $expr2 = $this->parseExpression(); + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, ':')) { + $this->stream->next(); + $expr3 = $this->parseExpression(); + } else { + $expr3 = new Node\ConstantNode(null); + } + } else { + $this->stream->next(); + $expr2 = $expr; + $expr3 = $this->parseExpression(); + } + $expr = new Node\ConditionalNode($expr, $expr2, $expr3); + } + return $expr; + } + public function parsePrimaryExpression() + { + $token = $this->stream->current; + switch ($token->type) { + case Token::NAME_TYPE: + $this->stream->next(); + switch ($token->value) { + case 'true': + case 'TRUE': + return new Node\ConstantNode(\true); + case 'false': + case 'FALSE': + return new Node\ConstantNode(\false); + case 'null': + case 'NULL': + return new Node\ConstantNode(null); + default: + if ('(' === $this->stream->current->value) { + if (\false === isset($this->functions[$token->value])) { + throw new SyntaxError(sprintf('The function "%s" does not exist.', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, array_keys($this->functions)); + } + $node = new Node\FunctionNode($token->value, $this->parseArguments()); + } else { + if (!$this->lint || \is_array($this->names)) { + if (!\in_array($token->value, $this->names, \true)) { + throw new SyntaxError(sprintf('Variable "%s" is not valid.', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, $this->names); + } + // is the name used in the compiled code different + // from the name used in the expression? + if (\is_int($name = array_search($token->value, $this->names))) { + $name = $token->value; + } + } else { + $name = $token->value; + } + $node = new Node\NameNode($name); + } + } + break; + case Token::NUMBER_TYPE: + case Token::STRING_TYPE: + $this->stream->next(); + return new Node\ConstantNode($token->value); + default: + if ($token->test(Token::PUNCTUATION_TYPE, '[')) { + $node = $this->parseArrayExpression(); + } elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) { + $node = $this->parseHashExpression(); + } else { + throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', $token->type, $token->value), $token->cursor, $this->stream->getExpression()); + } + } + return $this->parsePostfixExpression($node); + } + public function parseArrayExpression() + { + $this->stream->expect(Token::PUNCTUATION_TYPE, '[', 'An array element was expected'); + $node = new Node\ArrayNode(); + $first = \true; + while (!$this->stream->current->test(Token::PUNCTUATION_TYPE, ']')) { + if (!$first) { + $this->stream->expect(Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma'); + // trailing ,? + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, ']')) { + break; + } + } + $first = \false; + $node->addElement($this->parseExpression()); + } + $this->stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed'); + return $node; + } + public function parseHashExpression() + { + $this->stream->expect(Token::PUNCTUATION_TYPE, '{', 'A hash element was expected'); + $node = new Node\ArrayNode(); + $first = \true; + while (!$this->stream->current->test(Token::PUNCTUATION_TYPE, '}')) { + if (!$first) { + $this->stream->expect(Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma'); + // trailing ,? + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, '}')) { + break; + } + } + $first = \false; + // a hash key can be: + // + // * a number -- 12 + // * a string -- 'a' + // * a name, which is equivalent to a string -- a + // * an expression, which must be enclosed in parentheses -- (1 + 2) + if ($this->stream->current->test(Token::STRING_TYPE) || $this->stream->current->test(Token::NAME_TYPE) || $this->stream->current->test(Token::NUMBER_TYPE)) { + $key = new Node\ConstantNode($this->stream->current->value); + $this->stream->next(); + } elseif ($this->stream->current->test(Token::PUNCTUATION_TYPE, '(')) { + $key = $this->parseExpression(); + } else { + $current = $this->stream->current; + throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', $current->type, $current->value), $current->cursor, $this->stream->getExpression()); + } + $this->stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); + $value = $this->parseExpression(); + $node->addElement($value, $key); + } + $this->stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed'); + return $node; + } + public function parsePostfixExpression(Node\Node $node) + { + $token = $this->stream->current; + while (Token::PUNCTUATION_TYPE == $token->type) { + if ('.' === $token->value) { + $this->stream->next(); + $token = $this->stream->current; + $this->stream->next(); + if (Token::NAME_TYPE !== $token->type && (Token::OPERATOR_TYPE !== $token->type || !preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $token->value))) { + throw new SyntaxError('Expected name.', $token->cursor, $this->stream->getExpression()); + } + $arg = new Node\ConstantNode($token->value, \true); + $arguments = new Node\ArgumentsNode(); + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, '(')) { + $type = Node\GetAttrNode::METHOD_CALL; + foreach ($this->parseArguments()->nodes as $n) { + $arguments->addElement($n); + } + } else { + $type = Node\GetAttrNode::PROPERTY_CALL; + } + $node = new Node\GetAttrNode($node, $arg, $arguments, $type); + } elseif ('[' === $token->value) { + $this->stream->next(); + $arg = $this->parseExpression(); + $this->stream->expect(Token::PUNCTUATION_TYPE, ']'); + $node = new Node\GetAttrNode($node, $arg, new Node\ArgumentsNode(), Node\GetAttrNode::ARRAY_CALL); + } else { + break; + } + $token = $this->stream->current; + } + return $node; + } + /** + * Parses arguments. + */ + public function parseArguments() + { + $args = []; + $this->stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); + while (!$this->stream->current->test(Token::PUNCTUATION_TYPE, ')')) { + if (!empty($args)) { + $this->stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); + } + $args[] = $this->parseExpression(); + } + $this->stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); + return new Node\Node($args); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Resources/bin/generate_operator_regex.php b/src/modules/common/third-party/vendor/symfony/expression-language/Resources/bin/generate_operator_regex.php new file mode 100644 index 0000000000..d3eb1abeac --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Resources/bin/generate_operator_regex.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if ('cli' !== \PHP_SAPI) { + throw new \Exception('This script must be run from the command line.'); +} +$operators = ['not', '!', 'or', '||', '&&', 'and', '|', '^', '&', '==', '===', '!=', '!==', '<', '>', '>=', '<=', 'not in', 'in', '..', '+', '-', '~', '*', '/', '%', 'matches', '**']; +$operators = \array_combine($operators, \array_map('strlen', $operators)); +\arsort($operators); +$regex = []; +foreach ($operators as $operator => $length) { + // Collisions of character operators: + // - an operator that begins with a character must have a space or a parenthesis before or starting at the beginning of a string + // - an operator that ends with a character must be followed by a whitespace or a parenthesis + $regex[] = (\ctype_alpha($operator[0]) ? '(?<=^|[\s(])' : '') . \preg_quote($operator, '/') . (\ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : ''); +} +echo '/' . \implode('|', $regex) . '/A'; diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/SerializedParsedExpression.php b/src/modules/common/third-party/vendor/symfony/expression-language/SerializedParsedExpression.php new file mode 100644 index 0000000000..75a75f66b3 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/SerializedParsedExpression.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * Represents an already parsed expression. + * + * @author Fabien Potencier + */ +class SerializedParsedExpression extends ParsedExpression +{ + private $nodes; + /** + * @param string $expression An expression + * @param string $nodes The serialized nodes for the expression + */ + public function __construct(string $expression, string $nodes) + { + $this->expression = $expression; + $this->nodes = $nodes; + } + public function getNodes() + { + return unserialize($this->nodes); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/SyntaxError.php b/src/modules/common/third-party/vendor/symfony/expression-language/SyntaxError.php new file mode 100644 index 0000000000..f996fd0b4a --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/SyntaxError.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +class SyntaxError extends \LogicException +{ + public function __construct(string $message, int $cursor = 0, string $expression = '', ?string $subject = null, ?array $proposals = null) + { + $message = sprintf('%s around position %d', rtrim($message, '.'), $cursor); + if ($expression) { + $message = sprintf('%s for expression `%s`', $message, $expression); + } + $message .= '.'; + if (null !== $subject && null !== $proposals) { + $minScore = \INF; + foreach ($proposals as $proposal) { + $distance = levenshtein($subject, $proposal); + if ($distance < $minScore) { + $guess = $proposal; + $minScore = $distance; + } + } + if (isset($guess) && $minScore < 3) { + $message .= sprintf(' Did you mean "%s"?', $guess); + } + } + parent::__construct($message); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/Token.php b/src/modules/common/third-party/vendor/symfony/expression-language/Token.php new file mode 100644 index 0000000000..56d0acf073 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/Token.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * Represents a Token. + * + * @author Fabien Potencier + */ +class Token +{ + public $value; + public $type; + public $cursor; + public const EOF_TYPE = 'end of expression'; + public const NAME_TYPE = 'name'; + public const NUMBER_TYPE = 'number'; + public const STRING_TYPE = 'string'; + public const OPERATOR_TYPE = 'operator'; + public const PUNCTUATION_TYPE = 'punctuation'; + /** + * @param string $type The type of the token (self::*_TYPE) + * @param string|int|float|null $value The token value + * @param int|null $cursor The cursor position in the source + */ + public function __construct(string $type, $value, ?int $cursor) + { + $this->type = $type; + $this->value = $value; + $this->cursor = $cursor; + } + /** + * Returns a string representation of the token. + * + * @return string + */ + public function __toString() + { + return sprintf('%3d %-11s %s', $this->cursor, strtoupper($this->type), $this->value); + } + /** + * Tests the current token for a type and/or a value. + * + * @return bool + */ + public function test(string $type, ?string $value = null) + { + return $this->type === $type && (null === $value || $this->value == $value); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/TokenStream.php b/src/modules/common/third-party/vendor/symfony/expression-language/TokenStream.php new file mode 100644 index 0000000000..a9ccb5af20 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/TokenStream.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\ExpressionLanguage; + +/** + * Represents a token stream. + * + * @author Fabien Potencier + */ +class TokenStream +{ + public $current; + private $tokens; + private $position = 0; + private $expression; + public function __construct(array $tokens, string $expression = '') + { + $this->tokens = $tokens; + $this->current = $tokens[0]; + $this->expression = $expression; + } + /** + * Returns a string representation of the token stream. + * + * @return string + */ + public function __toString() + { + return implode("\n", $this->tokens); + } + /** + * Sets the pointer to the next token and returns the old one. + */ + public function next() + { + ++$this->position; + if (!isset($this->tokens[$this->position])) { + throw new SyntaxError('Unexpected end of expression.', $this->current->cursor, $this->expression); + } + $this->current = $this->tokens[$this->position]; + } + /** + * @param string|null $message The syntax error message + */ + public function expect(string $type, ?string $value = null, ?string $message = null) + { + $token = $this->current; + if (!$token->test($type, $value)) { + throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s).', $message ? $message . '. ' : '', $token->type, $token->value, $type, $value ? sprintf(' with value "%s"', $value) : ''), $token->cursor, $this->expression); + } + $this->next(); + } + /** + * Checks if end of stream was reached. + * + * @return bool + */ + public function isEOF() + { + return Token::EOF_TYPE === $this->current->type; + } + /** + * @internal + */ + public function getExpression(): string + { + return $this->expression; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/expression-language/composer.json b/src/modules/common/third-party/vendor/symfony/expression-language/composer.json new file mode 100644 index 0000000000..b1869992f5 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/expression-language/composer.json @@ -0,0 +1,32 @@ +{ + "name": "symfony\/expression-language", + "type": "library", + "description": "Provides an engine that can compile and evaluate expressions", + "keywords": [], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony\/cache": "^4.4|^5.0|^6.0", + "symfony\/service-contracts": "^1.1|^2|^3" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Component\\ExpressionLanguage\\": "" + }, + "exclude-from-classmap": [ + "\/Tests\/" + ] + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/filesystem/Exception/FileNotFoundException.php b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/FileNotFoundException.php index 6b9bf4274c..17534f4b2e 100644 --- a/src/modules/common/third-party/vendor/symfony/filesystem/Exception/FileNotFoundException.php +++ b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/FileNotFoundException.php @@ -18,13 +18,13 @@ */ class FileNotFoundException extends IOException { - public function __construct(string $message = null, int $code = 0, \Throwable $previous = null, string $path = null) + public function __construct(?string $message = null, int $code = 0, ?\Throwable $previous = null, ?string $path = null) { if (null === $message) { if (null === $path) { $message = 'File could not be found.'; } else { - $message = \sprintf('File "%s" could not be found.', $path); + $message = sprintf('File "%s" could not be found.', $path); } } parent::__construct($message, $code, $previous, $path); diff --git a/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOException.php b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOException.php index 758d90906d..11b9aaf711 100644 --- a/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOException.php +++ b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOException.php @@ -20,7 +20,7 @@ class IOException extends \RuntimeException implements IOExceptionInterface { private $path; - public function __construct(string $message, int $code = 0, \Throwable $previous = null, string $path = null) + public function __construct(string $message, int $code = 0, ?\Throwable $previous = null, ?string $path = null) { $this->path = $path; parent::__construct($message, $code, $previous); diff --git a/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOExceptionInterface.php b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOExceptionInterface.php index a443bf29a9..2feb81e83c 100644 --- a/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOExceptionInterface.php +++ b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/IOExceptionInterface.php @@ -20,7 +20,7 @@ interface IOExceptionInterface extends ExceptionInterface /** * Returns the associated path for the exception. * - * @return string|null The path + * @return string|null */ public function getPath(); } diff --git a/src/modules/common/third-party/vendor/symfony/filesystem/Exception/RuntimeException.php b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/RuntimeException.php new file mode 100644 index 0000000000..c7a2addecc --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/filesystem/Exception/RuntimeException.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Filesystem\Exception; + +/** + * @author Théo Fidry + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/modules/common/third-party/vendor/symfony/filesystem/Filesystem.php b/src/modules/common/third-party/vendor/symfony/filesystem/Filesystem.php index 60aa8718b9..ba8641bab5 100644 --- a/src/modules/common/third-party/vendor/symfony/filesystem/Filesystem.php +++ b/src/modules/common/third-party/vendor/symfony/filesystem/Filesystem.php @@ -28,45 +28,43 @@ class Filesystem * If the target file is newer, it is overwritten only when the * $overwriteNewerFiles option is set to true. * - * @param string $originFile The original filename - * @param string $targetFile The target filename - * @param bool $overwriteNewerFiles If true, target files newer than origin files are overwritten - * * @throws FileNotFoundException When originFile doesn't exist * @throws IOException When copy fails */ - public function copy($originFile, $targetFile, $overwriteNewerFiles = \false) + public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = \false) { - $originIsLocal = \stream_is_local($originFile) || 0 === \stripos($originFile, 'file://'); - if ($originIsLocal && !\is_file($originFile)) { - throw new FileNotFoundException(\sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); + $originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://'); + if ($originIsLocal && !is_file($originFile)) { + throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); } $this->mkdir(\dirname($targetFile)); $doCopy = \true; - if (!$overwriteNewerFiles && null === \parse_url($originFile, \PHP_URL_HOST) && \is_file($targetFile)) { - $doCopy = \filemtime($originFile) > \filemtime($targetFile); + if (!$overwriteNewerFiles && null === parse_url($originFile, \PHP_URL_HOST) && is_file($targetFile)) { + $doCopy = filemtime($originFile) > filemtime($targetFile); } if ($doCopy) { // https://bugs.php.net/64634 - if (\false === ($source = @\fopen($originFile, 'r'))) { - throw new IOException(\sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading.', $originFile, $targetFile), 0, null, $originFile); + if (!$source = self::box('fopen', $originFile, 'r')) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading: ', $originFile, $targetFile) . self::$lastError, 0, null, $originFile); } // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default - if (\false === ($target = @\fopen($targetFile, 'w', \false, \stream_context_create(['ftp' => ['overwrite' => \true]])))) { - throw new IOException(\sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing.', $originFile, $targetFile), 0, null, $originFile); + if (!$target = self::box('fopen', $targetFile, 'w', \false, stream_context_create(['ftp' => ['overwrite' => \true]]))) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing: ', $originFile, $targetFile) . self::$lastError, 0, null, $originFile); } - $bytesCopied = \stream_copy_to_stream($source, $target); - \fclose($source); - \fclose($target); + $bytesCopied = stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); unset($source, $target); - if (!\is_file($targetFile)) { - throw new IOException(\sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); + if (!is_file($targetFile)) { + throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); } if ($originIsLocal) { // Like `cp`, preserve executable permission bits - @\chmod($targetFile, \fileperms($targetFile) | \fileperms($originFile) & 0111); - if ($bytesCopied !== ($bytesOrigin = \filesize($originFile))) { - throw new IOException(\sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + self::box('chmod', $targetFile, fileperms($targetFile) | fileperms($originFile) & 0111); + // Like `cp`, preserve the file modification time + self::box('touch', $targetFile, filemtime($originFile)); + if ($bytesCopied !== $bytesOrigin = filesize($originFile)) { + throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); } } } @@ -75,24 +73,17 @@ public function copy($originFile, $targetFile, $overwriteNewerFiles = \false) * Creates a directory recursively. * * @param string|iterable $dirs The directory path - * @param int $mode The directory mode * * @throws IOException On any directory creation failure */ - public function mkdir($dirs, $mode = 0777) + public function mkdir($dirs, int $mode = 0777) { foreach ($this->toIterable($dirs) as $dir) { - if (\is_dir($dir)) { + if (is_dir($dir)) { continue; } - if (!self::box('mkdir', $dir, $mode, \true)) { - if (!\is_dir($dir)) { - // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one - if (self::$lastError) { - throw new IOException(\sprintf('Failed to create "%s": ', $dir) . self::$lastError, 0, null, $dir); - } - throw new IOException(\sprintf('Failed to create "%s".', $dir), 0, null, $dir); - } + if (!self::box('mkdir', $dir, $mode, \true) && !is_dir($dir)) { + throw new IOException(sprintf('Failed to create "%s": ', $dir) . self::$lastError, 0, null, $dir); } } } @@ -101,16 +92,16 @@ public function mkdir($dirs, $mode = 0777) * * @param string|iterable $files A filename, an array of files, or a \Traversable instance to check * - * @return bool true if the file exists, false otherwise + * @return bool */ public function exists($files) { $maxPathLength = \PHP_MAXPATHLEN - 2; foreach ($this->toIterable($files) as $file) { if (\strlen($file) > $maxPathLength) { - throw new IOException(\sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file); + throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file); } - if (!\file_exists($file)) { + if (!file_exists($file)) { return \false; } } @@ -125,12 +116,11 @@ public function exists($files) * * @throws IOException When touch fails */ - public function touch($files, $time = null, $atime = null) + public function touch($files, ?int $time = null, ?int $atime = null) { foreach ($this->toIterable($files) as $file) { - $touch = $time ? @\touch($file, $time, $atime) : @\touch($file); - if (\true !== $touch) { - throw new IOException(\sprintf('Failed to touch "%s".', $file), 0, null, $file); + if (!($time ? self::box('touch', $file, $time, $atime) : self::box('touch', $file))) { + throw new IOException(sprintf('Failed to touch "%s": ', $file) . self::$lastError, 0, null, $file); } } } @@ -144,24 +134,48 @@ public function touch($files, $time = null, $atime = null) public function remove($files) { if ($files instanceof \Traversable) { - $files = \iterator_to_array($files, \false); + $files = iterator_to_array($files, \false); } elseif (!\is_array($files)) { $files = [$files]; } - $files = \array_reverse($files); + self::doRemove($files, \false); + } + private static function doRemove(array $files, bool $isRecursive): void + { + $files = array_reverse($files); foreach ($files as $file) { - if (\is_link($file)) { + if (is_link($file)) { // See https://bugs.php.net/52176 - if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && \file_exists($file)) { - throw new IOException(\sprintf('Failed to remove symlink "%s": ', $file) . self::$lastError); + if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove symlink "%s": ', $file) . self::$lastError); } - } elseif (\is_dir($file)) { - $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); - if (!self::box('rmdir', $file) && \file_exists($file)) { - throw new IOException(\sprintf('Failed to remove directory "%s": ', $file) . self::$lastError); + } elseif (is_dir($file)) { + if (!$isRecursive) { + $tmpName = \dirname(realpath($file)) . '/.!' . strrev(strtr(base64_encode(random_bytes(2)), '/=', '-!')); + if (file_exists($tmpName)) { + try { + self::doRemove([$tmpName], \true); + } catch (IOException $e) { + } + } + if (!file_exists($tmpName) && self::box('rename', $file, $tmpName)) { + $origFile = $file; + $file = $tmpName; + } else { + $origFile = null; + } + } + $files = new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS); + self::doRemove(iterator_to_array($files, \true), \true); + if (!self::box('rmdir', $file) && file_exists($file) && !$isRecursive) { + $lastError = self::$lastError; + if (null !== $origFile && self::box('rename', $file, $origFile)) { + $file = $origFile; + } + throw new IOException(sprintf('Failed to remove directory "%s": ', $file) . $lastError); } - } elseif (!self::box('unlink', $file) && (\str_contains(self::$lastError, 'Permission denied') || \file_exists($file))) { - throw new IOException(\sprintf('Failed to remove file "%s": ', $file) . self::$lastError); + } elseif (!self::box('unlink', $file) && (self::$lastError && str_contains(self::$lastError, 'Permission denied') || file_exists($file))) { + throw new IOException(sprintf('Failed to remove file "%s": ', $file) . self::$lastError); } } } @@ -175,13 +189,13 @@ public function remove($files) * * @throws IOException When the change fails */ - public function chmod($files, $mode, $umask = 00, $recursive = \false) + public function chmod($files, int $mode, int $umask = 00, bool $recursive = \false) { foreach ($this->toIterable($files) as $file) { - if ((\PHP_VERSION_ID < 80000 || \is_int($mode)) && \true !== @\chmod($file, $mode & ~$umask)) { - throw new IOException(\sprintf('Failed to chmod file "%s".', $file), 0, null, $file); + if ((\PHP_VERSION_ID < 80000 || \is_int($mode)) && !self::box('chmod', $file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file "%s": ', $file) . self::$lastError, 0, null, $file); } - if ($recursive && \is_dir($file) && !\is_link($file)) { + if ($recursive && is_dir($file) && !is_link($file)) { $this->chmod(new \FilesystemIterator($file), $mode, $umask, \true); } } @@ -189,79 +203,77 @@ public function chmod($files, $mode, $umask = 00, $recursive = \false) /** * Change the owner of an array of files or directories. * + * This method always throws on Windows, as the underlying PHP function is not supported. + * @see https://www.php.net/chown + * * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner * @param string|int $user A user name or number * @param bool $recursive Whether change the owner recursively or not * * @throws IOException When the change fails */ - public function chown($files, $user, $recursive = \false) + public function chown($files, $user, bool $recursive = \false) { foreach ($this->toIterable($files) as $file) { - if ($recursive && \is_dir($file) && !\is_link($file)) { + if ($recursive && is_dir($file) && !is_link($file)) { $this->chown(new \FilesystemIterator($file), $user, \true); } - if (\is_link($file) && \function_exists('lchown')) { - if (\true !== @\lchown($file, $user)) { - throw new IOException(\sprintf('Failed to chown file "%s".', $file), 0, null, $file); - } - } else { - if (\true !== @\chown($file, $user)) { - throw new IOException(\sprintf('Failed to chown file "%s".', $file), 0, null, $file); + if (is_link($file) && \function_exists('lchown')) { + if (!self::box('lchown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file) . self::$lastError, 0, null, $file); } + } else if (!self::box('chown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file) . self::$lastError, 0, null, $file); } } } /** * Change the group of an array of files or directories. * + * This method always throws on Windows, as the underlying PHP function is not supported. + * @see https://www.php.net/chgrp + * * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group * @param string|int $group A group name or number * @param bool $recursive Whether change the group recursively or not * * @throws IOException When the change fails */ - public function chgrp($files, $group, $recursive = \false) + public function chgrp($files, $group, bool $recursive = \false) { foreach ($this->toIterable($files) as $file) { - if ($recursive && \is_dir($file) && !\is_link($file)) { + if ($recursive && is_dir($file) && !is_link($file)) { $this->chgrp(new \FilesystemIterator($file), $group, \true); } - if (\is_link($file) && \function_exists('lchgrp')) { - if (\true !== @\lchgrp($file, $group)) { - throw new IOException(\sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); - } - } else { - if (\true !== @\chgrp($file, $group)) { - throw new IOException(\sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + if (is_link($file) && \function_exists('lchgrp')) { + if (!self::box('lchgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file) . self::$lastError, 0, null, $file); } + } else if (!self::box('chgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file) . self::$lastError, 0, null, $file); } } } /** * Renames a file or a directory. * - * @param string $origin The origin filename or directory - * @param string $target The new filename or directory - * @param bool $overwrite Whether to overwrite the target if it already exists - * * @throws IOException When target file or directory already exists * @throws IOException When origin cannot be renamed */ - public function rename($origin, $target, $overwrite = \false) + public function rename(string $origin, string $target, bool $overwrite = \false) { // we check that target does not exist if (!$overwrite && $this->isReadable($target)) { - throw new IOException(\sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); + throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); } - if (\true !== @\rename($origin, $target)) { - if (\is_dir($origin)) { + if (!self::box('rename', $origin, $target)) { + if (is_dir($origin)) { // See https://bugs.php.net/54097 & https://php.net/rename#113943 $this->mirror($origin, $target, null, ['override' => $overwrite, 'delete' => $overwrite]); $this->remove($origin); return; } - throw new IOException(\sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target); + throw new IOException(sprintf('Cannot rename "%s" to "%s": ', $origin, $target) . self::$lastError, 0, null, $target); } } /** @@ -269,37 +281,33 @@ public function rename($origin, $target, $overwrite = \false) * * @throws IOException When windows path is longer than 258 characters */ - private function isReadable(string $filename) : bool + private function isReadable(string $filename): bool { $maxPathLength = \PHP_MAXPATHLEN - 2; if (\strlen($filename) > $maxPathLength) { - throw new IOException(\sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename); + throw new IOException(sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename); } - return \is_readable($filename); + return is_readable($filename); } /** * Creates a symbolic link or copy a directory. * - * @param string $originDir The origin directory path - * @param string $targetDir The symbolic link name - * @param bool $copyOnWindows Whether to copy files if on Windows - * * @throws IOException When symlink fails */ - public function symlink($originDir, $targetDir, $copyOnWindows = \false) + public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = \false) { self::assertFunctionExists('symlink'); if ('\\' === \DIRECTORY_SEPARATOR) { - $originDir = \strtr($originDir, '/', '\\'); - $targetDir = \strtr($targetDir, '/', '\\'); + $originDir = strtr($originDir, '/', '\\'); + $targetDir = strtr($targetDir, '/', '\\'); if ($copyOnWindows) { $this->mirror($originDir, $targetDir); return; } } $this->mkdir(\dirname($targetDir)); - if (\is_link($targetDir)) { - if (\readlink($targetDir) === $originDir) { + if (is_link($targetDir)) { + if (readlink($targetDir) === $originDir) { return; } $this->remove($targetDir); @@ -311,24 +319,23 @@ public function symlink($originDir, $targetDir, $copyOnWindows = \false) /** * Creates a hard link, or several hard links to a file. * - * @param string $originFile The original file * @param string|string[] $targetFiles The target file(s) * * @throws FileNotFoundException When original file is missing or not a file * @throws IOException When link fails, including if link already exists */ - public function hardlink($originFile, $targetFiles) + public function hardlink(string $originFile, $targetFiles) { self::assertFunctionExists('link'); if (!$this->exists($originFile)) { throw new FileNotFoundException(null, 0, null, $originFile); } - if (!\is_file($originFile)) { - throw new FileNotFoundException(\sprintf('Origin file "%s" is not a file.', $originFile)); + if (!is_file($originFile)) { + throw new FileNotFoundException(sprintf('Origin file "%s" is not a file.', $originFile)); } foreach ($this->toIterable($targetFiles) as $targetFile) { - if (\is_file($targetFile)) { - if (\fileinode($originFile) === \fileinode($targetFile)) { + if (is_file($targetFile)) { + if (fileinode($originFile) === fileinode($targetFile)) { continue; } $this->remove($targetFile); @@ -344,11 +351,11 @@ public function hardlink($originFile, $targetFiles) private function linkException(string $origin, string $target, string $linkType) { if (self::$lastError) { - if ('\\' === \DIRECTORY_SEPARATOR && \str_contains(self::$lastError, 'error code(1314)')) { - throw new IOException(\sprintf('Unable to create "%s" link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); + if ('\\' === \DIRECTORY_SEPARATOR && str_contains(self::$lastError, 'error code(1314)')) { + throw new IOException(sprintf('Unable to create "%s" link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); } } - throw new IOException(\sprintf('Failed to create "%s" link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target); + throw new IOException(sprintf('Failed to create "%s" link from "%s" to "%s": ', $linkType, $origin, $target) . self::$lastError, 0, null, $target); } /** * Resolves links in paths. @@ -361,14 +368,11 @@ private function linkException(string $origin, string $target, string $linkType) * - if $path does not exist, returns null * - if $path exists, returns its absolute fully resolved final version * - * @param string $path A filesystem path - * @param bool $canonicalize Whether or not to return a canonicalized path - * * @return string|null */ - public function readlink($path, $canonicalize = \false) + public function readlink(string $path, bool $canonicalize = \false) { - if (!$canonicalize && !\is_link($path)) { + if (!$canonicalize && !is_link($path)) { return null; } if ($canonicalize) { @@ -376,44 +380,41 @@ public function readlink($path, $canonicalize = \false) return null; } if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70410) { - $path = \readlink($path); + $path = readlink($path); } - return \realpath($path); + return realpath($path); } if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70400) { - return \realpath($path); + return realpath($path); } - return \readlink($path); + return readlink($path); } /** * Given an existing path, convert it to a path relative to a given starting path. * - * @param string $endPath Absolute path of target - * @param string $startPath Absolute path where traversal begins - * - * @return string Path of target relative to starting path + * @return string */ - public function makePathRelative($endPath, $startPath) + public function makePathRelative(string $endPath, string $startPath) { if (!$this->isAbsolutePath($startPath)) { - throw new InvalidArgumentException(\sprintf('The start path "%s" is not absolute.', $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)); + throw new InvalidArgumentException(sprintf('The end path "%s" is not absolute.', $endPath)); } // Normalize separators on Windows if ('\\' === \DIRECTORY_SEPARATOR) { - $endPath = \str_replace('\\', '/', $endPath); - $startPath = \str_replace('\\', '/', $startPath); + $endPath = str_replace('\\', '/', $endPath); + $startPath = str_replace('\\', '/', $startPath); } $splitDriveLetter = function ($path) { - return \strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && \ctype_alpha($path[0]) ? [\substr($path, 2), \strtoupper($path[0])] : [$path, null]; + return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) ? [substr($path, 2), strtoupper($path[0])] : [$path, null]; }; $splitPath = function ($path) { $result = []; - foreach (\explode('/', \trim($path, '/')) as $segment) { + foreach (explode('/', trim($path, '/')) as $segment) { if ('..' === $segment) { - \array_pop($result); + array_pop($result); } elseif ('.' !== $segment && '' !== $segment) { $result[] = $segment; } @@ -426,7 +427,7 @@ public function makePathRelative($endPath, $startPath) $endPathArr = $splitPath($endPath); if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) { // End path is on another drive, so no relative path exists - return $endDriveLetter . ':/' . ($endPathArr ? \implode('/', $endPathArr) . '/' : ''); + return $endDriveLetter . ':/' . ($endPathArr ? implode('/', $endPathArr) . '/' : ''); } // Find for which directory the common path stops $index = 0; @@ -440,11 +441,11 @@ public function makePathRelative($endPath, $startPath) $depth = \count($startPathArr) - $index; } // Repeated "../" for each level need to reach the common path - $traverser = \str_repeat('../', $depth); - $endPathRemainder = \implode('/', \array_slice($endPathArr, $index)); + $traverser = str_repeat('../', $depth); + $endPathRemainder = implode('/', \array_slice($endPathArr, $index)); // Construct $endPath from traversing to the common path, then to the remaining $endPath - $relativePath = $traverser . ('' !== $endPathRemainder ? $endPathRemainder . '/' : ''); - return '' === $relativePath ? './' : $relativePath; + $relativePath = $traverser . (('' !== $endPathRemainder) ? $endPathRemainder . '/' : ''); + return ('' === $relativePath) ? './' : $relativePath; } /** * Mirrors a directory to another. @@ -454,24 +455,22 @@ public function makePathRelative($endPath, $startPath) * - existing files in the target directory will be overwritten, except if they are newer (see the `override` option) * - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option) * - * @param string $originDir The origin directory - * @param string $targetDir The target directory - * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created - * @param array $options An array of boolean options - * Valid options are: - * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) - * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) - * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) * * @throws IOException When file type is unknown */ - public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = []) + public function mirror(string $originDir, string $targetDir, ?\Traversable $iterator = null, array $options = []) { - $targetDir = \rtrim($targetDir, '/\\'); - $originDir = \rtrim($originDir, '/\\'); + $targetDir = rtrim($targetDir, '/\\'); + $originDir = rtrim($originDir, '/\\'); $originDirLen = \strlen($originDir); if (!$this->exists($originDir)) { - throw new IOException(\sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir); + throw new IOException(sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir); } // Iterate in destination folder to remove obsolete entries if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { @@ -482,7 +481,7 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o } $targetDirLen = \strlen($targetDir); foreach ($deleteIterator as $file) { - $origin = $originDir . \substr($file->getPathname(), $targetDirLen); + $origin = $originDir . substr($file->getPathname(), $targetDirLen); if (!$this->exists($origin)) { $this->remove($file); } @@ -499,143 +498,143 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || isset($filesCreatedWhileMirroring[$file->getRealPath()])) { continue; } - $target = $targetDir . \substr($file->getPathname(), $originDirLen); + $target = $targetDir . substr($file->getPathname(), $originDirLen); $filesCreatedWhileMirroring[$target] = \true; - if (!$copyOnWindows && \is_link($file)) { + if (!$copyOnWindows && is_link($file)) { $this->symlink($file->getLinkTarget(), $target); - } elseif (\is_dir($file)) { + } elseif (is_dir($file)) { $this->mkdir($target); - } elseif (\is_file($file)) { + } elseif (is_file($file)) { $this->copy($file, $target, $options['override'] ?? \false); } else { - throw new IOException(\sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); } } } /** * Returns whether the file path is an absolute path. * - * @param string $file A file path - * * @return bool */ - public function isAbsolutePath($file) + public function isAbsolutePath(string $file) { - if (null === $file) { - @\trigger_error(\sprintf('Calling "%s()" with a null in the $file argument is deprecated since Symfony 4.4.', __METHOD__), \E_USER_DEPRECATED); - } - return '' !== (string) $file && (\strspn($file, '/\\', 0, 1) || \strlen($file) > 3 && \ctype_alpha($file[0]) && ':' === $file[1] && \strspn($file, '/\\', 2, 1) || null !== \parse_url($file, \PHP_URL_SCHEME)); + return '' !== $file && (strspn($file, '/\\', 0, 1) || \strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1) || null !== parse_url($file, \PHP_URL_SCHEME)); } /** * Creates a temporary file with support for custom stream wrappers. * - * @param string $dir The directory where the temporary filename will be created * @param string $prefix The prefix of the generated temporary filename * Note: Windows uses only the first three characters of prefix + * @param string $suffix The suffix of the generated temporary filename * * @return string The new temporary filename (with path), or throw an exception on failure */ - public function tempnam($dir, $prefix) + public function tempnam(string $dir, string $prefix) { + $suffix = (\func_num_args() > 2) ? func_get_arg(2) : ''; [$scheme, $hierarchy] = $this->getSchemeAndHierarchy($dir); // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem - if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) { - $tmpFile = @\tempnam($hierarchy, $prefix); + if ((null === $scheme || 'file' === $scheme || 'gs' === $scheme) && '' === $suffix) { // If tempnam failed or no scheme return the filename otherwise prepend the scheme - if (\false !== $tmpFile) { + if ($tmpFile = self::box('tempnam', $hierarchy, $prefix)) { if (null !== $scheme && 'gs' !== $scheme) { return $scheme . '://' . $tmpFile; } return $tmpFile; } - throw new IOException('A temporary file could not be created.'); + throw new IOException('A temporary file could not be created: ' . self::$lastError); } // Loop until we create a valid temp file or have reached 10 attempts for ($i = 0; $i < 10; ++$i) { // Create a unique filename - $tmpFile = $dir . '/' . $prefix . \uniqid(\mt_rand(), \true); + $tmpFile = $dir . '/' . $prefix . uniqid(mt_rand(), \true) . $suffix; // Use fopen instead of file_exists as some streams do not support stat // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability - $handle = @\fopen($tmpFile, 'x+'); - // If unsuccessful restart the loop - if (\false === $handle) { + if (!$handle = self::box('fopen', $tmpFile, 'x+')) { continue; } // Close the file if it was successfully opened - @\fclose($handle); + self::box('fclose', $handle); return $tmpFile; } - throw new IOException('A temporary file could not be created.'); + throw new IOException('A temporary file could not be created: ' . self::$lastError); } /** * Atomically dumps content into a file. * - * @param string $filename The file to be written to - * @param string|resource $content The data to write into the file + * @param string|resource $content The data to write into the file * * @throws IOException if the file cannot be written to */ - public function dumpFile($filename, $content) + public function dumpFile(string $filename, $content) { if (\is_array($content)) { - @\trigger_error(\sprintf('Calling "%s()" with an array in the $content argument is deprecated since Symfony 4.3.', __METHOD__), \E_USER_DEPRECATED); + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); } $dir = \dirname($filename); - if (!\is_dir($dir)) { + if (is_link($filename) && $linkTarget = $this->readlink($filename)) { + $this->dumpFile(Path::makeAbsolute($linkTarget, $dir), $content); + return; + } + if (!is_dir($dir)) { $this->mkdir($dir); } // Will create a temp file with 0600 access rights // when the filesystem supports chmod. - $tmpFile = $this->tempnam($dir, \basename($filename)); + $tmpFile = $this->tempnam($dir, basename($filename)); try { - if (\false === @\file_put_contents($tmpFile, $content)) { - throw new IOException(\sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + if (\false === self::box('file_put_contents', $tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename) . self::$lastError, 0, null, $filename); } - @\chmod($tmpFile, \file_exists($filename) ? \fileperms($filename) : 0666 & ~\umask()); + self::box('chmod', $tmpFile, self::box('fileperms', $filename) ?: (0666 & ~umask())); $this->rename($tmpFile, $filename, \true); } finally { - if (\file_exists($tmpFile)) { - @\unlink($tmpFile); + if (file_exists($tmpFile)) { + if ('\\' === \DIRECTORY_SEPARATOR && !is_writable($tmpFile)) { + self::box('chmod', $tmpFile, self::box('fileperms', $tmpFile) | 0200); + } + self::box('unlink', $tmpFile); } } } /** * Appends content to an existing file. * - * @param string $filename The file to which to append content - * @param string|resource $content The content to append + * @param string|resource $content The content to append + * @param bool $lock Whether the file should be locked when writing to it * * @throws IOException If the file is not writable */ - public function appendToFile($filename, $content) + public function appendToFile(string $filename, $content) { if (\is_array($content)) { - @\trigger_error(\sprintf('Calling "%s()" with an array in the $content argument is deprecated since Symfony 4.3.', __METHOD__), \E_USER_DEPRECATED); + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); } $dir = \dirname($filename); - if (!\is_dir($dir)) { + if (!is_dir($dir)) { $this->mkdir($dir); } - if (\false === @\file_put_contents($filename, $content, \FILE_APPEND)) { - throw new IOException(\sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + $lock = \func_num_args() > 2 && func_get_arg(2); + if (\false === self::box('file_put_contents', $filename, $content, \FILE_APPEND | ($lock ? \LOCK_EX : 0))) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename) . self::$lastError, 0, null, $filename); } } - private function toIterable($files) : iterable + private function toIterable($files): iterable { - return \is_iterable($files) ? $files : [$files]; + return is_iterable($files) ? $files : [$files]; } /** * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> [file, tmp]). */ - private function getSchemeAndHierarchy(string $filename) : array + private function getSchemeAndHierarchy(string $filename): array { - $components = \explode('://', $filename, 2); - return 2 === \count($components) ? [$components[0], $components[1]] : [null, $components[0]]; + $components = explode('://', $filename, 2); + return (2 === \count($components)) ? [$components[0], $components[1]] : [null, $components[0]]; } - private static function assertFunctionExists(string $func) : void + private static function assertFunctionExists(string $func): void { if (!\function_exists($func)) { - throw new IOException(\sprintf('Unable to perform filesystem operation because the "%s()" function has been disabled.', $func)); + throw new IOException(sprintf('Unable to perform filesystem operation because the "%s()" function has been disabled.', $func)); } } /** @@ -647,15 +646,12 @@ private static function box(string $func, ...$args) { self::assertFunctionExists($func); self::$lastError = null; - \set_error_handler(__CLASS__ . '::handleError'); + set_error_handler(__CLASS__ . '::handleError'); try { - $result = $func(...$args); - \restore_error_handler(); - return $result; - } catch (\Throwable $e) { + return $func(...$args); + } finally { + restore_error_handler(); } - \restore_error_handler(); - throw $e; } /** * @internal diff --git a/src/modules/common/third-party/vendor/symfony/filesystem/Path.php b/src/modules/common/third-party/vendor/symfony/filesystem/Path.php new file mode 100644 index 0000000000..ac41a46fc1 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/filesystem/Path.php @@ -0,0 +1,708 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\Filesystem; + +use Wordlift\Modules\Common\Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Wordlift\Modules\Common\Symfony\Component\Filesystem\Exception\RuntimeException; +/** + * Contains utility methods for handling path strings. + * + * The methods in this class are able to deal with both UNIX and Windows paths + * with both forward and backward slashes. All methods return normalized parts + * containing only forward slashes and no excess "." and ".." segments. + * + * @author Bernhard Schussek + * @author Thomas Schulz + * @author Théo Fidry + */ +final class Path +{ + /** + * The number of buffer entries that triggers a cleanup operation. + */ + private const CLEANUP_THRESHOLD = 1250; + /** + * The buffer size after the cleanup operation. + */ + private const CLEANUP_SIZE = 1000; + /** + * Buffers input/output of {@link canonicalize()}. + * + * @var array + */ + private static $buffer = []; + /** + * @var int + */ + private static $bufferSize = 0; + /** + * Canonicalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Furthermore, all "." and ".." segments are removed as far as possible. + * ".." segments at the beginning of relative paths are not removed. + * + * ```php + * echo Path::canonicalize("\symfony\puli\..\css\style.css"); + * // => /symfony/css/style.css + * + * echo Path::canonicalize("../css/./style.css"); + * // => ../css/style.css + * ``` + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function canonicalize(string $path): string + { + if ('' === $path) { + return ''; + } + // This method is called by many other methods in this class. Buffer + // the canonicalized paths to make up for the severe performance + // decrease. + if (isset(self::$buffer[$path])) { + return self::$buffer[$path]; + } + // Replace "~" with user's home directory. + if ('~' === $path[0]) { + $path = self::getHomeDirectory() . substr($path, 1); + } + $path = self::normalize($path); + [$root, $pathWithoutRoot] = self::split($path); + $canonicalParts = self::findCanonicalParts($root, $pathWithoutRoot); + // Add the root directory again + self::$buffer[$path] = $canonicalPath = $root . implode('/', $canonicalParts); + ++self::$bufferSize; + // Clean up regularly to prevent memory leaks + if (self::$bufferSize > self::CLEANUP_THRESHOLD) { + self::$buffer = \array_slice(self::$buffer, -self::CLEANUP_SIZE, null, \true); + self::$bufferSize = self::CLEANUP_SIZE; + } + return $canonicalPath; + } + /** + * Normalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Contrary to {@link canonicalize()}, this method does not remove invalid + * or dot path segments. Consequently, it is much more efficient and should + * be used whenever the given path is known to be a valid, absolute system + * path. + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function normalize(string $path): string + { + return str_replace('\\', '/', $path); + } + /** + * Returns the directory part of the path. + * + * This method is similar to PHP's dirname(), but handles various cases + * where dirname() returns a weird result: + * + * - dirname() does not accept backslashes on UNIX + * - dirname("C:/symfony") returns "C:", not "C:/" + * - dirname("C:/") returns ".", not "C:/" + * - dirname("C:") returns ".", not "C:/" + * - dirname("symfony") returns ".", not "" + * - dirname() does not canonicalize the result + * + * This method fixes these shortcomings and behaves like dirname() + * otherwise. + * + * The result is a canonical path. + * + * @return string The canonical directory part. Returns the root directory + * if the root directory is passed. Returns an empty string + * if a relative path is passed that contains no slashes. + * Returns an empty string if an empty string is passed. + */ + public static function getDirectory(string $path): string + { + if ('' === $path) { + return ''; + } + $path = self::canonicalize($path); + // Maintain scheme + if (\false !== $schemeSeparatorPosition = strpos($path, '://')) { + $scheme = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + if (\false === $dirSeparatorPosition = strrpos($path, '/')) { + return ''; + } + // Directory equals root directory "/" + if (0 === $dirSeparatorPosition) { + return $scheme . '/'; + } + // Directory equals Windows root "C:/" + if (2 === $dirSeparatorPosition && ctype_alpha($path[0]) && ':' === $path[1]) { + return $scheme . substr($path, 0, 3); + } + return $scheme . substr($path, 0, $dirSeparatorPosition); + } + /** + * Returns canonical path of the user's home directory. + * + * Supported operating systems: + * + * - UNIX + * - Windows8 and upper + * + * If your operating system or environment isn't supported, an exception is thrown. + * + * The result is a canonical path. + * + * @throws RuntimeException If your operating system or environment isn't supported + */ + public static function getHomeDirectory(): string + { + // For UNIX support + if (getenv('HOME')) { + return self::canonicalize(getenv('HOME')); + } + // For >= Windows8 support + if (getenv('HOMEDRIVE') && getenv('HOMEPATH')) { + return self::canonicalize(getenv('HOMEDRIVE') . getenv('HOMEPATH')); + } + throw new RuntimeException("Cannot find the home directory path: Your environment or operating system isn't supported."); + } + /** + * Returns the root directory of a path. + * + * The result is a canonical path. + * + * @return string The canonical root directory. Returns an empty string if + * the given path is relative or empty. + */ + public static function getRoot(string $path): string + { + if ('' === $path) { + return ''; + } + // Maintain scheme + if (\false !== $schemeSeparatorPosition = strpos($path, '://')) { + $scheme = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + $firstCharacter = $path[0]; + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return $scheme . '/'; + } + $length = \strlen($path); + // Windows root + if ($length > 1 && ':' === $path[1] && ctype_alpha($firstCharacter)) { + // Special case: "C:" + if (2 === $length) { + return $scheme . $path . '/'; + } + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return $scheme . $firstCharacter . $path[1] . '/'; + } + } + return ''; + } + /** + * Returns the file name without the extension from a file path. + * + * @param string|null $extension if specified, only that extension is cut + * off (may contain leading dot) + */ + public static function getFilenameWithoutExtension(string $path, ?string $extension = null): string + { + if ('' === $path) { + return ''; + } + if (null !== $extension) { + // remove extension and trailing dot + return rtrim(basename($path, $extension), '.'); + } + return pathinfo($path, \PATHINFO_FILENAME); + } + /** + * Returns the extension from a file path (without leading dot). + * + * @param bool $forceLowerCase forces the extension to be lower-case + */ + public static function getExtension(string $path, bool $forceLowerCase = \false): string + { + if ('' === $path) { + return ''; + } + $extension = pathinfo($path, \PATHINFO_EXTENSION); + if ($forceLowerCase) { + $extension = self::toLower($extension); + } + return $extension; + } + /** + * Returns whether the path has an (or the specified) extension. + * + * @param string $path the path string + * @param string|string[]|null $extensions if null or not provided, checks if + * an extension exists, otherwise + * checks for the specified extension + * or array of extensions (with or + * without leading dot) + * @param bool $ignoreCase whether to ignore case-sensitivity + */ + public static function hasExtension(string $path, $extensions = null, bool $ignoreCase = \false): bool + { + if ('' === $path) { + return \false; + } + $actualExtension = self::getExtension($path, $ignoreCase); + // Only check if path has any extension + if ([] === $extensions || null === $extensions) { + return '' !== $actualExtension; + } + if (\is_string($extensions)) { + $extensions = [$extensions]; + } + foreach ($extensions as $key => $extension) { + if ($ignoreCase) { + $extension = self::toLower($extension); + } + // remove leading '.' in extensions array + $extensions[$key] = ltrim($extension, '.'); + } + return \in_array($actualExtension, $extensions, \true); + } + /** + * Changes the extension of a path string. + * + * @param string $path The path string with filename.ext to change. + * @param string $extension new extension (with or without leading dot) + * + * @return string the path string with new file extension + */ + public static function changeExtension(string $path, string $extension): string + { + if ('' === $path) { + return ''; + } + $actualExtension = self::getExtension($path); + $extension = ltrim($extension, '.'); + // No extension for paths + if ('/' === substr($path, -1)) { + return $path; + } + // No actual extension in path + if (empty($actualExtension)) { + return $path . (('.' === substr($path, -1)) ? '' : '.') . $extension; + } + return substr($path, 0, -\strlen($actualExtension)) . $extension; + } + public static function isAbsolute(string $path): bool + { + if ('' === $path) { + return \false; + } + // Strip scheme + if (\false !== ($schemeSeparatorPosition = strpos($path, '://')) && 1 !== $schemeSeparatorPosition) { + $path = substr($path, $schemeSeparatorPosition + 3); + } + $firstCharacter = $path[0]; + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return \true; + } + // Windows root + if (\strlen($path) > 1 && ctype_alpha($firstCharacter) && ':' === $path[1]) { + // Special case: "C:" + if (2 === \strlen($path)) { + return \true; + } + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return \true; + } + } + return \false; + } + public static function isRelative(string $path): bool + { + return !self::isAbsolute($path); + } + /** + * Turns a relative path into an absolute path in canonical form. + * + * Usually, the relative path is appended to the given base path. Dot + * segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * echo Path::makeAbsolute("../style.css", "/symfony/puli/css"); + * // => /symfony/puli/style.css + * ``` + * + * If an absolute path is passed, that path is returned unless its root + * directory is different than the one of the base path. In that case, an + * exception is thrown. + * + * ```php + * Path::makeAbsolute("/style.css", "/symfony/puli/css"); + * // => /style.css + * + * Path::makeAbsolute("C:/style.css", "C:/symfony/puli/css"); + * // => C:/style.css + * + * Path::makeAbsolute("C:/style.css", "/symfony/puli/css"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @param string $basePath an absolute base path + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path is an absolute path with + * a different root than the base path + */ + public static function makeAbsolute(string $path, string $basePath): string + { + if ('' === $basePath) { + throw new InvalidArgumentException(sprintf('The base path must be a non-empty string. Got: "%s".', $basePath)); + } + if (!self::isAbsolute($basePath)) { + throw new InvalidArgumentException(sprintf('The base path "%s" is not an absolute path.', $basePath)); + } + if (self::isAbsolute($path)) { + return self::canonicalize($path); + } + if (\false !== $schemeSeparatorPosition = strpos($basePath, '://')) { + $scheme = substr($basePath, 0, $schemeSeparatorPosition + 3); + $basePath = substr($basePath, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + return $scheme . self::canonicalize(rtrim($basePath, '/\\') . '/' . $path); + } + /** + * Turns a path into a relative path. + * + * The relative path is created relative to the given base path: + * + * ```php + * echo Path::makeRelative("/symfony/style.css", "/symfony/puli"); + * // => ../style.css + * ``` + * + * If a relative path is passed and the base path is absolute, the relative + * path is returned unchanged: + * + * ```php + * Path::makeRelative("style.css", "/symfony/puli/css"); + * // => style.css + * ``` + * + * If both paths are relative, the relative path is created with the + * assumption that both paths are relative to the same directory: + * + * ```php + * Path::makeRelative("style.css", "symfony/puli/css"); + * // => ../../../style.css + * ``` + * + * If both paths are absolute, their root directory must be the same, + * otherwise an exception is thrown: + * + * ```php + * Path::makeRelative("C:/symfony/style.css", "/symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the passed path is absolute, but the base path is not, an exception + * is thrown as well: + * + * ```php + * Path::makeRelative("/symfony/style.css", "symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path has a different root + * than the base path + */ + public static function makeRelative(string $path, string $basePath): string + { + $path = self::canonicalize($path); + $basePath = self::canonicalize($basePath); + [$root, $relativePath] = self::split($path); + [$baseRoot, $relativeBasePath] = self::split($basePath); + // If the base path is given as absolute path and the path is already + // relative, consider it to be relative to the given absolute path + // already + if ('' === $root && '' !== $baseRoot) { + // If base path is already in its root + if ('' === $relativeBasePath) { + $relativePath = ltrim($relativePath, './\\'); + } + return $relativePath; + } + // If the passed path is absolute, but the base path is not, we + // cannot generate a relative path + if ('' !== $root && '' === $baseRoot) { + throw new InvalidArgumentException(sprintf('The absolute path "%s" cannot be made relative to the relative path "%s". You should provide an absolute base path instead.', $path, $basePath)); + } + // Fail if the roots of the two paths are different + if ($baseRoot && $root !== $baseRoot) { + throw new InvalidArgumentException(sprintf('The path "%s" cannot be made relative to "%s", because they have different roots ("%s" and "%s").', $path, $basePath, $root, $baseRoot)); + } + if ('' === $relativeBasePath) { + return $relativePath; + } + // Build a "../../" prefix with as many "../" parts as necessary + $parts = explode('/', $relativePath); + $baseParts = explode('/', $relativeBasePath); + $dotDotPrefix = ''; + // Once we found a non-matching part in the prefix, we need to add + // "../" parts for all remaining parts + $match = \true; + foreach ($baseParts as $index => $basePart) { + if ($match && isset($parts[$index]) && $basePart === $parts[$index]) { + unset($parts[$index]); + continue; + } + $match = \false; + $dotDotPrefix .= '../'; + } + return rtrim($dotDotPrefix . implode('/', $parts), '/'); + } + /** + * Returns whether the given path is on the local filesystem. + */ + public static function isLocal(string $path): bool + { + return '' !== $path && \false === strpos($path, '://'); + } + /** + * Returns the longest common base path in canonical form of a set of paths or + * `null` if the paths are on different Windows partitions. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/symfony/css/..' + * ); + * // => /symfony + * ``` + * + * The root is returned if no common base path can be found: + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/puli/css/..' + * ); + * // => / + * ``` + * + * If the paths are located on different Windows partitions, `null` is + * returned. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * 'C:/symfony/css/style.css', + * 'D:/symfony/css/..' + * ); + * // => null + * ``` + */ + public static function getLongestCommonBasePath(string ...$paths): ?string + { + [$bpRoot, $basePath] = self::split(self::canonicalize(reset($paths))); + for (next($paths); null !== key($paths) && '' !== $basePath; next($paths)) { + [$root, $path] = self::split(self::canonicalize(current($paths))); + // If we deal with different roots (e.g. C:/ vs. D:/), it's time + // to quit + if ($root !== $bpRoot) { + return null; + } + // Make the base path shorter until it fits into path + while (\true) { + if ('.' === $basePath) { + // No more base paths + $basePath = ''; + // next path + continue 2; + } + // Prevent false positives for common prefixes + // see isBasePath() + if (0 === strpos($path . '/', $basePath . '/')) { + // next path + continue 2; + } + $basePath = \dirname($basePath); + } + } + return $bpRoot . $basePath; + } + /** + * Joins two or more path strings into a canonical path. + */ + public static function join(string ...$paths): string + { + $finalPath = null; + $wasScheme = \false; + foreach ($paths as $path) { + if ('' === $path) { + continue; + } + if (null === $finalPath) { + // For first part we keep slashes, like '/top', 'C:\' or 'phar://' + $finalPath = $path; + $wasScheme = \false !== strpos($path, '://'); + continue; + } + // Only add slash if previous part didn't end with '/' or '\' + if (!\in_array(substr($finalPath, -1), ['/', '\\'])) { + $finalPath .= '/'; + } + // If first part included a scheme like 'phar://' we allow \current part to start with '/', otherwise trim + $finalPath .= $wasScheme ? $path : ltrim($path, '/'); + $wasScheme = \false; + } + if (null === $finalPath) { + return ''; + } + return self::canonicalize($finalPath); + } + /** + * Returns whether a path is a base path of another path. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * Path::isBasePath('/symfony', '/symfony/css'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony/..'); + * // => false + * + * Path::isBasePath('/symfony', '/puli'); + * // => false + * ``` + */ + public static function isBasePath(string $basePath, string $ofPath): bool + { + $basePath = self::canonicalize($basePath); + $ofPath = self::canonicalize($ofPath); + // Append slashes to prevent false positives when two paths have + // a common prefix, for example /base/foo and /base/foobar. + // Don't append a slash for the root "/", because then that root + // won't be discovered as common prefix ("//" is not a prefix of + // "/foobar/"). + return 0 === strpos($ofPath . '/', rtrim($basePath, '/') . '/'); + } + /** + * @return string[] + */ + private static function findCanonicalParts(string $root, string $pathWithoutRoot): array + { + $parts = explode('/', $pathWithoutRoot); + $canonicalParts = []; + // Collapse "." and "..", if possible + foreach ($parts as $part) { + if ('.' === $part || '' === $part) { + continue; + } + // Collapse ".." with the previous part, if one exists + // Don't collapse ".." if the previous part is also ".." + if ('..' === $part && \count($canonicalParts) > 0 && '..' !== $canonicalParts[\count($canonicalParts) - 1]) { + array_pop($canonicalParts); + continue; + } + // Only add ".." prefixes for relative paths + if ('..' !== $part || '' === $root) { + $canonicalParts[] = $part; + } + } + return $canonicalParts; + } + /** + * Splits a canonical path into its root directory and the remainder. + * + * If the path has no root directory, an empty root directory will be + * returned. + * + * If the root directory is a Windows style partition, the resulting root + * will always contain a trailing slash. + * + * list ($root, $path) = Path::split("C:/symfony") + * // => ["C:/", "symfony"] + * + * list ($root, $path) = Path::split("C:") + * // => ["C:/", ""] + * + * @return array{string, string} an array with the root directory and the remaining relative path + */ + private static function split(string $path): array + { + if ('' === $path) { + return ['', '']; + } + // Remember scheme as part of the root, if any + if (\false !== $schemeSeparatorPosition = strpos($path, '://')) { + $root = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $root = ''; + } + $length = \strlen($path); + // Remove and remember root directory + if (0 === strpos($path, '/')) { + $root .= '/'; + $path = ($length > 1) ? substr($path, 1) : ''; + } elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) { + if (2 === $length) { + // Windows special case: "C:" + $root .= $path . '/'; + $path = ''; + } elseif ('/' === $path[2]) { + // Windows normal case: "C:/".. + $root .= substr($path, 0, 3); + $path = ($length > 3) ? substr($path, 3) : ''; + } + } + return [$root, $path]; + } + private static function toLower(string $string): string + { + if (\false !== $encoding = mb_detect_encoding($string, null, \true)) { + return mb_strtolower($string, $encoding); + } + return strtolower($string); + } + private function __construct() + { + } +} diff --git a/src/modules/common/third-party/vendor/symfony/filesystem/composer.json b/src/modules/common/third-party/vendor/symfony/filesystem/composer.json new file mode 100644 index 0000000000..9781b73e15 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/filesystem/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony\/filesystem", + "type": "library", + "description": "Provides basic utilities for the filesystem", + "keywords": [], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony\/polyfill-ctype": "~1.8", + "symfony\/polyfill-mbstring": "~1.8", + "symfony\/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony\/process": "^5.4|^6.4" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "\/Tests\/" + ] + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-ctype/Ctype.php b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/Ctype.php index 945c4c1200..98c931d595 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-ctype/Ctype.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/Ctype.php @@ -31,7 +31,7 @@ final class Ctype public static function ctype_alnum($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Za-z0-9]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); } /** * Returns TRUE if every character in text is a letter, FALSE otherwise. @@ -45,7 +45,7 @@ public static function ctype_alnum($text) public static function ctype_alpha($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Za-z]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); } /** * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. @@ -59,7 +59,7 @@ public static function ctype_alpha($text) public static function ctype_cntrl($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^\\x00-\\x1f\\x7f]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); } /** * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. @@ -73,7 +73,7 @@ public static function ctype_cntrl($text) public static function ctype_digit($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^0-9]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); } /** * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. @@ -87,7 +87,7 @@ public static function ctype_digit($text) public static function ctype_graph($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^!-~]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); } /** * Returns TRUE if every character in text is a lowercase letter. @@ -101,7 +101,7 @@ public static function ctype_graph($text) public static function ctype_lower($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^a-z]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); } /** * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. @@ -115,7 +115,7 @@ public static function ctype_lower($text) public static function ctype_print($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^ -~]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); } /** * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. @@ -129,7 +129,7 @@ public static function ctype_print($text) public static function ctype_punct($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^!-\\/\\:-@\\[-`\\{-~]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); } /** * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. @@ -143,7 +143,7 @@ public static function ctype_punct($text) public static function ctype_space($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^\\s]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); } /** * Returns TRUE if every character in text is an uppercase letter. @@ -157,7 +157,7 @@ public static function ctype_space($text) public static function ctype_upper($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Z]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); } /** * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. @@ -171,7 +171,7 @@ public static function ctype_upper($text) public static function ctype_xdigit($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Fa-f0-9]/', $text); + return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); } /** * Converts integers to their char versions according to normal ctype behaviour, if needed. @@ -195,7 +195,7 @@ private static function convert_int_to_char_for_ctype($int, $function) return (string) $int; } if (\PHP_VERSION_ID >= 80100) { - @\trigger_error($function . '(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED); + @trigger_error($function . '(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED); } if ($int < 0) { $int += 256; diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap.php b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap.php index 98982540d4..17af5b6f0e 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap.php @@ -14,67 +14,67 @@ if (\PHP_VERSION_ID >= 80000) { return require __DIR__ . '/bootstrap80.php'; } -if (!\function_exists('ctype_alnum')) { +if (!\function_exists('ctype_alnum') && !\function_exists('Wordlift\Modules\Common\ctype_alnum')) { function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); } } -if (!\function_exists('ctype_alpha')) { +if (!\function_exists('ctype_alpha') && !\function_exists('Wordlift\Modules\Common\ctype_alpha')) { function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); } } -if (!\function_exists('ctype_cntrl')) { +if (!\function_exists('ctype_cntrl') && !\function_exists('Wordlift\Modules\Common\ctype_cntrl')) { function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); } } -if (!\function_exists('ctype_digit')) { +if (!\function_exists('ctype_digit') && !\function_exists('Wordlift\Modules\Common\ctype_digit')) { function ctype_digit($text) { return p\Ctype::ctype_digit($text); } } -if (!\function_exists('ctype_graph')) { +if (!\function_exists('ctype_graph') && !\function_exists('Wordlift\Modules\Common\ctype_graph')) { function ctype_graph($text) { return p\Ctype::ctype_graph($text); } } -if (!\function_exists('ctype_lower')) { +if (!\function_exists('ctype_lower') && !\function_exists('Wordlift\Modules\Common\ctype_lower')) { function ctype_lower($text) { return p\Ctype::ctype_lower($text); } } -if (!\function_exists('ctype_print')) { +if (!\function_exists('ctype_print') && !\function_exists('Wordlift\Modules\Common\ctype_print')) { function ctype_print($text) { return p\Ctype::ctype_print($text); } } -if (!\function_exists('ctype_punct')) { +if (!\function_exists('ctype_punct') && !\function_exists('Wordlift\Modules\Common\ctype_punct')) { function ctype_punct($text) { return p\Ctype::ctype_punct($text); } } -if (!\function_exists('ctype_space')) { +if (!\function_exists('ctype_space') && !\function_exists('Wordlift\Modules\Common\ctype_space')) { function ctype_space($text) { return p\Ctype::ctype_space($text); } } -if (!\function_exists('ctype_upper')) { +if (!\function_exists('ctype_upper') && !\function_exists('Wordlift\Modules\Common\ctype_upper')) { function ctype_upper($text) { return p\Ctype::ctype_upper($text); } } -if (!\function_exists('ctype_xdigit')) { +if (!\function_exists('ctype_xdigit') && !\function_exists('Wordlift\Modules\Common\ctype_xdigit')) { function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap80.php b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap80.php index 5f2a40407b..ac86485678 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap80.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/bootstrap80.php @@ -11,68 +11,68 @@ * file that was distributed with this source code. */ use Wordlift\Modules\Common\Symfony\Polyfill\Ctype as p; -if (!\function_exists('ctype_alnum')) { - function ctype_alnum(mixed $text) : bool +if (!\function_exists('ctype_alnum') && !\function_exists('Wordlift\Modules\Common\ctype_alnum')) { + function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); } } -if (!\function_exists('ctype_alpha')) { - function ctype_alpha(mixed $text) : bool +if (!\function_exists('ctype_alpha') && !\function_exists('Wordlift\Modules\Common\ctype_alpha')) { + function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); } } -if (!\function_exists('ctype_cntrl')) { - function ctype_cntrl(mixed $text) : bool +if (!\function_exists('ctype_cntrl') && !\function_exists('Wordlift\Modules\Common\ctype_cntrl')) { + function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); } } -if (!\function_exists('ctype_digit')) { - function ctype_digit(mixed $text) : bool +if (!\function_exists('ctype_digit') && !\function_exists('Wordlift\Modules\Common\ctype_digit')) { + function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); } } -if (!\function_exists('ctype_graph')) { - function ctype_graph(mixed $text) : bool +if (!\function_exists('ctype_graph') && !\function_exists('Wordlift\Modules\Common\ctype_graph')) { + function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); } } -if (!\function_exists('ctype_lower')) { - function ctype_lower(mixed $text) : bool +if (!\function_exists('ctype_lower') && !\function_exists('Wordlift\Modules\Common\ctype_lower')) { + function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); } } -if (!\function_exists('ctype_print')) { - function ctype_print(mixed $text) : bool +if (!\function_exists('ctype_print') && !\function_exists('Wordlift\Modules\Common\ctype_print')) { + function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); } } -if (!\function_exists('ctype_punct')) { - function ctype_punct(mixed $text) : bool +if (!\function_exists('ctype_punct') && !\function_exists('Wordlift\Modules\Common\ctype_punct')) { + function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); } } -if (!\function_exists('ctype_space')) { - function ctype_space(mixed $text) : bool +if (!\function_exists('ctype_space') && !\function_exists('Wordlift\Modules\Common\ctype_space')) { + function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); } } -if (!\function_exists('ctype_upper')) { - function ctype_upper(mixed $text) : bool +if (!\function_exists('ctype_upper') && !\function_exists('Wordlift\Modules\Common\ctype_upper')) { + function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); } } -if (!\function_exists('ctype_xdigit')) { - function ctype_xdigit(mixed $text) : bool +if (!\function_exists('ctype_xdigit') && !\function_exists('Wordlift\Modules\Common\ctype_xdigit')) { + function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-ctype/composer.json b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/composer.json new file mode 100644 index 0000000000..3fa7726a18 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-ctype/composer.json @@ -0,0 +1,47 @@ +{ + "name": "symfony\/polyfill-ctype", + "type": "library", + "description": "Symfony polyfill for ctype functions", + "keywords": [ + "polyfill", + "compatibility", + "portable", + "ctype" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony\/polyfill", + "url": "https:\/\/github.com\/symfony\/polyfill" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Mbstring.php b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Mbstring.php index 6c26750bbf..428d49c058 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Mbstring.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -47,6 +47,11 @@ * - mb_strstr - Finds first occurrence of a string within another * - mb_strwidth - Return width of string * - mb_substr_count - Count the number of substring occurrences + * - mb_ucfirst - Make a string's first character uppercase + * - mb_lcfirst - Make a string's first character lowercase + * - mb_trim - Strip whitespace (or other characters) from the beginning and end of a string + * - mb_ltrim - Strip whitespace (or other characters) from the beginning of a string + * - mb_rtrim - Strip whitespace (or other characters) from the end of a string * * Not implemented: * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) @@ -67,45 +72,52 @@ final class Mbstring { public const MB_CASE_FOLD = \PHP_INT_MAX; - private const CASE_FOLD = [['µ', 'ſ', "ͅ", 'ς', "ϐ", "ϑ", "ϕ", "ϖ", "ϰ", "ϱ", "ϵ", "ẛ", "ι"], ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "ṡ", 'ι']]; + private const SIMPLE_CASE_FOLD = [['µ', 'ſ', "ͅ", 'ς', "ϐ", "ϑ", "ϕ", "ϖ", "ϰ", "ϱ", "ϵ", "ẛ", "ι"], ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "ṡ", 'ι']]; private static $encodingList = ['ASCII', 'UTF-8']; private static $language = 'neutral'; private static $internalEncoding = 'UTF-8'; public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { - if (\is_array($fromEncoding) || null !== $fromEncoding && \false !== \strpos($fromEncoding, ',')) { + if (\is_array($s)) { + $r = []; + foreach ($s as $str) { + $r[] = self::mb_convert_encoding($str, $toEncoding, $fromEncoding); + } + return $r; + } + if (\is_array($fromEncoding) || null !== $fromEncoding && \false !== strpos($fromEncoding, ',')) { $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); } else { $fromEncoding = self::getEncoding($fromEncoding); } $toEncoding = self::getEncoding($toEncoding); if ('BASE64' === $fromEncoding) { - $s = \base64_decode($s); + $s = base64_decode($s); $fromEncoding = $toEncoding; } if ('BASE64' === $toEncoding) { - return \base64_encode($s); + return base64_encode($s); } if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { $fromEncoding = 'Windows-1252'; } if ('UTF-8' !== $fromEncoding) { - $s = \iconv($fromEncoding, 'UTF-8//IGNORE', $s); + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); } - return \preg_replace_callback('/[\\x80-\\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); + return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); } if ('HTML-ENTITIES' === $fromEncoding) { - $s = \html_entity_decode($s, \ENT_COMPAT, 'UTF-8'); + $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8'); $fromEncoding = 'UTF-8'; } - return \iconv($fromEncoding, $toEncoding . '//IGNORE', $s); + return iconv($fromEncoding, $toEncoding . '//IGNORE', $s); } public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars) { $ok = \true; - \array_walk_recursive($vars, function (&$v) use(&$ok, $toEncoding, $fromEncoding) { - if (\false === ($v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding))) { + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (\false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { $ok = \false; } }); @@ -113,23 +125,23 @@ public static function mb_convert_variables($toEncoding, $fromEncoding, &...$var } public static function mb_decode_mimeheader($s) { - return \iconv_mime_decode($s, 2, self::$internalEncoding); + return iconv_mime_decode($s, 2, self::$internalEncoding); } public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) { - \trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING); + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING); } public static function mb_decode_numericentity($s, $convmap, $encoding = null) { - if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { - \trigger_error('mb_decode_numericentity() expects parameter 1 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_decode_numericentity() expects parameter 1 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); return null; } if (!\is_array($convmap) || 80000 > \PHP_VERSION_ID && !$convmap) { return \false; } if (null !== $encoding && !\is_scalar($encoding)) { - \trigger_error('mb_decode_numericentity() expects parameter 3 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); + trigger_error('mb_decode_numericentity() expects parameter 3 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); return ''; // Instead of null (cf. mb_encode_numericentity). } @@ -140,20 +152,20 @@ public static function mb_decode_numericentity($s, $convmap, $encoding = null) $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; - if (!\preg_match('//u', $s)) { - $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } - $cnt = \floor(\count($convmap) / 4) * 4; + $cnt = floor(\count($convmap) / 4) * 4; for ($i = 0; $i < $cnt; $i += 4) { // collector_decode_htmlnumericentity ignores $convmap[$i + 3] $convmap[$i] += $convmap[$i + 2]; $convmap[$i + 1] += $convmap[$i + 2]; } - $s = \preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use($cnt, $convmap) { - $c = isset($m[2]) ? (int) \hexdec($m[2]) : $m[1]; + $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { + $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; for ($i = 0; $i < $cnt; $i += 4) { if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { return self::mb_chr($c - $convmap[$i + 2]); @@ -164,24 +176,24 @@ public static function mb_decode_numericentity($s, $convmap, $encoding = null) if (null === $encoding) { return $s; } - return \iconv('UTF-8', $encoding . '//IGNORE', $s); + return iconv('UTF-8', $encoding . '//IGNORE', $s); } public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = \false) { - if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { - \trigger_error('mb_encode_numericentity() expects parameter 1 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_encode_numericentity() expects parameter 1 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); return null; } if (!\is_array($convmap) || 80000 > \PHP_VERSION_ID && !$convmap) { return \false; } if (null !== $encoding && !\is_scalar($encoding)) { - \trigger_error('mb_encode_numericentity() expects parameter 3 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); + trigger_error('mb_encode_numericentity() expects parameter 3 to be string, ' . \gettype($s) . ' given', \E_USER_WARNING); return null; // Instead of '' (cf. mb_decode_numericentity). } if (null !== $is_hex && !\is_scalar($is_hex)) { - \trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, ' . \gettype($s) . ' given', \E_USER_WARNING); + trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, ' . \gettype($s) . ' given', \E_USER_WARNING); return null; } $s = (string) $s; @@ -191,26 +203,26 @@ public static function mb_encode_numericentity($s, $convmap, $encoding = null, $ $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; - if (!\preg_match('//u', $s)) { - $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } static $ulenMask = ["\xc0" => 2, "\xd0" => 2, "\xe0" => 3, "\xf0" => 4]; - $cnt = \floor(\count($convmap) / 4) * 4; + $cnt = floor(\count($convmap) / 4) * 4; $i = 0; $len = \strlen($s); $result = ''; while ($i < $len) { - $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xf0"]; - $uchr = \substr($s, $i, $ulen); + $ulen = ($s[$i] < "\x80") ? 1 : $ulenMask[$s[$i] & "\xf0"]; + $uchr = substr($s, $i, $ulen); $i += $ulen; $c = self::mb_ord($uchr); for ($j = 0; $j < $cnt; $j += 4) { if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { $cOffset = $c + $convmap[$j + 2] & $convmap[$j + 3]; - $result .= $is_hex ? \sprintf('&#x%X;', $cOffset) : '&#' . $cOffset . ';'; + $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : ('&#' . $cOffset . ';'); continue 2; } } @@ -219,7 +231,7 @@ public static function mb_encode_numericentity($s, $convmap, $encoding = null, $ if (null === $encoding) { return $result; } - return \iconv('UTF-8', $encoding . '//IGNORE', $result); + return iconv('UTF-8', $encoding . '//IGNORE', $result); } public static function mb_convert_case($s, $mode, $encoding = null) { @@ -230,18 +242,18 @@ public static function mb_convert_case($s, $mode, $encoding = null) $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; - if (!\preg_match('//u', $s)) { - $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } if (\MB_CASE_TITLE == $mode) { static $titleRegexp = null; if (null === $titleRegexp) { $titleRegexp = self::getData('titleCaseRegexp'); } - $s = \preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s); + $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s); } else { if (\MB_CASE_UPPER == $mode) { static $upper = null; @@ -251,7 +263,11 @@ public static function mb_convert_case($s, $mode, $encoding = null) $map = $upper; } else { if (self::MB_CASE_FOLD === $mode) { - $s = \str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s); + static $caseFolding = null; + if (null === $caseFolding) { + $caseFolding = self::getData('caseFolding'); + } + $s = strtr($s, $caseFolding); } static $lower = null; if (null === $lower) { @@ -263,8 +279,8 @@ public static function mb_convert_case($s, $mode, $encoding = null) $i = 0; $len = \strlen($s); while ($i < $len) { - $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xf0"]; - $uchr = \substr($s, $i, $ulen); + $ulen = ($s[$i] < "\x80") ? 1 : $ulenMask[$s[$i] & "\xf0"]; + $uchr = substr($s, $i, $ulen); $i += $ulen; if (isset($map[$uchr])) { $uchr = $map[$uchr]; @@ -275,7 +291,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) $s[--$nlen] = $uchr[--$ulen]; } while ($ulen); } else { - $s = \substr_replace($s, $uchr, $i - $ulen, $ulen); + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); $len += $nlen - $ulen; $i += $nlen - $ulen; } @@ -285,7 +301,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) if (null === $encoding) { return $s; } - return \iconv('UTF-8', $encoding . '//IGNORE', $s); + return iconv('UTF-8', $encoding . '//IGNORE', $s); } public static function mb_internal_encoding($encoding = null) { @@ -293,21 +309,21 @@ public static function mb_internal_encoding($encoding = null) return self::$internalEncoding; } $normalizedEncoding = self::getEncoding($encoding); - if ('UTF-8' === $normalizedEncoding || \false !== @\iconv($normalizedEncoding, $normalizedEncoding, ' ')) { + if ('UTF-8' === $normalizedEncoding || \false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) { self::$internalEncoding = $normalizedEncoding; return \true; } if (80000 > \PHP_VERSION_ID) { return \false; } - throw new \ValueError(\sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); + throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); } public static function mb_language($lang = null) { if (null === $lang) { return self::$language; } - switch ($normalizedLang = \strtolower($lang)) { + switch ($normalizedLang = strtolower($lang)) { case 'uni': case 'neutral': self::$language = $normalizedLang; @@ -316,7 +332,7 @@ public static function mb_language($lang = null) if (80000 > \PHP_VERSION_ID) { return \false; } - throw new \ValueError(\sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); + throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); } public static function mb_list_encodings() { @@ -324,7 +340,7 @@ public static function mb_list_encodings() } public static function mb_encoding_aliases($encoding) { - switch (\strtoupper($encoding)) { + switch (strtoupper($encoding)) { case 'UTF8': case 'UTF-8': return ['utf8']; @@ -339,7 +355,18 @@ public static function mb_check_encoding($var = null, $encoding = null) } $encoding = self::$internalEncoding; } - return self::mb_detect_encoding($var, [$encoding]) || \false !== @\iconv($encoding, $encoding, $var); + if (!\is_array($var)) { + return self::mb_detect_encoding($var, [$encoding]) || \false !== @iconv($encoding, $encoding, $var); + } + foreach ($var as $key => $value) { + if (!self::mb_check_encoding($key, $encoding)) { + return \false; + } + if (!self::mb_check_encoding($value, $encoding)) { + return \false; + } + } + return \true; } public static function mb_detect_encoding($str, $encodingList = null, $strict = \false) { @@ -347,25 +374,25 @@ public static function mb_detect_encoding($str, $encodingList = null, $strict = $encodingList = self::$encodingList; } else { if (!\is_array($encodingList)) { - $encodingList = \array_map('trim', \explode(',', $encodingList)); + $encodingList = array_map('trim', explode(',', $encodingList)); } - $encodingList = \array_map('strtoupper', $encodingList); + $encodingList = array_map('strtoupper', $encodingList); } foreach ($encodingList as $enc) { switch ($enc) { case 'ASCII': - if (!\preg_match('/[\\x80-\\xFF]/', $str)) { + if (!preg_match('/[\x80-\xFF]/', $str)) { return $enc; } break; case 'UTF8': case 'UTF-8': - if (\preg_match('//u', $str)) { + if (preg_match('//u', $str)) { return 'UTF-8'; } break; default: - if (0 === \strncmp($enc, 'ISO-8859-', 9)) { + if (0 === strncmp($enc, 'ISO-8859-', 9)) { return $enc; } } @@ -378,13 +405,13 @@ public static function mb_detect_order($encodingList = null) return self::$encodingList; } if (!\is_array($encodingList)) { - $encodingList = \array_map('trim', \explode(',', $encodingList)); + $encodingList = array_map('trim', explode(',', $encodingList)); } - $encodingList = \array_map('strtoupper', $encodingList); + $encodingList = array_map('strtoupper', $encodingList); foreach ($encodingList as $enc) { switch ($enc) { default: - if (\strncmp($enc, 'ISO-8859-', 9)) { + if (strncmp($enc, 'ISO-8859-', 9)) { return \false; } // no break @@ -402,35 +429,35 @@ public static function mb_strlen($s, $encoding = null) if ('CP850' === $encoding || 'ASCII' === $encoding) { return \strlen($s); } - return @\iconv_strlen($s, $encoding); + return @iconv_strlen($s, $encoding); } public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { - return \strpos($haystack, $needle, $offset); + return strpos($haystack, $needle, $offset); } $needle = (string) $needle; if ('' === $needle) { if (80000 > \PHP_VERSION_ID) { - \trigger_error(__METHOD__ . ': Empty delimiter', \E_USER_WARNING); + trigger_error(__METHOD__ . ': Empty delimiter', \E_USER_WARNING); return \false; } return 0; } - return \iconv_strpos($haystack, $needle, $offset, $encoding); + return iconv_strpos($haystack, $needle, $offset, $encoding); } public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { - return \strrpos($haystack, $needle, $offset); + return strrpos($haystack, $needle, $offset); } if ($offset != (int) $offset) { $offset = 0; } elseif ($offset = (int) $offset) { if ($offset < 0) { - if (0 > ($offset += self::mb_strlen($needle))) { + if (0 > $offset += self::mb_strlen($needle)) { $haystack = self::mb_substr($haystack, 0, $offset, $encoding); } $offset = 0; @@ -438,38 +465,38 @@ public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = n $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); } } - $pos = '' !== $needle || 80000 > \PHP_VERSION_ID ? \iconv_strrpos($haystack, $needle, $encoding) : self::mb_strlen($haystack, $encoding); - return \false !== $pos ? $offset + $pos : \false; + $pos = ('' !== $needle || 80000 > \PHP_VERSION_ID) ? iconv_strrpos($haystack, $needle, $encoding) : self::mb_strlen($haystack, $encoding); + return (\false !== $pos) ? $offset + $pos : \false; } public static function mb_str_split($string, $split_length = 1, $encoding = null) { - if (null !== $string && !\is_scalar($string) && !(\is_object($string) && \method_exists($string, '__toString'))) { - \trigger_error('mb_str_split() expects parameter 1 to be string, ' . \gettype($string) . ' given', \E_USER_WARNING); + if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { + trigger_error('mb_str_split() expects parameter 1 to be string, ' . \gettype($string) . ' given', \E_USER_WARNING); return null; } - if (1 > ($split_length = (int) $split_length)) { + if (1 > $split_length = (int) $split_length) { if (80000 > \PHP_VERSION_ID) { - \trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); + trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); return \false; } throw new \ValueError('Argument #2 ($length) must be greater than 0'); } if (null === $encoding) { - $encoding = \mb_internal_encoding(); + $encoding = mb_internal_encoding(); } - if ('UTF-8' === ($encoding = self::getEncoding($encoding))) { + if ('UTF-8' === $encoding = self::getEncoding($encoding)) { $rx = '/('; while (65535 < $split_length) { $rx .= '.{65535}'; $split_length -= 65535; } $rx .= '.{' . $split_length . '})/us'; - return \preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); + return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); } $result = []; - $length = \mb_strlen($string, $encoding); + $length = mb_strlen($string, $encoding); for ($i = 0; $i < $length; $i += $split_length) { - $result[] = \mb_substr($string, $i, $split_length, $encoding); + $result[] = mb_substr($string, $i, $split_length, $encoding); } return $result; } @@ -486,7 +513,7 @@ public static function mb_substitute_character($c = null) if (null === $c) { return 'none'; } - if (0 === \strcasecmp($c, 'none')) { + if (0 === strcasecmp($c, 'none')) { return \true; } if (80000 > \PHP_VERSION_ID) { @@ -501,10 +528,10 @@ public static function mb_substr($s, $start, $length = null, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { - return (string) \substr($s, $start, null === $length ? 2147483647 : $length); + return (string) substr($s, $start, (null === $length) ? 2147483647 : $length); } if ($start < 0) { - $start = \iconv_strlen($s, $encoding) + $start; + $start = iconv_strlen($s, $encoding) + $start; if ($start < 0) { $start = 0; } @@ -512,17 +539,16 @@ public static function mb_substr($s, $start, $length = null, $encoding = null) if (null === $length) { $length = 2147483647; } elseif ($length < 0) { - $length = \iconv_strlen($s, $encoding) + $length - $start; + $length = iconv_strlen($s, $encoding) + $length - $start; if ($length < 0) { return ''; } } - return (string) \iconv_substr($s, $start, $length, $encoding); + return (string) iconv_substr($s, $start, $length, $encoding); } public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { - $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); - $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + [$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding), self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding)]); return self::mb_strpos($haystack, $needle, $offset, $encoding); } public static function mb_stristr($haystack, $needle, $part = \false, $encoding = null) @@ -534,10 +560,10 @@ public static function mb_strrchr($haystack, $needle, $part = \false, $encoding { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { - $pos = \strrpos($haystack, $needle); + $pos = strrpos($haystack, $needle); } else { $needle = self::mb_substr($needle, 0, 1, $encoding); - $pos = \iconv_strrpos($haystack, $needle, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); } return self::getSubpart($pos, $part, $haystack, $encoding); } @@ -549,24 +575,26 @@ public static function mb_strrichr($haystack, $needle, $part = \false, $encoding } public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { - $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); - $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + $haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding); + $needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding); + $haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack); + $needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle); return self::mb_strrpos($haystack, $needle, $offset, $encoding); } public static function mb_strstr($haystack, $needle, $part = \false, $encoding = null) { - $pos = \strpos($haystack, $needle); + $pos = strpos($haystack, $needle); if (\false === $pos) { return \false; } if ($part) { - return \substr($haystack, 0, $pos); + return substr($haystack, 0, $pos); } - return \substr($haystack, $pos); + return substr($haystack, $pos); } public static function mb_get_info($type = 'all') { - $info = ['internal_encoding' => self::$internalEncoding, 'http_output' => 'pass', 'http_output_conv_mimetypes' => '^(text/|application/xhtml\\+xml)', 'func_overload' => 0, 'func_overload_list' => 'no overload', 'mail_charset' => 'UTF-8', 'mail_header_encoding' => 'BASE64', 'mail_body_encoding' => 'BASE64', 'illegal_chars' => 0, 'encoding_translation' => 'Off', 'language' => self::$language, 'detect_order' => self::$encodingList, 'substitute_character' => 'none', 'strict_detection' => 'Off']; + $info = ['internal_encoding' => self::$internalEncoding, 'http_output' => 'pass', 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', 'func_overload' => 0, 'func_overload_list' => 'no overload', 'mail_charset' => 'UTF-8', 'mail_header_encoding' => 'BASE64', 'mail_body_encoding' => 'BASE64', 'illegal_chars' => 0, 'encoding_translation' => 'Off', 'language' => self::$language, 'detect_order' => self::$encodingList, 'substitute_character' => 'none', 'strict_detection' => 'Off']; if ('all' === $type) { return $info; } @@ -581,20 +609,20 @@ public static function mb_http_input($type = '') } public static function mb_http_output($encoding = null) { - return null !== $encoding ? 'pass' === $encoding : 'pass'; + return (null !== $encoding) ? 'pass' === $encoding : 'pass'; } public static function mb_strwidth($s, $encoding = null) { $encoding = self::getEncoding($encoding); if ('UTF-8' !== $encoding) { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } - $s = \preg_replace('/[\\x{1100}-\\x{115F}\\x{2329}\\x{232A}\\x{2E80}-\\x{303E}\\x{3040}-\\x{A4CF}\\x{AC00}-\\x{D7A3}\\x{F900}-\\x{FAFF}\\x{FE10}-\\x{FE19}\\x{FE30}-\\x{FE6F}\\x{FF00}-\\x{FF60}\\x{FFE0}-\\x{FFE6}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}]/u', '', $s, -1, $wide); - return ($wide << 1) + \iconv_strlen($s, 'UTF-8'); + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); } public static function mb_substr_count($haystack, $needle, $encoding = null) { - return \substr_count($haystack, $needle); + return substr_count($haystack, $needle); } public static function mb_output_handler($contents, $status) { @@ -602,7 +630,7 @@ public static function mb_output_handler($contents, $status) } public static function mb_chr($code, $encoding = null) { - if (0x80 > ($code %= 0x200000)) { + if (0x80 > $code %= 0x200000) { $s = \chr($code); } elseif (0x800 > $code) { $s = \chr(0xc0 | $code >> 6) . \chr(0x80 | $code & 0x3f); @@ -611,20 +639,20 @@ public static function mb_chr($code, $encoding = null) } else { $s = \chr(0xf0 | $code >> 18) . \chr(0x80 | $code >> 12 & 0x3f) . \chr(0x80 | $code >> 6 & 0x3f) . \chr(0x80 | $code & 0x3f); } - if ('UTF-8' !== ($encoding = self::getEncoding($encoding))) { - $s = \mb_convert_encoding($s, $encoding, 'UTF-8'); + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); } return $s; } public static function mb_ord($s, $encoding = null) { - if ('UTF-8' !== ($encoding = self::getEncoding($encoding))) { - $s = \mb_convert_encoding($s, 'UTF-8', $encoding); + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); } if (1 === \strlen($s)) { return \ord($s); } - $code = ($s = \unpack('C*', \substr($s, 0, 4))) ? $s[1] : 0; + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; if (0xf0 <= $code) { return ($code - 0xf0 << 18) + ($s[2] - 0x80 << 12) + ($s[3] - 0x80 << 6) + $s[4] - 0x80; } @@ -636,6 +664,56 @@ public static function mb_ord($s, $encoding = null) } return $code; } + public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string + { + if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], \true)) { + throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH'); + } + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given'); + } + if (self::mb_strlen($pad_string, $encoding) <= 0) { + throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string'); + } + $paddingRequired = $length - self::mb_strlen($string, $encoding); + if ($paddingRequired < 1) { + return $string; + } + switch ($pad_type) { + case \STR_PAD_LEFT: + return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding) . $string; + case \STR_PAD_RIGHT: + return $string . self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding); + default: + $leftPaddingLength = floor($paddingRequired / 2); + $rightPaddingLength = $paddingRequired - $leftPaddingLength; + return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding) . $string . self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding); + } + } + public static function mb_ucfirst(string $string, ?string $encoding = null): string + { + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, 'mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given'); + } + $firstChar = mb_substr($string, 0, 1, $encoding); + $firstChar = mb_convert_case($firstChar, \MB_CASE_TITLE, $encoding); + return $firstChar . mb_substr($string, 1, null, $encoding); + } + public static function mb_lcfirst(string $string, ?string $encoding = null): string + { + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, 'mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given'); + } + $firstChar = mb_substr($string, 0, 1, $encoding); + $firstChar = mb_convert_case($firstChar, \MB_CASE_LOWER, $encoding); + return $firstChar . mb_substr($string, 1, null, $encoding); + } private static function getSubpart($pos, $part, $haystack, $encoding) { if (\false === $pos) { @@ -650,7 +728,7 @@ private static function html_encoding_callback(array $m) { $i = 1; $entities = ''; - $m = \unpack('C*', \htmlentities($m[0], \ENT_COMPAT, 'UTF-8')); + $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8')); while (isset($m[$i])) { if (0x80 > $m[$i]) { $entities .= \chr($m[$i++]); @@ -673,7 +751,7 @@ private static function title_case(array $s) } private static function getData($file) { - if (\file_exists($file = __DIR__ . '/Resources/unidata/' . $file . '.php')) { + if (file_exists($file = __DIR__ . '/Resources/unidata/' . $file . '.php')) { return require $file; } return \false; @@ -686,7 +764,7 @@ private static function getEncoding($encoding) if ('UTF-8' === $encoding) { return 'UTF-8'; } - $encoding = \strtoupper($encoding); + $encoding = strtoupper($encoding); if ('8BIT' === $encoding || 'BINARY' === $encoding) { return 'CP850'; } @@ -695,4 +773,63 @@ private static function getEncoding($encoding) } return $encoding; } + public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__); + } + public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__); + } + public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{[%s]+$}D', $string, $characters, $encoding, __FUNCTION__); + } + private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function): string + { + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, $function . '(): Argument #3 ($encoding) must be a valid encoding, "%s" given'); + } + if ('' === $characters) { + return (null === $encoding) ? $string : self::mb_convert_encoding($string, $encoding); + } + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $string)) { + $string = @iconv('UTF-8', 'UTF-8//IGNORE', $string); + } + if (null !== $characters && !preg_match('//u', $characters)) { + $characters = @iconv('UTF-8', 'UTF-8//IGNORE', $characters); + } + } else { + $string = iconv($encoding, 'UTF-8//IGNORE', $string); + if (null !== $characters) { + $characters = iconv($encoding, 'UTF-8//IGNORE', $characters); + } + } + if (null === $characters) { + $characters = "\\0 \f\n\r\t\v             

   …᠎"; + } else { + $characters = preg_quote($characters); + } + $string = preg_replace(sprintf($regex, $characters), '', $string); + if (null === $encoding) { + return $string; + } + return iconv('UTF-8', $encoding . '//IGNORE', $string); + } + private static function assertEncoding(string $encoding, string $errorFormat): void + { + try { + $validEncoding = @self::mb_check_encoding('', $encoding); + } catch (\ValueError $e) { + throw new \ValueError(sprintf($errorFormat, $encoding)); + } + // BC for PHP 7.3 and lower + if (!$validEncoding) { + throw new \ValueError(sprintf($errorFormat, $encoding)); + } + } } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php new file mode 100644 index 0000000000..a77a8db518 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php @@ -0,0 +1,5 @@ + 'i̇', 'µ' => 'μ', 'ſ' => 's', 'ͅ' => 'ι', 'ς' => 'σ', 'ϐ' => 'β', 'ϑ' => 'θ', 'ϕ' => 'φ', 'ϖ' => 'π', 'ϰ' => 'κ', 'ϱ' => 'ρ', 'ϵ' => 'ε', 'ẛ' => 'ṡ', 'ι' => 'ι', 'ß' => 'ss', 'ʼn' => 'ʼn', 'ǰ' => 'ǰ', 'ΐ' => 'ΐ', 'ΰ' => 'ΰ', 'և' => 'եւ', 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'ẘ', 'ẙ' => 'ẙ', 'ẚ' => 'aʾ', 'ẞ' => 'ss', 'ὐ' => 'ὐ', 'ὒ' => 'ὒ', 'ὔ' => 'ὔ', 'ὖ' => 'ὖ', 'ᾀ' => 'ἀι', 'ᾁ' => 'ἁι', 'ᾂ' => 'ἂι', 'ᾃ' => 'ἃι', 'ᾄ' => 'ἄι', 'ᾅ' => 'ἅι', 'ᾆ' => 'ἆι', 'ᾇ' => 'ἇι', 'ᾈ' => 'ἀι', 'ᾉ' => 'ἁι', 'ᾊ' => 'ἂι', 'ᾋ' => 'ἃι', 'ᾌ' => 'ἄι', 'ᾍ' => 'ἅι', 'ᾎ' => 'ἆι', 'ᾏ' => 'ἇι', 'ᾐ' => 'ἠι', 'ᾑ' => 'ἡι', 'ᾒ' => 'ἢι', 'ᾓ' => 'ἣι', 'ᾔ' => 'ἤι', 'ᾕ' => 'ἥι', 'ᾖ' => 'ἦι', 'ᾗ' => 'ἧι', 'ᾘ' => 'ἠι', 'ᾙ' => 'ἡι', 'ᾚ' => 'ἢι', 'ᾛ' => 'ἣι', 'ᾜ' => 'ἤι', 'ᾝ' => 'ἥι', 'ᾞ' => 'ἦι', 'ᾟ' => 'ἧι', 'ᾠ' => 'ὠι', 'ᾡ' => 'ὡι', 'ᾢ' => 'ὢι', 'ᾣ' => 'ὣι', 'ᾤ' => 'ὤι', 'ᾥ' => 'ὥι', 'ᾦ' => 'ὦι', 'ᾧ' => 'ὧι', 'ᾨ' => 'ὠι', 'ᾩ' => 'ὡι', 'ᾪ' => 'ὢι', 'ᾫ' => 'ὣι', 'ᾬ' => 'ὤι', 'ᾭ' => 'ὥι', 'ᾮ' => 'ὦι', 'ᾯ' => 'ὧι', 'ᾲ' => 'ὰι', 'ᾳ' => 'αι', 'ᾴ' => 'άι', 'ᾶ' => 'ᾶ', 'ᾷ' => 'ᾶι', 'ᾼ' => 'αι', 'ῂ' => 'ὴι', 'ῃ' => 'ηι', 'ῄ' => 'ήι', 'ῆ' => 'ῆ', 'ῇ' => 'ῆι', 'ῌ' => 'ηι', 'ῒ' => 'ῒ', 'ῖ' => 'ῖ', 'ῗ' => 'ῗ', 'ῢ' => 'ῢ', 'ῤ' => 'ῤ', 'ῦ' => 'ῦ', 'ῧ' => 'ῧ', 'ῲ' => 'ὼι', 'ῳ' => 'ωι', 'ῴ' => 'ώι', 'ῶ' => 'ῶ', 'ῷ' => 'ῶι', 'ῼ' => 'ωι', 'ff' => 'ff', 'fi' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', 'ffl' => 'ffl', 'ſt' => 'st', 'st' => 'st', 'ﬓ' => 'մն', 'ﬔ' => 'մե', 'ﬕ' => 'մի', 'ﬖ' => 'վն', 'ﬗ' => 'մխ']; diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php index 3eeecdc63a..25c5eac113 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php @@ -3,4 +3,4 @@ namespace Wordlift\Modules\Common; // from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt -return '/(? + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +use Wordlift\Modules\Common\Symfony\Polyfill\Mbstring as p; +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__ . '/bootstrap80.php'; +} +if (!\function_exists('mb_convert_encoding') && !\function_exists('Wordlift\Modules\Common\mb_convert_encoding')) { + function mb_convert_encoding($string, $to_encoding, $from_encoding = null) + { + return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); + } +} +if (!\function_exists('mb_decode_mimeheader') && !\function_exists('Wordlift\Modules\Common\mb_decode_mimeheader')) { + function mb_decode_mimeheader($string) + { + return p\Mbstring::mb_decode_mimeheader($string); + } +} +if (!\function_exists('mb_encode_mimeheader') && !\function_exists('Wordlift\Modules\Common\mb_encode_mimeheader')) { + function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) + { + return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); + } +} +if (!\function_exists('mb_decode_numericentity') && !\function_exists('Wordlift\Modules\Common\mb_decode_numericentity')) { + function mb_decode_numericentity($string, $map, $encoding = null) + { + return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); + } +} +if (!\function_exists('mb_encode_numericentity') && !\function_exists('Wordlift\Modules\Common\mb_encode_numericentity')) { + function mb_encode_numericentity($string, $map, $encoding = null, $hex = \false) + { + return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); + } +} +if (!\function_exists('mb_convert_case') && !\function_exists('Wordlift\Modules\Common\mb_convert_case')) { + function mb_convert_case($string, $mode, $encoding = null) + { + return p\Mbstring::mb_convert_case($string, $mode, $encoding); + } +} +if (!\function_exists('mb_internal_encoding') && !\function_exists('Wordlift\Modules\Common\mb_internal_encoding')) { + function mb_internal_encoding($encoding = null) + { + return p\Mbstring::mb_internal_encoding($encoding); + } +} +if (!\function_exists('mb_language') && !\function_exists('Wordlift\Modules\Common\mb_language')) { + function mb_language($language = null) + { + return p\Mbstring::mb_language($language); + } +} +if (!\function_exists('mb_list_encodings') && !\function_exists('Wordlift\Modules\Common\mb_list_encodings')) { + function mb_list_encodings() + { + return p\Mbstring::mb_list_encodings(); + } +} +if (!\function_exists('mb_encoding_aliases') && !\function_exists('Wordlift\Modules\Common\mb_encoding_aliases')) { + function mb_encoding_aliases($encoding) + { + return p\Mbstring::mb_encoding_aliases($encoding); + } +} +if (!\function_exists('mb_check_encoding') && !\function_exists('Wordlift\Modules\Common\mb_check_encoding')) { + function mb_check_encoding($value = null, $encoding = null) + { + return p\Mbstring::mb_check_encoding($value, $encoding); + } +} +if (!\function_exists('mb_detect_encoding') && !\function_exists('Wordlift\Modules\Common\mb_detect_encoding')) { + function mb_detect_encoding($string, $encodings = null, $strict = \false) + { + return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); + } +} +if (!\function_exists('mb_detect_order') && !\function_exists('Wordlift\Modules\Common\mb_detect_order')) { + function mb_detect_order($encoding = null) + { + return p\Mbstring::mb_detect_order($encoding); + } +} +if (!\function_exists('mb_parse_str') && !\function_exists('Wordlift\Modules\Common\mb_parse_str')) { + function mb_parse_str($string, &$result = []) + { + \parse_str($string, $result); + return (bool) $result; + } +} +if (!\function_exists('mb_strlen') && !\function_exists('Wordlift\Modules\Common\mb_strlen')) { + function mb_strlen($string, $encoding = null) + { + return p\Mbstring::mb_strlen($string, $encoding); + } +} +if (!\function_exists('mb_strpos') && !\function_exists('Wordlift\Modules\Common\mb_strpos')) { + function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); + } +} +if (!\function_exists('mb_strtolower') && !\function_exists('Wordlift\Modules\Common\mb_strtolower')) { + function mb_strtolower($string, $encoding = null) + { + return p\Mbstring::mb_strtolower($string, $encoding); + } +} +if (!\function_exists('mb_strtoupper') && !\function_exists('Wordlift\Modules\Common\mb_strtoupper')) { + function mb_strtoupper($string, $encoding = null) + { + return p\Mbstring::mb_strtoupper($string, $encoding); + } +} +if (!\function_exists('mb_substitute_character') && !\function_exists('Wordlift\Modules\Common\mb_substitute_character')) { + function mb_substitute_character($substitute_character = null) + { + return p\Mbstring::mb_substitute_character($substitute_character); + } +} +if (!\function_exists('mb_substr') && !\function_exists('Wordlift\Modules\Common\mb_substr')) { + function mb_substr($string, $start, $length = 2147483647, $encoding = null) + { + return p\Mbstring::mb_substr($string, $start, $length, $encoding); + } +} +if (!\function_exists('mb_stripos') && !\function_exists('Wordlift\Modules\Common\mb_stripos')) { + function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); + } +} +if (!\function_exists('mb_stristr') && !\function_exists('Wordlift\Modules\Common\mb_stristr')) { + function mb_stristr($haystack, $needle, $before_needle = \false, $encoding = null) + { + return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); + } +} +if (!\function_exists('mb_strrchr') && !\function_exists('Wordlift\Modules\Common\mb_strrchr')) { + function mb_strrchr($haystack, $needle, $before_needle = \false, $encoding = null) + { + return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); + } +} +if (!\function_exists('mb_strrichr') && !\function_exists('Wordlift\Modules\Common\mb_strrichr')) { + function mb_strrichr($haystack, $needle, $before_needle = \false, $encoding = null) + { + return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); + } +} +if (!\function_exists('mb_strripos') && !\function_exists('Wordlift\Modules\Common\mb_strripos')) { + function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); + } +} +if (!\function_exists('mb_strrpos') && !\function_exists('Wordlift\Modules\Common\mb_strrpos')) { + function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); + } +} +if (!\function_exists('mb_strstr') && !\function_exists('Wordlift\Modules\Common\mb_strstr')) { + function mb_strstr($haystack, $needle, $before_needle = \false, $encoding = null) + { + return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); + } +} +if (!\function_exists('mb_get_info') && !\function_exists('Wordlift\Modules\Common\mb_get_info')) { + function mb_get_info($type = 'all') + { + return p\Mbstring::mb_get_info($type); + } +} +if (!\function_exists('mb_http_output') && !\function_exists('Wordlift\Modules\Common\mb_http_output')) { + function mb_http_output($encoding = null) + { + return p\Mbstring::mb_http_output($encoding); + } +} +if (!\function_exists('mb_strwidth') && !\function_exists('Wordlift\Modules\Common\mb_strwidth')) { + function mb_strwidth($string, $encoding = null) + { + return p\Mbstring::mb_strwidth($string, $encoding); + } +} +if (!\function_exists('mb_substr_count') && !\function_exists('Wordlift\Modules\Common\mb_substr_count')) { + function mb_substr_count($haystack, $needle, $encoding = null) + { + return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); + } +} +if (!\function_exists('mb_output_handler') && !\function_exists('Wordlift\Modules\Common\mb_output_handler')) { + function mb_output_handler($string, $status) + { + return p\Mbstring::mb_output_handler($string, $status); + } +} +if (!\function_exists('mb_http_input') && !\function_exists('Wordlift\Modules\Common\mb_http_input')) { + function mb_http_input($type = null) + { + return p\Mbstring::mb_http_input($type); + } +} +if (!\function_exists('mb_convert_variables') && !\function_exists('Wordlift\Modules\Common\mb_convert_variables')) { + function mb_convert_variables($to_encoding, $from_encoding, &...$vars) + { + return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); + } +} +if (!\function_exists('mb_ord') && !\function_exists('Wordlift\Modules\Common\mb_ord')) { + function mb_ord($string, $encoding = null) + { + return p\Mbstring::mb_ord($string, $encoding); + } +} +if (!\function_exists('mb_chr') && !\function_exists('Wordlift\Modules\Common\mb_chr')) { + function mb_chr($codepoint, $encoding = null) + { + return p\Mbstring::mb_chr($codepoint, $encoding); + } +} +if (!\function_exists('mb_scrub') && !\function_exists('Wordlift\Modules\Common\mb_scrub')) { + function mb_scrub($string, $encoding = null) + { + $encoding = (null === $encoding) ? \mb_internal_encoding() : $encoding; + return \mb_convert_encoding($string, $encoding, $encoding); + } +} +if (!\function_exists('mb_str_split') && !\function_exists('Wordlift\Modules\Common\mb_str_split')) { + function mb_str_split($string, $length = 1, $encoding = null) + { + return p\Mbstring::mb_str_split($string, $length, $encoding); + } +} +if (!\function_exists('mb_str_pad') && !\function_exists('Wordlift\Modules\Common\mb_str_pad')) { + function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string + { + return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_ucfirst')) { + function mb_ucfirst(string $string, ?string $encoding = null): string + { + return p\Mbstring::mb_ucfirst($string, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_lcfirst')) { + function mb_lcfirst(string $string, ?string $encoding = null): string + { + return p\Mbstring::mb_lcfirst($string, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_trim')) { + function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return p\Mbstring::mb_trim($string, $characters, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_ltrim')) { + function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return p\Mbstring::mb_ltrim($string, $characters, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_rtrim')) { + function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return p\Mbstring::mb_rtrim($string, $characters, $encoding); + } +} +if (\extension_loaded('mbstring')) { + return; +} +if (!\defined('MB_CASE_UPPER')) { + \define('MB_CASE_UPPER', 0); +} +if (!\defined('MB_CASE_LOWER')) { + \define('MB_CASE_LOWER', 1); +} +if (!\defined('MB_CASE_TITLE')) { + \define('MB_CASE_TITLE', 2); +} diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/bootstrap80.php b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/bootstrap80.php new file mode 100644 index 0000000000..e0b370a15a --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/bootstrap80.php @@ -0,0 +1,291 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +use Wordlift\Modules\Common\Symfony\Polyfill\Mbstring as p; +if (!\function_exists('mb_convert_encoding') && !\function_exists('Wordlift\Modules\Common\mb_convert_encoding')) { + function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false + { + return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); + } +} +if (!\function_exists('mb_decode_mimeheader') && !\function_exists('Wordlift\Modules\Common\mb_decode_mimeheader')) { + function mb_decode_mimeheader(?string $string): string + { + return p\Mbstring::mb_decode_mimeheader((string) $string); + } +} +if (!\function_exists('mb_encode_mimeheader') && !\function_exists('Wordlift\Modules\Common\mb_encode_mimeheader')) { + function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string + { + return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); + } +} +if (!\function_exists('mb_decode_numericentity') && !\function_exists('Wordlift\Modules\Common\mb_decode_numericentity')) { + function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string + { + return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); + } +} +if (!\function_exists('mb_encode_numericentity') && !\function_exists('Wordlift\Modules\Common\mb_encode_numericentity')) { + function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = \false): string + { + return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); + } +} +if (!\function_exists('mb_convert_case') && !\function_exists('Wordlift\Modules\Common\mb_convert_case')) { + function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string + { + return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); + } +} +if (!\function_exists('mb_internal_encoding') && !\function_exists('Wordlift\Modules\Common\mb_internal_encoding')) { + function mb_internal_encoding(?string $encoding = null): string|bool + { + return p\Mbstring::mb_internal_encoding($encoding); + } +} +if (!\function_exists('mb_language') && !\function_exists('Wordlift\Modules\Common\mb_language')) { + function mb_language(?string $language = null): string|bool + { + return p\Mbstring::mb_language($language); + } +} +if (!\function_exists('mb_list_encodings') && !\function_exists('Wordlift\Modules\Common\mb_list_encodings')) { + function mb_list_encodings(): array + { + return p\Mbstring::mb_list_encodings(); + } +} +if (!\function_exists('mb_encoding_aliases') && !\function_exists('Wordlift\Modules\Common\mb_encoding_aliases')) { + function mb_encoding_aliases(?string $encoding): array + { + return p\Mbstring::mb_encoding_aliases((string) $encoding); + } +} +if (!\function_exists('mb_check_encoding') && !\function_exists('Wordlift\Modules\Common\mb_check_encoding')) { + function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool + { + return p\Mbstring::mb_check_encoding($value, $encoding); + } +} +if (!\function_exists('mb_detect_encoding') && !\function_exists('Wordlift\Modules\Common\mb_detect_encoding')) { + function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = \false): string|false + { + return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); + } +} +if (!\function_exists('mb_detect_order') && !\function_exists('Wordlift\Modules\Common\mb_detect_order')) { + function mb_detect_order(array|string|null $encoding = null): array|bool + { + return p\Mbstring::mb_detect_order($encoding); + } +} +if (!\function_exists('mb_parse_str') && !\function_exists('Wordlift\Modules\Common\mb_parse_str')) { + function mb_parse_str(?string $string, &$result = []): bool + { + \parse_str((string) $string, $result); + return (bool) $result; + } +} +if (!\function_exists('mb_strlen') && !\function_exists('Wordlift\Modules\Common\mb_strlen')) { + function mb_strlen(?string $string, ?string $encoding = null): int + { + return p\Mbstring::mb_strlen((string) $string, $encoding); + } +} +if (!\function_exists('mb_strpos') && !\function_exists('Wordlift\Modules\Common\mb_strpos')) { + function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false + { + return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); + } +} +if (!\function_exists('mb_strtolower') && !\function_exists('Wordlift\Modules\Common\mb_strtolower')) { + function mb_strtolower(?string $string, ?string $encoding = null): string + { + return p\Mbstring::mb_strtolower((string) $string, $encoding); + } +} +if (!\function_exists('mb_strtoupper') && !\function_exists('Wordlift\Modules\Common\mb_strtoupper')) { + function mb_strtoupper(?string $string, ?string $encoding = null): string + { + return p\Mbstring::mb_strtoupper((string) $string, $encoding); + } +} +if (!\function_exists('mb_substitute_character') && !\function_exists('Wordlift\Modules\Common\mb_substitute_character')) { + function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool + { + return p\Mbstring::mb_substitute_character($substitute_character); + } +} +if (!\function_exists('mb_substr') && !\function_exists('Wordlift\Modules\Common\mb_substr')) { + function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string + { + return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); + } +} +if (!\function_exists('mb_stripos') && !\function_exists('Wordlift\Modules\Common\mb_stripos')) { + function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false + { + return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); + } +} +if (!\function_exists('mb_stristr') && !\function_exists('Wordlift\Modules\Common\mb_stristr')) { + function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = \false, ?string $encoding = null): string|false + { + return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); + } +} +if (!\function_exists('mb_strrchr') && !\function_exists('Wordlift\Modules\Common\mb_strrchr')) { + function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = \false, ?string $encoding = null): string|false + { + return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); + } +} +if (!\function_exists('mb_strrichr') && !\function_exists('Wordlift\Modules\Common\mb_strrichr')) { + function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = \false, ?string $encoding = null): string|false + { + return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); + } +} +if (!\function_exists('mb_strripos') && !\function_exists('Wordlift\Modules\Common\mb_strripos')) { + function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false + { + return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); + } +} +if (!\function_exists('mb_strrpos') && !\function_exists('Wordlift\Modules\Common\mb_strrpos')) { + function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false + { + return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); + } +} +if (!\function_exists('mb_strstr') && !\function_exists('Wordlift\Modules\Common\mb_strstr')) { + function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = \false, ?string $encoding = null): string|false + { + return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); + } +} +if (!\function_exists('mb_get_info') && !\function_exists('Wordlift\Modules\Common\mb_get_info')) { + function mb_get_info(?string $type = 'all'): array|string|int|false|null + { + return p\Mbstring::mb_get_info((string) $type); + } +} +if (!\function_exists('mb_http_output') && !\function_exists('Wordlift\Modules\Common\mb_http_output')) { + function mb_http_output(?string $encoding = null): string|bool + { + return p\Mbstring::mb_http_output($encoding); + } +} +if (!\function_exists('mb_strwidth') && !\function_exists('Wordlift\Modules\Common\mb_strwidth')) { + function mb_strwidth(?string $string, ?string $encoding = null): int + { + return p\Mbstring::mb_strwidth((string) $string, $encoding); + } +} +if (!\function_exists('mb_substr_count') && !\function_exists('Wordlift\Modules\Common\mb_substr_count')) { + function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int + { + return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); + } +} +if (!\function_exists('mb_output_handler') && !\function_exists('Wordlift\Modules\Common\mb_output_handler')) { + function mb_output_handler(?string $string, ?int $status): string + { + return p\Mbstring::mb_output_handler((string) $string, (int) $status); + } +} +if (!\function_exists('mb_http_input') && !\function_exists('Wordlift\Modules\Common\mb_http_input')) { + function mb_http_input(?string $type = null): array|string|false + { + return p\Mbstring::mb_http_input($type); + } +} +if (!\function_exists('mb_convert_variables') && !\function_exists('Wordlift\Modules\Common\mb_convert_variables')) { + function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false + { + return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); + } +} +if (!\function_exists('mb_ord') && !\function_exists('Wordlift\Modules\Common\mb_ord')) { + function mb_ord(?string $string, ?string $encoding = null): int|false + { + return p\Mbstring::mb_ord((string) $string, $encoding); + } +} +if (!\function_exists('mb_chr') && !\function_exists('Wordlift\Modules\Common\mb_chr')) { + function mb_chr(?int $codepoint, ?string $encoding = null): string|false + { + return p\Mbstring::mb_chr((int) $codepoint, $encoding); + } +} +if (!\function_exists('mb_scrub') && !\function_exists('Wordlift\Modules\Common\mb_scrub')) { + function mb_scrub(?string $string, ?string $encoding = null): string + { + $encoding ??= \mb_internal_encoding(); + return \mb_convert_encoding((string) $string, $encoding, $encoding); + } +} +if (!\function_exists('mb_str_split') && !\function_exists('Wordlift\Modules\Common\mb_str_split')) { + function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array + { + return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); + } +} +if (!\function_exists('mb_str_pad') && !\function_exists('Wordlift\Modules\Common\mb_str_pad')) { + function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string + { + return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_ucfirst')) { + function mb_ucfirst($string, ?string $encoding = null): string + { + return p\Mbstring::mb_ucfirst($string, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_lcfirst')) { + function mb_lcfirst($string, ?string $encoding = null): string + { + return p\Mbstring::mb_lcfirst($string, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_trim')) { + function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return p\Mbstring::mb_trim($string, $characters, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_ltrim')) { + function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return p\Mbstring::mb_ltrim($string, $characters, $encoding); + } +} +if (!\function_exists('Wordlift\Modules\Common\mb_rtrim')) { + function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return p\Mbstring::mb_rtrim($string, $characters, $encoding); + } +} +if (\extension_loaded('mbstring')) { + return; +} +if (!\defined('MB_CASE_UPPER')) { + \define('MB_CASE_UPPER', 0); +} +if (!\defined('MB_CASE_LOWER')) { + \define('MB_CASE_LOWER', 1); +} +if (!\defined('MB_CASE_TITLE')) { + \define('MB_CASE_TITLE', 2); +} diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/composer.json b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/composer.json index 3a66b5a4b5..8010498e5f 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/composer.json +++ b/src/modules/common/third-party/vendor/symfony/polyfill-mbstring/composer.json @@ -22,7 +22,7 @@ } ], "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -40,9 +40,6 @@ }, "minimum-stability": "dev", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony\/polyfill", "url": "https:\/\/github.com\/symfony\/polyfill" diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php73/Php73.php b/src/modules/common/third-party/vendor/symfony/polyfill-php73/Php73.php index 78df628d4e..f7d2da484c 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php73/Php73.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php73/Php73.php @@ -26,12 +26,12 @@ final class Php73 */ public static function hrtime($asNum = \false) { - $ns = \microtime(\false); - $s = \substr($ns, 11) - self::$startAt; + $ns = microtime(\false); + $s = substr($ns, 11) - self::$startAt; $ns = 1000000000.0 * (float) $ns; if ($asNum) { $ns += $s * 1000000000.0; - return \PHP_INT_SIZE === 4 ? $ns : (int) $ns; + return (\PHP_INT_SIZE === 4) ? $ns : (int) $ns; } return [$s, (int) $ns]; } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php b/src/modules/common/third-party/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php index 8ae6d93d8f..96266b8043 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php @@ -14,4 +14,5 @@ class JsonException extends \Exception { } + \class_alias('Wordlift\Modules\Common\JsonException', 'JsonException', \false); } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php73/bootstrap.php b/src/modules/common/third-party/vendor/symfony/polyfill-php73/bootstrap.php index 6055a0a7b8..2fc0003e0b 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php73/bootstrap.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php73/bootstrap.php @@ -14,13 +14,13 @@ if (\PHP_VERSION_ID >= 70300) { return; } -if (!\function_exists('is_countable')) { +if (!\function_exists('is_countable') && !\function_exists('Wordlift\Modules\Common\is_countable')) { function is_countable($value) { return \is_array($value) || $value instanceof \Countable || $value instanceof \ResourceBundle || $value instanceof \SimpleXmlElement; } } -if (!\function_exists('hrtime')) { +if (!\function_exists('hrtime') && !\function_exists('Wordlift\Modules\Common\hrtime')) { require_once __DIR__ . '/Php73.php'; p\Php73::$startAt = (int) \microtime(\true); function hrtime($as_number = \false) @@ -28,7 +28,7 @@ function hrtime($as_number = \false) return p\Php73::hrtime($as_number); } } -if (!\function_exists('array_key_first')) { +if (!\function_exists('array_key_first') && !\function_exists('Wordlift\Modules\Common\array_key_first')) { function array_key_first(array $array) { foreach ($array as $key => $value) { @@ -36,7 +36,7 @@ function array_key_first(array $array) } } } -if (!\function_exists('array_key_last')) { +if (!\function_exists('array_key_last') && !\function_exists('Wordlift\Modules\Common\array_key_last')) { function array_key_last(array $array) { return \key(\array_slice($array, -1, 1, \true)); diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php73/composer.json b/src/modules/common/third-party/vendor/symfony/polyfill-php73/composer.json new file mode 100644 index 0000000000..cde78170c9 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php73/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony\/polyfill-php73", + "type": "library", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "keywords": [ + "polyfill", + "shim", + "compatibility", + "portable" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources\/stubs" + ] + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony\/polyfill", + "url": "https:\/\/github.com\/symfony\/polyfill" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Php80.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Php80.php index fe056de3b3..1d208d5b0f 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Php80.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Php80.php @@ -19,11 +19,11 @@ */ final class Php80 { - public static function fdiv(float $dividend, float $divisor) : float + public static function fdiv(float $dividend, float $divisor): float { return @($dividend / $divisor); } - public static function get_debug_type($value) : string + public static function get_debug_type($value): string { switch (\true) { case null === $value: @@ -43,7 +43,7 @@ public static function get_debug_type($value) : string case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; default: - if (null === ($type = @\get_resource_type($value))) { + if (null === $type = @get_resource_type($value)) { return 'unknown'; } if ('Unknown' === $type) { @@ -52,21 +52,21 @@ public static function get_debug_type($value) : string return "resource ({$type})"; } $class = \get_class($value); - if (\false === \strpos($class, '@')) { + if (\false === strpos($class, '@')) { return $class; } - return ((\get_parent_class($class) ?: \key(\class_implements($class))) ?: 'class') . '@anonymous'; + return ((get_parent_class($class) ?: key(class_implements($class))) ?: 'class') . '@anonymous'; } - public static function get_resource_id($res) : int + public static function get_resource_id($res): int { - if (!\is_resource($res) && null === @\get_resource_type($res)) { - throw new \TypeError(\sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', \get_debug_type($res))); + if (!\is_resource($res) && null === @get_resource_type($res)) { + throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); } return (int) $res; } - public static function preg_last_error_msg() : string + public static function preg_last_error_msg(): string { - switch (\preg_last_error()) { + switch (preg_last_error()) { case \PREG_INTERNAL_ERROR: return 'Internal error'; case \PREG_BAD_UTF8_ERROR: @@ -85,15 +85,15 @@ public static function preg_last_error_msg() : string return 'Unknown error'; } } - public static function str_contains(string $haystack, string $needle) : bool + public static function str_contains(string $haystack, string $needle): bool { - return '' === $needle || \false !== \strpos($haystack, $needle); + return '' === $needle || \false !== strpos($haystack, $needle); } - public static function str_starts_with(string $haystack, string $needle) : bool + public static function str_starts_with(string $haystack, string $needle): bool { - return 0 === \strncmp($haystack, $needle, \strlen($needle)); + return 0 === strncmp($haystack, $needle, \strlen($needle)); } - public static function str_ends_with(string $haystack, string $needle) : bool + public static function str_ends_with(string $haystack, string $needle): bool { if ('' === $needle || $needle === $haystack) { return \true; @@ -102,6 +102,6 @@ public static function str_ends_with(string $haystack, string $needle) : bool return \false; } $needleLength = \strlen($needle); - return $needleLength <= \strlen($haystack) && 0 === \substr_compare($haystack, $needle, -$needleLength); + return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); } } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/PhpToken.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/PhpToken.php index 6d7fcb0854..87f332d387 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/PhpToken.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/PhpToken.php @@ -10,12 +10,14 @@ */ namespace Wordlift\Modules\Common\Symfony\Polyfill\Php80; +use Wordlift\Modules\Common\Stringable; + /** * @author Fedonyuk Anton * * @internal */ -class PhpToken implements \Stringable +class PhpToken implements Stringable { /** * @var int @@ -40,17 +42,17 @@ public function __construct(int $id, string $text, int $line = -1, int $position $this->line = $line; $this->pos = $position; } - public function getTokenName() : ?string + public function getTokenName(): ?string { - if ('UNKNOWN' === ($name = \token_name($this->id))) { - $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; + if ('UNKNOWN' === $name = token_name($this->id)) { + $name = (\strlen($this->text) > 1 || \ord($this->text) < 32) ? null : $this->text; } return $name; } /** * @param int|string|array $kind */ - public function is($kind) : bool + public function is($kind): bool { foreach ((array) $kind as $value) { if (\in_array($value, [$this->id, $this->text], \true)) { @@ -59,22 +61,22 @@ public function is($kind) : bool } return \false; } - public function isIgnorable() : bool + public function isIgnorable(): bool { return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], \true); } - public function __toString() : string + public function __toString(): string { return (string) $this->text; } /** * @return static[] */ - public static function tokenize(string $code, int $flags = 0) : array + public static function tokenize(string $code, int $flags = 0): array { $line = 1; $position = 0; - $tokens = \token_get_all($code, $flags); + $tokens = token_get_all($code, $flags); foreach ($tokens as $index => $token) { if (\is_string($token)) { $id = \ord($token); diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php index c97db442ea..f6190fe6e8 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php @@ -10,7 +10,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -#[Attribute(Attribute::TARGET_CLASS)] +#[\Attribute(\Attribute::TARGET_CLASS)] final class Attribute { public const TARGET_CLASS = 1; @@ -28,3 +28,12 @@ public function __construct(int $flags = self::TARGET_ALL) $this->flags = $flags; } } +/* + * 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. + */ +\class_alias('Wordlift\Modules\Common\Attribute', 'Attribute', \false); diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php index 1d8817d4de..e7a37f13b7 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php @@ -11,7 +11,8 @@ * file that was distributed with this source code. */ if (\PHP_VERSION_ID < 80000 && \extension_loaded('tokenizer')) { - class PhpToken extends \Wordlift\Modules\Common\Symfony\Polyfill\Php80\PhpToken + class PhpToken extends Symfony\Polyfill\Php80\PhpToken { } + \class_alias('Wordlift\Modules\Common\PhpToken', 'PhpToken', \false); } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php index 060b3b3a2b..517c2db46d 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php @@ -18,4 +18,5 @@ interface Stringable */ public function __toString(); } + \class_alias('Wordlift\Modules\Common\Stringable', 'Stringable', \false); } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php index f166becb9e..dc5169a25e 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php @@ -14,4 +14,5 @@ class UnhandledMatchError extends \Error { } + \class_alias('Wordlift\Modules\Common\UnhandledMatchError', 'UnhandledMatchError', \false); } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php index 2aff89bca0..6ba0f8e451 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php @@ -14,4 +14,5 @@ class ValueError extends \Error { } + \class_alias('Wordlift\Modules\Common\ValueError', 'ValueError', \false); } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/bootstrap.php b/src/modules/common/third-party/vendor/symfony/polyfill-php80/bootstrap.php index cb8249b39d..d024c3134b 100644 --- a/src/modules/common/third-party/vendor/symfony/polyfill-php80/bootstrap.php +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/bootstrap.php @@ -17,44 +17,44 @@ if (!\defined('FILTER_VALIDATE_BOOL') && \defined('FILTER_VALIDATE_BOOLEAN')) { \define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); } -if (!\function_exists('fdiv')) { - function fdiv(float $num1, float $num2) : float +if (!\function_exists('fdiv') && !\function_exists('Wordlift\Modules\Common\fdiv')) { + function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } } -if (!\function_exists('preg_last_error_msg')) { - function preg_last_error_msg() : string +if (!\function_exists('preg_last_error_msg') && !\function_exists('Wordlift\Modules\Common\preg_last_error_msg')) { + function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } } -if (!\function_exists('str_contains')) { - function str_contains(?string $haystack, ?string $needle) : bool +if (!\function_exists('str_contains') && !\function_exists('Wordlift\Modules\Common\str_contains')) { + function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } } -if (!\function_exists('str_starts_with')) { - function str_starts_with(?string $haystack, ?string $needle) : bool +if (!\function_exists('str_starts_with') && !\function_exists('Wordlift\Modules\Common\str_starts_with')) { + function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } } -if (!\function_exists('str_ends_with')) { - function str_ends_with(?string $haystack, ?string $needle) : bool +if (!\function_exists('str_ends_with') && !\function_exists('Wordlift\Modules\Common\str_ends_with')) { + function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } } -if (!\function_exists('get_debug_type')) { - function get_debug_type($value) : string +if (!\function_exists('get_debug_type') && !\function_exists('Wordlift\Modules\Common\get_debug_type')) { + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } } -if (!\function_exists('get_resource_id')) { - function get_resource_id($resource) : int +if (!\function_exists('get_resource_id') && !\function_exists('Wordlift\Modules\Common\get_resource_id')) { + function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php80/composer.json b/src/modules/common/third-party/vendor/symfony/polyfill-php80/composer.json new file mode 100644 index 0000000000..015b7a3a39 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php80/composer.json @@ -0,0 +1,48 @@ +{ + "name": "symfony\/polyfill-php80", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "keywords": [ + "polyfill", + "shim", + "compatibility", + "portable" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources\/stubs" + ] + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony\/polyfill", + "url": "https:\/\/github.com\/symfony\/polyfill" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php81/Php81.php b/src/modules/common/third-party/vendor/symfony/polyfill-php81/Php81.php new file mode 100644 index 0000000000..8f28a25a3b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php81/Php81.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Polyfill\Php81; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class Php81 +{ + public static function array_is_list(array $array): bool + { + if ([] === $array || $array === array_values($array)) { + return \true; + } + $nextKey = -1; + foreach ($array as $k => $v) { + if ($k !== ++$nextKey) { + return \false; + } + } + return \true; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php b/src/modules/common/third-party/vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php new file mode 100644 index 0000000000..ab675f37c6 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (\PHP_VERSION_ID >= 70400 && \extension_loaded('curl')) { + /** + * @property string $data + */ + class CURLStringFile extends \CURLFile + { + private $data; + public function __construct(string $data, string $postname, string $mime = 'application/octet-stream') + { + $this->data = $data; + parent::__construct('data://application/octet-stream;base64,' . \base64_encode($data), $mime, $postname); + } + public function __set(string $name, $value): void + { + if ('data' !== $name) { + $this->{$name} = $value; + return; + } + if (\is_object($value) ? !\method_exists($value, '__toString') : !\is_scalar($value)) { + throw new \TypeError('Cannot assign ' . \gettype($value) . ' to property CURLStringFile::$data of type string'); + } + $this->name = 'data://application/octet-stream;base64,' . \base64_encode($value); + } + public function __isset(string $name): bool + { + return isset($this->{$name}); + } + public function &__get(string $name) + { + return $this->{$name}; + } + } + /** + * @property string $data + */ + \class_alias('Wordlift\Modules\Common\CURLStringFile', 'CURLStringFile', \false); +} diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php b/src/modules/common/third-party/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php new file mode 100644 index 0000000000..6decc0a114 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (\PHP_VERSION_ID < 80100) { + #[\Attribute(\Attribute::TARGET_METHOD)] + final class ReturnTypeWillChange + { + public function __construct() + { + } + } + \class_alias('Wordlift\Modules\Common\ReturnTypeWillChange', 'ReturnTypeWillChange', \false); +} diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php81/bootstrap.php b/src/modules/common/third-party/vendor/symfony/polyfill-php81/bootstrap.php new file mode 100644 index 0000000000..04fd6dda62 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php81/bootstrap.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +use Wordlift\Modules\Common\Symfony\Polyfill\Php81 as p; +if (\PHP_VERSION_ID >= 80100) { + return; +} +if (\defined('MYSQLI_REFRESH_SLAVE') && !\defined('MYSQLI_REFRESH_REPLICA')) { + \define('MYSQLI_REFRESH_REPLICA', 64); +} +if (!\function_exists('array_is_list') && !\function_exists('Wordlift\Modules\Common\array_is_list')) { + function array_is_list(array $array): bool + { + return p\Php81::array_is_list($array); + } +} +if (!\function_exists('enum_exists') && !\function_exists('Wordlift\Modules\Common\enum_exists')) { + function enum_exists(string $enum, bool $autoload = \true): bool + { + return $autoload && \class_exists($enum) && \false; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/polyfill-php81/composer.json b/src/modules/common/third-party/vendor/symfony/polyfill-php81/composer.json new file mode 100644 index 0000000000..60527bc1b2 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/polyfill-php81/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony\/polyfill-php81", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "keywords": [ + "polyfill", + "shim", + "compatibility", + "portable" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Polyfill\\Php81\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources\/stubs" + ] + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony\/polyfill", + "url": "https:\/\/github.com\/symfony\/polyfill" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/Attribute/Required.php b/src/modules/common/third-party/vendor/symfony/service-contracts/Attribute/Required.php new file mode 100644 index 0000000000..dd2106fbbe --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/Attribute/Required.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 Wordlift\Modules\Common\Symfony\Contracts\Service\Attribute; + +/** + * A required dependency. + * + * This attribute indicates that a property holds a required dependency. The annotated property or method should be + * considered during the instantiation process of the containing class. + * + * @author Alexander M. Turek + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)] +final class Required +{ +} diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/Attribute/SubscribedService.php b/src/modules/common/third-party/vendor/symfony/service-contracts/Attribute/SubscribedService.php new file mode 100644 index 0000000000..9c4b84bb9c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/Attribute/SubscribedService.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 Wordlift\Modules\Common\Symfony\Contracts\Service\Attribute; + +use Wordlift\Modules\Common\Symfony\Contracts\Service\ServiceSubscriberTrait; +/** + * Use with {@see ServiceSubscriberTrait} to mark a method's return type + * as a subscribed service. + * + * @author Kevin Bond + */ +#[\Attribute(\Attribute::TARGET_METHOD)] +final class SubscribedService +{ + /** + * @param string|null $key The key to use for the service + * If null, use "ClassName::methodName" + */ + public function __construct(public ?string $key = null) + { + } +} diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/ResetInterface.php b/src/modules/common/third-party/vendor/symfony/service-contracts/ResetInterface.php new file mode 100644 index 0000000000..7d5c57a65b --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/ResetInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Service; + +/** + * Provides a way to reset an object to its initial state. + * + * When calling the "reset()" method on an object, it should be put back to its + * initial state. This usually means clearing any internal buffers and forwarding + * the call to internal dependencies. All properties of the object should be put + * back to the same state it had when it was first ready to use. + * + * This method could be called, for example, to recycle objects that are used as + * services, so that they can be used to handle several requests in the same + * process loop (note that we advise making your services stateless instead of + * implementing this interface when possible.) + */ +interface ResetInterface +{ + public function reset(); +} diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceLocatorTrait.php b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceLocatorTrait.php new file mode 100644 index 0000000000..1654e6e261 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceLocatorTrait.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Service; + +use Wordlift\Modules\Common\Psr\Container\ContainerExceptionInterface; +use Wordlift\Modules\Common\Psr\Container\NotFoundExceptionInterface; +// Help opcache.preload discover always-needed symbols +class_exists(ContainerExceptionInterface::class); +class_exists(NotFoundExceptionInterface::class); +/** + * A trait to help implement ServiceProviderInterface. + * + * @author Robin Chalas + * @author Nicolas Grekas + */ +trait ServiceLocatorTrait +{ + private $factories; + private $loading = []; + private $providedTypes; + /** + * @param callable[] $factories + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + /** + * {@inheritdoc} + * + * @return bool + */ + public function has(string $id) + { + return isset($this->factories[$id]); + } + /** + * {@inheritdoc} + * + * @return mixed + */ + public function get(string $id) + { + if (!isset($this->factories[$id])) { + throw $this->createNotFoundException($id); + } + if (isset($this->loading[$id])) { + $ids = array_values($this->loading); + $ids = \array_slice($this->loading, array_search($id, $ids)); + $ids[] = $id; + throw $this->createCircularReferenceException($id, $ids); + } + $this->loading[$id] = $id; + try { + return $this->factories[$id]($this); + } finally { + unset($this->loading[$id]); + } + } + /** + * {@inheritdoc} + */ + public function getProvidedServices(): array + { + if (null === $this->providedTypes) { + $this->providedTypes = []; + foreach ($this->factories as $name => $factory) { + if (!\is_callable($factory)) { + $this->providedTypes[$name] = '?'; + } else { + $type = (new \ReflectionFunction($factory))->getReturnType(); + $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '') . (($type instanceof \ReflectionNamedType) ? $type->getName() : $type) : '?'; + } + } + } + return $this->providedTypes; + } + private function createNotFoundException(string $id): NotFoundExceptionInterface + { + if (!$alternatives = array_keys($this->factories)) { + $message = 'is empty...'; + } else { + $last = array_pop($alternatives); + if ($alternatives) { + $message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last); + } else { + $message = sprintf('only knows about the "%s" service.', $last); + } + } + if ($this->loading) { + $message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message); + } else { + $message = sprintf('Service "%s" not found: the current service locator %s', $id, $message); + } + return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface + { + }; + } + private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface + { + return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface + { + }; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceProviderInterface.php b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceProviderInterface.php new file mode 100644 index 0000000000..c85b765df3 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceProviderInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Service; + +use Wordlift\Modules\Common\Psr\Container\ContainerInterface; +/** + * A ServiceProviderInterface exposes the identifiers and the types of services provided by a container. + * + * @author Nicolas Grekas + * @author Mateusz Sip + */ +interface ServiceProviderInterface extends ContainerInterface +{ + /** + * Returns an associative array of service types keyed by the identifiers provided by the current container. + * + * Examples: + * + * * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface + * * ['foo' => '?'] means the container provides service name "foo" of unspecified type + * * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null + * + * @return string[] The provided service types, keyed by service names + */ + public function getProvidedServices(): array; +} diff --git a/src/modules/common/third-party/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceSubscriberInterface.php similarity index 81% rename from src/modules/common/third-party/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php rename to src/modules/common/third-party/vendor/symfony/service-contracts/ServiceSubscriberInterface.php index 73c88cb197..b0102a3614 100644 --- a/src/modules/common/third-party/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceSubscriberInterface.php @@ -8,7 +8,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Wordlift\Modules\Common\Symfony\Component\DependencyInjection; +namespace Wordlift\Modules\Common\Symfony\Contracts\Service; /** * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. @@ -34,16 +34,19 @@ interface ServiceSubscriberInterface * * * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name * internally to fetch a service which must implement Psr\Log\LoggerInterface. + * * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name + * internally to fetch an iterable of Psr\Log\LoggerInterface instances. * * ['Psr\Log\LoggerInterface'] is a shortcut for * * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface'] * * otherwise: * * * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency + * * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency * * ['?Psr\Log\LoggerInterface'] is a shortcut for * * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface'] * - * @return array The required service types, optionally keyed by service names + * @return string[] The required service types, optionally keyed by service names */ public static function getSubscribedServices(); } diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceSubscriberTrait.php b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceSubscriberTrait.php new file mode 100644 index 0000000000..bd23d8ff10 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/ServiceSubscriberTrait.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Service; + +use Wordlift\Modules\Common\Psr\Container\ContainerInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Service\Attribute\SubscribedService; +/** + * Implementation of ServiceSubscriberInterface that determines subscribed services from + * method return types. Service ids are available as "ClassName::methodName". + * + * @author Kevin Bond + */ +trait ServiceSubscriberTrait +{ + /** @var ContainerInterface */ + protected $container; + /** + * {@inheritdoc} + */ + public static function getSubscribedServices(): array + { + $services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : []; + $attributeOptIn = \false; + if (\PHP_VERSION_ID >= 80000) { + foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { + if (self::class !== $method->getDeclaringClass()->name) { + continue; + } + if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) { + continue; + } + if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { + throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name)); + } + if (!$returnType = $method->getReturnType()) { + throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); + } + $serviceId = ($returnType instanceof \ReflectionNamedType) ? $returnType->getName() : (string) $returnType; + if ($returnType->allowsNull()) { + $serviceId = '?' . $serviceId; + } + $services[$attribute->newInstance()->key ?? self::class . '::' . $method->name] = $serviceId; + $attributeOptIn = \true; + } + } + if (!$attributeOptIn) { + foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { + if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { + continue; + } + if (self::class !== $method->getDeclaringClass()->name) { + continue; + } + if (!($returnType = $method->getReturnType()) instanceof \ReflectionNamedType) { + continue; + } + if ($returnType->isBuiltin()) { + continue; + } + if (\PHP_VERSION_ID >= 80000) { + trigger_deprecation('symfony/service-contracts', '2.5', 'Using "%s" in "%s" without using the "%s" attribute on any method is deprecated.', ServiceSubscriberTrait::class, self::class, SubscribedService::class); + } + $services[self::class . '::' . $method->name] = '?' . (($returnType instanceof \ReflectionNamedType) ? $returnType->getName() : $returnType); + } + } + return $services; + } + /** + * @required + * + * @return ContainerInterface|null + */ + public function setContainer(ContainerInterface $container) + { + $ret = null; + if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) { + $ret = parent::setContainer($container); + } + $this->container = $container; + return $ret; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php b/src/modules/common/third-party/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php new file mode 100644 index 0000000000..e060af6987 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Service\Test; + +class_alias(ServiceLocatorTestCase::class, ServiceLocatorTest::class); +if (\false) { + /** + * @deprecated since PHPUnit 9.6 + */ + class ServiceLocatorTest + { + } +} diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/Test/ServiceLocatorTestCase.php b/src/modules/common/third-party/vendor/symfony/service-contracts/Test/ServiceLocatorTestCase.php new file mode 100644 index 0000000000..36cf4131b7 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/Test/ServiceLocatorTestCase.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Contracts\Service\Test; + +use Wordlift\Modules\Common\PHPUnit\Framework\TestCase; +use Wordlift\Modules\Common\Psr\Container\ContainerInterface; +use Wordlift\Modules\Common\Symfony\Contracts\Service\ServiceLocatorTrait; +abstract class ServiceLocatorTestCase extends TestCase +{ + /** + * @return ContainerInterface + */ + protected function getServiceLocator(array $factories) + { + return new class($factories) implements ContainerInterface + { + use ServiceLocatorTrait; + }; + } + public function testHas() + { + $locator = $this->getServiceLocator(['foo' => function () { + return 'bar'; + }, 'bar' => function () { + return 'baz'; + }, function () { + return 'dummy'; + }]); + $this->assertTrue($locator->has('foo')); + $this->assertTrue($locator->has('bar')); + $this->assertFalse($locator->has('dummy')); + } + public function testGet() + { + $locator = $this->getServiceLocator(['foo' => function () { + return 'bar'; + }, 'bar' => function () { + return 'baz'; + }]); + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('baz', $locator->get('bar')); + } + public function testGetDoesNotMemoize() + { + $i = 0; + $locator = $this->getServiceLocator(['foo' => function () use (&$i) { + ++$i; + return 'bar'; + }]); + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame(2, $i); + } + public function testThrowsOnUndefinedInternalService() + { + if (!$this->getExpectedException()) { + $this->expectException(\Wordlift\Modules\Common\Psr\Container\NotFoundExceptionInterface::class); + $this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.'); + } + $locator = $this->getServiceLocator(['foo' => function () use (&$locator) { + return $locator->get('bar'); + }]); + $locator->get('foo'); + } + public function testThrowsOnCircularReference() + { + $this->expectException(\Wordlift\Modules\Common\Psr\Container\ContainerExceptionInterface::class); + $this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".'); + $locator = $this->getServiceLocator(['foo' => function () use (&$locator) { + return $locator->get('bar'); + }, 'bar' => function () use (&$locator) { + return $locator->get('baz'); + }, 'baz' => function () use (&$locator) { + return $locator->get('bar'); + }]); + $locator->get('foo'); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/service-contracts/composer.json b/src/modules/common/third-party/vendor/symfony/service-contracts/composer.json new file mode 100644 index 0000000000..fbb214fcfc --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/service-contracts/composer.json @@ -0,0 +1,51 @@ +{ + "name": "symfony\/service-contracts", + "type": "library", + "description": "Generic abstractions related to writing services", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "psr\/container": "^1.1", + "symfony\/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony\/service-implementation": "" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Contracts\\Service\\": "" + } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony\/contracts", + "url": "https:\/\/github.com\/symfony\/contracts" + } + } +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/ClassNotFoundException.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/ClassNotFoundException.php new file mode 100644 index 0000000000..59b7db0158 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/ClassNotFoundException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception; + +class ClassNotFoundException extends \Exception implements ExceptionInterface +{ + public function __construct(string $class, ?\Throwable $previous = null) + { + parent::__construct(sprintf('Class "%s" not found.', $class), 0, $previous); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/ExceptionInterface.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..a2efc2dcb5 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/ExceptionInterface.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception; + +interface ExceptionInterface extends \Throwable +{ +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/NotInstantiableTypeException.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/NotInstantiableTypeException.php new file mode 100644 index 0000000000..e7bdfc528c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Exception/NotInstantiableTypeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception; + +class NotInstantiableTypeException extends \Exception implements ExceptionInterface +{ + public function __construct(string $type, ?\Throwable $previous = null) + { + parent::__construct(sprintf('Type "%s" is not instantiable.', $type), 0, $previous); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Instantiator.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Instantiator.php new file mode 100644 index 0000000000..fcc64cb837 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Instantiator.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter; + +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception\ExceptionInterface; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal\Hydrator; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal\Registry; +/** + * A utility class to create objects without calling their constructor. + * + * @author Nicolas Grekas + */ +final class Instantiator +{ + /** + * Creates an object and sets its properties without calling its constructor nor any other methods. + * + * For example: + * + * // creates an empty instance of Foo + * Instantiator::instantiate(Foo::class); + * + * // creates a Foo instance and sets one of its properties + * Instantiator::instantiate(Foo::class, ['propertyName' => $propertyValue]); + * + * // creates a Foo instance and sets a private property defined on its parent Bar class + * Instantiator::instantiate(Foo::class, [], [ + * Bar::class => ['privateBarProperty' => $propertyValue], + * ]); + * + * Instances of ArrayObject, ArrayIterator and SplObjectStorage can be created + * by using the special "\0" property name to define their internal value: + * + * // creates an SplObjectStorage where $info1 is attached to $obj1, etc. + * Instantiator::instantiate(SplObjectStorage::class, ["\0" => [$obj1, $info1, $obj2, $info2...]]); + * + * // creates an ArrayObject populated with $inputArray + * Instantiator::instantiate(ArrayObject::class, ["\0" => [$inputArray]]); + * + * @param string $class The class of the instance to create + * @param array $properties The properties to set on the instance + * @param array $privateProperties The private properties to set on the instance, + * keyed by their declaring class + * + * @throws ExceptionInterface When the instance cannot be created + */ + public static function instantiate(string $class, array $properties = [], array $privateProperties = []): object + { + $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + if (Registry::$cloneable[$class]) { + $wrappedInstance = [clone Registry::$prototypes[$class]]; + } elseif (Registry::$instantiableWithoutConstructor[$class]) { + $wrappedInstance = [$reflector->newInstanceWithoutConstructor()]; + } elseif (null === Registry::$prototypes[$class]) { + throw new NotInstantiableTypeException($class); + } elseif ($reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__unserialize'))) { + $wrappedInstance = [unserialize('C:' . \strlen($class) . ':"' . $class . '":0:{}')]; + } else { + $wrappedInstance = [unserialize('O:' . \strlen($class) . ':"' . $class . '":0:{}')]; + } + if ($properties) { + $privateProperties[$class] = isset($privateProperties[$class]) ? $properties + $privateProperties[$class] : $properties; + } + foreach ($privateProperties as $class => $properties) { + if (!$properties) { + continue; + } + foreach ($properties as $name => $value) { + // because they're also used for "unserialization", hydrators + // deal with array of instances, so we need to wrap values + $properties[$name] = [$value]; + } + (Hydrator::$hydrators[$class] ?? Hydrator::getHydrator($class))($properties, $wrappedInstance); + } + return $wrappedInstance[0]; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Exporter.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Exporter.php new file mode 100644 index 0000000000..5f479cbdae --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Exporter.php @@ -0,0 +1,358 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal; + +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; +/** + * @author Nicolas Grekas + * + * @internal + */ +class Exporter +{ + /** + * Prepares an array of values for VarExporter. + * + * For performance this method is public and has no type-hints. + * + * @param array &$values + * @param \SplObjectStorage $objectsPool + * @param array &$refsPool + * @param int &$objectsCount + * @param bool &$valuesAreStatic + * + * @throws NotInstantiableTypeException When a value cannot be serialized + */ + public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic): array + { + $refs = $values; + foreach ($values as $k => $value) { + if (\is_resource($value)) { + throw new NotInstantiableTypeException(get_resource_type($value) . ' resource'); + } + $refs[$k] = $objectsPool; + if ($isRef = !$valueIsStatic = $values[$k] !== $objectsPool) { + $values[$k] =& $value; + // Break hard references to make $values completely + unset($value); + // independent from the original structure + $refs[$k] = $value = $values[$k]; + if ($value instanceof Reference && 0 > $value->id) { + $valuesAreStatic = \false; + ++$value->count; + continue; + } + $refsPool[] = [&$refs[$k], $value, &$value]; + $refs[$k] = $values[$k] = new Reference(-\count($refsPool), $value); + } + if (\is_array($value)) { + if ($value) { + $value = self::prepare($value, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); + } + goto handle_value; + } elseif (!\is_object($value) || $value instanceof \UnitEnum) { + goto handle_value; + } + $valueIsStatic = \false; + if (isset($objectsPool[$value])) { + ++$objectsCount; + $value = new Reference($objectsPool[$value][0]); + goto handle_value; + } + $class = \get_class($value); + $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + $properties = []; + if ($reflector->hasMethod('__serialize')) { + if (!$reflector->getMethod('__serialize')->isPublic()) { + throw new \Error(sprintf('Call to %s method "%s::__serialize()".', $reflector->getMethod('__serialize')->isProtected() ? 'protected' : 'private', $class)); + } + if (!\is_array($serializeProperties = $value->__serialize())) { + throw new \TypeError($class . '::__serialize() must return an array'); + } + if ($reflector->hasMethod('__unserialize')) { + $properties = $serializeProperties; + } else { + foreach ($serializeProperties as $n => $v) { + $c = (\PHP_VERSION_ID >= 80100 && $reflector->hasProperty($n) && ($p = $reflector->getProperty($n))->isReadOnly()) ? $p->class : 'stdClass'; + $properties[$c][$n] = $v; + } + } + goto prepare_value; + } + $sleep = null; + $proto = Registry::$prototypes[$class]; + if (($value instanceof \ArrayIterator || $value instanceof \ArrayObject) && null !== $proto) { + // ArrayIterator and ArrayObject need special care because their "flags" + // option changes the behavior of the (array) casting operator. + [$arrayValue, $properties] = self::getArrayObjectProperties($value, $proto); + // populates Registry::$prototypes[$class] with a new instance + Registry::getClassReflector($class, Registry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]); + } elseif ($value instanceof \SplObjectStorage && Registry::$cloneable[$class] && null !== $proto) { + // By implementing Serializable, SplObjectStorage breaks + // internal references; let's deal with it on our own. + foreach (clone $value as $v) { + $properties[] = $v; + $properties[] = $value[$v]; + } + $properties = ['SplObjectStorage' => ["\x00" => $properties]]; + $arrayValue = (array) $value; + } elseif ($value instanceof \Serializable || $value instanceof \__PHP_Incomplete_Class || \PHP_VERSION_ID < 80200 && $value instanceof \DatePeriod) { + ++$objectsCount; + $objectsPool[$value] = [$id = \count($objectsPool), serialize($value), [], 0]; + $value = new Reference($id); + goto handle_value; + } else { + if (method_exists($class, '__sleep')) { + if (!\is_array($sleep = $value->__sleep())) { + trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', \E_USER_NOTICE); + $value = null; + goto handle_value; + } + $sleep = array_flip($sleep); + } + $arrayValue = (array) $value; + } + $proto = (array) $proto; + foreach ($arrayValue as $name => $v) { + $i = 0; + $n = (string) $name; + if ('' === $n || "\x00" !== $n[0]) { + $c = (\PHP_VERSION_ID >= 80100 && $reflector->hasProperty($n) && ($p = $reflector->getProperty($n))->isReadOnly()) ? $p->class : 'stdClass'; + } elseif ('*' === $n[1]) { + $n = substr($n, 3); + $c = $reflector->getProperty($n)->class; + if ('Error' === $c) { + $c = 'TypeError'; + } elseif ('Exception' === $c) { + $c = 'ErrorException'; + } + } else { + $i = strpos($n, "\x00", 2); + $c = substr($n, 1, $i - 1); + $n = substr($n, 1 + $i); + } + if (null !== $sleep) { + if (!isset($sleep[$name]) && (!isset($sleep[$n]) || $i && $c !== $class)) { + unset($arrayValue[$name]); + continue; + } + unset($sleep[$name], $sleep[$n]); + } + if (!\array_key_exists($name, $proto) || $proto[$name] !== $v || "\x00Error\x00trace" === $name || "\x00Exception\x00trace" === $name) { + $properties[$c][$n] = $v; + } + } + if ($sleep) { + foreach ($sleep as $n => $v) { + trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $n), \E_USER_NOTICE); + } + } + if (method_exists($class, '__unserialize')) { + $properties = $arrayValue; + } + prepare_value: + $objectsPool[$value] = [$id = \count($objectsPool)]; + $properties = self::prepare($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); + ++$objectsCount; + $objectsPool[$value] = [$id, $class, $properties, method_exists($class, '__unserialize') ? -$objectsCount : (method_exists($class, '__wakeup') ? $objectsCount : 0)]; + $value = new Reference($id); + handle_value: + if ($isRef) { + unset($value); + // Break the hard reference created above + } elseif (!$valueIsStatic) { + $values[$k] = $value; + } + $valuesAreStatic = $valueIsStatic && $valuesAreStatic; + } + return $values; + } + public static function export($value, string $indent = '') + { + switch (\true) { + case \is_int($value) || \is_float($value): + return var_export($value, \true); + case [] === $value: + return '[]'; + case \false === $value: + return 'false'; + case \true === $value: + return 'true'; + case null === $value: + return 'null'; + case '' === $value: + return "''"; + case $value instanceof \UnitEnum: + return '\\' . ltrim(var_export($value, \true), '\\'); + } + if ($value instanceof Reference) { + if (0 <= $value->id) { + return '$o[' . $value->id . ']'; + } + if (!$value->count) { + return self::export($value->value, $indent); + } + $value = -$value->id; + return '&$r[' . $value . ']'; + } + $subIndent = $indent . ' '; + if (\is_string($value)) { + $code = sprintf("'%s'", addcslashes($value, "'\\")); + $code = preg_replace_callback("/((?:[\\0\\r\\n]|‪|‫|‭|‮|⁦|⁧|⁨|‬|⁩)++)(.)/", function ($m) use ($subIndent) { + $m[1] = sprintf('\'."%s".\'', str_replace(["\x00", "\r", "\n", "‪", "‫", "‭", "‮", "⁦", "⁧", "⁨", "‬", "⁩", '\n\\'], ['\0', '\r', '\n', '\u{202A}', '\u{202B}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', '\u{202C}', '\u{2069}', '\n"' . "\n" . $subIndent . '."\\'], $m[1])); + if ("'" === $m[2]) { + return substr($m[1], 0, -2); + } + if ('n".\'' === substr($m[1], -4)) { + return substr_replace($m[1], "\n" . $subIndent . ".'" . $m[2], -2); + } + return $m[1] . $m[2]; + }, $code, -1, $count); + if ($count && str_starts_with($code, "''.")) { + $code = substr($code, 3); + } + return $code; + } + if (\is_array($value)) { + $j = -1; + $code = ''; + foreach ($value as $k => $v) { + $code .= $subIndent; + if (!\is_int($k) || 1 !== $k - $j) { + $code .= self::export($k, $subIndent) . ' => '; + } + if (\is_int($k) && $k > $j) { + $j = $k; + } + $code .= self::export($v, $subIndent) . ",\n"; + } + return "[\n" . $code . $indent . ']'; + } + if ($value instanceof Values) { + $code = $subIndent . "\$r = [],\n"; + foreach ($value->values as $k => $v) { + $code .= $subIndent . '$r[' . $k . '] = ' . self::export($v, $subIndent) . ",\n"; + } + return "[\n" . $code . $indent . ']'; + } + if ($value instanceof Registry) { + return self::exportRegistry($value, $indent, $subIndent); + } + if ($value instanceof Hydrator) { + return self::exportHydrator($value, $indent, $subIndent); + } + throw new \UnexpectedValueException(sprintf('Cannot export value of type "%s".', get_debug_type($value))); + } + private static function exportRegistry(Registry $value, string $indent, string $subIndent): string + { + $code = ''; + $serializables = []; + $seen = []; + $prototypesAccess = 0; + $factoriesAccess = 0; + $r = '\\' . Registry::class; + $j = -1; + foreach ($value->classes as $k => $class) { + if (':' === ($class[1] ?? null)) { + $serializables[$k] = $class; + continue; + } + if (!Registry::$instantiableWithoutConstructor[$class]) { + if (is_subclass_of($class, 'Serializable') && !method_exists($class, '__unserialize')) { + $serializables[$k] = 'C:' . \strlen($class) . ':"' . $class . '":0:{}'; + } else { + $serializables[$k] = 'O:' . \strlen($class) . ':"' . $class . '":0:{}'; + } + if (is_subclass_of($class, 'Throwable')) { + $eol = is_subclass_of($class, 'Error') ? "\x00Error\x00" : "\x00Exception\x00"; + $serializables[$k] = substr_replace($serializables[$k], '1:{s:' . (5 + \strlen($eol)) . ':"' . $eol . 'trace";a:0:{}}', -4); + } + continue; + } + $code .= $subIndent . ((1 !== $k - $j) ? $k . ' => ' : ''); + $j = $k; + $eol = ",\n"; + $c = '[' . self::export($class) . ']'; + if ($seen[$class] ?? \false) { + if (Registry::$cloneable[$class]) { + ++$prototypesAccess; + $code .= 'clone $p' . $c; + } else { + ++$factoriesAccess; + $code .= '$f' . $c . '()'; + } + } else { + $seen[$class] = \true; + if (Registry::$cloneable[$class]) { + $code .= 'clone (' . ($prototypesAccess++ ? '$p' : ('($p = &' . $r . '::$prototypes)')) . $c . ' ?? ' . $r . '::p'; + } else { + $code .= '(' . ($factoriesAccess++ ? '$f' : ('($f = &' . $r . '::$factories)')) . $c . ' ?? ' . $r . '::f'; + $eol = '()' . $eol; + } + $code .= '(' . substr($c, 1, -1) . '))'; + } + $code .= $eol; + } + if (1 === $prototypesAccess) { + $code = str_replace('($p = &' . $r . '::$prototypes)', $r . '::$prototypes', $code); + } + if (1 === $factoriesAccess) { + $code = str_replace('($f = &' . $r . '::$factories)', $r . '::$factories', $code); + } + if ('' !== $code) { + $code = "\n" . $code . $indent; + } + if ($serializables) { + $code = $r . '::unserialize([' . $code . '], ' . self::export($serializables, $indent) . ')'; + } else { + $code = '[' . $code . ']'; + } + return '$o = ' . $code; + } + private static function exportHydrator(Hydrator $value, string $indent, string $subIndent): string + { + $code = ''; + foreach ($value->properties as $class => $properties) { + $code .= $subIndent . ' ' . self::export($class) . ' => ' . self::export($properties, $subIndent . ' ') . ",\n"; + } + $code = [self::export($value->registry, $subIndent), self::export($value->values, $subIndent), ('' !== $code) ? "[\n" . $code . $subIndent . ']' : '[]', self::export($value->value, $subIndent), self::export($value->wakeups, $subIndent)]; + return '\\' . \get_class($value) . "::hydrate(\n" . $subIndent . implode(",\n" . $subIndent, $code) . "\n" . $indent . ')'; + } + /** + * @param \ArrayIterator|\ArrayObject $value + * @param \ArrayIterator|\ArrayObject $proto + */ + private static function getArrayObjectProperties($value, $proto): array + { + $reflector = ($value instanceof \ArrayIterator) ? 'ArrayIterator' : 'ArrayObject'; + $reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector); + $properties = [$arrayValue = (array) $value, $reflector->getMethod('getFlags')->invoke($value), ($value instanceof \ArrayObject) ? $reflector->getMethod('getIteratorClass')->invoke($value) : 'ArrayIterator']; + $reflector = $reflector->getMethod('setFlags'); + $reflector->invoke($proto, \ArrayObject::STD_PROP_LIST); + if ($properties[1] & \ArrayObject::STD_PROP_LIST) { + $reflector->invoke($value, 0); + $properties[0] = (array) $value; + } else { + $reflector->invoke($value, \ArrayObject::STD_PROP_LIST); + $arrayValue = (array) $value; + } + $reflector->invoke($value, $properties[1]); + if ([[], 0, 'ArrayIterator'] === $properties) { + $properties = []; + } else { + if ('ArrayIterator' === $properties[2]) { + unset($properties[2]); + } + $properties = [$reflector->class => ["\x00" => $properties]]; + } + return [$arrayValue, $properties]; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Hydrator.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Hydrator.php new file mode 100644 index 0000000000..c2db114a73 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Hydrator.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal; + +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception\ClassNotFoundException; +/** + * @author Nicolas Grekas + * + * @internal + */ +class Hydrator +{ + public static $hydrators = []; + public $registry; + public $values; + public $properties; + public $value; + public $wakeups; + public function __construct(?Registry $registry, ?Values $values, array $properties, $value, array $wakeups) + { + $this->registry = $registry; + $this->values = $values; + $this->properties = $properties; + $this->value = $value; + $this->wakeups = $wakeups; + } + public static function hydrate($objects, $values, $properties, $value, $wakeups) + { + foreach ($properties as $class => $vars) { + (self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects); + } + foreach ($wakeups as $k => $v) { + if (\is_array($v)) { + $objects[-$k]->__unserialize($v); + } else { + $objects[$v]->__wakeup(); + } + } + return $value; + } + public static function getHydrator($class) + { + switch ($class) { + case 'stdClass': + return self::$hydrators[$class] = static function ($properties, $objects) { + foreach ($properties as $name => $values) { + foreach ($values as $i => $v) { + $objects[$i]->{$name} = $v; + } + } + }; + case 'ErrorException': + return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class extends \ErrorException + { + }); + case 'TypeError': + return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class extends \Error + { + }); + case 'SplObjectStorage': + return self::$hydrators[$class] = static function ($properties, $objects) { + foreach ($properties as $name => $values) { + if ("\x00" === $name) { + foreach ($values as $i => $v) { + for ($j = 0; $j < \count($v); ++$j) { + $objects[$i]->attach($v[$j], $v[++$j]); + } + } + continue; + } + foreach ($values as $i => $v) { + $objects[$i]->{$name} = $v; + } + } + }; + } + if (!class_exists($class) && !interface_exists($class, \false) && !trait_exists($class, \false)) { + throw new ClassNotFoundException($class); + } + $classReflector = new \ReflectionClass($class); + switch ($class) { + case 'ArrayIterator': + case 'ArrayObject': + $constructor = \Closure::fromCallable([$classReflector->getConstructor(), 'invokeArgs']); + return self::$hydrators[$class] = static function ($properties, $objects) use ($constructor) { + foreach ($properties as $name => $values) { + if ("\x00" !== $name) { + foreach ($values as $i => $v) { + $objects[$i]->{$name} = $v; + } + } + } + foreach ($properties["\x00"] ?? [] as $i => $v) { + $constructor($objects[$i], $v); + } + }; + } + if (!$classReflector->isInternal()) { + return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, $class); + } + if ($classReflector->name !== $class) { + return self::$hydrators[$classReflector->name] ?? self::getHydrator($classReflector->name); + } + $propertySetters = []; + foreach ($classReflector->getProperties() as $propertyReflector) { + if (!$propertyReflector->isStatic()) { + $propertyReflector->setAccessible(\true); + $propertySetters[$propertyReflector->name] = \Closure::fromCallable([$propertyReflector, 'setValue']); + } + } + if (!$propertySetters) { + return self::$hydrators[$class] = self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'); + } + return self::$hydrators[$class] = static function ($properties, $objects) use ($propertySetters) { + foreach ($properties as $name => $values) { + if ($setValue = $propertySetters[$name] ?? null) { + foreach ($values as $i => $v) { + $setValue($objects[$i], $v); + } + continue; + } + foreach ($values as $i => $v) { + $objects[$i]->{$name} = $v; + } + } + }; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Reference.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Reference.php new file mode 100644 index 0000000000..c64d0131a9 --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Reference.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class Reference +{ + public $id; + public $value; + public $count = 0; + public function __construct(int $id, $value = null) + { + $this->id = $id; + $this->value = $value; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Registry.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Registry.php new file mode 100644 index 0000000000..8d83f8916d --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Registry.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal; + +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception\ClassNotFoundException; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; +/** + * @author Nicolas Grekas + * + * @internal + */ +class Registry +{ + public static $reflectors = []; + public static $prototypes = []; + public static $factories = []; + public static $cloneable = []; + public static $instantiableWithoutConstructor = []; + public $classes = []; + public function __construct(array $classes) + { + $this->classes = $classes; + } + public static function unserialize($objects, $serializables) + { + $unserializeCallback = ini_set('unserialize_callback_func', __CLASS__ . '::getClassReflector'); + try { + foreach ($serializables as $k => $v) { + $objects[$k] = unserialize($v); + } + } finally { + ini_set('unserialize_callback_func', $unserializeCallback); + } + return $objects; + } + public static function p($class) + { + self::getClassReflector($class, \true, \true); + return self::$prototypes[$class]; + } + public static function f($class) + { + $reflector = self::$reflectors[$class] ?? self::getClassReflector($class, \true, \false); + return self::$factories[$class] = \Closure::fromCallable([$reflector, 'newInstanceWithoutConstructor']); + } + public static function getClassReflector($class, $instantiableWithoutConstructor = \false, $cloneable = null) + { + if (!($isClass = class_exists($class)) && !interface_exists($class, \false) && !trait_exists($class, \false)) { + throw new ClassNotFoundException($class); + } + $reflector = new \ReflectionClass($class); + if ($instantiableWithoutConstructor) { + $proto = $reflector->newInstanceWithoutConstructor(); + } elseif (!$isClass || $reflector->isAbstract()) { + throw new NotInstantiableTypeException($class); + } elseif ($reflector->name !== $class) { + $reflector = self::$reflectors[$name = $reflector->name] ?? self::getClassReflector($name, \false, $cloneable); + self::$cloneable[$class] = self::$cloneable[$name]; + self::$instantiableWithoutConstructor[$class] = self::$instantiableWithoutConstructor[$name]; + self::$prototypes[$class] = self::$prototypes[$name]; + return self::$reflectors[$class] = $reflector; + } else { + try { + $proto = $reflector->newInstanceWithoutConstructor(); + $instantiableWithoutConstructor = \true; + } catch (\ReflectionException $e) { + $proto = ($reflector->implementsInterface('Serializable') && !method_exists($class, '__unserialize')) ? 'C:' : 'O:'; + if ('C:' === $proto && !$reflector->getMethod('unserialize')->isInternal()) { + $proto = null; + } else { + try { + $proto = @unserialize($proto . \strlen($class) . ':"' . $class . '":0:{}'); + } catch (\Exception $e) { + if (__FILE__ !== $e->getFile()) { + throw $e; + } + throw new NotInstantiableTypeException($class, $e); + } + if (\false === $proto) { + throw new NotInstantiableTypeException($class); + } + } + } + if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !method_exists($class, '__sleep') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__serialize'))) { + try { + serialize($proto); + } catch (\Exception $e) { + throw new NotInstantiableTypeException($class, $e); + } + } + } + if (null === $cloneable) { + if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !method_exists($proto, '__wakeup') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__unserialize')))) { + throw new NotInstantiableTypeException($class); + } + $cloneable = $reflector->isCloneable() && !$reflector->hasMethod('__clone'); + } + self::$cloneable[$class] = $cloneable; + self::$instantiableWithoutConstructor[$class] = $instantiableWithoutConstructor; + self::$prototypes[$class] = $proto; + if ($proto instanceof \Throwable) { + static $setTrace; + if (null === $setTrace) { + $setTrace = [new \ReflectionProperty(\Error::class, 'trace'), new \ReflectionProperty(\Exception::class, 'trace')]; + $setTrace[0]->setAccessible(\true); + $setTrace[1]->setAccessible(\true); + $setTrace[0] = \Closure::fromCallable([$setTrace[0], 'setValue']); + $setTrace[1] = \Closure::fromCallable([$setTrace[1], 'setValue']); + } + $setTrace[$proto instanceof \Exception]($proto, []); + } + return self::$reflectors[$class] = $reflector; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Values.php b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Values.php new file mode 100644 index 0000000000..973ce2d03f --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/Internal/Values.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class Values +{ + public $values; + public function __construct(array $values) + { + $this->values = $values; + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/VarExporter.php b/src/modules/common/third-party/vendor/symfony/var-exporter/VarExporter.php new file mode 100644 index 0000000000..94eb37613c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/VarExporter.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Wordlift\Modules\Common\Symfony\Component\VarExporter; + +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Exception\ExceptionInterface; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal\Exporter; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal\Hydrator; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal\Registry; +use Wordlift\Modules\Common\Symfony\Component\VarExporter\Internal\Values; +/** + * Exports serializable PHP values to PHP code. + * + * VarExporter allows serializing PHP data structures to plain PHP code (like var_export()) + * while preserving all the semantics associated with serialize() (unlike var_export()). + * + * By leveraging OPcache, the generated PHP code is faster than doing the same with unserialize(). + * + * @author Nicolas Grekas + */ +final class VarExporter +{ + /** + * Exports a serializable PHP value to PHP code. + * + * @param mixed $value The value to export + * @param bool &$isStaticValue Set to true after execution if the provided value is static, false otherwise + * @param array &$foundClasses Classes found in the value are added to this list as both keys and values + * + * @throws ExceptionInterface When the provided value cannot be serialized + */ + public static function export($value, ?bool &$isStaticValue = null, array &$foundClasses = []): string + { + $isStaticValue = \true; + if (!\is_object($value) && !(\is_array($value) && $value) && !\is_resource($value) || $value instanceof \UnitEnum) { + return Exporter::export($value); + } + $objectsPool = new \SplObjectStorage(); + $refsPool = []; + $objectsCount = 0; + try { + $value = Exporter::prepare([$value], $objectsPool, $refsPool, $objectsCount, $isStaticValue)[0]; + } finally { + $references = []; + foreach ($refsPool as $i => $v) { + if ($v[0]->count) { + $references[1 + $i] = $v[2]; + } + $v[0] = $v[1]; + } + } + if ($isStaticValue) { + return Exporter::export($value); + } + $classes = []; + $values = []; + $states = []; + foreach ($objectsPool as $i => $v) { + [, $class, $values[], $wakeup] = $objectsPool[$v]; + $foundClasses[$class] = $classes[] = $class; + if (0 < $wakeup) { + $states[$wakeup] = $i; + } elseif (0 > $wakeup) { + $states[-$wakeup] = [$i, array_pop($values)]; + $values[] = []; + } + } + ksort($states); + $wakeups = [null]; + foreach ($states as $v) { + if (\is_array($v)) { + $wakeups[-$v[0]] = $v[1]; + } else { + $wakeups[] = $v; + } + } + if (null === $wakeups[0]) { + unset($wakeups[0]); + } + $properties = []; + foreach ($values as $i => $vars) { + foreach ($vars as $class => $values) { + foreach ($values as $name => $v) { + $properties[$class][$name][$i] = $v; + } + } + } + if ($classes || $references) { + $value = new Hydrator(new Registry($classes), $references ? new Values($references) : null, $properties, $value, $wakeups); + } else { + $isStaticValue = \true; + } + return Exporter::export($value); + } +} diff --git a/src/modules/common/third-party/vendor/symfony/var-exporter/composer.json b/src/modules/common/third-party/vendor/symfony/var-exporter/composer.json new file mode 100644 index 0000000000..60cb40cb9c --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/var-exporter/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony\/var-exporter", + "type": "library", + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "keywords": [ + "export", + "serialize", + "instantiate", + "hydrate", + "construct", + "clone" + ], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony\/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony\/var-dumper": "^4.4.9|^5.0.9|^6.0" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "\/Tests\/" + ] + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Command/LintCommand.php b/src/modules/common/third-party/vendor/symfony/yaml/Command/LintCommand.php index ac755b6fba..1a12ec5312 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Command/LintCommand.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Command/LintCommand.php @@ -10,9 +10,13 @@ */ namespace Wordlift\Modules\Common\Symfony\Component\Yaml\Command; +use Wordlift\Modules\Common\Symfony\Component\Console\CI\GithubActionReporter; use Wordlift\Modules\Common\Symfony\Component\Console\Command\Command; +use Wordlift\Modules\Common\Symfony\Component\Console\Completion\CompletionInput; +use Wordlift\Modules\Common\Symfony\Component\Console\Completion\CompletionSuggestions; use Wordlift\Modules\Common\Symfony\Component\Console\Exception\InvalidArgumentException; use Wordlift\Modules\Common\Symfony\Component\Console\Exception\RuntimeException; +use Wordlift\Modules\Common\Symfony\Component\Console\Input\InputArgument; use Wordlift\Modules\Common\Symfony\Component\Console\Input\InputInterface; use Wordlift\Modules\Common\Symfony\Component\Console\Input\InputOption; use Wordlift\Modules\Common\Symfony\Component\Console\Output\OutputInterface; @@ -29,12 +33,13 @@ class LintCommand extends Command { protected static $defaultName = 'lint:yaml'; + protected static $defaultDescription = 'Lint a YAML file and outputs encountered errors'; private $parser; private $format; private $displayCorrectFiles; 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); $this->directoryIteratorProvider = $directoryIteratorProvider; @@ -45,13 +50,13 @@ public function __construct($name = null, $directoryIteratorProvider = null, $is */ protected function configure() { - $this->setDescription('Lints a file and outputs encountered errors')->addArgument('filename', null, 'A file or a directory or STDIN')->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')->addOption('parse-tags', null, InputOption::VALUE_NONE, 'Parse custom tags')->setHelp(<<setDescription(self::$defaultDescription)->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude')->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null)->setHelp(<<%command.name% command lints a YAML file and outputs to STDOUT the first encountered syntax error. You can validates YAML contents passed from STDIN: - cat filename | php %command.full_name% + cat filename | php %command.full_name% - You can also validate the syntax of a file: @@ -62,34 +67,51 @@ protected function configure() php %command.full_name% dirname php %command.full_name% dirname --format=json +You can also exclude one or more specific files: + + php %command.full_name% dirname --exclude="dirname/foo.yaml" --exclude="dirname/bar.yaml" + EOF ); } protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); - $filename = $input->getArgument('filename'); + $filenames = (array) $input->getArgument('filename'); + $excludes = $input->getOption('exclude'); $this->format = $input->getOption('format'); + $flags = $input->getOption('parse-tags'); + if ('github' === $this->format && !class_exists(GithubActionReporter::class)) { + throw new \InvalidArgumentException('The "github" format is only available since "symfony/console" >= 5.3.'); + } + if (null === $this->format) { + // Autodetect format according to CI environment + $this->format = (class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment()) ? 'github' : 'txt'; + } + $flags = $flags ? Yaml::PARSE_CUSTOM_TAGS : 0; $this->displayCorrectFiles = $output->isVerbose(); - $flags = $input->getOption('parse-tags') ? Yaml::PARSE_CUSTOM_TAGS : 0; - if (!$filename) { - if (!($stdin = $this->getStdin())) { - throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); - } - return $this->display($io, [$this->validate($stdin, $flags)]); + if (['-'] === $filenames) { + return $this->display($io, [$this->validate(file_get_contents('php://stdin'), $flags)]); } - if (!$this->isReadable($filename)) { - throw new RuntimeException(\sprintf('File or directory "%s" is not readable.', $filename)); + if (!$filenames) { + throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); } $filesInfo = []; - foreach ($this->getFiles($filename) as $file) { - $filesInfo[] = $this->validate(\file_get_contents($file), $flags, $file); + foreach ($filenames as $filename) { + if (!$this->isReadable($filename)) { + throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); + } + foreach ($this->getFiles($filename) as $file) { + if (!\in_array($file->getPathname(), $excludes, \true)) { + $filesInfo[] = $this->validate(file_get_contents($file), $flags, $file); + } + } } return $this->display($io, $filesInfo); } - private function validate($content, $flags, $file = null) + private function validate(string $content, int $flags, ?string $file = null) { - $prevErrorHandler = \set_error_handler(function ($level, $message, $file, $line) use(&$prevErrorHandler) { + $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) { if (\E_USER_DEPRECATED === $level) { throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1); } @@ -100,105 +122,112 @@ private function validate($content, $flags, $file = null) } catch (ParseException $e) { return ['file' => $file, 'line' => $e->getParsedLine(), 'valid' => \false, 'message' => $e->getMessage()]; } finally { - \restore_error_handler(); + restore_error_handler(); } return ['file' => $file, 'valid' => \true]; } - private function display(SymfonyStyle $io, array $files) + private function display(SymfonyStyle $io, array $files): int { switch ($this->format) { case 'txt': return $this->displayTxt($io, $files); case 'json': return $this->displayJson($io, $files); + case 'github': + return $this->displayTxt($io, $files, \true); default: - throw new InvalidArgumentException(\sprintf('The format "%s" is not supported.', $this->format)); + throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); } } - private function displayTxt(SymfonyStyle $io, array $filesInfo) + private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = \false): int { $countFiles = \count($filesInfo); $erroredFiles = 0; + $suggestTagOption = \false; + if ($errorAsGithubAnnotations) { + $githubReporter = new GithubActionReporter($io); + } foreach ($filesInfo as $info) { if ($info['valid'] && $this->displayCorrectFiles) { - $io->comment('OK' . ($info['file'] ? \sprintf(' in %s', $info['file']) : '')); + $io->comment('OK' . ($info['file'] ? sprintf(' in %s', $info['file']) : '')); } elseif (!$info['valid']) { ++$erroredFiles; - $io->text(' ERROR ' . ($info['file'] ? \sprintf(' in %s', $info['file']) : '')); - $io->text(\sprintf(' >> %s', $info['message'])); + $io->text(' ERROR ' . ($info['file'] ? sprintf(' in %s', $info['file']) : '')); + $io->text(sprintf(' >> %s', $info['message'])); + if (\false !== strpos($info['message'], 'PARSE_CUSTOM_TAGS')) { + $suggestTagOption = \true; + } + if ($errorAsGithubAnnotations) { + $githubReporter->error($info['message'], $info['file'] ?? 'php://stdin', $info['line']); + } } } if (0 === $erroredFiles) { - $io->success(\sprintf('All %d YAML files contain valid syntax.', $countFiles)); + $io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles)); } else { - $io->warning(\sprintf('%d YAML files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles)); + $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.%s', $countFiles - $erroredFiles, $erroredFiles, $suggestTagOption ? ' Use the --parse-tags option if you want parse custom tags.' : '')); } - return \min($erroredFiles, 1); + return min($erroredFiles, 1); } - private function displayJson(SymfonyStyle $io, array $filesInfo) + private function displayJson(SymfonyStyle $io, array $filesInfo): int { $errors = 0; - \array_walk($filesInfo, function (&$v) use(&$errors) { + array_walk($filesInfo, function (&$v) use (&$errors) { $v['file'] = (string) $v['file']; if (!$v['valid']) { ++$errors; } + if (isset($v['message']) && \false !== strpos($v['message'], 'PARSE_CUSTOM_TAGS')) { + $v['message'] .= ' Use the --parse-tags option if you want parse custom tags.'; + } }); - $io->writeln(\json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); - return \min($errors, 1); + $io->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); + return min($errors, 1); } - private function getFiles($fileOrDirectory) + private function getFiles(string $fileOrDirectory): iterable { - if (\is_file($fileOrDirectory)) { - (yield new \SplFileInfo($fileOrDirectory)); + if (is_file($fileOrDirectory)) { + yield new \SplFileInfo($fileOrDirectory); return; } foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) { if (!\in_array($file->getExtension(), ['yml', 'yaml'])) { continue; } - (yield $file); + yield $file; } } - /** - * @return string|null - */ - private function getStdin() - { - if (0 !== \ftell(\STDIN)) { - return null; - } - $inputs = ''; - while (!\feof(\STDIN)) { - $inputs .= \fread(\STDIN, 1024); - } - return $inputs; - } - private function getParser() + private function getParser(): Parser { if (!$this->parser) { $this->parser = new Parser(); } return $this->parser; } - private function getDirectoryIterator($directory) + private function getDirectoryIterator(string $directory): iterable { $default = function ($directory) { return new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), \RecursiveIteratorIterator::LEAVES_ONLY); }; if (null !== $this->directoryIteratorProvider) { - return \call_user_func($this->directoryIteratorProvider, $directory, $default); + return ($this->directoryIteratorProvider)($directory, $default); } return $default($directory); } - private function isReadable($fileOrDirectory) + private function isReadable(string $fileOrDirectory): bool { $default = function ($fileOrDirectory) { - return \is_readable($fileOrDirectory); + return is_readable($fileOrDirectory); }; if (null !== $this->isReadableProvider) { - return \call_user_func($this->isReadableProvider, $fileOrDirectory, $default); + return ($this->isReadableProvider)($fileOrDirectory, $default); } return $default($fileOrDirectory); } + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(['txt', 'json', 'github']); + } + } } diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Dumper.php b/src/modules/common/third-party/vendor/symfony/yaml/Dumper.php index a96c05ef47..ed00f2193f 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Dumper.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Dumper.php @@ -16,7 +16,7 @@ * * @author Fabien Potencier * - * @final since version 3.4 + * @final */ class Dumper { @@ -26,28 +26,13 @@ class Dumper * @var int */ 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.'); } $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. * @@ -55,55 +40,51 @@ public function setIndentation($num) * @param int $inline The level where you switch to inline YAML * @param int $indent The level of indentation (used internally) * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string - * - * @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) : ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; $dumpObjectAsInlineMap = \true; if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) { $dumpObjectAsInlineMap = empty((array) $input); } if ($inline <= 0 || !\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap || empty($input)) { $output .= $prefix . Inline::dump($input, $flags); + } elseif ($input instanceof TaggedValue) { + $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix); } else { $dumpAsMap = Inline::isHash($input); foreach ($input as $key => $value) { - if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && \false !== \strpos($value, "\n") && \false === \strpos($value, "\r")) { - // If the first line starts with a space character, the spec requires a blockIndicationIndicator - // http://www.yaml.org/spec/1.2/spec.html#id2793979 - $blockIndentationIndicator = ' ' === \substr($value, 0, 1) ? (string) $this->indentation : ''; - $output .= \sprintf("%s%s%s |%s\n", $prefix, $dumpAsMap ? Inline::dump($key, $flags) . ':' : '-', '', $blockIndentationIndicator); - foreach (\explode("\n", $value) as $row) { - $output .= \sprintf("%s%s%s\n", $prefix, \str_repeat(' ', $this->indentation), $row); + if ('' !== $output && "\n" !== $output[-1]) { + $output .= "\n"; + } + if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && \false !== strpos($value, "\n") && \false === strpos($value, "\r")) { + $blockIndentationIndicator = $this->getBlockIndentationIndicator($value); + if (isset($value[-2]) && "\n" === $value[-2] && "\n" === $value[-1]) { + $blockChompingIndicator = '+'; + } elseif ("\n" === $value[-1]) { + $blockChompingIndicator = ''; + } else { + $blockChompingIndicator = '-'; + } + $output .= sprintf('%s%s%s |%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags) . ':' : '-', '', $blockIndentationIndicator, $blockChompingIndicator); + foreach (explode("\n", $value) as $row) { + if ('' === $row) { + $output .= "\n"; + } else { + $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); + } } continue; } if ($value instanceof TaggedValue) { - $output .= \sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags) . ':' : '-', $value->getTag()); - if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && \false !== \strpos($value->getValue(), "\n") && \false === \strpos($value->getValue(), "\r\n")) { - // If the first line starts with a space character, the spec requires a blockIndicationIndicator - // http://www.yaml.org/spec/1.2/spec.html#id2793979 - $blockIndentationIndicator = ' ' === \substr($value->getValue(), 0, 1) ? (string) $this->indentation : ''; - $output .= \sprintf(" |%s\n", $blockIndentationIndicator); - foreach (\explode("\n", $value->getValue()) as $row) { - $output .= \sprintf("%s%s%s\n", $prefix, \str_repeat(' ', $this->indentation), $row); + $output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags) . ':' : '-', $value->getTag()); + if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && \false !== strpos($value->getValue(), "\n") && \false === strpos($value->getValue(), "\r\n")) { + $blockIndentationIndicator = $this->getBlockIndentationIndicator($value->getValue()); + $output .= sprintf(' |%s', $blockIndentationIndicator); + foreach (explode("\n", $value->getValue()) as $row) { + $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); } continue; } @@ -111,7 +92,7 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0) $output .= ' ' . $this->dump($value->getValue(), $inline - 1, 0, $flags) . "\n"; } else { $output .= "\n"; - $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags); + $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : ($indent + 2), $flags); } continue; } @@ -120,9 +101,38 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0) $dumpObjectAsInlineMap = empty((array) $value); } $willBeInlined = $inline - 1 <= 0 || !\is_array($value) && $dumpObjectAsInlineMap || empty($value); - $output .= \sprintf('%s%s%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags) . ':' : '-', $willBeInlined ? ' ' : "\n", $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)) . ($willBeInlined ? "\n" : ''); + $output .= sprintf('%s%s%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags) . ':' : '-', $willBeInlined ? ' ' : "\n", $this->dump($value, $inline - 1, $willBeInlined ? 0 : ($indent + $this->indentation), $flags)) . ($willBeInlined ? "\n" : ''); } } return $output; } + private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string + { + $output = sprintf('%s!%s', $prefix ? $prefix . ' ' : '', $value->getTag()); + if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && \false !== strpos($value->getValue(), "\n") && \false === strpos($value->getValue(), "\r\n")) { + $blockIndentationIndicator = $this->getBlockIndentationIndicator($value->getValue()); + $output .= sprintf(' |%s', $blockIndentationIndicator); + foreach (explode("\n", $value->getValue()) as $row) { + $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); + } + return $output; + } + if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { + return $output . ' ' . $this->dump($value->getValue(), $inline - 1, 0, $flags) . "\n"; + } + return $output . "\n" . $this->dump($value->getValue(), $inline - 1, $indent, $flags); + } + private function getBlockIndentationIndicator(string $value): string + { + $lines = explode("\n", $value); + // If the first line (that is neither empty nor contains only spaces) + // starts with a space character, the spec requires a block indentation indicator + // http://www.yaml.org/spec/1.2/spec.html#id2793979 + foreach ($lines as $line) { + if ('' !== trim($line, ' ')) { + return (' ' === substr($line, 0, 1)) ? (string) $this->indentation : ''; + } + } + return ''; + } } diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Escaper.php b/src/modules/common/third-party/vendor/symfony/yaml/Escaper.php index 70886ec1c0..598789ce9a 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Escaper.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Escaper.php @@ -21,62 +21,54 @@ class Escaper { // Characters that would cause a dumped string to require double quoting. - const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]||…| |
|
"; + public const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]||…| |
|
"; // Mapping arrays for escaping a double quoted string. The backslash is // first to ensure proper escaping because str_replace operates iteratively // on the input arrays. This ordering of the characters avoids the use of strtr, // which performs more slowly. - private static $escapees = ['\\', '\\\\', '\\"', '"', "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\t", "\n", "\v", "\f", "\r", "\x0e", "\x0f", "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", "", "…", " ", "
", "
"]; - private static $escaped = ['\\\\', '\\"', '\\\\', '\\"', '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', '\\x7f', '\\N', '\\_', '\\L', '\\P']; + private const ESCAPEES = ['\\', '\\\\', '\"', '"', "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\t", "\n", "\v", "\f", "\r", "\x0e", "\x0f", "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", "", "…", " ", "
", "
"]; + private const ESCAPED = ['\\\\', '\"', '\\\\', '\"', '\0', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\a', '\b', '\t', '\n', '\v', '\f', '\r', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\e', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f', '\N', '\_', '\L', '\P']; /** * Determines if a PHP value would require double quoting in YAML. * * @param string $value A PHP value - * - * @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); + return 0 < preg_match('/' . self::REGEX_CHARACTER_TO_ESCAPE . '/u', $value); } /** * Escapes and surrounds a PHP value with double quotes. * * @param string $value A PHP 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)); + return sprintf('"%s"', str_replace(self::ESCAPEES, self::ESCAPED, $value)); } /** * Determines if a PHP value would require single quoting in YAML. * * @param string $value A PHP 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. - if (\in_array(\strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'])) { + if (\in_array(strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'])) { return \true; } // Determines if the PHP value contains any single characters that would // cause it to require single quoting in YAML. - return 0 < \preg_match('/[ \\s \' " \\: \\{ \\} \\[ \\] , & \\* \\# \\?] | \\A[ \\- ? | < > = ! % @ ` ]/x', $value); + return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` \p{Zs}]/xu', $value); } /** * Escapes and surrounds a PHP value with single quotes. * * @param string $value A PHP 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)); + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); } } diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Exception/ExceptionInterface.php b/src/modules/common/third-party/vendor/symfony/yaml/Exception/ExceptionInterface.php index 369612c952..d147c5f8cb 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Exception/ExceptionInterface.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Exception/ExceptionInterface.php @@ -15,6 +15,6 @@ * * @author Fabien Potencier */ -interface ExceptionInterface +interface ExceptionInterface extends \Throwable { } diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Exception/ParseException.php b/src/modules/common/third-party/vendor/symfony/yaml/Exception/ParseException.php index 9383edbd80..bca19d8dd9 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Exception/ParseException.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Exception/ParseException.php @@ -22,13 +22,12 @@ class ParseException extends RuntimeException private $snippet; private $rawMessage; /** - * @param string $message The error message - * @param int $parsedLine The line where the error occurred - * @param string|null $snippet The snippet of code near the problem - * @param string|null $parsedFile The file name where the error occurred - * @param \Exception|null $previous The previous exception + * @param string $message The error message + * @param int $parsedLine The line where the error occurred + * @param string|null $snippet The snippet of code near the problem + * @param string|null $parsedFile The file name where the error occurred */ - 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, ?\Throwable $previous = null) { $this->parsedFile = $parsedFile; $this->parsedLine = $parsedLine; @@ -40,7 +39,7 @@ public function __construct($message, $parsedLine = -1, $snippet = null, $parsed /** * Gets the snippet of code near the error. * - * @return string The snippet of code + * @return string */ public function getSnippet() { @@ -48,10 +47,8 @@ public function getSnippet() } /** * Sets the snippet of code near the error. - * - * @param string $snippet The code snippet */ - public function setSnippet($snippet) + public function setSnippet(string $snippet) { $this->snippet = $snippet; $this->updateRepr(); @@ -61,7 +58,7 @@ public function setSnippet($snippet) * * This method returns null if a string is parsed. * - * @return string The filename + * @return string */ public function getParsedFile() { @@ -69,10 +66,8 @@ public function getParsedFile() } /** * Sets the filename where the error occurred. - * - * @param string $parsedFile The filename */ - public function setParsedFile($parsedFile) + public function setParsedFile(string $parsedFile) { $this->parsedFile = $parsedFile; $this->updateRepr(); @@ -80,7 +75,7 @@ public function setParsedFile($parsedFile) /** * Gets the line where the error occurred. * - * @return int The file line + * @return int */ public function getParsedLine() { @@ -88,10 +83,8 @@ public function getParsedLine() } /** * Sets the line where the error occurred. - * - * @param int $parsedLine The file line */ - public function setParsedLine($parsedLine) + public function setParsedLine(int $parsedLine) { $this->parsedLine = $parsedLine; $this->updateRepr(); @@ -100,18 +93,18 @@ private function updateRepr() { $this->message = $this->rawMessage; $dot = \false; - if ('.' === \substr($this->message, -1)) { - $this->message = \substr($this->message, 0, -1); + if ('.' === substr($this->message, -1)) { + $this->message = substr($this->message, 0, -1); $dot = \true; } if (null !== $this->parsedFile) { - $this->message .= \sprintf(' in %s', \json_encode($this->parsedFile, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE)); + $this->message .= sprintf(' in %s', json_encode($this->parsedFile, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE)); } if ($this->parsedLine >= 0) { - $this->message .= \sprintf(' at line %d', $this->parsedLine); + $this->message .= sprintf(' at line %d', $this->parsedLine); } if ($this->snippet) { - $this->message .= \sprintf(' (near "%s")', $this->snippet); + $this->message .= sprintf(' (near "%s")', $this->snippet); } if ($dot) { $this->message .= '.'; diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Inline.php b/src/modules/common/third-party/vendor/symfony/yaml/Inline.php index 50ea5e6af2..696265aea9 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Inline.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Inline.php @@ -22,19 +22,14 @@ */ class Inline { - const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; + public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; public static $parsedLineNumber = -1; public static $parsedFilename; private static $exceptionOnInvalidType = \false; private static $objectSupport = \false; private static $objectForMap = \false; private static $constantSupport = \false; - /** - * @param int $flags - * @param int|null $parsedLineNumber - * @param string|null $parsedFilename - */ - public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null) + public static function initialize(int $flags, ?int $parsedLineNumber = null, ?string $parsedFilename = null) { self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags); @@ -48,49 +43,27 @@ public static function initialize($flags, $parsedLineNumber = null, $parsedFilen /** * Converts a YAML string to a PHP value. * - * @param string $value A YAML string - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * @param array $references Mapping of variable names to values + * @param string|null $value A YAML string + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior + * @param array $references Mapping of variable names to values * - * @return mixed A PHP value + * @return mixed * * @throws ParseException */ - public static function parse($value, $flags = 0, $references = []) + public static function parse(?string $value = null, int $flags = 0, array &$references = []) { - 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 = []; - } + if (null === $value) { + return ''; } self::initialize($flags); - $value = \trim($value); + $value = trim($value); if ('' === $value) { return ''; } if (2 & (int) \ini_get('mbstring.func_overload')) { - $mbEncoding = \mb_internal_encoding(); - \mb_internal_encoding('ASCII'); + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); } try { $i = 0; @@ -105,19 +78,19 @@ public static function parse($value, $flags = 0, $references = []) ++$i; break; default: - $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references); + $result = self::parseScalar($value, $flags, null, $i, \true, $references); } // some comments are allowed at the end - if (\preg_replace('/\\s*#.*$/A', '', \substr($value, $i))) { - throw new ParseException(\sprintf('Unexpected characters near "%s".', \substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + if (preg_replace('/\s*#.*$/A', '', substr($value, $i))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } - if (null !== $tag) { + if (null !== $tag && '' !== $tag) { return new TaggedValue($tag, $result); } return $result; } finally { if (isset($mbEncoding)) { - \mb_internal_encoding($mbEncoding); + mb_internal_encoding($mbEncoding); } } } @@ -127,82 +100,74 @@ public static function parse($value, $flags = 0, $references = []) * @param mixed $value The PHP variable to convert * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string * - * @return string The YAML string representing the PHP value - * * @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) { - throw new DumpException(\sprintf('Unable to dump PHP resources in a YAML file ("%s").', \get_resource_type($value))); + throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); } - return 'null'; + return self::dumpNull($flags); case $value instanceof \DateTimeInterface: return $value->format('c'); + case $value instanceof \UnitEnum: + return sprintf('!php/const %s::%s', \get_class($value), $value->name); case \is_object($value): if ($value instanceof TaggedValue) { return '!' . $value->getTag() . ' ' . self::dump($value->getValue(), $flags); } if (Yaml::DUMP_OBJECT & $flags) { - return '!php/object ' . self::dump(\serialize($value)); + return '!php/object ' . self::dump(serialize($value)); } 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 = []; + 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) { throw new DumpException('Object support when dumping a YAML file has been disabled.'); } - return 'null'; + return self::dumpNull($flags); case \is_array($value): return self::dumpArray($value, $flags); case null === $value: - return 'null'; + return self::dumpNull($flags); case \true === $value: return 'true'; case \false === $value: return 'false'; - case \ctype_digit($value): - return \is_string($value) ? "'{$value}'" : (int) $value; - case \is_numeric($value) && \false === \strpos($value, "\f") && \false === \strpos($value, "\n") && \false === \strpos($value, "\r") && \false === \strpos($value, "\t") && \false === \strpos($value, "\v"): - $locale = \setlocale(\LC_NUMERIC, 0); + case \is_int($value): + return $value; + case is_numeric($value) && \false === strpbrk($value, "\f\n\r\t\v"): + $locale = setlocale(\LC_NUMERIC, 0); if (\false !== $locale) { - \setlocale(\LC_NUMERIC, 'C'); + setlocale(\LC_NUMERIC, 'C'); } if (\is_float($value)) { $repr = (string) $value; - if (\is_infinite($value)) { - $repr = \str_ireplace('INF', '.Inf', $repr); - } elseif (\floor($value) == $value && $repr == $value) { + if (is_infinite($value)) { + $repr = str_ireplace('INF', '.Inf', $repr); + } elseif (floor($value) == $value && $repr == $value) { // Preserve float data type since storing a whole number will result in integer value. - $repr = '!!float ' . $repr; + if (\false === strpos($repr, 'E')) { + $repr = $repr . '.0'; + } } } else { $repr = \is_string($value) ? "'{$value}'" : (string) $value; } if (\false !== $locale) { - \setlocale(\LC_NUMERIC, $locale); + setlocale(\LC_NUMERIC, $locale); } return $repr; case '' == $value: return "''"; case self::isBinaryString($value): - return '!!binary ' . \base64_encode($value); + return '!!binary ' . base64_encode($value); case Escaper::requiresDoubleQuoting($value): return Escaper::escapeWithDoubleQuotes($value); case Escaper::requiresSingleQuoting($value): @@ -217,13 +182,9 @@ 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; @@ -241,10 +202,8 @@ public static function isHash($value) * * @param array $value The PHP array to dump * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string - * - * @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)) { @@ -252,69 +211,67 @@ private static function dumpArray($value, $flags) foreach ($value as $val) { $output[] = self::dump($val, $flags); } - return \sprintf('[%s]', \implode(', ', $output)); + return sprintf('[%s]', implode(', ', $output)); } // hash $output = []; foreach ($value as $key => $val) { - $output[] = \sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); + $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); + } + return sprintf('{ %s }', implode(', ', $output)); + } + private static function dumpNull(int $flags): string + { + if (Yaml::DUMP_NULL_AS_TILDE & $flags) { + return '~'; } - return \sprintf('{ %s }', \implode(', ', $output)); + return 'null'; } /** * 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 = [], $legacyOmittedKeySupport = \false) + public static function parseScalar(string $scalar, int $flags = 0, ?array $delimiters = null, int &$i = 0, bool $evaluate = \true, array &$references = [], ?bool &$isQuoted = null) { - if (\in_array($scalar[$i], ['"', "'"])) { + if (\in_array($scalar[$i], ['"', "'"], \true)) { // quoted scalar + $isQuoted = \true; $output = self::parseQuotedScalar($scalar, $i); if (null !== $delimiters) { - $tmp = \ltrim(\substr($scalar, $i), ' '); + $tmp = ltrim(substr($scalar, $i), " \n"); if ('' === $tmp) { - throw new ParseException(\sprintf('Unexpected end of line, expected one of "%s".', \implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } if (!\in_array($tmp[0], $delimiters)) { - throw new ParseException(\sprintf('Unexpected characters (%s).', \substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } } } else { // "normal" string + $isQuoted = \false; if (!$delimiters) { - $output = \substr($scalar, $i); + $output = substr($scalar, $i); $i += \strlen($output); // remove comments - if (Parser::preg_match('/[ \\t]+#/', $output, $match, \PREG_OFFSET_CAPTURE)) { - $output = \substr($output, 0, $match[0][1]); + 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); + 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])) { - 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 ($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 ($evaluate) { - $output = self::evaluateScalar($output, $flags, $references); + $output = self::evaluateScalar($output, $flags, $references, $isQuoted); } } return $output; @@ -322,19 +279,14 @@ 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 = 0): 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); + 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); } - $output = \substr($match[0], 1, \strlen($match[0]) - 2); + $output = substr($match[0], 1, -1); $unescaper = new Unescaper(); if ('"' == $scalar[$i]) { $output = $unescaper->unescapeDoubleQuotedString($output); @@ -347,26 +299,25 @@ 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 = []) + private static function parseSequence(string $sequence, int $flags, int &$i = 0, array &$references = []): array { $output = []; $len = \strlen($sequence); ++$i; // [foo, bar, ...] + $lastToken = null; while ($i < $len) { if (']' === $sequence[$i]) { return $output; } if (',' === $sequence[$i] || ' ' === $sequence[$i]) { + if (',' === $sequence[$i] && (null === $lastToken || 'separator' === $lastToken)) { + $output[] = null; + } elseif (',' === $sequence[$i]) { + $lastToken = 'separator'; + } ++$i; continue; } @@ -381,10 +332,9 @@ private static function parseSequence($sequence, $flags, &$i = 0, $references = $value = self::parseMapping($sequence, $flags, $i, $references); break; default: - $isQuoted = \in_array($sequence[$i], ['"', "'"]); - $value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references); + $value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references, $isQuoted); // the value can be an array if a reference has been resolved to an array var - if (\is_string($value) && !$isQuoted && \false !== \strpos($value, ': ')) { + if (\is_string($value) && !$isQuoted && \false !== strpos($value, ': ')) { // embedded mapping? try { $pos = 0; @@ -393,29 +343,29 @@ private static function parseSequence($sequence, $flags, &$i = 0, $references = // no, it's not } } + if (!$isQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) { + $references[$matches['ref']] = $matches['value']; + $value = $matches['value']; + } --$i; } - if (null !== $tag) { + if (null !== $tag && '' !== $tag) { $value = new TaggedValue($tag, $value); } $output[] = $value; + $lastToken = 'value'; ++$i; } - throw new ParseException(\sprintf('Malformed inline YAML string: "%s".', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename); + throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename); } /** * 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 = []) + private static function parseMapping(string $mapping, int $flags, int &$i = 0, array &$references = []) { $output = []; $len = \strlen($mapping); @@ -426,6 +376,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = [] switch ($mapping[$i]) { case ' ': case ',': + case "\n": ++$i; continue 2; case '}': @@ -435,37 +386,33 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = [] return $output; } // key + $offsetBeforeKeyParsing = $i; $isKeyQuoted = \in_array($mapping[$i], ['"', "'"], \true); - $key = self::parseScalar($mapping, $flags, [':', ' '], $i, \false, [], \true); + $key = self::parseScalar($mapping, $flags, [':', ' '], $i, \false); + if ($offsetBeforeKeyParsing === $i) { + throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping); + } if ('!php/const' === $key) { - $key .= self::parseScalar($mapping, $flags, [':', ' '], $i, \false, [], \true); - if ('!php/const:' === $key && ':' !== $mapping[$i]) { - $key = ''; - --$i; - } else { - $key = self::evaluateScalar($key, $flags); - } + $key .= ' ' . self::parseScalar($mapping, $flags, [':'], $i, \false); + $key = self::evaluateScalar($key, $flags); } - if (':' !== $key && \false === ($i = \strpos($mapping, ':', $i))) { + if (\false === $i = strpos($mapping, ':', $i)) { break; } - 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 (!$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], [' ', ',', '[', ']', '{', '}'], \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], [' ', ',', '[', ']', '{', '}', "\n"], \true))) { + throw new ParseException('Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}").', self::$parsedLineNumber + 1, $mapping); } if ('<<' === $key) { $allowOverwrite = \true; } while ($i < $len) { - if (':' === $mapping[$i] || ' ' === $mapping[$i]) { + if (':' === $mapping[$i] || ' ' === $mapping[$i] || "\n" === $mapping[$i]) { ++$i; continue; } @@ -489,7 +436,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = [] $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 '{': @@ -508,11 +455,11 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = [] $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: - $value = self::parseScalar($mapping, $flags, [',', '}'], $i, null === $tag, $references); + $value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references, $isValueQuoted); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. @@ -520,13 +467,17 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = [] if ('<<' === $key) { $output += $value; } elseif ($allowOverwrite || !isset($output[$key])) { + if (!$isValueQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && !self::isBinaryString($value) && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) { + $references[$matches['ref']] = $matches['value']; + $value = $matches['value']; + } if (null !== $tag) { $output[$key] = new TaggedValue($tag, $value); } else { $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; } @@ -534,38 +485,35 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = [] continue 2; } } - throw new ParseException(\sprintf('Malformed inline YAML string: "%s".', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename); + throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename); } /** * Evaluates scalars and replaces magic values. * - * @param string $scalar - * @param int $flags - * @param array $references - * - * @return mixed The evaluated YAML string + * @return mixed * * @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 = []) + private static function evaluateScalar(string $scalar, int $flags, array &$references = [], ?bool &$isQuotedString = null) { - $scalar = \trim($scalar); - $scalarLower = \strtolower($scalar); - if (0 === \strpos($scalar, '*')) { - if (\false !== ($pos = \strpos($scalar, '#'))) { - $value = \substr($scalar, 1, $pos - 2); + $isQuotedString = \false; + $scalar = trim($scalar); + if (0 === strpos($scalar, '*')) { + if (\false !== $pos = strpos($scalar, '#')) { + $value = substr($scalar, 1, $pos - 2); } else { - $value = \substr($scalar, 1); + $value = substr($scalar, 1); } // an unquoted * if (\false === $value || '' === $value) { throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename); } if (!\array_key_exists($value, $references)) { - throw new ParseException(\sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } return $references[$value]; } + $scalarLower = strtolower($scalar); switch (\true) { case 'null' === $scalarLower: case '' === $scalar: @@ -577,186 +525,159 @@ private static function evaluateScalar($scalar, $flags, $references = []) 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); + case 0 === strpos($scalar, '!!str '): + $s = (string) substr($scalar, 6); + if (\in_array($s[0] ?? '', ['"', "'"], \true)) { + $isQuotedString = \true; + $s = self::parseQuotedScalar($s); } - return null; - 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 null; - case 0 === \strpos($scalar, '!php/object'): + return $s; + case 0 === strpos($scalar, '! '): + return substr($scalar, 2); + case 0 === strpos($scalar, '!php/object'): if (self::$objectSupport) { if (!isset($scalar[12])) { + trigger_deprecation('symfony/yaml', '5.1', 'Using the !php/object tag without a value is deprecated.'); return \false; } - return \unserialize(self::parseScalar(\substr($scalar, 12))); + return unserialize(self::parseScalar(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 null; - 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. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); - } - return null; - case 0 === \strpos($scalar, '!php/const'): + case 0 === strpos($scalar, '!php/const'): if (self::$constantSupport) { if (!isset($scalar[11])) { + trigger_deprecation('symfony/yaml', '5.1', 'Using the !php/const tag without a value is deprecated.'); return ''; } $i = 0; - if (\defined($const = self::parseScalar(\substr($scalar, 11), 0, null, $i, \false))) { + if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, \false))) { return \constant($const); } - throw new ParseException(\sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + 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. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } return null; - case 0 === \strpos($scalar, '!!float '): - return (float) \substr($scalar, 8); - 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); + case 0 === strpos($scalar, '!!float '): + return (float) substr($scalar, 8); + case 0 === strpos($scalar, '!!binary '): + return self::evaluateBinaryScalar(substr($scalar, 9)); + } + 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); + case preg_match('/^(?:\+|-)?0o(?P[0-7_]++)$/', $scalar, $matches): + $value = str_replace('_', '', $matches['value']); + if ('-' === $scalar[0]) { + return -octdec($value); } - // Optimize for returning strings. - // no break - case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || \is_numeric($scalar[0]): + return octdec($value); + case \in_array($scalar[0], ['+', '-', '.'], \true) || is_numeric($scalar[0]): if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) { - $scalar = \str_replace('_', '', (string) $scalar); + $scalar = str_replace('_', '', $scalar); } switch (\true) { - case \ctype_digit($scalar): - if (\preg_match('/^0[0-7]+$/', $scalar)) { - return \octdec($scalar); + case ctype_digit($scalar): + if (preg_match('/^0[0-7]+$/', $scalar)) { + trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0. Use "%s" to represent the octal number.', '0o' . substr($scalar, 1)); + return octdec($scalar); } $cast = (int) $scalar; - return $scalar === (string) $cast ? $cast : $scalar; - case '-' === $scalar[0] && \ctype_digit(\substr($scalar, 1)): - if (\preg_match('/^-0[0-7]+$/', $scalar)) { - return -\octdec(\substr($scalar, 1)); + return ($scalar === (string) $cast) ? $cast : $scalar; + case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): + if (preg_match('/^-0[0-7]+$/', $scalar)) { + trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0. Use "%s" to represent the octal number.', '-0o' . substr($scalar, 2)); + return -octdec(substr($scalar, 1)); } $cast = (int) $scalar; - return $scalar === (string) $cast ? $cast : $scalar; - case \is_numeric($scalar): + return ($scalar === (string) $cast) ? $cast : $scalar; + case is_numeric($scalar): case Parser::preg_match(self::getHexRegex(), $scalar): - $scalar = \str_replace('_', '', $scalar); - return '0x' === $scalar[0] . $scalar[1] ? \hexdec($scalar) : (float) $scalar; + $scalar = str_replace('_', '', $scalar); + return ('0x' === $scalar[0] . $scalar[1]) ? hexdec($scalar) : (float) $scalar; case '.inf' === $scalarLower: case '.nan' === $scalarLower: - return -\log(0); + 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([',', '_'], '', $scalar); + return log(0); + case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): + return (float) str_replace('_', '', $scalar); case Parser::preg_match(self::getTimestampRegex(), $scalar): - if (Yaml::PARSE_DATETIME & $flags) { + try { // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. - return new \DateTime($scalar, new \DateTimeZone('UTC')); + $time = new \DateTime($scalar, new \DateTimeZone('UTC')); + } catch (\Exception $e) { + // Some dates accepted by the regex are not valid dates. + throw new ParseException(\sprintf('The date "%s" could not be parsed as it is an invalid date.', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename, $e); + } + if (Yaml::PARSE_DATETIME & $flags) { + return $time; + } + try { + if (\false !== $scalar = $time->getTimestamp()) { + return $scalar; + } + } catch (\ValueError $e) { + // no-op } - $timeZone = \date_default_timezone_get(); - \date_default_timezone_set('UTC'); - $time = \strtotime($scalar); - \date_default_timezone_set($timeZone); - return $time; + return $time->format('U'); } } return (string) $scalar; } - /** - * @param string $value - * @param int &$i - * @param int $flags - * - * @return string|null - */ - private static function parseTag($value, &$i, $flags) + private static function parseTag(string $value, int &$i, int $flags): ?string { if ('!' !== $value[$i]) { return null; } - $tagLength = \strcspn($value, " \t\n", $i + 1); - $tag = \substr($value, $i + 1, $tagLength); + $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], ['[', '{'], \true)) && 'tagged' !== $tag) { - // Manage non-whitelisted scalars in {@link self::evaluateScalar()} + $nextOffset += strspn($value, ' ', $nextOffset); + if ('' === $tag && (!isset($value[$nextOffset]) || \in_array($value[$nextOffset], [']', '}', ','], \true))) { + throw new ParseException('Using the unquoted scalar value "!" is not supported. You must quote it.', self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + // Is followed by a scalar and is a built-in tag + if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], \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 ('' !== $tag && '!' === $tag[0]) { + throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + if ('' !== $tag && !isset($value[$i])) { + throw new ParseException(sprintf('Missing value for tag "%s".', $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); + 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)); + $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); if (0 !== \strlen($parsedBinaryData) % 4) { - throw new ParseException(\sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) { - throw new ParseException(\sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } - return \base64_decode($parsedBinaryData, \true); + return base64_decode($parsedBinaryData, \true); } - private static function isBinaryString($value) + private static function isBinaryString(string $value): bool { - return !\preg_match('//u', $value) || \preg_match('/[^\\x00\\x07-\\x0d\\x1B\\x20-\\xff]/', $value); + return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value); } /** * Gets a regex that matches a YAML date. * - * @return string The regular expression - * * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 */ - private static function getTimestampRegex() + private static function getTimestampRegex(): string { return << * - * @final since version 3.4 + * @final */ class Parser { - const TAG_PATTERN = '(?P![\\w!.\\/:-]+)'; - const BLOCK_SCALAR_HEADER_PATTERN = '(?P\\||>)(?P\\+|\\-|\\d+|\\+\\d+|\\-\\d+|\\d+\\+|\\d+\\-)?(?P +#.*)?'; + public const TAG_PATTERN = '(?P![\w!.\/:-]+)'; + public const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; + public const REFERENCE_PATTERN = '#^&(?P[^ ]++) *+(?P.*)#u'; private $filename; private $offset = 0; + private $numberOfParsedLines = 0; private $totalNumberOfLines; private $lines = []; private $currentLineNb = -1; @@ -33,40 +35,27 @@ class Parser private $skippedLineNumbers = []; private $locallySkippedLineNumbers = []; private $refsBeingParsed = []; - public function __construct() - { - if (\func_num_args() > 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. * * @param string $filename The path to the YAML file to be parsed - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior * - * @return mixed The YAML converted to a PHP value + * @return mixed * * @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)); + if (!is_file($filename)) { + throw new ParseException(sprintf('File "%s" does not exist.', $filename)); } - if (!\is_readable($filename)) { - throw new ParseException(\sprintf('File "%s" cannot be read.', $filename)); + if (!is_readable($filename)) { + throw new ParseException(sprintf('File "%s" cannot be read.', $filename)); } $this->filename = $filename; try { - return $this->parse(\file_get_contents($filename), $flags); + return $this->parse(file_get_contents($filename), $flags); } finally { $this->filename = null; } @@ -75,76 +64,51 @@ public function parseFile($filename, $flags = 0) * Parses a YAML string to a PHP value. * * @param string $value A YAML string - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior * - * @return mixed A PHP value + * @return mixed * * @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)) { + if (\false === preg_match('//u', $value)) { throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename); } $this->refs = []; $mbEncoding = null; - $e = null; - $data = null; if (2 & (int) \ini_get('mbstring.func_overload')) { - $mbEncoding = \mb_internal_encoding(); - \mb_internal_encoding('UTF-8'); + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); } try { $data = $this->doParse($value, $flags); - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - if (null !== $mbEncoding) { - \mb_internal_encoding($mbEncoding); - } - $this->lines = []; - $this->currentLine = ''; - $this->refs = []; - $this->skippedLineNumbers = []; - $this->locallySkippedLineNumbers = []; - $this->totalNumberOfLines = null; - if (null !== $e) { - throw $e; + } finally { + if (null !== $mbEncoding) { + mb_internal_encoding($mbEncoding); + } + $this->refsBeingParsed = []; + $this->offset = 0; + $this->lines = []; + $this->currentLine = ''; + $this->numberOfParsedLines = 0; + $this->refs = []; + $this->skippedLineNumbers = []; + $this->locallySkippedLineNumbers = []; + $this->totalNumberOfLines = null; } return $data; } - private function doParse($value, $flags) + private function doParse(string $value, int $flags) { $this->currentLineNb = -1; $this->currentLine = ''; $value = $this->cleanup($value); - $this->lines = \explode("\n", $value); + $this->lines = explode("\n", $value); + $this->numberOfParsedLines = \count($this->lines); $this->locallySkippedLineNumbers = []; if (null === $this->totalNumberOfLines) { - $this->totalNumberOfLines = \count($this->lines); + $this->totalNumberOfLines = $this->numberOfParsedLines; } if (!$this->moveToNextLine()) { return null; @@ -171,76 +135,73 @@ private function doParse($value, $flags) } Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename); $isRef = $mergeNode = \false; - if (self::preg_match('#^\\-((?P\\s+)(?P.+))?$#u', \rtrim($this->currentLine), $values)) { + if ('-' === $this->currentLine[0] && self::preg_match('#^\-((?P\s+)(?P.+))?$#u', rtrim($this->currentLine), $values)) { if ($context && 'mapping' == $context) { throw new ParseException('You cannot define a sequence item when in a mapping.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } $context = 'sequence'; - if (isset($values['value']) && self::preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) { $isRef = $matches['ref']; $this->refsBeingParsed[] = $isRef; $values['value'] = $matches['value']; } 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); - } elseif (null !== ($subTag = $this->getLineTag(\ltrim($values['value'], ' '), $flags))) { + if (isset($values['value']) && 0 === strpos(ltrim($values['value'], ' '), '-')) { + // Inline first child + $currentLineNumber = $this->getRealCurrentLineNb(); + $sequenceIndentation = \strlen($values['leadspaces']) + 1; + $sequenceYaml = substr($this->currentLine, $sequenceIndentation); + $sequenceYaml .= "\n" . $this->getNextEmbedBlock($sequenceIndentation, \true); + $data[] = $this->parseBlock($currentLineNumber, rtrim($sequenceYaml), $flags); + } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, \true) ?? '', $flags); + } elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) { $data[] = new TaggedValue($subTag, $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, \true), $flags)); - } else { - if (isset($values['leadspaces']) && ('!' === $values['value'][0] || self::preg_match('#^(?P' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\\{\\[].*?) *\\:(\\s+(?P.+?))?\\s*$#u', $this->trimTag($values['value']), $matches))) { - // this is a compact notation element, add to next block and parse - $block = $values['value']; - if ($this->isNextLineIndented()) { - $block .= "\n" . $this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1); - } - $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags); - } else { - $data[] = $this->parseValue($values['value'], $flags, $context); + } else if (isset($values['leadspaces']) && ('!' === $values['value'][0] || self::preg_match('#^(?P' . Inline::REGEX_QUOTED_STRING . '|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $this->trimTag($values['value']), $matches))) { + $block = $values['value']; + if ($this->isNextLineIndented() || isset($matches['value']) && '>-' === $matches['value']) { + $block .= "\n" . $this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1); } + $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags); + } else { + $data[] = $this->parseValue($values['value'], $flags, $context); } if ($isRef) { - $this->refs[$isRef] = \end($data); - \array_pop($this->refsBeingParsed); + $this->refs[$isRef] = end($data); + array_pop($this->refsBeingParsed); } - } elseif (self::preg_match('#^(?P(?:![^\\s]++\\s++)?(?:' . Inline::REGEX_QUOTED_STRING . '|(?:!?!php/const:)?[^ \'"\\[\\{!].*?)) *\\:(\\s++(?P.+))?$#u', \rtrim($this->currentLine), $values) && (\false === \strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"]))) { + } elseif (self::preg_match('#^(?P(?:![^\s]++\s++)?(?:' . Inline::REGEX_QUOTED_STRING . '|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(( |\t)++(?P.+))?$#u', rtrim($this->currentLine), $values) && (\false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"]))) { if ($context && 'sequence' == $context) { throw new ParseException('You cannot define a mapping item when in a sequence.', $this->currentLineNb + 1, $this->currentLine, $this->filename); } $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); throw $e; } 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((is_numeric($key) ? 'Numeric' : 'Non-string') . ' keys are not supported. Quote your evaluable mapping keys instead.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } // Convert float keys to strings, to avoid being converted to integers by PHP if (\is_float($key)) { $key = (string) $key; } - if ('<<' === $key && (!isset($values['value']) || !self::preg_match('#^&(?P[^ ]+)#u', $values['value'], $refMatches))) { + if ('<<' === $key && (!isset($values['value']) || '&' !== $values['value'][0] || !self::preg_match('#^&(?P[^ ]+)#u', $values['value'], $refMatches))) { $mergeNode = \true; $allowOverwrite = \true; if (isset($values['value'][0]) && '*' === $values['value'][0]) { - $refName = \substr(\rtrim($values['value']), 1); + $refName = substr(rtrim($values['value']), 1); if (!\array_key_exists($refName, $this->refs)) { - if (\false !== ($pos = \array_search($refName, $this->refsBeingParsed, \true))) { - throw new ParseException(\sprintf('Circular reference [%s, %s] detected for reference "%s".', \implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); + if (\false !== $pos = array_search($refName, $this->refsBeingParsed, \true)) { + throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$refName])), $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); } - throw new ParseException(\sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } $refValue = $this->refs[$refName]; if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) { @@ -285,7 +246,7 @@ private function doParse($value, $flags) // array union } } - } elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P[^ ]++) *+(?P.*)#u', $values['value'], $matches)) { + } elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) { $isRef = $matches['ref']; $this->refsBeingParsed[] = $isRef; $values['value'] = $matches['value']; @@ -293,7 +254,7 @@ private function doParse($value, $flags) $subTag = null; if ($mergeNode) { // Merge keys - } elseif (!isset($values['value']) || '' === $values['value'] || 0 === \strpos($values['value'], '#') || null !== ($subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { + } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || null !== ($subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { // hash // if next line is less indented or equal, then it means that the current value is null if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { @@ -306,9 +267,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; @@ -325,22 +288,67 @@ 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 { - $value = $this->parseValue(\rtrim($values['value']), $flags, $context); + $value = $this->parseValue(rtrim($values['value']), $flags, $context); // Spec: Keys MUST be unique; first one wins. // But overwriting is allowed when a merge node is used in current block. 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) { $this->refs[$isRef] = $data[$key]; - \array_pop($this->refsBeingParsed); + array_pop($this->refsBeingParsed); + } + } elseif ('"' === $this->currentLine[0] || "'" === $this->currentLine[0]) { + if (null !== $context) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + try { + return Inline::parse($this->lexInlineQuotedString(), $flags, $this->refs); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + throw $e; + } + } elseif ('{' === $this->currentLine[0]) { + if (null !== $context) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + try { + $parsedMapping = Inline::parse($this->lexInlineMapping(), $flags, $this->refs); + while ($this->moveToNextLine()) { + if (!$this->isCurrentLineEmpty()) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + } + return $parsedMapping; + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + throw $e; + } + } elseif ('[' === $this->currentLine[0]) { + if (null !== $context) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + try { + $parsedSequence = Inline::parse($this->lexInlineSequence(), $flags, $this->refs); + while ($this->moveToNextLine()) { + if (!$this->isCurrentLineEmpty()) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + } + return $parsedSequence; + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + throw $e; } } else { // multiple documents are not supported @@ -348,10 +356,10 @@ private function doParse($value, $flags) throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename); } if ($deprecatedUsage = 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) - if (\is_string($value) && $this->lines[0] === \trim($value)) { + if (\is_string($value) && $this->lines[0] === trim($value)) { try { $value = Inline::parse($this->lines[0], $flags, $this->refs); } catch (ParseException $e) { @@ -367,27 +375,31 @@ private function doParse($value, $flags) $previousLineWasTerminatedWithBackslash = \false; $value = ''; foreach ($this->lines as $line) { - if ('' !== \ltrim($line) && '#' === \ltrim($line)[0]) { + $trimmedLine = trim($line); + if ('#' === ($trimmedLine[0] ?? '')) { continue; } // If the indentation is not consistent at offset 0, it is to be considered as a ParseError if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } - if ('' === \trim($line)) { + if (\false !== strpos($line, ': ')) { + throw new ParseException('Mapping values are not allowed in multi-line blocks.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + if ('' === $trimmedLine) { $value .= "\n"; } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { $value .= ' '; } - if ('' !== \trim($line) && '\\' === \substr($line, -1)) { - $value .= \ltrim(\substr($line, 0, -1)); - } elseif ('' !== \trim($line)) { - $value .= \trim($line); + if ('' !== $trimmedLine && '\\' === substr($line, -1)) { + $value .= ltrim(substr($line, 0, -1)); + } elseif ('' !== $trimmedLine) { + $value .= $trimmedLine; } - if ('' === \trim($line)) { + if ('' === $trimmedLine) { $previousLineWasNewline = \true; $previousLineWasTerminatedWithBackslash = \false; - } elseif ('\\' === \substr($line, -1)) { + } elseif ('\\' === substr($line, -1)) { $previousLineWasNewline = \false; $previousLineWasTerminatedWithBackslash = \true; } else { @@ -396,7 +408,7 @@ private function doParse($value, $flags) } } try { - return Inline::parse(\trim($value)); + return Inline::parse(trim($value)); } catch (ParseException $e) { // fall-through to the ParseException thrown below } @@ -407,7 +419,7 @@ private function doParse($value, $flags) if (null !== $tag) { $data = new TaggedValue($tag, $data); } - if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !\is_object($data) && 'mapping' === $context) { + if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && 'mapping' === $context && !\is_object($data)) { $object = new \stdClass(); foreach ($data as $key => $value) { $object->{$key} = $value; @@ -416,7 +428,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; foreach ($this->locallySkippedLineNumbers as $lineNumber) { @@ -437,10 +449,8 @@ private function parseBlock($offset, $yaml, $flags) * Returns the current line number (takes the offset into account). * * @internal - * - * @return int The current line number */ - public function getRealCurrentLineNb() + public function getRealCurrentLineNb(): int { $realCurrentLineNumber = $this->currentLineNb + $this->offset; foreach ($this->skippedLineNumbers as $skippedLineNumber) { @@ -453,24 +463,23 @@ public function getRealCurrentLineNb() } /** * Returns the current line indentation. - * - * @return int The current line indentation */ - private function getCurrentLineIndentation() + private function getCurrentLineIndentation(): int { - return \strlen($this->currentLine) - \strlen(\ltrim($this->currentLine, ' ')); + if (' ' !== ($this->currentLine[0] ?? '')) { + return 0; + } + return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' ')); } /** * 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 - * - * @return string A YAML string + * @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 * * @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(); if (!$this->moveToNextLine()) { @@ -503,7 +512,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = \false) } $data = []; if ($this->getCurrentLineIndentation() >= $newIndent) { - $data[] = \substr($this->currentLine, $newIndent); + $data[] = substr($this->currentLine, $newIndent ?? 0); } elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) { $data[] = $this->currentLine; } else { @@ -529,11 +538,11 @@ private function getNextEmbedBlock($indentation = null, $inSequence = \false) break; } if ($this->isCurrentLineBlank()) { - $data[] = \substr($this->currentLine, $newIndent); + $data[] = substr($this->currentLine, $newIndent ?? 0); continue; } if ($indent >= $newIndent) { - $data[] = \substr($this->currentLine, $newIndent); + $data[] = substr($this->currentLine, $newIndent ?? 0); } elseif ($this->isCurrentLineComment()) { $data[] = $this->currentLine; } elseif (0 == $indent) { @@ -543,16 +552,18 @@ private function getNextEmbedBlock($indentation = null, $inSequence = \false) throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } } - return \implode("\n", $data); + return implode("\n", $data); + } + private function hasMoreLines(): bool + { + return \count($this->lines) - 1 > $this->currentLineNb; } /** * Moves the parser to the next line. - * - * @return bool */ - private function moveToNextLine() + private function moveToNextLine(): bool { - if ($this->currentLineNb >= \count($this->lines) - 1) { + if ($this->currentLineNb >= $this->numberOfParsedLines - 1) { return \false; } $this->currentLine = $this->lines[++$this->currentLineNb]; @@ -560,10 +571,8 @@ private function moveToNextLine() } /** * Moves the parser to the previous line. - * - * @return bool */ - private function moveToPreviousLine() + private function moveToPreviousLine(): bool { if ($this->currentLineNb < 1) { return \false; @@ -575,81 +584,86 @@ private function moveToPreviousLine() * Parses a YAML value. * * @param string $value A YAML value - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior * @param string $context The parser context (either sequence or mapping) * - * @return mixed A PHP value + * @return mixed * * @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, '#'))) { - $value = \substr($value, 1, $pos - 2); + if (0 === strpos($value, '*')) { + if (\false !== $pos = strpos($value, '#')) { + $value = substr($value, 1, $pos - 2); } else { - $value = \substr($value, 1); + $value = substr($value, 1); } if (!\array_key_exists($value, $this->refs)) { - if (\false !== ($pos = \array_search($value, $this->refsBeingParsed, \true))) { - throw new ParseException(\sprintf('Circular reference [%s, %s] detected for reference "%s".', \implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); + if (\false !== $pos = array_search($value, $this->refsBeingParsed, \true)) { + throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$value])), $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } - throw new ParseException(\sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } return $this->refs[$value]; } - if (self::preg_match('/^(?:' . self::TAG_PATTERN . ' +)?' . self::BLOCK_SCALAR_HEADER_PATTERN . '$/', $value, $matches)) { - $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; - $data = $this->parseBlockScalar($matches['separator'], \preg_replace('#\\d+#', '', $modifiers), \abs((int) $modifiers)); - if ('' !== $matches['tag']) { + if (\in_array($value[0], ['!', '|', '>'], \true) && self::preg_match('/^(?:' . self::TAG_PATTERN . ' +)?' . self::BLOCK_SCALAR_HEADER_PATTERN . '$/', $value, $matches)) { + $modifiers = $matches['modifiers'] ?? ''; + $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers)); + 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; } try { - $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null; - // do not take following lines into account when the current line is a quoted single line value - if (null !== $quotation && self::preg_match('/^' . $quotation . '.*' . $quotation . '(\\s*#.*)?$/', $value)) { - return Inline::parse($value, $flags, $this->refs); - } - $lines = []; - while ($this->moveToNextLine()) { - // unquoted strings end before the first unindented line - if (null === $quotation && 0 === $this->getCurrentLineIndentation()) { - $this->moveToPreviousLine(); - break; - } - $lines[] = \trim($this->currentLine); - // quoted string values end with a line that is terminated with the quotation character - $escapedLine = \str_replace(['\\\\', '\\"'], '', $this->currentLine); - if ('' !== $escapedLine && \substr($escapedLine, -1) === $quotation) { - break; - } - } - for ($i = 0, $linesCount = \count($lines), $previousLineBlank = \false; $i < $linesCount; ++$i) { - if ('' === $lines[$i]) { - $value .= "\n"; - $previousLineBlank = \true; - } elseif ($previousLineBlank) { - $value .= $lines[$i]; - $previousLineBlank = \false; - } else { - $value .= ' ' . $lines[$i]; - $previousLineBlank = \false; - } + if ('' !== $value && '{' === $value[0]) { + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); + return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs); + } elseif ('' !== $value && '[' === $value[0]) { + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); + return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs); } - Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); - $parsedValue = Inline::parse($value, $flags, $this->refs); - if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && \false !== \strpos($parsedValue, ': ')) { - throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename); + switch ($value[0] ?? '') { + case '"': + case "'": + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); + $parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs); + if (isset($this->currentLine[$cursor]) && preg_replace('/\s*(#.*)?$/A', '', substr($this->currentLine, $cursor))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor))); + } + return $parsedValue; + default: + $lines = []; + while ($this->moveToNextLine()) { + // unquoted strings end before the first unindented line + if (0 === $this->getCurrentLineIndentation()) { + $this->moveToPreviousLine(); + break; + } + $lines[] = trim($this->currentLine); + } + for ($i = 0, $linesCount = \count($lines), $previousLineBlank = \false; $i < $linesCount; ++$i) { + if ('' === $lines[$i]) { + $value .= "\n"; + $previousLineBlank = \true; + } elseif ($previousLineBlank) { + $value .= $lines[$i]; + $previousLineBlank = \false; + } else { + $value .= ' ' . $lines[$i]; + $previousLineBlank = \false; + } + } + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); + $parsedValue = Inline::parse($value, $flags, $this->refs); + if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && \false !== strpos($parsedValue, ': ')) { + throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename); + } + return $parsedValue; } - return $parsedValue; } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); @@ -662,10 +676,8 @@ private function parseValue($value, $flags, $context) * @param string $style The style indicator that was used to begin this block scalar (| or >) * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -) * @param int $indentation The indentation indicator that was used to begin this block scalar - * - * @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) { @@ -683,15 +695,16 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0) } // determine indentation if not specified if (0 === $indentation) { - if (self::preg_match('/^ +/', $this->currentLine, $matches)) { - $indentation = \strlen($matches[0]); + $currentLineLength = \strlen($this->currentLine); + for ($i = 0; $i < $currentLineLength && ' ' === $this->currentLine[$i]; ++$i) { + ++$indentation; } } if ($indentation > 0) { - $pattern = \sprintf('/^ {%d}(.*)$/', $indentation); + $pattern = sprintf('/^ {%d}(.*)$/', $indentation); while ($notEOF && ($isCurrentLineBlank || self::preg_match($pattern, $this->currentLine, $matches))) { if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) { - $blockLines[] = \substr($this->currentLine, $indentation); + $blockLines[] = substr($this->currentLine, $indentation); } elseif ($isCurrentLineBlank) { $blockLines[] = ''; } else { @@ -740,22 +753,20 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0) } } } else { - $text = \implode("\n", $blockLines); + $text = implode("\n", $blockLines); } // deal with trailing newlines if ('' === $chomping) { - $text = \preg_replace('/\\n+$/', "\n", $text); + $text = preg_replace('/\n+$/', "\n", $text); } elseif ('-' === $chomping) { - $text = \preg_replace('/\\n+$/', '', $text); + $text = preg_replace('/\n+$/', '', $text); } return $text; } /** * Returns true if the next line is indented. - * - * @return bool Returns true if the next line is indented, false otherwise */ - private function isNextLineIndented() + private function isNextLineIndented(): bool { $currentIndentation = $this->getCurrentLineIndentation(); $movements = 0; @@ -766,6 +777,9 @@ private function isNextLineIndented() } } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment())); if ($EOF) { + for ($i = 0; $i < $movements; ++$i) { + $this->moveToPreviousLine(); + } return \false; } $ret = $this->getCurrentLineIndentation() > $currentIndentation; @@ -776,34 +790,28 @@ private function isNextLineIndented() } /** * Returns true if the current line is blank or if it is a comment line. - * - * @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(); } /** * Returns true if the current line is blank. - * - * @return bool Returns true if the current line is blank, false otherwise */ - private function isCurrentLineBlank() + private function isCurrentLineBlank(): bool { - return '' == \trim($this->currentLine, ' '); + return '' === $this->currentLine || '' === trim($this->currentLine, ' '); } /** * Returns true if the current line is a comment line. - * - * @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, ' '); + // checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = ('' !== $this->currentLine && ' ' === $this->currentLine[0]) ? ltrim($this->currentLine, ' ') : $this->currentLine; return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; } - private function isCurrentLineLastLineInDocument() + private function isCurrentLineLastLineInDocument(): bool { return $this->offset + $this->currentLineNb >= $this->totalNumberOfLines - 1; } @@ -811,40 +819,36 @@ private function isCurrentLineLastLineInDocument() * Cleanups a YAML string to be parsed. * * @param string $value The input YAML string - * - * @return string A cleaned up YAML string */ - private function cleanup($value) + private function cleanup(string $value): string { - $value = \str_replace(["\r\n", "\r"], "\n", $value); + $value = str_replace(["\r\n", "\r"], "\n", $value); // strip YAML header $count = 0; - $value = \preg_replace('#^\\%YAML[: ][\\d\\.]+.*\\n#u', '', $value, -1, $count); + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); $this->offset += $count; // remove leading comments - $trimmedValue = \preg_replace('#^(\\#.*?\\n)+#s', '', $value, -1, $count); + $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); if (1 === $count) { // items have been removed, update the offset - $this->offset += \substr_count($value, "\n") - \substr_count($trimmedValue, "\n"); + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; } // remove start of the document marker (---) - $trimmedValue = \preg_replace('#^\\-\\-\\-.*?\\n#s', '', $value, -1, $count); + $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); if (1 === $count) { // items have been removed, update the offset - $this->offset += \substr_count($value, "\n") - \substr_count($trimmedValue, "\n"); + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; // remove end of the document marker (...) - $value = \preg_replace('#\\.\\.\\.\\s*$#', '', $value); + $value = preg_replace('#\.\.\.\s*$#', '', $value); } return $value; } /** * Returns true if the next line starts unindented collection. - * - * @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; @@ -865,15 +869,13 @@ private function isNextLineUnIndentedCollection() } /** * Returns true if the string is un-indented collection item. - * - * @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, '- '); + return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } /** - * A local wrapper for `preg_match` which will throw a ParseException if there + * A local wrapper for "preg_match" which will throw a ParseException if there * is an internal error in the PCRE engine. * * This avoids us needing to check for "false" every time PCRE is used @@ -885,10 +887,10 @@ private function isStringUnIndentedCollectionItem() * * @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()) { + if (\false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { + switch (preg_last_error()) { case \PREG_INTERNAL_ERROR: $error = 'Internal PCRE error.'; break; @@ -914,20 +916,17 @@ public static function preg_match($pattern, $subject, &$matches = null, $flags = /** * Trim the tag on top of the value. * - * Prevent values such as `!foo {quz: bar}` to be considered as + * 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)), ' '); + return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' '); } return $value; } - /** - * @return string|null - */ - 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 null; @@ -935,23 +934,142 @@ private function getLineTag($value, $flags, $nextLineCheck = \true) if ($nextLineCheck && !$this->isNextLineIndented()) { return null; } - $tag = \substr($matches['tag'], 1); + $tag = substr($matches['tag'], 1); // Built-in tags if ($tag && '!' === $tag[0]) { - throw new ParseException(\sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename); + throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename); } if (Yaml::PARSE_CUSTOM_TAGS & $flags) { return $tag; } - 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); + 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) + private function lexInlineQuotedString(int &$cursor = 0): string { - $message = \rtrim($message, '.'); - if (null !== $this->filename) { - $message .= ' in ' . $this->filename; + $quotation = $this->currentLine[$cursor]; + $value = $quotation; + ++$cursor; + $previousLineWasNewline = \true; + $previousLineWasTerminatedWithBackslash = \false; + $lineNumber = 0; + do { + if (++$lineNumber > 1) { + $cursor += strspn($this->currentLine, ' ', $cursor); + } + if ($this->isCurrentLineBlank()) { + $value .= "\n"; + } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { + $value .= ' '; + } + for (; \strlen($this->currentLine) > $cursor; ++$cursor) { + switch ($this->currentLine[$cursor]) { + case '\\': + if ("'" === $quotation) { + $value .= '\\'; + } elseif (isset($this->currentLine[++$cursor])) { + $value .= '\\' . $this->currentLine[$cursor]; + } + break; + case $quotation: + ++$cursor; + if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) { + $value .= "''"; + break; + } + return $value . $quotation; + default: + $value .= $this->currentLine[$cursor]; + } + } + if ($this->isCurrentLineBlank()) { + $previousLineWasNewline = \true; + $previousLineWasTerminatedWithBackslash = \false; + } elseif ('\\' === $this->currentLine[-1]) { + $previousLineWasNewline = \false; + $previousLineWasTerminatedWithBackslash = \true; + } else { + $previousLineWasNewline = \false; + $previousLineWasTerminatedWithBackslash = \false; + } + if ($this->hasMoreLines()) { + $cursor = 0; + } + } while ($this->moveToNextLine()); + throw new ParseException('Malformed inline YAML string.'); + } + private function lexUnquotedString(int &$cursor): string + { + $offset = $cursor; + $cursor += strcspn($this->currentLine, '[]{},: ', $cursor); + if ($cursor === $offset) { + throw new ParseException('Malformed unquoted YAML string.'); } - $message .= ' on line ' . ($this->getRealCurrentLineNb() + 1); - return $message . '.'; + return substr($this->currentLine, $offset, $cursor - $offset); + } + private function lexInlineMapping(int &$cursor = 0): string + { + return $this->lexInlineStructure($cursor, '}'); + } + private function lexInlineSequence(int &$cursor = 0): string + { + return $this->lexInlineStructure($cursor, ']'); + } + private function lexInlineStructure(int &$cursor, string $closingTag): string + { + $value = $this->currentLine[$cursor]; + ++$cursor; + do { + $this->consumeWhitespaces($cursor); + while (isset($this->currentLine[$cursor])) { + switch ($this->currentLine[$cursor]) { + case '"': + case "'": + $value .= $this->lexInlineQuotedString($cursor); + break; + case ':': + case ',': + $value .= $this->currentLine[$cursor]; + ++$cursor; + break; + case '{': + $value .= $this->lexInlineMapping($cursor); + break; + case '[': + $value .= $this->lexInlineSequence($cursor); + break; + case $closingTag: + $value .= $this->currentLine[$cursor]; + ++$cursor; + return $value; + case '#': + break 2; + default: + $value .= $this->lexUnquotedString($cursor); + } + if ($this->consumeWhitespaces($cursor)) { + $value .= ' '; + } + } + if ($this->hasMoreLines()) { + $cursor = 0; + } + } while ($this->moveToNextLine()); + throw new ParseException('Malformed inline YAML string.'); + } + private function consumeWhitespaces(int &$cursor): bool + { + $whitespacesConsumed = 0; + do { + $whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor); + $whitespacesConsumed += $whitespaceOnlyTokenLength; + $cursor += $whitespaceOnlyTokenLength; + if (isset($this->currentLine[$cursor])) { + return 0 < $whitespacesConsumed; + } + if ($this->hasMoreLines()) { + $cursor = 0; + } + } while ($this->moveToNextLine()); + return 0 < $whitespacesConsumed; } } diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Resources/bin/yaml-lint b/src/modules/common/third-party/vendor/symfony/yaml/Resources/bin/yaml-lint new file mode 100755 index 0000000000..0c9eec1e4d --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/yaml/Resources/bin/yaml-lint @@ -0,0 +1,35 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if ('cli' !== \PHP_SAPI) { + throw new \Exception('This script must be run from the command line.'); +} +/** + * Runs the Yaml lint command. + * + * @author Jan Schädlich + */ +use Wordlift\Modules\Common\Symfony\Component\Console\Application; +use Wordlift\Modules\Common\Symfony\Component\Yaml\Command\LintCommand; +function includeIfExists(string $file): bool +{ + return \file_exists($file) && include $file; +} +if (!includeIfExists(__DIR__ . '/../../../../autoload.php') && !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php')) { + \fwrite(\STDERR, 'Install dependencies using Composer.' . \PHP_EOL); + exit(1); +} +if (!\class_exists(Application::class)) { + \fwrite(\STDERR, 'You need the "symfony/console" component in order to run the Yaml linter.' . \PHP_EOL); + exit(1); +} +(new Application())->add($command = new LintCommand())->getApplication()->setDefaultCommand($command->getName(), \true)->run(); diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Tag/TaggedValue.php b/src/modules/common/third-party/vendor/symfony/yaml/Tag/TaggedValue.php index 32a9e7dbae..666effeabb 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Tag/TaggedValue.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Tag/TaggedValue.php @@ -18,25 +18,15 @@ 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/modules/common/third-party/vendor/symfony/yaml/Unescaper.php b/src/modules/common/third-party/vendor/symfony/yaml/Unescaper.php index 83f051d36a..c53ba3c8fe 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Unescaper.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Unescaper.php @@ -24,41 +24,35 @@ class Unescaper /** * Regex fragment that matches an escaped character in a double quoted string. */ - const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; + public const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; /** * Unescapes a single quoted string. * * @param string $value A single quoted string - * - * @return string The unescaped string */ - public function unescapeSingleQuotedString($value) + public function unescapeSingleQuotedString(string $value): string { - return \str_replace('\'\'', '\'', $value); + return str_replace('\'\'', '\'', $value); } /** * Unescapes a double quoted string. * * @param string $value A double quoted string - * - * @return string The unescaped string */ - public function unescapeDoubleQuotedString($value) + public function unescapeDoubleQuotedString(string $value): string { $callback = function ($match) { return $this->unescapeCharacter($match[0]); }; // evaluate the string - return \preg_replace_callback('/' . self::REGEX_ESCAPED_CHARACTER . '/u', $callback, $value); + return preg_replace_callback('/' . self::REGEX_ESCAPED_CHARACTER . '/u', $callback, $value); } /** * Unescapes a character that was found in a double-quoted string. * * @param string $value An escaped character - * - * @return string The unescaped character */ - private function unescapeCharacter($value) + private function unescapeCharacter(string $value): string { switch ($value[1]) { case '0': @@ -102,25 +96,21 @@ private function unescapeCharacter($value) // U+2029 PARAGRAPH SEPARATOR return "
"; case 'x': - return self::utf8chr(\hexdec(\substr($value, 2, 2))); + return self::utf8chr(hexdec(substr($value, 2, 2))); case 'u': - return self::utf8chr(\hexdec(\substr($value, 2, 4))); + return self::utf8chr(hexdec(substr($value, 2, 4))); case 'U': - return self::utf8chr(\hexdec(\substr($value, 2, 8))); + return self::utf8chr(hexdec(substr($value, 2, 8))); default: - throw new ParseException(\sprintf('Found unknown escape character "%s".', $value)); + throw new ParseException(sprintf('Found unknown escape character "%s".', $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)) { + if (0x80 > $c %= 0x200000) { return \chr($c); } if (0x800 > $c) { diff --git a/src/modules/common/third-party/vendor/symfony/yaml/Yaml.php b/src/modules/common/third-party/vendor/symfony/yaml/Yaml.php index d87910b051..c5ad03f217 100644 --- a/src/modules/common/third-party/vendor/symfony/yaml/Yaml.php +++ b/src/modules/common/third-party/vendor/symfony/yaml/Yaml.php @@ -16,25 +16,22 @@ * * @author Fabien Potencier * - * @final since version 3.4 + * @final */ class Yaml { - const DUMP_OBJECT = 1; - const PARSE_EXCEPTION_ON_INVALID_TYPE = 2; - const PARSE_OBJECT = 4; - const PARSE_OBJECT_FOR_MAP = 8; - const DUMP_EXCEPTION_ON_INVALID_TYPE = 16; - const PARSE_DATETIME = 32; - const DUMP_OBJECT_AS_MAP = 64; - const DUMP_MULTI_LINE_LITERAL_BLOCK = 128; - const PARSE_CONSTANT = 256; - 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; + public const DUMP_OBJECT = 1; + public const PARSE_EXCEPTION_ON_INVALID_TYPE = 2; + public const PARSE_OBJECT = 4; + public const PARSE_OBJECT_FOR_MAP = 8; + public const DUMP_EXCEPTION_ON_INVALID_TYPE = 16; + public const PARSE_DATETIME = 32; + public const DUMP_OBJECT_AS_MAP = 64; + public const DUMP_MULTI_LINE_LITERAL_BLOCK = 128; + public const PARSE_CONSTANT = 256; + public const PARSE_CUSTOM_TAGS = 512; + public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024; + public const DUMP_NULL_AS_TILDE = 2048; /** * Parses a YAML file into a PHP value. * @@ -46,11 +43,11 @@ class Yaml * @param string $filename The path to the YAML file to be parsed * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior * - * @return mixed The YAML converted to a PHP value + * @return mixed * * @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(); return $yaml->parseFile($filename, $flags); @@ -67,32 +64,12 @@ public static function parseFile($filename, $flags = 0) * @param string $input A string containing YAML * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior * - * @return mixed The YAML converted to a PHP value + * @return mixed * * @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); } @@ -106,25 +83,9 @@ public static function parse($input, $flags = 0) * @param int $inline The level where you switch to inline YAML * @param int $indent The amount of spaces to use for indentation of nested nodes * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string - * - * @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/modules/common/third-party/vendor/symfony/yaml/composer.json b/src/modules/common/third-party/vendor/symfony/yaml/composer.json new file mode 100644 index 0000000000..94f3916fed --- /dev/null +++ b/src/modules/common/third-party/vendor/symfony/yaml/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony\/yaml", + "type": "library", + "description": "Loads and dumps YAML files", + "keywords": [], + "homepage": "https:\/\/symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https:\/\/symfony.com\/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony\/deprecation-contracts": "^2.1|^3", + "symfony\/polyfill-ctype": "^1.8" + }, + "require-dev": { + "symfony\/console": "^5.3|^6.0" + }, + "conflict": { + "symfony\/console": "<5.3" + }, + "suggest": { + "symfony\/console": "For validating YAML files using the lint command" + }, + "autoload": { + "psr-4": { + "Wordlift\\Modules\\Common\\Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "\/Tests\/" + ] + }, + "bin": [ + "Resources\/bin\/yaml-lint" + ], + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/src/modules/dashboard/includes/Common/Cursor.php b/src/modules/dashboard/includes/Common/Cursor.php index 1bcc8c2b21..060a7c1056 100644 --- a/src/modules/dashboard/includes/Common/Cursor.php +++ b/src/modules/dashboard/includes/Common/Cursor.php @@ -126,6 +126,7 @@ public function __unserialize( array $data ) { $this->set_direction( $data['direction'] ); } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'position' => $this->position, diff --git a/src/modules/dashboard/includes/Common/Page.php b/src/modules/dashboard/includes/Common/Page.php index fd8fcfba0d..94b9664662 100644 --- a/src/modules/dashboard/includes/Common/Page.php +++ b/src/modules/dashboard/includes/Common/Page.php @@ -59,6 +59,7 @@ public function __unserialize( array $data ) { $this->items = $data['items']; } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'self' => $this->self, diff --git a/src/modules/dashboard/includes/Match/Match_Entry.php b/src/modules/dashboard/includes/Match/Match_Entry.php index 17191879df..fdcb5146a2 100644 --- a/src/modules/dashboard/includes/Match/Match_Entry.php +++ b/src/modules/dashboard/includes/Match/Match_Entry.php @@ -97,6 +97,7 @@ private function get_name() { return $data['name']; } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'id' => $this->id, diff --git a/src/modules/dashboard/includes/Post_Entity_Match/Cursor.php b/src/modules/dashboard/includes/Post_Entity_Match/Cursor.php index 07b089c24d..2fdff054e3 100644 --- a/src/modules/dashboard/includes/Post_Entity_Match/Cursor.php +++ b/src/modules/dashboard/includes/Post_Entity_Match/Cursor.php @@ -126,6 +126,7 @@ public function __unserialize( array $data ) { $this->set_direction( $data['direction'] ); } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'position' => $this->position, diff --git a/src/modules/dashboard/includes/Post_Entity_Match/Page.php b/src/modules/dashboard/includes/Post_Entity_Match/Page.php index 3959d09779..22dd52591b 100644 --- a/src/modules/dashboard/includes/Post_Entity_Match/Page.php +++ b/src/modules/dashboard/includes/Post_Entity_Match/Page.php @@ -59,6 +59,7 @@ public function __unserialize( array $data ) { $this->items = $data['items']; } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'self' => $this->self, diff --git a/src/modules/dashboard/includes/Stats/Stats_Settings.php b/src/modules/dashboard/includes/Stats/Stats_Settings.php index aec9e922f7..9981ff71e2 100644 --- a/src/modules/dashboard/includes/Stats/Stats_Settings.php +++ b/src/modules/dashboard/includes/Stats/Stats_Settings.php @@ -33,6 +33,7 @@ public function __construct( $description, $title, $label, $color, $show_all_lin $this->lifted = $lifted; } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'description' => $this->description, diff --git a/src/modules/dashboard/includes/Synchronization/Synchronization.php b/src/modules/dashboard/includes/Synchronization/Synchronization.php index 7bae65777c..164a9c37f4 100644 --- a/src/modules/dashboard/includes/Synchronization/Synchronization.php +++ b/src/modules/dashboard/includes/Synchronization/Synchronization.php @@ -228,6 +228,7 @@ public function unserialize( $data ) { $this->__unserialize( unserialize( $data ) ); } + #[\ReturnTypeWillChange] public function jsonSerialize() { return array( 'created_at' => is_a( $this->created_at, 'DateTimeInterface' ) diff --git a/src/modules/gardening-kg/includes/Jsonld.php b/src/modules/gardening-kg/includes/Jsonld.php index 8d1b3d7247..7dff85ca70 100644 --- a/src/modules/gardening-kg/includes/Jsonld.php +++ b/src/modules/gardening-kg/includes/Jsonld.php @@ -65,6 +65,10 @@ private function add_about_jsonld( &$list, $content_id ) { $about_jsonld = $this->content_service->get_about_jsonld( $content_id ); + if ( empty( $about_jsonld ) ) { + return; + } + $decoded_jsonld = json_decode( $about_jsonld, true ); if ( null === $decoded_jsonld ) { diff --git a/src/readme.txt b/src/readme.txt index 5d76e38393..efc01b4327 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -5,7 +5,7 @@ Contributors: wordlift, insideout10, ziodave Tags: SEO, structured data, ai, linked data, semantic web Requires at least: 5.3 Tested up to: 6.6 -Requires PHP: 5.6 +Requires PHP: 7.4 Stable tag: 3.52.9 License: GPLv2 or later diff --git a/src/wordlift.php b/src/wordlift.php index eac6be4fcb..fc337aa1aa 100644 --- a/src/wordlift.php +++ b/src/wordlift.php @@ -16,6 +16,8 @@ * Plugin URI: https://wordlift.io * Description: WordLift brings the power of AI to organize content, attract new readers and get their attention. To activate the plugin visit our website. * Version: 3.52.9 + * Requires PHP: 7.4 + * Requires at least: 5.3 * Author: WordLift * Author URI: https://wordlift.io * License: GPL-2.0+ @@ -298,7 +300,13 @@ function wl_enqueue_leaflet( $in_footer = false ) { wp_enqueue_script( 'wl-leaflet', plugin_dir_url( __FILE__ ) . 'js/leaflet/leaflet.js', array(), '1.6.0', $in_footer ); } -add_filter( 'block_categories', 'wl_block_categories', 10 ); +add_filter( + version_compare( get_bloginfo( 'version' ), '5.8', '>=' ) + ? 'block_categories_all' + : 'block_categories', + 'wl_block_categories', + 10 +); // Temporary fix for a typo in WooCommerce Extension. add_filter(