diff --git a/lefthook.yml b/lefthook.yml index 1c582c06d..87b832798 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -13,15 +13,14 @@ pre-push: parallel: true jobs: - name: phpcs - files: git diff --name-only HEAD @{push} - run: ./vendor/bin/phpcs + run: ./vendor/bin/phpcs {push_files} glob: - ".phpstorm.meta.php" - "local-config-sample.php" - "wp-config.php" - "wp-config-environment.php" - - "wp-content/themes/core/*.php" - - "wp-content/plugins/core/*.php" + - "wp-content/themes/core/**/*.php" + - "wp-content/plugins/core/**/*.php" - name: phpstan run: ./vendor/bin/phpstan analyse --memory-limit=-1 glob: @@ -29,27 +28,23 @@ pre-push: - "local-config-sample.php" - "wp-config.php" - "wp-config-environment.php" - - "wp-content/themes/core/*.php" - - "wp-content/plugins/core/*.php" + - "wp-content/themes/core/**/*.php" + - "wp-content/plugins/core/**/*.php" - name: lintcss - files: git diff --name-only HEAD @{push} - run: ./node_modules/.bin/wp-scripts lint-style + run: ./node_modules/.bin/wp-scripts lint-style {push_files} glob: - - "wp-content/themes/core/*.pcss" + - "wp-content/themes/core/**/*.pcss" - name: lintjs - files: git diff --name-only HEAD @{push} - run: ./node_modules/.bin/wp-scripts lint-js + run: ./node_modules/.bin/wp-scripts lint-js {push_files} glob: - - "wp-content/themes/core/*.js" + - "wp-content/themes/core/**/*.js" - name: lintconfigs - files: git diff --name-only HEAD @{push} - run: ./node_modules/.bin/wp-scripts lint-js + run: ./node_modules/.bin/wp-scripts lint-js {push_files} glob: - "browsersync.config.js" - "postcss.config.js" - "webpack.config.js" - name: lintpkgjson - files: git diff --name-only HEAD @{push} run: ./node_modules/.bin/wp-scripts lint-pkg-json glob: - "package.json" diff --git a/wp-content/plugins/pattern-sync/README.md b/wp-content/plugins/pattern-sync/README.md new file mode 100644 index 000000000..2fd17ca2f --- /dev/null +++ b/wp-content/plugins/pattern-sync/README.md @@ -0,0 +1,34 @@ +# Pattern Sync + +Sync registered block patterns between WordPress installations via REST API and Application Passwords. + +## Requirements + +- PHP 8.2+ +- WordPress 5.6+ (Application Passwords) +- Composer (for installation) + +## Installation + +1. Install dependencies: `composer install` in the plugin directory. +2. Activate the plugin in WordPress. +3. Go to **Tools → Pattern Sync** to add a connection and sync patterns. + +## Usage + +1. **Add a connection**: Enter a connection name, the remote site URL, WordPress username, and an Application Password (created under Users → Profile → Application Passwords on the remote site). +2. **Test connection**: Use the "Test connection" button to verify the remote site has Pattern Sync and accepts your credentials. +3. **Sync patterns**: Click "Sync with this site" on a connection. Select patterns from the remote or local list and use "Pull selected to this site" or "Push selected to remote". +4. **Sync Log**: View recent sync runs under "Sync Log". + +## REST API + +The plugin registers `pattern-sync/v1` REST routes (authenticated via Application Passwords): + +- `GET /wp-json/pattern-sync/v1/patterns` – List registered block patterns +- `GET /wp-json/pattern-sync/v1/pattern-categories` – List pattern categories +- `POST /wp-json/pattern-sync/v1/patterns` – Store a pattern (used when another site pushes to this one) + +## License + +GPL-2.0-or-later diff --git a/wp-content/plugins/pattern-sync/composer.json b/wp-content/plugins/pattern-sync/composer.json new file mode 100644 index 000000000..9f011a862 --- /dev/null +++ b/wp-content/plugins/pattern-sync/composer.json @@ -0,0 +1,18 @@ +{ + "name": "pattern-sync/pattern-sync", + "description": "Sync registered block patterns between WordPress installations.", + "type": "wordpress-plugin", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.2", + "php-di/php-di": "^7.1" + }, + "autoload": { + "psr-4": { + "PatternSync\\": "src/" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/wp-content/plugins/pattern-sync/composer.lock b/wp-content/plugins/pattern-sync/composer.lock new file mode 100644 index 000000000..505c2e7de --- /dev/null +++ b/wp-content/plugins/pattern-sync/composer.lock @@ -0,0 +1,263 @@ +{ + "_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": "1a2a4fb435ff4ea6b40c887a44d342a2", + "packages": [ + { + "name": "laravel/serializable-closure", + "version": "v2.0.9", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "8f631589ab07b7b52fead814965f5a800459cb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/8f631589ab07b7b52fead814965f5a800459cb3e", + "reference": "8f631589ab07b7b52fead814965f5a800459cb3e", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2026-02-03T06:55:34+00:00" + }, + { + "name": "php-di/invoker", + "version": "2.3.7", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/3c1ddfdef181431fbc4be83378f6d036d59e81e1", + "reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "psr/container": "^1.0|^2.0" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0 || ^10 || ^11 || ^12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Invoker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", + "keywords": [ + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" + ], + "support": { + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.7" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2025-08-30T10:22:22+00:00" + }, + { + "name": "php-di/php-di", + "version": "7.1.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PHP-DI.git", + "reference": "f88054cc052e40dbe7b383c8817c19442d480352" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/f88054cc052e40dbe7b383c8817c19442d480352", + "reference": "f88054cc052e40dbe7b383c8817c19442d480352", + "shasum": "" + }, + "require": { + "laravel/serializable-closure": "^1.0 || ^2.0", + "php": ">=8.0", + "php-di/invoker": "^2.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3", + "friendsofphp/proxy-manager-lts": "^1", + "mnapoli/phpunit-easymock": "^1.3", + "phpunit/phpunit": "^9.6 || ^10 || ^11", + "vimeo/psalm": "^5|^6" + }, + "suggest": { + "friendsofphp/proxy-manager-lts": "Install it if you want to use lazy injection (version ^1)" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "DI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The dependency injection container for humans", + "homepage": "https://php-di.org/", + "keywords": [ + "PSR-11", + "container", + "container-interop", + "dependency injection", + "di", + "ioc", + "psr11" + ], + "support": { + "issues": "https://github.com/PHP-DI/PHP-DI/issues", + "source": "https://github.com/PHP-DI/PHP-DI/tree/7.1.1" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", + "type": "tidelift" + } + ], + "time": "2025-08-16T11:10:48+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.2" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/wp-content/plugins/pattern-sync/pattern-sync.php b/wp-content/plugins/pattern-sync/pattern-sync.php new file mode 100644 index 000000000..7ba8d52cb --- /dev/null +++ b/wp-content/plugins/pattern-sync/pattern-sync.php @@ -0,0 +1,31 @@ +init( __FILE__ ); +}, 1, 0 ); + +function pattern_sync(): Core { + return Core::instance(); +} diff --git a/wp-content/plugins/pattern-sync/src/Admin/Admin_Definer.php b/wp-content/plugins/pattern-sync/src/Admin/Admin_Definer.php new file mode 100644 index 000000000..051e7f538 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Admin/Admin_Definer.php @@ -0,0 +1,13 @@ +container->get( Connection_Manager::class ); + $connection = $manager->get_by_id( $connection_id ); + if ( $connection !== null ) { + $this->render_sync_ui( $connection ); + + return; + } + } + + $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; + $edit_id = isset( $_GET['edit'] ) ? sanitize_text_field( wp_unslash( $_GET['edit'] ) ) : ''; + + if ( $action === 'log' ) { + $this->render_sync_log(); + + return; + } + + if ( $action === 'add' || $edit_id !== '' ) { + $this->render_connection_form( $edit_id ); + + return; + } + + $this->render_connections_list(); + } + + public function handle_save_connection(): void { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'Unauthorized.', 'pattern-sync' ) ); + } + if ( ! isset( $_POST['pattern_sync_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pattern_sync_nonce'] ) ), 'pattern_sync_save_connection' ) ) { + wp_die( esc_html__( 'Security check failed.', 'pattern-sync' ) ); + } + + $edit_id = isset( $_GET['edit'] ) ? sanitize_text_field( wp_unslash( $_GET['edit'] ) ) : ''; + $name = isset( $_POST['pattern_sync_name'] ) ? sanitize_text_field( wp_unslash( $_POST['pattern_sync_name'] ) ) : ''; + $url = isset( $_POST['pattern_sync_url'] ) ? esc_url_raw( wp_unslash( $_POST['pattern_sync_url'] ) ) : ''; + $username = isset( $_POST['pattern_sync_username'] ) ? sanitize_text_field( wp_unslash( $_POST['pattern_sync_username'] ) ) : ''; + $app_pass = isset( $_POST['pattern_sync_app_password'] ) ? wp_unslash( $_POST['pattern_sync_app_password'] ) : ''; + + if ( $name === '' || $url === '' || $username === '' ) { + $this->set_admin_message( __( 'Name, URL and Username are required.', 'pattern-sync' ), 'error' ); + wp_safe_redirect( admin_url( 'tools.php?page=pattern-sync&action=' . ( $edit_id !== '' ? 'add&edit=' . rawurlencode( $edit_id ) : 'add' ) ) ); + exit; + } + + $manager = $this->container->get( Connection_Manager::class ); + $connection = [ + 'name' => $name, + 'url' => $url, + 'username' => $username, + 'app_password' => is_string( $app_pass ) ? $app_pass : '', + ]; + if ( $edit_id !== '' ) { + $existing = $manager->get_by_id( $edit_id ); + if ( $existing ) { + $connection['id'] = $edit_id; + if ( $connection['app_password'] === '' ) { + $connection['app_password'] = $existing['app_password']; + } + } + } + + $manager->save( $connection ); + $this->set_admin_message( __( 'Connection saved.', 'pattern-sync' ), 'success' ); + wp_safe_redirect( admin_url( 'tools.php?page=pattern-sync' ) ); + exit; + } + + public function handle_delete_connection(): void { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'Unauthorized.', 'pattern-sync' ) ); + } + $id = isset( $_GET['id'] ) ? sanitize_text_field( wp_unslash( $_GET['id'] ) ) : ''; + if ( $id === '' || ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'pattern_sync_delete_' . $id ) ) { + wp_die( esc_html__( 'Security check failed.', 'pattern-sync' ) ); + } + + $manager = $this->container->get( Connection_Manager::class ); + $manager->delete( $id ); + $this->set_admin_message( __( 'Connection deleted.', 'pattern-sync' ), 'success' ); + wp_safe_redirect( admin_url( 'tools.php?page=pattern-sync' ) ); + exit; + } + + public function ajax_test_connection(): void { + check_ajax_referer( 'pattern_sync_test', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => __( 'Unauthorized.', 'pattern-sync' ) ] ); + } + + $url = isset( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : ''; + $username = isset( $_POST['username'] ) ? sanitize_text_field( wp_unslash( $_POST['username'] ) ) : ''; + $app_pass = isset( $_POST['app_password'] ) ? wp_unslash( $_POST['app_password'] ) : ''; + if ( ! is_string( $app_pass ) ) { + $app_pass = ''; + } + + if ( $url === '' || $username === '' || $app_pass === '' ) { + wp_send_json_error( [ 'message' => __( 'URL, username and Application Password are required.', 'pattern-sync' ) ] ); + } + + $manager = $this->container->get( Connection_Manager::class ); + $ok = $manager->test( $url, $username, $app_pass ); + if ( $ok ) { + wp_send_json_success( [ 'message' => __( 'Connection successful.', 'pattern-sync' ) ] ); + } + wp_send_json_error( [ 'message' => __( 'Connection failed. Check URL and Application Password.', 'pattern-sync' ) ] ); + } + + public function enqueue_scripts( string $hook ): void { + if ( $hook !== 'tools_page_pattern-sync' ) { + return; + } + wp_add_inline_script( 'jquery', $this->get_test_connection_script(), 'after' ); + } + + public function handle_run_sync(): void { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'Unauthorized.', 'pattern-sync' ) ); + } + if ( ! isset( $_POST['pattern_sync_run_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pattern_sync_run_nonce'] ) ), 'pattern_sync_run' ) ) { + wp_die( esc_html__( 'Security check failed.', 'pattern-sync' ) ); + } + + $connection_id = isset( $_POST['connection_id'] ) ? sanitize_text_field( wp_unslash( $_POST['connection_id'] ) ) : ''; + $direction = isset( $_POST['direction'] ) ? sanitize_text_field( wp_unslash( $_POST['direction'] ) ) : ''; + $pull_patterns = isset( $_POST['pull_patterns'] ) && is_array( $_POST['pull_patterns'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['pull_patterns'] ) ) : []; + $push_patterns = isset( $_POST['push_patterns'] ) && is_array( $_POST['push_patterns'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['push_patterns'] ) ) : []; + + if ( $connection_id === '' ) { + $this->set_admin_message( __( 'Missing connection.', 'pattern-sync' ), 'error' ); + wp_safe_redirect( admin_url( 'tools.php?page=pattern-sync' ) ); + exit; + } + + $sync_service = $this->container->get( Sync_Service::class ); + $results = []; + if ( $direction === 'pull' && $pull_patterns !== [] ) { + $results = $sync_service->pull( $connection_id, $pull_patterns ); + } elseif ( $direction === 'push' && $push_patterns !== [] ) { + $results = $sync_service->push( $connection_id, $push_patterns ); + } else { + $this->set_admin_message( __( 'Select at least one pattern and use Pull or Push.', 'pattern-sync' ), 'warning' ); + wp_safe_redirect( admin_url( 'tools.php?page=pattern-sync&connection_id=' . rawurlencode( $connection_id ) ) ); + exit; + } + + $log_service = $this->container->get( Sync_Log_Service::class ); + $log_service->add_entry( $connection_id, $direction, (int) get_current_user_id(), $results ); + + $success_count = count( array_filter( $results, static fn ( $r ) => $r['success'] ) ); + $fail_count = count( $results ) - $success_count; + $msg = $direction === 'pull' + ? sprintf( __( 'Pull: %1$d succeeded, %2$d failed.', 'pattern-sync' ), $success_count, $fail_count ) + : sprintf( __( 'Push: %1$d succeeded, %2$d failed.', 'pattern-sync' ), $success_count, $fail_count ); + $this->set_admin_message( $msg, $fail_count > 0 ? 'warning' : 'success' ); + wp_safe_redirect( admin_url( 'tools.php?page=pattern-sync&connection_id=' . rawurlencode( $connection_id ) ) ); + exit; + } + + private function render_connections_list(): void { + // phpcs:disable SlevomatCodingStandard.Variables.UnusedVariable.UnusedVariable -- Variables used in included view. + $manager = $this->container->get( Connection_Manager::class ); + $connections = $manager->get_all(); + $messages = $this->get_admin_messages(); + $add_form_url = admin_url( 'tools.php?page=pattern-sync&action=add' ); + $sync_url_template = admin_url( 'tools.php?page=pattern-sync&connection_id=%s' ); + // phpcs:enable + include __DIR__ . '/Views/connections.php'; + } + + private function render_connection_form( string $edit_id ): void { + // phpcs:disable SlevomatCodingStandard.Variables.UnusedVariable.UnusedVariable -- Variables used in included view. + $manager = $this->container->get( Connection_Manager::class ); + $connection = null; + $submit_label = __( 'Add connection', 'pattern-sync' ); + if ( $edit_id !== '' ) { + $full = $manager->get_by_id( $edit_id ); + $connection = $full ? [ + 'name' => $full['name'], + 'url' => $full['url'], + 'username' => $full['username'], + ] : null; + $submit_label = __( 'Update connection', 'pattern-sync' ); + } + + $form_action = admin_url( 'admin-post.php' ); + $nonce_field = wp_nonce_field( 'pattern_sync_save_connection', 'pattern_sync_nonce', true, false ); + $back_url = admin_url( 'tools.php?page=pattern-sync' ); + if ( $edit_id !== '' ) { + $form_action .= '?action=pattern_sync_save&edit=' . rawurlencode( $edit_id ); + } else { + $form_action .= '?action=pattern_sync_save'; + } + // phpcs:enable + include __DIR__ . '/Views/connection-form.php'; + } + + private function get_test_connection_script(): string { + return <<<'JS' +(function($){ + $(function(){ + $(document).on('click', '#pattern-sync-test-connection', function(){ + var $btn = $(this), $result = $('#pattern-sync-test-result'); + var url = $('#pattern_sync_url').val(), username = $('#pattern_sync_username').val(), appPassword = $('#pattern_sync_app_password').val(); + if (!url || !username || !appPassword) { + $result.removeClass('notice-success notice-info').addClass('notice notice-error').show().find('p').text('Please fill URL, Username and Application Password.'); + return; + } + $btn.prop('disabled', true); + $result.removeClass('notice-success notice-error').addClass('notice notice-info').show().find('p').text('Testing connection…'); + $.post(ajaxurl, { + action: 'pattern_sync_test_connection', + nonce: $('#pattern_sync_test_nonce').val(), + url: url, + username: username, + app_password: appPassword + }).done(function(r){ + var msg = (r.data && r.data.message) ? r.data.message : (r.success ? 'Connection successful.' : 'Connection failed.'); + if (r.success) { + $result.removeClass('notice-error notice-info').addClass('notice notice-success').show().find('p').text(msg); + } else { + $result.removeClass('notice-success notice-info').addClass('notice notice-error').show().find('p').text(msg); + } + }).fail(function(xhr){ + var msg = 'Request failed.'; + if (xhr && xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) { + msg = xhr.responseJSON.data.message; + } else if (xhr && xhr.statusText) { + msg = 'Request failed: ' + xhr.statusText; + } + $result.removeClass('notice-success notice-info').addClass('notice notice-error').show().find('p').text(msg); + }).always(function(){ $btn.prop('disabled', false); }); + }); + }); +})(jQuery); +JS; + } + + private function get_admin_messages(): array { + $messages = get_transient( 'pattern_sync_admin_messages' ); + delete_transient( 'pattern_sync_admin_messages' ); + + return is_array( $messages ) ? $messages : []; + } + + private function set_admin_message( string $text, string $type = 'info' ): void { + $messages = get_transient( 'pattern_sync_admin_messages' ); + $messages = is_array( $messages ) ? $messages : []; + $messages[] = [ 'type' => $type, 'text' => $text ]; + set_transient( 'pattern_sync_admin_messages', $messages, 60 ); + } + + private function render_sync_ui( array $connection ): void { + $remote_client = $this->container->get( Remote_Client::class ); + $pattern_registry = $this->container->get( Pattern_Registry_Service::class ); + + $remote_list = $remote_client->get_patterns( $connection['url'], $connection['username'], $connection['app_password'] ); + $local_list = $pattern_registry->list_all(); + + // phpcs:disable SlevomatCodingStandard.Variables.UnusedVariable.UnusedVariable -- Variables used in included view. + $remote_by_category = $this->group_patterns_by_category( $remote_list ); + $local_by_category = $this->group_patterns_by_category( $local_list ); + + $back_url = admin_url( 'tools.php?page=pattern-sync' ); + $refresh_url = admin_url( 'tools.php?page=pattern-sync&connection_id=' . rawurlencode( $connection['id'] ) ); + $form_action = admin_url( 'admin-post.php?action=pattern_sync_run' ); + $nonce = wp_nonce_field( 'pattern_sync_run', 'pattern_sync_run_nonce', true, false ); + $messages = $this->get_admin_messages(); + // phpcs:enable + include __DIR__ . '/Views/sync.php'; + } + + /** + * @param array $patterns + * + * @return array> + */ + private function group_patterns_by_category( array $patterns ): array { + $by_cat = []; + foreach ( $patterns as $p ) { + $name = $p['name'] ?? ''; + $title = $p['title'] ?? $name; + $source = $p['source'] ?? ''; + $syncable = $p['syncable'] ?? false; + $origin = $p['origin'] ?? 'code'; + $cats = $p['categories'] ?? []; + $cat_key = is_array( $cats ) && $cats !== [] ? (string) reset( $cats ) : 'uncategorized'; + if ( ! isset( $by_cat[ $cat_key ] ) ) { + $by_cat[ $cat_key ] = []; + } + $by_cat[ $cat_key ][] = [ 'name' => $name, 'title' => $title, 'source' => $source, 'syncable' => $syncable, 'origin' => $origin ]; + } + + return $by_cat; + } + + private function render_sync_log(): void { + // phpcs:disable SlevomatCodingStandard.Variables.UnusedVariable.UnusedVariable -- Variables used in included view. + $log_service = $this->container->get( Sync_Log_Service::class ); + $entries = $log_service->get_recent( 100 ); + $manager = $this->container->get( Connection_Manager::class ); + $connections = []; + foreach ( $manager->get_all() as $c ) { + $connections[ $c['id'] ] = $c['name']; + } + $back_url = admin_url( 'tools.php?page=pattern-sync' ); + // phpcs:enable + include __DIR__ . '/Views/sync-log.php'; + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Admin/Views/connection-form.php b/wp-content/plugins/pattern-sync/src/Admin/Views/connection-form.php new file mode 100644 index 000000000..89a8c79a2 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Admin/Views/connection-form.php @@ -0,0 +1,64 @@ + +
+

+

+ +
+ + + + + + + + + + + + + + + + + + + + +

+ + +

+
+ +
diff --git a/wp-content/plugins/pattern-sync/src/Admin/Views/connections.php b/wp-content/plugins/pattern-sync/src/Admin/Views/connections.php new file mode 100644 index 000000000..0157011f6 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Admin/Views/connections.php @@ -0,0 +1,60 @@ + $connections + * @var string $add_form_url + * @var string $sync_url_template + * @var array $messages + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +?> +
+

+ + +
+ + +
+

+
+ + + +

+

+ + + + + + + + + + + + + + + + + + + + +
+ + | + + | + +
+ +
diff --git a/wp-content/plugins/pattern-sync/src/Admin/Views/sync-log.php b/wp-content/plugins/pattern-sync/src/Admin/Views/sync-log.php new file mode 100644 index 000000000..ef8c6432e --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Admin/Views/sync-log.php @@ -0,0 +1,56 @@ + $entries + * @var array $connections connection_id => name + * @var string $back_url + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +?> +
+

+

+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + +
display_name ) : esc_html( (string) $entry['user_id'] ); + ?>' . esc_html__( 'Success', 'pattern-sync' ) . '' : '' . esc_html__( 'Failure', 'pattern-sync' ) . ''; ?> + ( $p['success'] ? '✓ ' : '✗ ' ) . ( $p['name'] ?? '' ), $entry['patterns'] ); + echo esc_html( implode( ', ', $names ) ); + ?> +
+ +
diff --git a/wp-content/plugins/pattern-sync/src/Admin/Views/sync.php b/wp-content/plugins/pattern-sync/src/Admin/Views/sync.php new file mode 100644 index 000000000..b696e3239 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Admin/Views/sync.php @@ -0,0 +1,106 @@ +> $remote_by_category + * @var array> $local_by_category + * @var string $back_url + * @var string $refresh_url + * @var string $form_action + * @var string $nonce + * @var array $messages + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +?> +
+ +

+ +

+

+ + +

+

+ + | +

+ +

+
+
+

+

+
+ + + + $patterns ) { + echo '
' . esc_html( $cat_label ) . '
    '; + foreach ( $patterns as $p ) { + $syncable = $p['syncable'] ?? false; + $label = $p['title'] ?: $p['name']; + $origin_label = ( $p['origin'] ?? '' ) === 'code' ? ' (' . esc_html__( 'Locked', 'pattern-sync' ) . ')' : ' (' . esc_html( ( $p['origin'] ?? '' ) === 'user' ? __( 'User-created', 'pattern-sync' ) : __( 'Synced', 'pattern-sync' ) ) . ')'; + if ( $syncable ) { + echo '
  • '; + } else { + echo '
  • ' . esc_html( $label ) . $origin_label . ' ' . esc_html( $p['name'] ) . '
  • '; + } + } + echo '
'; + } + if ( array_sum( array_map( 'count', $remote_by_category ) ) === 0 ) { + echo '

' . esc_html__( 'No patterns on remote site.', 'pattern-sync' ) . '

'; + } + ?> +

+
+
+
+

+

+
+ + + + $patterns ) { + echo '
' . esc_html( $cat_label ) . '
    '; + foreach ( $patterns as $p ) { + $syncable = $p['syncable'] ?? false; + $label = $p['title'] ?: $p['name']; + $origin_label = ( $p['origin'] ?? '' ) === 'code' ? ' (' . esc_html__( 'Locked', 'pattern-sync' ) . ')' : ' (' . esc_html( ( $p['origin'] ?? '' ) === 'user' ? __( 'User-created', 'pattern-sync' ) : __( 'Synced', 'pattern-sync' ) ) . ')'; + if ( $syncable ) { + echo '
  • '; + } else { + echo '
  • ' . esc_html( $label ) . $origin_label . ' ' . esc_html( $p['name'] ) . '
  • '; + } + } + echo '
'; + } + if ( array_sum( array_map( 'count', $local_by_category ) ) === 0 ) { + echo '

' . esc_html__( 'No patterns on this site.', 'pattern-sync' ) . '

'; + } + ?> +

+
+
+
+ +
diff --git a/wp-content/plugins/pattern-sync/src/Connections/Connection_Manager.php b/wp-content/plugins/pattern-sync/src/Connections/Connection_Manager.php new file mode 100644 index 000000000..4e8a00aed --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Connections/Connection_Manager.php @@ -0,0 +1,95 @@ + + */ + public function get_all(): array { + $all = $this->repository->get_all(); + + return array_map( static function ( array $row ) { + return [ + 'id' => $row['id'], + 'name' => $row['name'], + 'url' => $row['url'], + 'username' => $row['username'], + ]; + }, $all ); + } + + /** + * @return array{id: string, name: string, url: string, username: string, app_password: string}|null + */ + public function get_by_id( string $id ): ?array { + return $this->repository->get_by_id( $id ); + } + + /** + * @param array{id?: string, name: string, url: string, username: string, app_password: string} $connection + */ + public function save( array $connection ): string { + return $this->repository->save( $connection ); + } + + public function delete( string $id ): bool { + return $this->repository->delete( $id ); + } + + /** + * Test REST connection to the given URL with username and application password. + * Returns true if GET pattern-sync/v1/patterns returns 200. + */ + public function test( string $url, string $username, string $app_password ): bool { + $base = untrailingslashit( $url ); + $rest_url = $base . '/wp-json/pattern-sync/v1/patterns?per_page=1'; + $response = $this->remote_get( $rest_url, $username, $app_password ); + + return is_array( $response ) && ( $response['response']['code'] ?? 0 ) === 200; + } + + /** + * Perform authenticated GET request. Returns wp_remote_get result (array or WP_Error). + * + * @return array{response: array{code: int}}|\WP_Error + */ + public function remote_get( string $url, string $username, string $app_password ): array|\WP_Error { + $auth = base64_encode( $username . ':' . $app_password ); + + return wp_remote_get( $url, [ + 'headers' => [ + 'Authorization' => 'Basic ' . $auth, + 'Content-Type' => 'application/json', + ], + 'timeout' => 15, + ] ); + } + + /** + * Perform authenticated POST request with JSON body. + * + * @param array $body + * + * @return array{response: array{code: int}, body: string}|\WP_Error + */ + public function remote_post( string $url, string $username, string $app_password, array $body ): array|\WP_Error { + $auth = base64_encode( $username . ':' . $app_password ); + + return wp_remote_post( $url, [ + 'headers' => [ + 'Authorization' => 'Basic ' . $auth, + 'Content-Type' => 'application/json', + ], + 'body' => wp_json_encode( $body ), + 'timeout' => 30, + ] ); + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Connections/Connection_Repository.php b/wp-content/plugins/pattern-sync/src/Connections/Connection_Repository.php new file mode 100644 index 000000000..919c2c2b2 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Connections/Connection_Repository.php @@ -0,0 +1,165 @@ + + */ + public function get_all(): array { + $value = get_option( self::OPTION, [] ); + if ( ! is_array( $value ) ) { + return []; + } + + return array_values( $value ); + } + + /** + * @return array{id: string, name: string, url: string, username: string, app_password: string}|null + */ + public function get_by_id( string $id ): ?array { + $all = $this->get_all_indexed(); + $raw = $all[ $id ] ?? null; + if ( $raw === null ) { + return null; + } + $decrypted = $this->decrypt( $raw['app_password_encrypted'] ?? '' ); + + return [ + 'id' => $raw['id'], + 'name' => $raw['name'], + 'url' => $raw['url'], + 'username' => $raw['username'], + 'app_password' => $decrypted, + ]; + } + + /** + * Get connection by id without decrypting password (for display). + * + * @return array{id: string, name: string, url: string, username: string}|null + */ + public function get_by_id_metadata_only( string $id ): ?array { + $all = $this->get_all_indexed(); + $raw = $all[ $id ] ?? null; + if ( $raw === null ) { + return null; + } + + return [ + 'id' => $raw['id'], + 'name' => $raw['name'], + 'url' => $raw['url'], + 'username' => $raw['username'], + ]; + } + + /** + * @param array{name: string, url: string, username: string, app_password: string} $connection + */ + public function save( array $connection ): string { + $id = $connection['id'] ?? $this->generate_id(); + $encrypted = $this->encrypt( $connection['app_password'] ?? '' ); + $all = $this->get_all_indexed(); + $all[ $id ] = [ + 'id' => $id, + 'name' => $connection['name'], + 'url' => untrailingslashit( $connection['url'] ), + 'username' => $connection['username'], + 'app_password_encrypted' => $encrypted, + ]; + update_option( self::OPTION, $all, true ); + + return $id; + } + + public function delete( string $id ): bool { + $all = $this->get_all_indexed(); + if ( ! isset( $all[ $id ] ) ) { + return false; + } + unset( $all[ $id ] ); + update_option( self::OPTION, $all, true ); + + return true; + } + + private function get_all_indexed(): array { + $value = get_option( self::OPTION, [] ); + if ( ! is_array( $value ) ) { + return []; + } + $indexed = []; + foreach ( $value as $conn ) { + if ( ! isset( $conn['id'] ) ) { + continue; + } + + $indexed[ $conn['id'] ] = $conn; + } + + return $indexed; + } + + private function encrypt( string $plain ): string { + $key = $this->get_encryption_key(); + if ( $plain === '' ) { + return ''; + } + if ( ! function_exists( 'openssl_encrypt' ) ) { + return base64_encode( $plain ); + } + $iv_len = openssl_cipher_iv_length( self::CIPHER ); + $iv = $iv_len ? random_bytes( $iv_len ) : ''; + $tag = ''; + $cipher = openssl_encrypt( $plain, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag, '', 16 ); + if ( $cipher === false ) { + return base64_encode( $plain ); + } + + return base64_encode( $iv . $tag . $cipher ); + } + + private function decrypt( string $encoded ): string { + $key = $this->get_encryption_key(); + if ( $encoded === '' ) { + return ''; + } + $raw = base64_decode( $encoded, true ); + if ( $raw === false ) { + return base64_decode( $encoded, true ) ?: ''; + } + if ( ! function_exists( 'openssl_decrypt' ) ) { + return $raw; + } + $iv_len = openssl_cipher_iv_length( self::CIPHER ); + if ( strlen( $raw ) < $iv_len + 16 ) { + return $raw; + } + $iv = substr( $raw, 0, $iv_len ); + $tag = substr( $raw, $iv_len, 16 ); + $data = substr( $raw, $iv_len + 16 ); + $out = openssl_decrypt( $data, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag ); + + return $out !== false ? $out : $raw; + } + + private function get_encryption_key(): string { + if ( defined( 'AUTH_KEY' ) && AUTH_KEY !== '' ) { + return hash( 'sha256', AUTH_KEY, true ); + } + + return hash( 'sha256', 'pattern-sync-fallback', true ); + } + + private function generate_id(): string { + return 'conn_' . wp_generate_password( 12, false ); + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Connections/Connections_Definer.php b/wp-content/plugins/pattern-sync/src/Connections/Connections_Definer.php new file mode 100644 index 000000000..6f2b4e2d1 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Connections/Connections_Definer.php @@ -0,0 +1,16 @@ + DI\create()->constructor( DI\get( Connection_Repository::class ) ), + ]; + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Core.php b/wp-content/plugins/pattern-sync/src/Core.php new file mode 100644 index 000000000..426d76e1c --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Core.php @@ -0,0 +1,79 @@ +init_container( $plugin_path ); + } + + public function container(): \DI\FactoryInterface|\Invoker\InvokerInterface|\Psr\Container\ContainerInterface { + return $this->container; + } + + /** + * @throws \Exception + */ + private function init_container( string $plugin_path ): void { + $definers = apply_filters( 'pattern_sync/definers', $this->definers ); + $subscribers = apply_filters( 'pattern_sync/subscribers', $this->subscribers ); + + $builder = new ContainerBuilder(); + $builder->useAutowiring( true ); + $builder->addDefinitions( [ self::PLUGIN_FILE => $plugin_path ] ); + $builder->addDefinitions( ...array_map( static fn ( string $classname ) => ( new $classname() )->define(), $definers ) ); + + $this->container = $builder->build(); + + foreach ( $subscribers as $subscriber_class ) { + ( new $subscriber_class( $this->container ) )->register(); + } + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Core/Abstract_Subscriber.php b/wp-content/plugins/pattern-sync/src/Core/Abstract_Subscriber.php new file mode 100644 index 000000000..05ba0c8df --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Core/Abstract_Subscriber.php @@ -0,0 +1,16 @@ +container = $container; + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Core/Interfaces/Definer_Interface.php b/wp-content/plugins/pattern-sync/src/Core/Interfaces/Definer_Interface.php new file mode 100644 index 000000000..86305ccf5 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Core/Interfaces/Definer_Interface.php @@ -0,0 +1,9 @@ + $results + */ + public function add_entry( string $connection_id, string $direction, int $user_id, array $results ): void { + $success_count = count( array_filter( $results, static fn ( $r ) => $r['success'] ) ); + $overall_success = $success_count === count( $results ); + + $entry = [ + 'id' => 'log_' . wp_generate_password( 8, false ), + 'connection_id' => $connection_id, + 'direction' => $direction, + 'user_id' => $user_id, + 'timestamp' => time(), + 'patterns' => $results, + 'overall_success' => $overall_success, + ]; + + $log = get_option( self::OPTION, [] ); + if ( ! is_array( $log ) ) { + $log = []; + } + array_unshift( $log, $entry ); + $log = array_slice( $log, 0, self::MAX_ENTRIES ); + update_option( self::OPTION, $log, true ); + } + + /** + * Get recent log entries. + * + * @return array + */ + public function get_recent( int $limit = 50 ): array { + $log = get_option( self::OPTION, [] ); + if ( ! is_array( $log ) ) { + return []; + } + + return array_slice( $log, 0, $limit ); + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Patterns/Pattern_Registry_Service.php b/wp-content/plugins/pattern-sync/src/Patterns/Pattern_Registry_Service.php new file mode 100644 index 000000000..381cbfa95 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Patterns/Pattern_Registry_Service.php @@ -0,0 +1,273 @@ +get_stored(); + foreach ( $stored as $name => $properties ) { + $props = $this->normalize_properties_for_register( $properties ); + register_block_pattern( $name, $props ); + } + } + + /** + * Get all patterns: code-registered (locked), user-created (wp_block), and plugin-stored, in a consistent schema. + * Includes 'syncable' (true = user-created or synced; false = code/locked) and 'origin' (code|user|pattern-sync). + * + * @return array + */ + public function list_all(): array { + $from_registry = $this->list_from_registry(); + $from_wp_block = $this->list_from_wp_block(); + $from_stored = $this->list_from_stored(); + + return array_values( array_merge( $from_registry, $from_wp_block, $from_stored ) ); + } + + /** + * Store a pattern definition (from REST POST). Overwrites by name. + * + * @param array $pattern_def Pattern with name, title, content, description, categories, keywords, etc. + */ + public function store( array $pattern_def ): void { + $name = $this->get_pattern_name( $pattern_def ); + if ( $name === '' ) { + return; + } + + $stored = $this->get_stored(); + $stored[ $name ] = $pattern_def; + update_option( self::OPTION_STORED, $stored, true ); + } + + /** + * Get stored pattern definitions (raw, keyed by name). + * + * @return array> + */ + public function get_stored(): array { + $value = get_option( self::OPTION_STORED, [] ); + + return is_array( $value ) ? $value : []; + } + + /** + * Get a single pattern by name (from registry or stored). + * + * @return array|null + */ + public function get_by_name( string $name ): ?array { + $all = $this->list_all(); + foreach ( $all as $pattern ) { + if ( isset( $pattern['name'] ) && $pattern['name'] === $name ) { + return $pattern; + } + } + + return null; + } + + public function get_name_prefix(): string { + return self::NAME_PREFIX; + } + + /** + * List patterns from WP_Block_Patterns_Registry (code-registered only; locked, not syncable). + * + * @return array + */ + private function list_from_registry(): array { + if ( ! class_exists( 'WP_Block_Patterns_Registry' ) ) { + return []; + } + + $registry = \WP_Block_Patterns_Registry::get_instance(); + if ( ! method_exists( $registry, 'get_all_registered' ) ) { + return []; + } + + $registered = $registry->get_all_registered(); + $wp_block_names = $this->get_wp_block_pattern_names(); + $out = []; + foreach ( $registered as $pattern ) { + $name = $pattern['name'] ?? ''; + if ( $name === '' ) { + continue; + } + if ( isset( $wp_block_names[ $name ] ) ) { + continue; + } + $out[] = $this->normalize_to_schema( $pattern, $this->infer_source( $name ), false, 'code' ); + } + + return $out; + } + + /** + * List user-created patterns from wp_block post type (syncable). + * + * @return array + */ + private function list_from_wp_block(): array { + $posts = get_posts( [ + 'post_type' => 'wp_block', + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'orderby' => 'title', + 'order' => 'ASC', + ] ); + $out = []; + foreach ( $posts as $post ) { + $name = self::WP_BLOCK_PREFIX . $post->ID; + $out[] = $this->normalize_to_schema( [ + 'name' => $name, + 'title' => $post->post_title ?: __( '(Untitled)', 'pattern-sync' ), + 'content' => $post->post_content, + 'description' => '', + 'categories' => [], + 'keywords' => [], + ], 'user', true, 'user' ); + } + + return $out; + } + + /** + * Names of patterns that are backed by wp_block (so we exclude them from code list). + * + * @return array + */ + private function get_wp_block_pattern_names(): array { + $posts = get_posts( [ + 'post_type' => 'wp_block', + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'fields' => 'ids', + ] ); + $map = []; + foreach ( $posts as $id ) { + $map[ self::WP_BLOCK_PREFIX . $id ] = true; + } + + return $map; + } + + /** + * List patterns from our stored option (synced from other sites; syncable). + * + * @return array + */ + private function list_from_stored(): array { + $stored = $this->get_stored(); + $out = []; + foreach ( $stored as $name => $props ) { + $out[] = $this->normalize_to_schema( array_merge( [ 'name' => $name ], $props ), 'pattern-sync', true, 'pattern-sync' ); + } + + return $out; + } + + /** + * Normalize a pattern array to our REST schema (snake_case). + * + * @param array $pattern Raw pattern from registry, wp_block, or stored. + * @param string $source Source label: core, theme, plugin, pattern-sync, user. + * @param bool $syncable Whether the pattern can be synced (user-created or synced). + * @param string $origin Origin: 'code' (locked), 'user' (wp_block), 'pattern-sync'. + * + * @return array{name: string, title: string, content: string, description: string, categories: array, keywords: array, viewport_width: int, inserter: bool, block_types: array, post_types: array, source: string, syncable: bool, origin: string} + */ + private function normalize_to_schema( array $pattern, string $source, bool $syncable = false, string $origin = 'code' ): array { + return [ + 'name' => (string) ( $pattern['name'] ?? '' ), + 'title' => (string) ( $pattern['title'] ?? '' ), + 'content' => (string) ( $pattern['content'] ?? '' ), + 'description' => (string) ( $pattern['description'] ?? '' ), + 'categories' => isset( $pattern['categories'] ) && is_array( $pattern['categories'] ) ? $pattern['categories'] : [], + 'keywords' => isset( $pattern['keywords'] ) && is_array( $pattern['keywords'] ) ? $pattern['keywords'] : [], + 'viewport_width' => (int) ( $pattern['viewportWidth'] ?? $pattern['viewport_width'] ?? 0 ), + 'inserter' => (bool) ( $pattern['inserter'] ?? true ), + 'block_types' => isset( $pattern['blockTypes'] ) && is_array( $pattern['blockTypes'] ) ? $pattern['blockTypes'] : ( isset( $pattern['block_types'] ) && is_array( $pattern['block_types'] ) ? $pattern['block_types'] : [] ), + 'post_types' => isset( $pattern['postTypes'] ) && is_array( $pattern['postTypes'] ) ? $pattern['postTypes'] : ( isset( $pattern['post_types'] ) && is_array( $pattern['post_types'] ) ? $pattern['post_types'] : [] ), + 'source' => $source, + 'syncable' => $syncable, + 'origin' => $origin, + ]; + } + + /** + * Convert schema (snake_case) to properties for register_block_pattern (camelCase where needed). + * + * @param array $properties + * + * @return array + */ + private function normalize_properties_for_register( array $properties ): array { + $out = [ + 'title' => (string) ( $properties['title'] ?? '' ), + 'content' => (string) ( $properties['content'] ?? '' ), + 'description' => (string) ( $properties['description'] ?? '' ), + 'categories' => isset( $properties['categories'] ) && is_array( $properties['categories'] ) ? $properties['categories'] : [], + 'keywords' => isset( $properties['keywords'] ) && is_array( $properties['keywords'] ) ? $properties['keywords'] : [], + 'inserter' => (bool) ( $properties['inserter'] ?? true ), + ]; + $viewport = $properties['viewportWidth'] ?? $properties['viewport_width'] ?? 0; + if ( (int) $viewport > 0 ) { + $out['viewportWidth'] = (int) $viewport; + } + $block_types = $properties['blockTypes'] ?? $properties['block_types'] ?? null; + if ( $block_types !== null && $block_types !== '' && is_array( $block_types ) && $block_types !== [] ) { + $out['blockTypes'] = $block_types; + } + $post_types = $properties['postTypes'] ?? $properties['post_types'] ?? null; + if ( $post_types !== null && $post_types !== '' && is_array( $post_types ) && $post_types !== [] ) { + $out['postTypes'] = $post_types; + } + + return array_filter( $out, static fn ( $v ) => $v !== '' || is_array( $v ) ); + } + + private function get_pattern_name( array $pattern_def ): string { + $name = $pattern_def['name'] ?? ''; + if ( $name !== '' ) { + return $name; + } + $title = $pattern_def['title'] ?? ''; + if ( $title !== '' ) { + $slug = sanitize_title( $title ); + + return self::NAME_PREFIX . $slug; + } + + return ''; + } + + private function infer_source( string $name ): string { + if ( str_starts_with( $name, self::NAME_PREFIX ) ) { + return 'pattern-sync'; + } + if ( str_starts_with( $name, 'core/' ) ) { + return 'core'; + } + + return 'plugin'; + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Patterns/Patterns_Subscriber.php b/wp-content/plugins/pattern-sync/src/Patterns/Patterns_Subscriber.php new file mode 100644 index 000000000..af364743b --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Patterns/Patterns_Subscriber.php @@ -0,0 +1,17 @@ +container->get( Pattern_Registry_Service::class )->register_stored_patterns(); + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Rest/Controllers/Patterns_Controller.php b/wp-content/plugins/pattern-sync/src/Rest/Controllers/Patterns_Controller.php new file mode 100644 index 000000000..b380abaa0 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Rest/Controllers/Patterns_Controller.php @@ -0,0 +1,70 @@ +pattern_registry->list_all(); + $page = (int) $request->get_param( 'page' ); + $per = (int) $request->get_param( 'per_page' ); + $per = $per >= 1 && $per <= 100 ? $per : 10; + $offset = ( $page >= 1 ? $page - 1 : 0 ) * $per; + $slice = array_slice( $all, $offset, $per ); + $total = count( $all ); + + $response = new WP_REST_Response( $slice, 200 ); + $response->header( 'X-WP-Total', (string) $total ); + $response->header( 'X-WP-TotalPages', (string) ( (int) ceil( $total / $per ) ) ); + + return $response; + } + + public function get_categories( WP_REST_Request $request ): WP_REST_Response { + $categories = []; + if ( class_exists( 'WP_Block_Pattern_Categories_Registry' ) ) { + $registry = \WP_Block_Pattern_Categories_Registry::get_instance(); + if ( method_exists( $registry, 'get_all_registered' ) ) { + $registered = $registry->get_all_registered(); + foreach ( $registered as $cat ) { + $categories[] = [ + 'name' => $cat['name'] ?? '', + 'label' => $cat['label'] ?? '', + 'description' => $cat['description'] ?? '', + ]; + } + } + } + + return new WP_REST_Response( $categories, 200 ); + } + + public function create_pattern( WP_REST_Request $request ): WP_REST_Response { + $body = $request->get_json_params(); + if ( ! is_array( $body ) ) { + return new WP_REST_Response( [ 'code' => 'invalid_body', 'message' => __( 'Invalid JSON body.', 'pattern-sync' ) ], 400 ); + } + + $title = $body['title'] ?? ''; + $content = $body['content'] ?? ''; + if ( $title === '' || $content === '' ) { + return new WP_REST_Response( [ 'code' => 'missing_fields', 'message' => __( 'title and content are required.', 'pattern-sync' ) ], 400 ); + } + + $this->pattern_registry->store( $body ); + $name = $body['name'] ?? ( $this->pattern_registry->get_name_prefix() . sanitize_title( $title ) ); + $saved = $this->pattern_registry->get_by_name( $name ); + + return new WP_REST_Response( $saved ?? array_merge( $body, [ 'name' => $name ] ), 201 ); + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Rest/Rest_Definer.php b/wp-content/plugins/pattern-sync/src/Rest/Rest_Definer.php new file mode 100644 index 000000000..1bfd419f3 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Rest/Rest_Definer.php @@ -0,0 +1,17 @@ + DI\create()->constructor( DI\get( \PatternSync\Patterns\Pattern_Registry_Service::class ) ), + ]; + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Rest/Rest_Subscriber.php b/wp-content/plugins/pattern-sync/src/Rest/Rest_Subscriber.php new file mode 100644 index 000000000..c0d4ca526 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Rest/Rest_Subscriber.php @@ -0,0 +1,58 @@ +container->get( Patterns_Controller::class ); + + register_rest_route( self::NAMESPACE, '/patterns', [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => [ $controller, 'get_patterns' ], + 'permission_callback' => [ $this, 'check_manage_options' ], + 'args' => [ + 'page' => [ + 'type' => 'integer', + 'default' => 1, + 'minimum' => 1, + 'sanitize_callback' => 'absint', + ], + 'per_page' => [ + 'type' => 'integer', + 'default' => 10, + 'minimum' => 1, + 'maximum' => 100, + 'sanitize_callback' => 'absint', + ], + ], + ] ); + + register_rest_route( self::NAMESPACE, '/pattern-categories', [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => [ $controller, 'get_categories' ], + 'permission_callback' => [ $this, 'check_manage_options' ], + ] ); + + register_rest_route( self::NAMESPACE, '/patterns', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => [ $controller, 'create_pattern' ], + 'permission_callback' => [ $this, 'check_manage_options' ], + ] ); + } + + public function check_manage_options(): bool { + return current_user_can( 'manage_options' ); + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Sync/Remote_Client.php b/wp-content/plugins/pattern-sync/src/Sync/Remote_Client.php new file mode 100644 index 000000000..d3f044814 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Sync/Remote_Client.php @@ -0,0 +1,82 @@ +> + */ + public function get_patterns( string $base_url, string $username, string $app_password ): array { + $url = untrailingslashit( $base_url ) . '/wp-json/pattern-sync/v1/patterns'; + $all = []; + $page = 1; + $per_page = 100; + + do { + $response = $this->connection_manager->remote_get( + $url . '?per_page=' . $per_page . '&page=' . $page, + $username, + $app_password + ); + + if ( is_wp_error( $response ) ) { + return []; + } + + $code = (int) ( $response['response']['code'] ?? 0 ); + if ( $code !== 200 ) { + return $all; + } + + $body = wp_remote_retrieve_body( $response ); + $data = json_decode( $body, true ); + if ( ! is_array( $data ) ) { + return $all; + } + + $all = array_merge( $all, $data ); + $total_pages = (int) ( wp_remote_retrieve_header( $response, 'x-wp-totalpages' ) ?: 1 ); + $page++; + } while ( $page <= $total_pages ); + + return $all; + } + + /** + * POST a single pattern to the remote site. + * + * @param array $pattern_def + * + * @return array{success: bool, message?: string} + */ + public function post_pattern( string $base_url, string $username, string $app_password, array $pattern_def ): array { + $url = untrailingslashit( $base_url ) . '/wp-json/pattern-sync/v1/patterns'; + $response = $this->connection_manager->remote_post( $url, $username, $app_password, $pattern_def ); + + if ( is_wp_error( $response ) ) { + return [ 'success' => false, 'message' => $response->get_error_message() ]; + } + + $code = (int) ( $response['response']['code'] ?? 0 ); + if ( $code >= 200 && $code < 300 ) { + return [ 'success' => true ]; + } + + $body = wp_remote_retrieve_body( $response ); + $data = json_decode( $body, true ); + $message = is_array( $data ) && isset( $data['message'] ) ? $data['message'] : (string) $code; + + return [ 'success' => false, 'message' => $message ]; + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Sync/Sync_Definer.php b/wp-content/plugins/pattern-sync/src/Sync/Sync_Definer.php new file mode 100644 index 000000000..02a3e7f02 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Sync/Sync_Definer.php @@ -0,0 +1,23 @@ + DI\create()->constructor( DI\get( Connection_Manager::class ) ), + Sync_Service::class => DI\create()->constructor( + DI\get( Connection_Manager::class ), + DI\get( Pattern_Registry_Service::class ), + DI\get( Remote_Client::class ) + ), + ]; + } + +} diff --git a/wp-content/plugins/pattern-sync/src/Sync/Sync_Service.php b/wp-content/plugins/pattern-sync/src/Sync/Sync_Service.php new file mode 100644 index 000000000..86e3ab505 --- /dev/null +++ b/wp-content/plugins/pattern-sync/src/Sync/Sync_Service.php @@ -0,0 +1,109 @@ + + */ + public function pull( string $connection_id, array $pattern_names ): array { + $connection = $this->connection_manager->get_by_id( $connection_id ); + if ( $connection === null ) { + return []; + } + + $remote_patterns = $this->remote_client->get_patterns( + $connection['url'], + $connection['username'], + $connection['app_password'] + ); + + $by_name = []; + foreach ( $remote_patterns as $p ) { + $n = $p['name'] ?? ''; + if ( $n === '' ) { + continue; + } + + $by_name[ $n ] = $p; + } + + $results = []; + foreach ( $pattern_names as $name ) { + $pattern_def = $by_name[ $name ] ?? null; + if ( $pattern_def === null ) { + $results[] = [ 'name' => $name, 'success' => false, 'message' => __( 'Pattern not found on remote.', 'pattern-sync' ) ]; + continue; + } + if ( ! ( $pattern_def['syncable'] ?? false ) ) { + $results[] = [ 'name' => $name, 'success' => false, 'message' => __( 'Only user-created or synced patterns can be pulled.', 'pattern-sync' ) ]; + continue; + } + + $this->pattern_registry->store( $pattern_def ); + $results[] = [ 'name' => $name, 'success' => true, 'message' => __( 'Pulled successfully.', 'pattern-sync' ) ]; + } + + return $results; + } + + /** + * Push selected patterns from local to remote. + * + * @param string $connection_id + * @param string[] $pattern_names + * + * @return array + */ + public function push( string $connection_id, array $pattern_names ): array { + $connection = $this->connection_manager->get_by_id( $connection_id ); + if ( $connection === null ) { + return []; + } + + $results = []; + foreach ( $pattern_names as $name ) { + $pattern_def = $this->pattern_registry->get_by_name( $name ); + if ( $pattern_def === null ) { + $results[] = [ 'name' => $name, 'success' => false, 'message' => __( 'Pattern not found locally.', 'pattern-sync' ) ]; + continue; + } + if ( ! ( $pattern_def['syncable'] ?? false ) ) { + $results[] = [ 'name' => $name, 'success' => false, 'message' => __( 'Only user-created or synced patterns can be pushed.', 'pattern-sync' ) ]; + continue; + } + + $result = $this->remote_client->post_pattern( + $connection['url'], + $connection['username'], + $connection['app_password'], + $pattern_def + ); + + $results[] = [ + 'name' => $name, + 'success' => $result['success'], + 'message' => $result['message'] ?? ( $result['success'] ? __( 'Pushed successfully.', 'pattern-sync' ) : __( 'Push failed.', 'pattern-sync' ) ), + ]; + } + + return $results; + } + +} diff --git a/wp-content/plugins/pattern-sync/vendor/autoload.php b/wp-content/plugins/pattern-sync/vendor/autoload.php new file mode 100644 index 000000000..41f830eb1 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/autoload.php @@ -0,0 +1,22 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * 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 list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $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 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( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * 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 list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * 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 list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $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 list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + 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; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/composer/InstalledVersions.php b/wp-content/plugins/pattern-sync/vendor/composer/InstalledVersions.php new file mode 100644 index 000000000..2052022fd --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/InstalledVersions.php @@ -0,0 +1,396 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool + */ + private static $installedIsLocalDir; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + $copiedLocalDir = false; + + if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; + } + } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array() && !$copiedLocalDir) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/composer/LICENSE b/wp-content/plugins/pattern-sync/vendor/composer/LICENSE new file mode 100644 index 000000000..f27399a04 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/wp-content/plugins/pattern-sync/vendor/composer/autoload_classmap.php b/wp-content/plugins/pattern-sync/vendor/composer/autoload_classmap.php new file mode 100644 index 000000000..0fb0a2c19 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/composer/InstalledVersions.php', +); diff --git a/wp-content/plugins/pattern-sync/vendor/composer/autoload_files.php b/wp-content/plugins/pattern-sync/vendor/composer/autoload_files.php new file mode 100644 index 000000000..f227c181e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/php-di/php-di/src/functions.php', +); diff --git a/wp-content/plugins/pattern-sync/vendor/composer/autoload_namespaces.php b/wp-content/plugins/pattern-sync/vendor/composer/autoload_namespaces.php new file mode 100644 index 000000000..15a2ff3ad --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/psr/container/src'), + 'PatternSync\\' => array($baseDir . '/src'), + 'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'), + 'Invoker\\' => array($vendorDir . '/php-di/invoker/src'), + 'DI\\' => array($vendorDir . '/php-di/php-di/src'), +); diff --git a/wp-content/plugins/pattern-sync/vendor/composer/autoload_real.php b/wp-content/plugins/pattern-sync/vendor/composer/autoload_real.php new file mode 100644 index 000000000..82dedd083 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/autoload_real.php @@ -0,0 +1,50 @@ +register(true); + + $filesToLoad = \Composer\Autoload\ComposerStaticInit1a2a4fb435ff4ea6b40c887a44d342a2::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); + } + + return $loader; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/composer/autoload_static.php b/wp-content/plugins/pattern-sync/vendor/composer/autoload_static.php new file mode 100644 index 000000000..98c8b311c --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/autoload_static.php @@ -0,0 +1,69 @@ + __DIR__ . '/..' . '/php-di/php-di/src/functions.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'P' => + array ( + 'Psr\\Container\\' => 14, + 'PatternSync\\' => 12, + ), + 'L' => + array ( + 'Laravel\\SerializableClosure\\' => 28, + ), + 'I' => + array ( + 'Invoker\\' => 8, + ), + 'D' => + array ( + 'DI\\' => 3, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Psr\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/container/src', + ), + 'PatternSync\\' => + array ( + 0 => __DIR__ . '/../..' . '/src', + ), + 'Laravel\\SerializableClosure\\' => + array ( + 0 => __DIR__ . '/..' . '/laravel/serializable-closure/src', + ), + 'Invoker\\' => + array ( + 0 => __DIR__ . '/..' . '/php-di/invoker/src', + ), + 'DI\\' => + array ( + 0 => __DIR__ . '/..' . '/php-di/php-di/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit1a2a4fb435ff4ea6b40c887a44d342a2::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit1a2a4fb435ff4ea6b40c887a44d342a2::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit1a2a4fb435ff4ea6b40c887a44d342a2::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/composer/installed.json b/wp-content/plugins/pattern-sync/vendor/composer/installed.json new file mode 100644 index 000000000..795e8553e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/installed.json @@ -0,0 +1,260 @@ +{ + "packages": [ + { + "name": "laravel/serializable-closure", + "version": "v2.0.9", + "version_normalized": "2.0.9.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "8f631589ab07b7b52fead814965f5a800459cb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/8f631589ab07b7b52fead814965f5a800459cb3e", + "reference": "8f631589ab07b7b52fead814965f5a800459cb3e", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "time": "2026-02-03T06:55:34+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "install-path": "../laravel/serializable-closure" + }, + { + "name": "php-di/invoker", + "version": "2.3.7", + "version_normalized": "2.3.7.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/3c1ddfdef181431fbc4be83378f6d036d59e81e1", + "reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "psr/container": "^1.0|^2.0" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0 || ^10 || ^11 || ^12" + }, + "time": "2025-08-30T10:22:22+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Invoker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", + "keywords": [ + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" + ], + "support": { + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.7" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "install-path": "../php-di/invoker" + }, + { + "name": "php-di/php-di", + "version": "7.1.1", + "version_normalized": "7.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PHP-DI.git", + "reference": "f88054cc052e40dbe7b383c8817c19442d480352" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/f88054cc052e40dbe7b383c8817c19442d480352", + "reference": "f88054cc052e40dbe7b383c8817c19442d480352", + "shasum": "" + }, + "require": { + "laravel/serializable-closure": "^1.0 || ^2.0", + "php": ">=8.0", + "php-di/invoker": "^2.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3", + "friendsofphp/proxy-manager-lts": "^1", + "mnapoli/phpunit-easymock": "^1.3", + "phpunit/phpunit": "^9.6 || ^10 || ^11", + "vimeo/psalm": "^5|^6" + }, + "suggest": { + "friendsofphp/proxy-manager-lts": "Install it if you want to use lazy injection (version ^1)" + }, + "time": "2025-08-16T11:10:48+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "DI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The dependency injection container for humans", + "homepage": "https://php-di.org/", + "keywords": [ + "PSR-11", + "container", + "container-interop", + "dependency injection", + "di", + "ioc", + "psr11" + ], + "support": { + "issues": "https://github.com/PHP-DI/PHP-DI/issues", + "source": "https://github.com/PHP-DI/PHP-DI/tree/7.1.1" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", + "type": "tidelift" + } + ], + "install-path": "../php-di/php-di" + }, + { + "name": "psr/container", + "version": "2.0.2", + "version_normalized": "2.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "time": "2021-11-05T16:47:00+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "install-path": "../psr/container" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/wp-content/plugins/pattern-sync/vendor/composer/installed.php b/wp-content/plugins/pattern-sync/vendor/composer/installed.php new file mode 100644 index 000000000..f28d3f1d7 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/installed.php @@ -0,0 +1,65 @@ + array( + 'name' => 'pattern-sync/pattern-sync', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'af0b315bdc21b7a688275f77115559a7db68f646', + 'type' => 'wordpress-plugin', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + 'laravel/serializable-closure' => array( + 'pretty_version' => 'v2.0.9', + 'version' => '2.0.9.0', + 'reference' => '8f631589ab07b7b52fead814965f5a800459cb3e', + 'type' => 'library', + 'install_path' => __DIR__ . '/../laravel/serializable-closure', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'pattern-sync/pattern-sync' => array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'af0b315bdc21b7a688275f77115559a7db68f646', + 'type' => 'wordpress-plugin', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'php-di/invoker' => array( + 'pretty_version' => '2.3.7', + 'version' => '2.3.7.0', + 'reference' => '3c1ddfdef181431fbc4be83378f6d036d59e81e1', + 'type' => 'library', + 'install_path' => __DIR__ . '/../php-di/invoker', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'php-di/php-di' => array( + 'pretty_version' => '7.1.1', + 'version' => '7.1.1.0', + 'reference' => 'f88054cc052e40dbe7b383c8817c19442d480352', + 'type' => 'library', + 'install_path' => __DIR__ . '/../php-di/php-di', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/container' => array( + 'pretty_version' => '2.0.2', + 'version' => '2.0.2.0', + 'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/container', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/container-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '^1.0', + ), + ), + ), +); diff --git a/wp-content/plugins/pattern-sync/vendor/composer/platform_check.php b/wp-content/plugins/pattern-sync/vendor/composer/platform_check.php new file mode 100644 index 000000000..14bf88da3 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/composer/platform_check.php @@ -0,0 +1,25 @@ += 80200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + throw new \RuntimeException( + 'Composer detected issues in your platform: ' . implode(' ', $issues) + ); +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/LICENSE.md b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/LICENSE.md new file mode 100644 index 000000000..79810c848 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Taylor Otwell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/README.md b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/README.md new file mode 100644 index 000000000..ee0d01a15 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/README.md @@ -0,0 +1,73 @@ +# Serializable Closure + + + Build Status + + + Total Downloads + + + Latest Stable Version + + + License + + +## Introduction + +> This project is a fork of the excellent [opis/closure: 3.x](https://github.com/opis/closure) package. At Laravel, we decided to fork this package as the upcoming version [4.x](https://github.com/opis/closure) is a complete rewrite on top of the [FFI extension](https://www.php.net/manual/en/book.ffi.php). As Laravel is a web framework, and FFI is not enabled by default in web requests, this fork allows us to keep using the `3.x` series while adding support for new PHP versions. + +Laravel Serializable Closure provides an easy and secure way to **serialize closures in PHP**. + +## Official Documentation + +### Installation + +> **Requires [PHP 7.4+](https://php.net/releases/)** + +First, install Laravel Serializable Closure via the [Composer](https://getcomposer.org/) package manager: + +```bash +composer require laravel/serializable-closure +``` + +### Usage + +You may serialize a closure this way: + +```php +use Laravel\SerializableClosure\SerializableClosure; + +$closure = fn () => 'james'; + +// Recommended +SerializableClosure::setSecretKey('secret'); + +$serialized = serialize(new SerializableClosure($closure)); +$closure = unserialize($serialized)->getClosure(); + +echo $closure(); // james; +``` + +### Caveats + +* Anonymous classes cannot be created within closures. +* Attributes cannot be used within closures. +* Serializing closures on REPL environments like Laravel Tinker is not supported. +* Serializing closures that reference objects with readonly properties is not supported. + +## Contributing + +Thank you for considering contributing to Serializable Closure! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +Please review [our security policy](https://github.com/laravel/serializable-closure/security/policy) on how to report security vulnerabilities. + +## License + +Serializable Closure is open-sourced software licensed under the [MIT license](LICENSE.md). diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/composer.json b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/composer.json new file mode 100644 index 000000000..dfe64eeac --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/composer.json @@ -0,0 +1,56 @@ +{ + "name": "laravel/serializable-closure", + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": ["laravel", "Serializable", "closure"], + "license": "MIT", + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + }, + "audit": { + "block-insecure": false + }, + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Contracts/Serializable.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Contracts/Serializable.php new file mode 100644 index 000000000..1a62922ea --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Contracts/Serializable.php @@ -0,0 +1,20 @@ +serializable = Serializers\Signed::$signer + ? new Serializers\Signed($closure) + : new Serializers\Native($closure); + } + + /** + * Resolve the closure with the given arguments. + * + * @return mixed + */ + public function __invoke() + { + return call_user_func_array($this->serializable, func_get_args()); + } + + /** + * Gets the closure. + * + * @return \Closure + */ + public function getClosure() + { + return $this->serializable->getClosure(); + } + + /** + * Create a new unsigned serializable closure instance. + * + * @param Closure $closure + * @return \Laravel\SerializableClosure\UnsignedSerializableClosure + */ + public static function unsigned(Closure $closure) + { + return new UnsignedSerializableClosure($closure); + } + + /** + * Sets the serializable closure secret key. + * + * @param string|null $secret + * @return void + */ + public static function setSecretKey($secret) + { + Serializers\Signed::$signer = $secret + ? new Hmac($secret) + : null; + } + + /** + * Sets the serializable closure secret key. + * + * @param \Closure|null $transformer + * @return void + */ + public static function transformUseVariablesUsing($transformer) + { + Serializers\Native::$transformUseVariables = $transformer; + } + + /** + * Sets the serializable closure secret key. + * + * @param \Closure|null $resolver + * @return void + */ + public static function resolveUseVariablesUsing($resolver) + { + Serializers\Native::$resolveUseVariables = $resolver; + } + + /** + * Get the serializable representation of the closure. + * + * @return array{serializable: \Laravel\SerializableClosure\Serializers\Signed|\Laravel\SerializableClosure\Contracts\Serializable} + */ + public function __serialize() + { + return [ + 'serializable' => $this->serializable, + ]; + } + + /** + * Restore the closure after serialization. + * + * @param array{serializable: \Laravel\SerializableClosure\Serializers\Signed|\Laravel\SerializableClosure\Contracts\Serializable} $data + * @return void + * + * @throws \Laravel\SerializableClosure\Exceptions\InvalidSignatureException + */ + public function __unserialize($data) + { + if (Signed::$signer && ! $data['serializable'] instanceof Signed) { + throw new InvalidSignatureException(); + } + + $this->serializable = $data['serializable']; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Serializers/Native.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Serializers/Native.php new file mode 100644 index 000000000..dbfeb8945 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Serializers/Native.php @@ -0,0 +1,520 @@ +closure = $closure; + } + + /** + * Resolve the closure with the given arguments. + * + * @return mixed + */ + public function __invoke() + { + return call_user_func_array($this->closure, func_get_args()); + } + + /** + * Gets the closure. + * + * @return \Closure + */ + public function getClosure() + { + return $this->closure; + } + + /** + * Get the serializable representation of the closure. + * + * @return array + */ + public function __serialize() + { + if ($this->scope === null) { + $this->scope = new ClosureScope(); + $this->scope->toSerialize++; + } + + $this->scope->serializations++; + + $scope = $object = null; + $reflector = $this->getReflector(); + + if ($reflector->isBindingRequired()) { + $object = $reflector->getClosureThis(); + + static::wrapClosures($object, $this->scope); + } + + if ($scope = $reflector->getClosureScopeClass()) { + $scope = $scope->name; + } + + $this->reference = spl_object_hash($this->closure); + + $this->scope[$this->closure] = $this; + + $use = $reflector->getUseVariables(); + + if (static::$transformUseVariables) { + $use = call_user_func(static::$transformUseVariables, $reflector->getUseVariables()); + } + + $code = $reflector->getCode(); + + $this->mapByReference($use); + + $data = [ + 'use' => $use, + 'function' => $code, + 'scope' => $scope, + 'this' => $object, + 'self' => $this->reference, + ]; + + if (! --$this->scope->serializations && ! --$this->scope->toSerialize) { + $this->scope = null; + } + + return $data; + } + + /** + * Restore the closure after serialization. + * + * @param array $data + * @return void + */ + public function __unserialize($data) + { + ClosureStream::register(); + + $this->code = $data; + unset($data); + + $this->code['objects'] = []; + + if ($this->code['use']) { + $this->scope = new ClosureScope(); + + if (static::$resolveUseVariables) { + $this->code['use'] = call_user_func(static::$resolveUseVariables, $this->code['use']); + } + + $this->mapPointers($this->code['use']); + + extract($this->code['use'], EXTR_OVERWRITE | EXTR_REFS); + + $this->scope = null; + } + + $this->closure = include ClosureStream::STREAM_PROTO.'://'.$this->code['function']; + + if ($this->code['this'] === $this) { + $this->code['this'] = null; + } + + $this->closure = $this->closure->bindTo($this->code['this'], $this->code['scope']); + + if (! empty($this->code['objects'])) { + foreach ($this->code['objects'] as $item) { + $item['property']->setValue($item['instance'], $item['object']->getClosure()); + } + } + + $this->code = $this->code['function']; + } + + /** + * Ensures the given closures are serializable. + * + * @param mixed $data + * @param \Laravel\SerializableClosure\Support\ClosureScope $storage + * @return void + */ + public static function wrapClosures(&$data, $storage) + { + if ($data instanceof Closure) { + $data = new static($data); + } elseif (is_array($data)) { + if (isset($data[self::ARRAY_RECURSIVE_KEY])) { + return; + } + + $data[self::ARRAY_RECURSIVE_KEY] = true; + + foreach ($data as $key => &$value) { + if ($key === self::ARRAY_RECURSIVE_KEY) { + continue; + } + static::wrapClosures($value, $storage); + } + + unset($value); + unset($data[self::ARRAY_RECURSIVE_KEY]); + } elseif ($data instanceof \stdClass) { + if (isset($storage[$data])) { + $data = $storage[$data]; + + return; + } + + $data = $storage[$data] = clone $data; + + foreach ($data as &$value) { + static::wrapClosures($value, $storage); + } + + unset($value); + } elseif (is_object($data) && ! $data instanceof static && ! $data instanceof UnitEnum) { + if (isset($storage[$data])) { + $data = $storage[$data]; + + return; + } + + $instance = $data; + $reflection = new ReflectionObject($instance); + + if (! $reflection->isUserDefined() || $reflection->hasMethod('__serialize')) { + $storage[$instance] = $data; + + return; + } + + $storage[$instance] = $data = $reflection->newInstanceWithoutConstructor(); + + do { + if (! $reflection->isUserDefined()) { + break; + } + + foreach ($reflection->getProperties() as $property) { + if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined() || static::isVirtualProperty($property)) { + continue; + } + + if (! $property->isInitialized($instance)) { + continue; + } + + $value = $property->getValue($instance); + + if (is_array($value) || is_object($value)) { + static::wrapClosures($value, $storage); + } + + $property->setValue($data, $value); + } + } while ($reflection = $reflection->getParentClass()); + } + } + + /** + * Gets the closure's reflector. + * + * @return \Laravel\SerializableClosure\Support\ReflectionClosure + */ + public function getReflector() + { + if ($this->reflector === null) { + $this->code = null; + $this->reflector = new ReflectionClosure($this->closure); + } + + return $this->reflector; + } + + /** + * Internal method used to map closure pointers. + * + * @param mixed $data + * @return void + */ + protected function mapPointers(&$data) + { + $scope = $this->scope; + + if ($data instanceof static) { + $data = &$data->closure; + } elseif (is_array($data)) { + if (isset($data[self::ARRAY_RECURSIVE_KEY])) { + return; + } + + $data[self::ARRAY_RECURSIVE_KEY] = true; + + foreach ($data as $key => &$value) { + if ($key === self::ARRAY_RECURSIVE_KEY) { + continue; + } elseif ($value instanceof static) { + $data[$key] = &$value->closure; + } elseif ($value instanceof SelfReference && $value->hash === $this->code['self']) { + $data[$key] = &$this->closure; + } else { + $this->mapPointers($value); + } + } + + unset($value); + unset($data[self::ARRAY_RECURSIVE_KEY]); + } elseif ($data instanceof \stdClass) { + if (isset($scope[$data])) { + return; + } + + $scope[$data] = true; + + foreach ($data as $key => &$value) { + if ($value instanceof SelfReference && $value->hash === $this->code['self']) { + $data->{$key} = &$this->closure; + } elseif (is_array($value) || is_object($value)) { + $this->mapPointers($value); + } + } + + unset($value); + } elseif (is_object($data) && ! ($data instanceof Closure)) { + if (isset($scope[$data])) { + return; + } + + $scope[$data] = true; + $reflection = new ReflectionObject($data); + + do { + if (! $reflection->isUserDefined()) { + break; + } + + foreach ($reflection->getProperties() as $property) { + if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined() || static::isVirtualProperty($property)) { + continue; + } + + if (! $property->isInitialized($data) || $property->isReadOnly()) { + continue; + } + + $item = $property->getValue($data); + + if ($item instanceof SerializableClosure || $item instanceof UnsignedSerializableClosure || ($item instanceof SelfReference && $item->hash === $this->code['self'])) { + $this->code['objects'][] = [ + 'instance' => $data, + 'property' => $property, + 'object' => $item instanceof SelfReference ? $this : $item, + ]; + } elseif (is_array($item) || is_object($item)) { + $this->mapPointers($item); + $property->setValue($data, $item); + } + } + } while ($reflection = $reflection->getParentClass()); + } + } + + /** + * Internal method used to map closures by reference. + * + * @param mixed $data + * @return void + */ + protected function mapByReference(&$data) + { + if ($data instanceof Closure) { + if ($data === $this->closure) { + $data = new SelfReference($this->reference); + + return; + } + + if (isset($this->scope[$data])) { + $data = $this->scope[$data]; + + return; + } + + $instance = new static($data); + + $instance->scope = $this->scope; + + $data = $this->scope[$data] = $instance; + } elseif (is_array($data)) { + if (isset($data[self::ARRAY_RECURSIVE_KEY])) { + return; + } + + $data[self::ARRAY_RECURSIVE_KEY] = true; + + foreach ($data as $key => &$value) { + if ($key === self::ARRAY_RECURSIVE_KEY) { + continue; + } + + $this->mapByReference($value); + } + + unset($value); + unset($data[self::ARRAY_RECURSIVE_KEY]); + } elseif ($data instanceof \stdClass) { + if (isset($this->scope[$data])) { + $data = $this->scope[$data]; + + return; + } + + $instance = $data; + $this->scope[$instance] = $data = clone $data; + + foreach ($data as &$value) { + $this->mapByReference($value); + } + + unset($value); + } elseif (is_object($data) && ! $data instanceof SerializableClosure && ! $data instanceof UnsignedSerializableClosure) { + if (isset($this->scope[$data])) { + $data = $this->scope[$data]; + + return; + } + + $instance = $data; + + if ($data instanceof DateTimeInterface) { + $this->scope[$instance] = $data; + + return; + } + + if ($data instanceof UnitEnum) { + $this->scope[$instance] = $data; + + return; + } + + $reflection = new ReflectionObject($data); + + if (! $reflection->isUserDefined() || $reflection->hasMethod('__serialize')) { + $this->scope[$instance] = $data; + + return; + } + + $this->scope[$instance] = $data = $reflection->newInstanceWithoutConstructor(); + + do { + if (! $reflection->isUserDefined()) { + break; + } + + foreach ($reflection->getProperties() as $property) { + if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined() || static::isVirtualProperty($property)) { + continue; + } + + if (! $property->isInitialized($instance) || ($property->isReadOnly() && $property->class !== $reflection->name)) { + continue; + } + + $value = $property->getValue($instance); + + if (is_array($value) || is_object($value)) { + $this->mapByReference($value); + } + + $property->setValue($data, $value); + } + } while ($reflection = $reflection->getParentClass()); + } + } + + /** + * Determine is virtual property. + * + * @param \ReflectionProperty $property + * @return bool + */ + protected static function isVirtualProperty(ReflectionProperty $property): bool + { + return method_exists($property, 'isVirtual') && $property->isVirtual(); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Serializers/Signed.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Serializers/Signed.php new file mode 100644 index 000000000..16f9593f5 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Serializers/Signed.php @@ -0,0 +1,91 @@ +closure = $closure; + } + + /** + * Resolve the closure with the given arguments. + * + * @return mixed + */ + public function __invoke() + { + return call_user_func_array($this->closure, func_get_args()); + } + + /** + * Gets the closure. + * + * @return \Closure + */ + public function getClosure() + { + return $this->closure; + } + + /** + * Get the serializable representation of the closure. + * + * @return array + */ + public function __serialize() + { + if (! static::$signer) { + throw new MissingSecretKeyException(); + } + + return static::$signer->sign( + serialize(new Native($this->closure)) + ); + } + + /** + * Restore the closure after serialization. + * + * @param array{serializable: string, hash: string} $signature + * @return void + * + * @throws \Laravel\SerializableClosure\Exceptions\InvalidSignatureException + */ + public function __unserialize($signature) + { + if (static::$signer && ! static::$signer->verify($signature)) { + throw new InvalidSignatureException(); + } + + /** @var \Laravel\SerializableClosure\Contracts\Serializable $serializable */ + $serializable = unserialize($signature['serializable']); + + $this->closure = $serializable->getClosure(); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Signers/Hmac.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Signers/Hmac.php new file mode 100644 index 000000000..41ed01ae8 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Signers/Hmac.php @@ -0,0 +1,53 @@ +secret = $secret; + } + + /** + * Sign the given serializable. + * + * @param string $serialized + * @return array + */ + public function sign($serialized) + { + return [ + 'serializable' => $serialized, + 'hash' => base64_encode(hash_hmac('sha256', $serialized, $this->secret, true)), + ]; + } + + /** + * Verify the given signature. + * + * @param array{serializable: string, hash: string} $signature + * @return bool + */ + public function verify($signature) + { + return hash_equals(base64_encode( + hash_hmac('sha256', $signature['serializable'], $this->secret, true) + ), $signature['hash']); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/ClosureScope.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/ClosureScope.php new file mode 100644 index 000000000..64ca19a3c --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/ClosureScope.php @@ -0,0 +1,22 @@ +content = "length = strlen($this->content); + + return true; + } + + /** + * Read from stream. + * + * @param int $count + * @return string + */ + public function stream_read($count) + { + $value = substr($this->content, $this->pointer, $count); + + $this->pointer += $count; + + return $value; + } + + /** + * Tests for end-of-file on a file pointer. + * + * @return bool + */ + public function stream_eof() + { + return $this->pointer >= $this->length; + } + + /** + * Change stream options. + * + * @param int $option + * @param int $arg1 + * @param int $arg2 + * @return bool + */ + public function stream_set_option($option, $arg1, $arg2) + { + return false; + } + + /** + * Retrieve information about a file resource. + * + * @return array|bool + */ + public function stream_stat() + { + $stat = stat(__FILE__); + // @phpstan-ignore-next-line + $stat[7] = $stat['size'] = $this->length; + + return $stat; + } + + /** + * Retrieve information about a file. + * + * @param string $path + * @param int $flags + * @return array|bool + */ + public function url_stat($path, $flags) + { + $stat = stat(__FILE__); + // @phpstan-ignore-next-line + $stat[7] = $stat['size'] = $this->length; + + return $stat; + } + + /** + * Seeks to specific location in a stream. + * + * @param int $offset + * @param int $whence + * @return bool + */ + public function stream_seek($offset, $whence = SEEK_SET) + { + $crt = $this->pointer; + + switch ($whence) { + case SEEK_SET: + $this->pointer = $offset; + break; + case SEEK_CUR: + $this->pointer += $offset; + break; + case SEEK_END: + $this->pointer = $this->length + $offset; + break; + } + + if ($this->pointer < 0 || $this->pointer >= $this->length) { + $this->pointer = $crt; + + return false; + } + + return true; + } + + /** + * Retrieve the current position of a stream. + * + * @return int + */ + public function stream_tell() + { + return $this->pointer; + } + + /** + * Registers the stream. + * + * @return void + */ + public static function register() + { + if (! static::$isRegistered) { + static::$isRegistered = stream_wrapper_register(static::STREAM_PROTO, __CLASS__); + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/ReflectionClosure.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/ReflectionClosure.php new file mode 100644 index 000000000..a029cf642 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/ReflectionClosure.php @@ -0,0 +1,1423 @@ +isStaticClosure === null) { + $this->isStaticClosure = strtolower(substr($this->getCode(), 0, 6)) === 'static'; + } + + return $this->isStaticClosure; + } + + /** + * Checks if the closure is a "short closure". + * + * @return bool + */ + public function isShortClosure() + { + if ($this->isShortClosure === null) { + $code = $this->getCode(); + + if ($this->isStatic()) { + $code = substr($code, 6); + } + + $this->isShortClosure = strtolower(substr(trim($code), 0, 2)) === 'fn'; + } + + return $this->isShortClosure; + } + + /** + * Get the closure's code. + * + * @return string + */ + public function getCode() + { + if ($this->code !== null) { + return $this->code; + } + + $fileName = $this->getFileName(); + $line = $this->getStartLine() - 1; + + $className = null; + + if (null !== $className = $this->getClosureScopeClass()) { + $className = '\\'.trim($className->getName(), '\\'); + } + + $builtin_types = self::getBuiltinTypes(); + $class_keywords = ['self', 'static', 'parent']; + + $ns = $this->getClosureNamespaceName(); + $nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\'.$ns); + + $_file = var_export($fileName, true); + $_dir = var_export(dirname($fileName), true); + $_namespace = var_export($ns, true); + $_class = var_export(trim($className ?: '', '\\'), true); + $_function = $ns.($ns == '' ? '' : '\\').'{closure}'; + $_method = ($className == '' ? '' : trim($className, '\\').'::').$_function; + $_function = var_export($_function, true); + $_method = var_export($_method, true); + $_trait = null; + + $tokens = $this->getTokens(); + $state = $lastState = 'start'; + $inside_structure = false; + $isFirstClassCallable = false; + $isShortClosure = false; + + $inside_structure_mark = 0; + $open = 0; + $code = ''; + $id_start = $id_start_ci = $id_name = $context = ''; + $classes = $functions = $constants = null; + $use = []; + $lineAdd = 0; + $isUsingScope = false; + $isUsingThisObject = false; + + $candidates = []; + + for ($i = 0, $l = count($tokens); $i < $l; $i++) { + $token = $tokens[$i]; + + switch ($state) { + case 'start': + if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) { + $code .= $token[1]; + + $state = $token[0] === T_FUNCTION ? 'function' : 'static'; + } elseif ($token[0] === T_FN) { + $isShortClosure = true; + $code .= $token[1]; + $state = 'closure_args'; + } elseif ($token[0] === T_PUBLIC || $token[0] === T_PROTECTED || $token[0] === T_PRIVATE) { + $code = ''; + $isFirstClassCallable = true; + } + break; + case 'static': + if ($token[0] === T_WHITESPACE || $token[0] === T_COMMENT || $token[0] === T_FUNCTION) { + $code .= $token[1]; + if ($token[0] === T_FUNCTION) { + $state = 'function'; + } + } elseif ($token[0] === T_FN) { + $isShortClosure = true; + $code .= $token[1]; + $state = 'closure_args'; + } else { + $code = ''; + $state = 'start'; + } + break; + case 'function': + switch ($token[0]) { + case T_STRING: + if ($isFirstClassCallable) { + $state = 'closure_args'; + break; + } + + $code = ''; + $state = 'named_function'; + break; + case '(': + $code .= '('; + $state = 'closure_args'; + break; + default: + $code .= is_array($token) ? $token[1] : $token; + } + break; + case 'named_function': + if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) { + $code = $token[1]; + $state = $token[0] === T_FUNCTION ? 'function' : 'static'; + } elseif ($token[0] === T_FN) { + $isShortClosure = true; + $code .= $token[1]; + $state = 'closure_args'; + } + break; + case 'closure_args': + switch ($token[0]) { + case T_NAME_QUALIFIED: + [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); + $context = 'args'; + $state = 'id_name'; + $lastState = 'closure_args'; + break; + case T_NS_SEPARATOR: + case T_STRING: + $id_start = $token[1]; + $id_start_ci = strtolower($id_start); + $id_name = ''; + $context = 'args'; + $state = 'id_name'; + $lastState = 'closure_args'; + break; + case T_USE: + $code .= $token[1]; + $state = 'use'; + break; + case T_DOUBLE_ARROW: + $code .= $token[1]; + if ($isShortClosure) { + $state = 'closure'; + } + break; + case ':': + $code .= ':'; + $state = 'return'; + break; + case '{': + $code .= '{'; + $state = 'closure'; + $open++; + break; + default: + $code .= is_array($token) ? $token[1] : $token; + } + break; + case 'use': + switch ($token[0]) { + case T_VARIABLE: + $use[] = substr($token[1], 1); + $code .= $token[1]; + break; + case '{': + $code .= '{'; + $state = 'closure'; + $open++; + break; + case ':': + $code .= ':'; + $state = 'return'; + break; + default: + $code .= is_array($token) ? $token[1] : $token; + break; + } + break; + case 'return': + switch ($token[0]) { + case T_WHITESPACE: + case T_COMMENT: + case T_DOC_COMMENT: + $code .= $token[1]; + break; + case T_NS_SEPARATOR: + case T_STRING: + $id_start = $token[1]; + $id_start_ci = strtolower($id_start); + $id_name = ''; + $context = 'return_type'; + $state = 'id_name'; + $lastState = 'return'; + break 2; + case T_NAME_QUALIFIED: + [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); + $context = 'return_type'; + $state = 'id_name'; + $lastState = 'return'; + break 2; + case T_DOUBLE_ARROW: + $code .= $token[1]; + if ($isShortClosure) { + $state = 'closure'; + } + break; + case '{': + $code .= '{'; + $state = 'closure'; + $open++; + break; + default: + $code .= is_array($token) ? $token[1] : $token; + break; + } + break; + case 'closure': + switch ($token[0]) { + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + case '{': + $code .= is_array($token) ? $token[1] : $token; + $open++; + break; + case '}': + $code .= '}'; + if (--$open === 0 && ! $isShortClosure) { + $reset = $this->collectCandidate($candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope); + $code = $reset['code']; + $state = $reset['state']; + $open = $reset['open']; + $use = $reset['use']; + $isShortClosure = $reset['isShortClosure']; + $isUsingThisObject = $reset['isUsingThisObject']; + $isUsingScope = $reset['isUsingScope']; + } elseif ($inside_structure) { + $inside_structure = ! ($open === $inside_structure_mark); + } + break; + case '(': + case '[': + $code .= $token[0]; + if ($isShortClosure) { + $open++; + } + break; + case ')': + case ']': + if ($isShortClosure) { + if ($open === 0) { + $reset = $this->collectCandidate($candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope); + $code = $reset['code']; + $state = $reset['state']; + $open = $reset['open']; + $use = $reset['use']; + $isShortClosure = $reset['isShortClosure']; + $isUsingThisObject = $reset['isUsingThisObject']; + $isUsingScope = $reset['isUsingScope']; + continue 3; + } + $open--; + } + $code .= $token[0]; + break; + case ',': + case ';': + if ($isShortClosure && $open === 0) { + $reset = $this->collectCandidate($candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope); + $code = $reset['code']; + $state = $reset['state']; + $open = $reset['open']; + $use = $reset['use']; + $isShortClosure = $reset['isShortClosure']; + $isUsingThisObject = $reset['isUsingThisObject']; + $isUsingScope = $reset['isUsingScope']; + continue 3; + } + $code .= $token[0]; + break; + case T_LINE: + $code .= $token[2] - $line + $lineAdd; + break; + case T_FILE: + $code .= $_file; + break; + case T_DIR: + $code .= $_dir; + break; + case T_NS_C: + $code .= $_namespace; + break; + case T_CLASS_C: + $code .= $inside_structure ? $token[1] : $_class; + break; + case T_FUNC_C: + $code .= $inside_structure ? $token[1] : $_function; + break; + case T_METHOD_C: + $code .= $inside_structure ? $token[1] : $_method; + break; + case T_COMMENT: + if (substr($token[1], 0, 8) === '#trackme') { + $timestamp = time(); + $code .= '/**'.PHP_EOL; + $code .= '* Date : '.date(DATE_W3C, $timestamp).PHP_EOL; + $code .= '* Timestamp : '.$timestamp.PHP_EOL; + $code .= '* Line : '.($line + 1).PHP_EOL; + $code .= '* File : '.$_file.PHP_EOL.'*/'.PHP_EOL; + $lineAdd += 5; + } else { + $code .= $token[1]; + } + break; + case T_VARIABLE: + if ($token[1] == '$this' && ! $inside_structure) { + $isUsingThisObject = true; + } + $code .= $token[1]; + break; + case T_STATIC: + case T_NS_SEPARATOR: + case T_STRING: + $id_start = $token[1]; + $id_start_ci = strtolower($id_start); + $id_name = ''; + $context = 'root'; + $state = 'id_name'; + $lastState = 'closure'; + break 2; + case T_NAME_QUALIFIED: + [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); + $context = 'root'; + $state = 'id_name'; + $lastState = 'closure'; + break 2; + case T_NEW: + $code .= $token[1]; + $context = 'new'; + $state = 'id_start'; + $lastState = 'closure'; + break 2; + case T_USE: + $code .= $token[1]; + $context = 'use'; + $state = 'id_start'; + $lastState = 'closure'; + break; + case T_INSTANCEOF: + case T_INSTEADOF: + $code .= $token[1]; + $context = 'instanceof'; + $state = 'id_start'; + $lastState = 'closure'; + break; + case T_OBJECT_OPERATOR: + case T_NULLSAFE_OBJECT_OPERATOR: + case T_DOUBLE_COLON: + $code .= $token[1]; + $lastState = 'closure'; + $state = 'ignore_next'; + break; + case T_FUNCTION: + $code .= $token[1]; + $state = 'closure_args'; + if (! $inside_structure) { + $inside_structure = true; + $inside_structure_mark = $open; + } + break; + case T_TRAIT_C: + if ($_trait === null) { + $startLine = $this->getStartLine(); + $endLine = $this->getEndLine(); + $structures = $this->getStructures(); + + $_trait = ''; + + foreach ($structures as &$struct) { + if ($struct['type'] === 'trait' && + $struct['start'] <= $startLine && + $struct['end'] >= $endLine + ) { + $_trait = ($ns == '' ? '' : $ns.'\\').$struct['name']; + break; + } + } + + $_trait = var_export($_trait, true); + } + + $code .= $_trait; + break; + default: + $code .= is_array($token) ? $token[1] : $token; + } + break; + case 'ignore_next': + switch ($token[0]) { + case T_WHITESPACE: + case T_COMMENT: + case T_DOC_COMMENT: + $code .= $token[1]; + break; + case T_CLASS: + case T_NEW: + case T_STATIC: + case T_VARIABLE: + case T_STRING: + case T_CLASS_C: + case T_FILE: + case T_DIR: + case T_METHOD_C: + case T_FUNC_C: + case T_FUNCTION: + case T_INSTANCEOF: + case T_LINE: + case T_NS_C: + case T_TRAIT_C: + case T_USE: + $code .= $token[1]; + $state = $lastState; + break; + default: + $state = $lastState; + $i--; + } + break; + case 'id_start': + switch ($token[0]) { + case T_WHITESPACE: + case T_COMMENT: + case T_DOC_COMMENT: + $code .= $token[1]; + break; + case T_NS_SEPARATOR: + case T_NAME_FULLY_QUALIFIED: + case T_STRING: + case T_STATIC: + $id_start = $token[1]; + $id_start_ci = strtolower($id_start); + $id_name = ''; + $state = 'id_name'; + break 2; + case T_NAME_QUALIFIED: + [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); + $state = 'id_name'; + break 2; + case T_VARIABLE: + $code .= $token[1]; + $state = $lastState; + break; + case T_CLASS: + $code .= $token[1]; + $state = 'anonymous'; + break; + default: + $i--; //reprocess last + $state = 'id_name'; + } + break; + case 'id_name': + switch ($token[0]) { + case $token[0] === ':' && ! in_array($context, ['instanceof', 'new'], true): + if ($lastState === 'closure' && $context === 'root') { + $state = 'closure'; + $code .= $id_start.$token; + } + + break; + case T_NAME_QUALIFIED: + case T_NS_SEPARATOR: + case T_STRING: + case T_WHITESPACE: + case T_COMMENT: + case T_DOC_COMMENT: + $id_name .= $token[1]; + break; + case '(': + if ($isShortClosure) { + $open++; + } + if ($context === 'new' || false !== strpos($id_name, '\\')) { + if ($id_start_ci === 'self' || $id_start_ci === 'static') { + if (! $inside_structure) { + $isUsingScope = true; + } + } elseif ($id_start !== '\\' && ! in_array($id_start_ci, $class_keywords)) { + if ($classes === null) { + $classes = $this->getClasses(); + } + if (isset($classes[$id_start_ci])) { + $id_start = $classes[$id_start_ci]; + } + if ($id_start[0] !== '\\') { + $id_start = $nsf.'\\'.$id_start; + } + } + } else { + if ($id_start !== '\\') { + if ($functions === null) { + $functions = $this->getFunctions(); + } + if (isset($functions[$id_start_ci])) { + $id_start = $functions[$id_start_ci]; + } elseif ($nsf !== '\\' && function_exists($nsf.'\\'.$id_start)) { + $id_start = $nsf.'\\'.$id_start; + // Cache it to functions array + $functions[$id_start_ci] = $id_start; + } + } + } + $code .= $id_start.$id_name.'('; + $state = $lastState; + break; + case T_VARIABLE: + case T_DOUBLE_COLON: + if ($id_start !== '\\') { + if ($id_start_ci === 'self' || $id_start_ci === 'parent') { + if (! $inside_structure) { + $isUsingScope = true; + } + } elseif ($id_start_ci === 'static') { + if (! $inside_structure) { + $isUsingScope = $token[0] === T_DOUBLE_COLON; + } + } elseif (! (\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))) { + if ($classes === null) { + $classes = $this->getClasses(); + } + if (isset($classes[$id_start_ci])) { + $id_start = $classes[$id_start_ci]; + } + if ($id_start[0] !== '\\') { + $id_start = $nsf.'\\'.$id_start; + } + } + } + + $code .= $id_start.$id_name.$token[1]; + $state = $token[0] === T_DOUBLE_COLON ? 'ignore_next' : $lastState; + break; + default: + if ($id_start !== '\\' && ! defined($id_start)) { + if ($constants === null) { + $constants = $this->getConstants(); + } + if (isset($constants[$id_start])) { + $id_start = $constants[$id_start]; + } elseif ($context === 'new') { + if (in_array($id_start_ci, $class_keywords)) { + if (! $inside_structure) { + $isUsingScope = true; + } + } else { + if ($classes === null) { + $classes = $this->getClasses(); + } + if (isset($classes[$id_start_ci])) { + $id_start = $classes[$id_start_ci]; + } + if ($id_start[0] !== '\\') { + $id_start = $nsf.'\\'.$id_start; + } + } + } elseif ($context === 'use' || + $context === 'instanceof' || + $context === 'args' || + $context === 'return_type' || + $context === 'extends' || + $context === 'root' + ) { + if (in_array($id_start_ci, $class_keywords)) { + if (! $inside_structure && ! $id_start_ci === 'static') { + $isUsingScope = true; + } + } elseif (! (\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))) { + if ($classes === null) { + $classes = $this->getClasses(); + } + if (isset($classes[$id_start_ci])) { + $id_start = $classes[$id_start_ci]; + } + if ($id_start[0] !== '\\') { + $id_start = $nsf.'\\'.$id_start; + } + } + } + } + $code .= $id_start.$id_name; + $state = $lastState; + $i--; //reprocess last token + } + break; + case 'anonymous': + switch ($token[0]) { + case T_NAME_QUALIFIED: + [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); + $state = 'id_name'; + $lastState = 'anonymous'; + break 2; + case T_NS_SEPARATOR: + case T_STRING: + $id_start = $token[1]; + $id_start_ci = strtolower($id_start); + $id_name = ''; + $state = 'id_name'; + $context = 'extends'; + $lastState = 'anonymous'; + break; + case '{': + $state = 'closure'; + if (! $inside_structure) { + $inside_structure = true; + $inside_structure_mark = $open; + } + $i--; + break; + default: + $code .= is_array($token) ? $token[1] : $token; + } + break; + } + } + + $attributesCode = array_map(function ($attribute) { + $arguments = $attribute->getArguments(); + + $name = $attribute->getName(); + $arguments = implode(', ', array_map(function ($argument, $key) { + $argument = var_export($argument, true); + + if (is_string($key)) { + $argument = sprintf('%s: %s', $key, $argument); + } + + return $argument; + }, $arguments, array_keys($arguments))); + + return "#[$name($arguments)]"; + }, $this->getAttributes()); + + if (count($candidates) > 1) { + $lastItem = array_pop($candidates); + + foreach ($candidates as $candidate) { + if (! $this->verifyCandidateSignature($candidate)) { + continue; + } + + $this->applyCandidate($candidate); + + $code = $candidate['code']; + + if (! empty($attributesCode)) { + $code = implode("\n", array_merge($attributesCode, [$code])); + } + + $this->code = $code; + + return $this->code; + } + + $candidates[] = $lastItem; + } + + $lastItem = array_pop($candidates); + + $this->applyCandidate($lastItem); + + $code = $lastItem['code']; + + if (! empty($attributesCode)) { + $code = implode("\n", array_merge($attributesCode, [$code])); + } + + $this->code = $code; + + return $this->code; + } + + /** + * Get PHP native built in types. + * + * @return array + */ + protected static function getBuiltinTypes() + { + return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object', 'mixed', 'false', 'null', 'never']; + } + + /** + * Gets the use variables by the closure. + * + * @return array + */ + public function getUseVariables() + { + if ($this->useVariables !== null) { + return $this->useVariables; + } + + if ($this->isShortClosure()) { + return $this->useVariables; + } + + $tokens = $this->getTokens(); + $use = []; + $state = 'start'; + + foreach ($tokens as &$token) { + $is_array = is_array($token); + + switch ($state) { + case 'start': + if ($is_array && $token[0] === T_USE) { + $state = 'use'; + } + break; + case 'use': + if ($is_array) { + if ($token[0] === T_VARIABLE) { + $use[] = substr($token[1], 1); + } + } elseif ($token == ')') { + break 2; + } + break; + } + } + + $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use)); + + return $this->useVariables; + } + + /** + * Checks if binding is required. + * + * @return bool + */ + public function isBindingRequired() + { + if ($this->isBindingRequired === null) { + $this->getCode(); + } + + return $this->isBindingRequired; + } + + /** + * Checks if access to the scope is required. + * + * @return bool + */ + public function isScopeRequired() + { + if ($this->isScopeRequired === null) { + $this->getCode(); + } + + return $this->isScopeRequired; + } + + /** + * The hash of the current file name. + * + * @return string + */ + protected function getHashedFileName() + { + if ($this->hashedName === null) { + $this->hashedName = sha1($this->getFileName()); + } + + return $this->hashedName; + } + + /** + * Get the file tokens. + * + * @return array + */ + protected function getFileTokens() + { + $key = $this->getHashedFileName(); + + if (! isset(static::$files[$key])) { + static::$files[$key] = token_get_all(file_get_contents($this->getFileName())); + } + + return static::$files[$key]; + } + + /** + * Get the tokens. + * + * @return array + */ + protected function getTokens() + { + if ($this->tokens === null) { + $tokens = $this->getFileTokens(); + $startLine = $this->getStartLine(); + $endLine = $this->getEndLine(); + $results = []; + $start = false; + + foreach ($tokens as &$token) { + if (! is_array($token)) { + if ($start) { + $results[] = $token; + } + + continue; + } + + $line = $token[2]; + + if ($line <= $endLine) { + if ($line >= $startLine) { + $start = true; + $results[] = $token; + } + + continue; + } + + break; + } + + $this->tokens = $results; + } + + return $this->tokens; + } + + /** + * Get the classes. + * + * @return array + */ + protected function getClasses() + { + $line = $this->getStartLine(); + + foreach ($this->getStructures() as $struct) { + if ($struct['type'] === 'namespace' && + $struct['start'] <= $line && + $struct['end'] >= $line + ) { + return $struct['classes']; + } + } + + return []; + } + + /** + * Get the functions. + * + * @return array + */ + protected function getFunctions() + { + $key = $this->getHashedFileName(); + + if (! isset(static::$functions[$key])) { + $this->fetchItems(); + } + + return static::$functions[$key]; + } + + /** + * Gets the constants. + * + * @return array + */ + protected function getConstants() + { + $key = $this->getHashedFileName(); + + if (! isset(static::$constants[$key])) { + $this->fetchItems(); + } + + return static::$constants[$key]; + } + + /** + * Get the structures. + * + * @return array + */ + protected function getStructures() + { + $key = $this->getHashedFileName(); + + if (! isset(static::$structures[$key])) { + $this->fetchItems(); + } + + return static::$structures[$key]; + } + + /** + * Fetch the items. + * + * @return void. + */ + protected function fetchItems() + { + $key = $this->getHashedFileName(); + + $classes = []; + $functions = []; + $constants = []; + $structures = []; + $tokens = $this->getFileTokens(); + + $open = 0; + $state = 'start'; + $lastState = ''; + $prefix = ''; + $name = ''; + $alias = ''; + $isFunc = $isConst = false; + + $startLine = $lastKnownLine = 0; + $structType = $structName = ''; + $structIgnore = false; + + $namespace = ''; + $namespaceStartLine = 0; + $namespaceBraced = false; + $namespaceClasses = []; + + foreach ($tokens as $token) { + if (is_array($token)) { + $lastKnownLine = $token[2]; + } + + switch ($state) { + case 'start': + switch ($token[0]) { + case T_NAMESPACE: + $structures[] = [ + 'type' => 'namespace', + 'name' => $namespace, + 'start' => $namespaceStartLine, + 'end' => $token[2] - 1, + 'classes' => $namespaceClasses, + ]; + $namespace = ''; + $namespaceClasses = []; + $state = 'namespace'; + $namespaceStartLine = $token[2]; + break; + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + $state = 'before_structure'; + $startLine = $token[2]; + $structType = $token[0] == T_CLASS + ? 'class' + : ($token[0] == T_INTERFACE ? 'interface' : 'trait'); + break; + case T_USE: + $state = 'use'; + $prefix = $name = $alias = ''; + $isFunc = $isConst = false; + break; + case T_FUNCTION: + $state = 'structure'; + $structIgnore = true; + break; + case T_NEW: + $state = 'new'; + break; + case T_OBJECT_OPERATOR: + case T_DOUBLE_COLON: + $state = 'invoke'; + break; + case '}': + if ($namespaceBraced) { + $structures[] = [ + 'type' => 'namespace', + 'name' => $namespace, + 'start' => $namespaceStartLine, + 'end' => $lastKnownLine, + 'classes' => $namespaceClasses, + ]; + $namespaceBraced = false; + $namespace = ''; + $namespaceClasses = []; + } + break; + } + break; + case 'namespace': + switch ($token[0]) { + case T_STRING: + case T_NAME_QUALIFIED: + $namespace = $token[1]; + break; + case ';': + case '{': + $state = 'start'; + $namespaceBraced = $token[0] === '{'; + break; + } + break; + case 'use': + switch ($token[0]) { + case T_FUNCTION: + $isFunc = true; + break; + case T_CONST: + $isConst = true; + break; + case T_NS_SEPARATOR: + $name .= $token[1]; + break; + case T_STRING: + $name .= $token[1]; + $alias = $token[1]; + break; + case T_NAME_QUALIFIED: + $name .= $token[1]; + $pieces = explode('\\', $token[1]); + $alias = end($pieces); + break; + case T_AS: + $lastState = 'use'; + $state = 'alias'; + break; + case '{': + $prefix = $name; + $name = $alias = ''; + $state = 'use-group'; + break; + case ',': + case ';': + if ($name === '' || $name[0] !== '\\') { + $name = '\\'.$name; + } + + if ($alias !== '') { + if ($isFunc) { + $functions[strtolower($alias)] = $name; + } elseif ($isConst) { + $constants[$alias] = $name; + } else { + $classes[strtolower($alias)] = $name; + $namespaceClasses[strtolower($alias)] = $name; + } + } + $name = $alias = ''; + $state = $token === ';' ? 'start' : 'use'; + break; + } + break; + case 'use-group': + switch ($token[0]) { + case T_NS_SEPARATOR: + $name .= $token[1]; + break; + case T_NAME_QUALIFIED: + $name .= $token[1]; + $pieces = explode('\\', $token[1]); + $alias = end($pieces); + break; + case T_STRING: + $name .= $token[1]; + $alias = $token[1]; + break; + case T_AS: + $lastState = 'use-group'; + $state = 'alias'; + break; + case ',': + case '}': + + if ($prefix === '' || $prefix[0] !== '\\') { + $prefix = '\\'.$prefix; + } + + if ($alias !== '') { + if ($isFunc) { + $functions[strtolower($alias)] = $prefix.$name; + } elseif ($isConst) { + $constants[$alias] = $prefix.$name; + } else { + $classes[strtolower($alias)] = $prefix.$name; + $namespaceClasses[strtolower($alias)] = $prefix.$name; + } + } + $name = $alias = ''; + $state = $token === '}' ? 'use' : 'use-group'; + break; + } + break; + case 'alias': + if ($token[0] === T_STRING) { + $alias = $token[1]; + $state = $lastState; + } + break; + case 'new': + switch ($token[0]) { + case T_WHITESPACE: + case T_COMMENT: + case T_DOC_COMMENT: + break 2; + case T_CLASS: + $state = 'structure'; + $structIgnore = true; + break; + default: + $state = 'start'; + } + break; + case 'invoke': + switch ($token[0]) { + case T_WHITESPACE: + case T_COMMENT: + case T_DOC_COMMENT: + break 2; + default: + $state = 'start'; + } + break; + case 'before_structure': + if ($token[0] == T_STRING) { + $structName = $token[1]; + $state = 'structure'; + } + break; + case 'structure': + switch ($token[0]) { + case '{': + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + $open++; + break; + case '}': + if (--$open == 0) { + if (! $structIgnore) { + $structures[] = [ + 'type' => $structType, + 'name' => $structName, + 'start' => $startLine, + 'end' => $lastKnownLine, + ]; + } + $structIgnore = false; + $state = 'start'; + } + break; + } + break; + } + } + + $structures[] = [ + 'type' => 'namespace', + 'name' => $namespace, + 'start' => $namespaceStartLine, + 'end' => PHP_INT_MAX, + 'classes' => $namespaceClasses, + ]; + + static::$classes[$key] = $classes; + static::$functions[$key] = $functions; + static::$constants[$key] = $constants; + static::$structures[$key] = $structures; + } + + /** + * Returns the namespace associated to the closure. + * + * @return string + */ + protected function getClosureNamespaceName() + { + $startLine = $this->getStartLine(); + $endLine = $this->getEndLine(); + + foreach ($this->getStructures() as $struct) { + if ($struct['type'] === 'namespace' && + $struct['start'] <= $startLine && + $struct['end'] >= $endLine + ) { + return $struct['name']; + } + } + + return ''; + } + + /** + * Parse the given token. + * + * @param string $token + * @return array + */ + protected function parseNameQualified($token) + { + $pieces = explode('\\', $token); + + $id_start = array_shift($pieces); + + $id_start_ci = strtolower($id_start); + + $id_name = '\\'.implode('\\', $pieces); + + return [$id_start, $id_start_ci, $id_name]; + } + + /** + * Collect a closure candidate and reset state for finding the next one. + * + * @param array $candidates + * @param string $code + * @param array $use + * @param bool $isShortClosure + * @param bool $isUsingThisObject + * @param bool $isUsingScope + * @return array + */ + protected function collectCandidate(&$candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope) + { + $candidates[] = [ + 'code' => $code, + 'use' => $use, + 'isShortClosure' => $isShortClosure, + 'isUsingThisObject' => $isUsingThisObject, + 'isUsingScope' => $isUsingScope, + ]; + + return [ + 'code' => '', + 'state' => 'start', + 'open' => 0, + 'use' => [], + 'isShortClosure' => false, + 'isUsingThisObject' => false, + 'isUsingScope' => false, + ]; + } + + /** + * Apply a candidate's properties to this instance. + * + * @param array $candidate + * @return void + */ + protected function applyCandidate($candidate) + { + if ($candidate['isShortClosure']) { + $this->useVariables = $this->getStaticVariables(); + } else { + $this->useVariables = empty($candidate['use']) + ? $candidate['use'] + : array_intersect_key($this->getStaticVariables(), array_flip($candidate['use'])); + } + + $this->isShortClosure = $candidate['isShortClosure']; + $this->isBindingRequired = $candidate['isUsingThisObject']; + $this->isScopeRequired = $candidate['isUsingScope']; + } + + /** + * Verify that a candidate matches the closure's signature. + * + * @param array $candidate + * @return bool + */ + protected function verifyCandidateSignature($candidate) + { + $code = $candidate['code']; + $use = $candidate['use']; + $isShortClosure = $candidate['isShortClosure']; + + // Check if code starts with 'static' (more precise than searching anywhere in code) + $isStaticCode = strtolower(substr(ltrim($code), 0, 6)) === 'static'; + if (parent::isStatic() !== $isStaticCode) { + return false; + } + + // Parse the candidate to extract parameters and variables + $tokens = token_get_all(' 0) { + return false; + } + } else { + $actualStaticVariables = array_keys(parent::getStaticVariables()); + + if (! empty($use) && count(array_diff($use, $actualStaticVariables)) > 0) { + return false; + } + + if (count($use) !== count(parent::getStaticVariables())) { + return false; + } + } + + return true; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/SelfReference.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/SelfReference.php new file mode 100644 index 000000000..58319504a --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/Support/SelfReference.php @@ -0,0 +1,24 @@ +hash = $hash; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/UnsignedSerializableClosure.php b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/UnsignedSerializableClosure.php new file mode 100644 index 000000000..afc2127ed --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/laravel/serializable-closure/src/UnsignedSerializableClosure.php @@ -0,0 +1,69 @@ +serializable = new Serializers\Native($closure); + } + + /** + * Resolve the closure with the given arguments. + * + * @return mixed + */ + public function __invoke() + { + return call_user_func_array($this->serializable, func_get_args()); + } + + /** + * Gets the closure. + * + * @return \Closure + */ + public function getClosure() + { + return $this->serializable->getClosure(); + } + + /** + * Get the serializable representation of the closure. + * + * @return array{serializable: \Laravel\SerializableClosure\Contracts\Serializable} + */ + public function __serialize() + { + return [ + 'serializable' => $this->serializable, + ]; + } + + /** + * Restore the closure after serialization. + * + * @param array{serializable: \Laravel\SerializableClosure\Contracts\Serializable} $data + * @return void + */ + public function __unserialize($data) + { + $this->serializable = $data['serializable']; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/LICENSE b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/LICENSE new file mode 100644 index 000000000..b2b04161b --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Matthieu Napoli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/README.md b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/README.md new file mode 100644 index 000000000..88ef215aa --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/README.md @@ -0,0 +1,234 @@ +# Invoker + +Generic and extensible callable invoker. + +[![CI](https://github.com/PHP-DI/Invoker/actions/workflows/ci.yml/badge.svg)](https://github.com/PHP-DI/Invoker/actions/workflows/ci.yml) +[![Latest Version](https://img.shields.io/github/release/PHP-DI/invoker.svg?style=flat-square)](https://packagist.org/packages/PHP-DI/invoker) +[![Total Downloads](https://img.shields.io/packagist/dt/php-di/invoker.svg?style=flat-square)](https://packagist.org/packages/php-di/invoker) + +## Why? + +Who doesn't need an over-engineered `call_user_func()`? + +### Named parameters + +Does this [Silex](https://github.com/silexphp/Silex#readme) example look familiar: + +```php +$app->get('/project/{project}/issue/{issue}', function ($project, $issue) { + // ... +}); +``` + +Or this command defined with [Silly](https://github.com/mnapoli/silly#usage): + +```php +$app->command('greet [name] [--yell]', function ($name, $yell) { + // ... +}); +``` + +Same pattern in [Slim](https://www.slimframework.com): + +```php +$app->get('/hello/:name', function ($name) { + // ... +}); +``` + +You get the point. These frameworks invoke the controller/command/handler using something akin to named parameters: whatever the order of the parameters, they are matched by their name. + +**This library allows to invoke callables with named parameters in a generic and extensible way.** + +### Dependency injection + +Anyone familiar with AngularJS is familiar with how dependency injection is performed: + +```js +angular.controller('MyController', ['dep1', 'dep2', function(dep1, dep2) { + // ... +}]); +``` + +In PHP we find this pattern again in some frameworks and DI containers with partial to full support. For example in Silex you can type-hint the application to get it injected, but it only works with `Silex\Application`: + +```php +$app->get('/hello/{name}', function (Silex\Application $app, $name) { + // ... +}); +``` + +In Silly, it only works with `OutputInterface` to inject the application output: + +```php +$app->command('greet [name]', function ($name, OutputInterface $output) { + // ... +}); +``` + +[PHP-DI](https://php-di.org/doc/container.html) provides a way to invoke a callable and resolve all dependencies from the container using type-hints: + +```php +$container->call(function (Logger $logger, EntityManager $em) { + // ... +}); +``` + +**This library provides clear extension points to let frameworks implement any kind of dependency injection support they want.** + +### TL/DR + +In short, this library is meant to be a base building block for calling a function with named parameters and/or dependency injection. + +## Installation + +```sh +$ composer require PHP-DI/invoker +``` + +## Usage + +### Default behavior + +By default the `Invoker` can call using named parameters: + +```php +$invoker = new Invoker\Invoker; + +$invoker->call(function () { + echo 'Hello world!'; +}); + +// Simple parameter array +$invoker->call(function ($name) { + echo 'Hello ' . $name; +}, ['John']); + +// Named parameters +$invoker->call(function ($name) { + echo 'Hello ' . $name; +}, [ + 'name' => 'John' +]); + +// Use the default value +$invoker->call(function ($name = 'world') { + echo 'Hello ' . $name; +}); + +// Invoke any PHP callable +$invoker->call(['MyClass', 'myStaticMethod']); + +// Using Class::method syntax +$invoker->call('MyClass::myStaticMethod'); +``` + +Dependency injection in parameters is supported but needs to be configured with your container. Read on or jump to [*Built-in support for dependency injection*](#built-in-support-for-dependency-injection) if you are impatient. + +Additionally, callables can also be resolved from your container. Read on or jump to [*Resolving callables from a container*](#resolving-callables-from-a-container) if you are impatient. + +### Parameter resolvers + +Extending the behavior of the `Invoker` is easy and is done by implementing a [`ParameterResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ParameterResolver.php). + +This is explained in details the [Parameter resolvers documentation](doc/parameter-resolvers.md). + +#### Built-in support for dependency injection + +Rather than have you re-implement support for dependency injection with different containers every time, this package ships with 2 optional resolvers: + +- [`TypeHintContainerResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/Container/TypeHintContainerResolver.php) + + This resolver will inject container entries by searching for the class name using the type-hint: + + ```php + $invoker->call(function (Psr\Logger\LoggerInterface $logger) { + // ... + }); + ``` + + In this example it will `->get('Psr\Logger\LoggerInterface')` from the container and inject it. + + This resolver is only useful if you store objects in your container using the class (or interface) name. Silex or Symfony for example store services under a custom name (e.g. `twig`, `db`, etc.) instead of the class name: in that case use the resolver shown below. + +- [`ParameterNameContainerResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/Container/ParameterNameContainerResolver.php) + + This resolver will inject container entries by searching for the name of the parameter: + + ```php + $invoker->call(function ($twig) { + // ... + }); + ``` + + In this example it will `->get('twig')` from the container and inject it. + +These resolvers can work with any dependency injection container compliant with [PSR-11](http://www.php-fig.org/psr/psr-11/). + +Setting up those resolvers is simple: + +```php +// $container must be an instance of Psr\Container\ContainerInterface +$container = ... + +$containerResolver = new TypeHintContainerResolver($container); +// or +$containerResolver = new ParameterNameContainerResolver($container); + +$invoker = new Invoker\Invoker; +// Register it before all the other parameter resolvers +$invoker->getParameterResolver()->prependResolver($containerResolver); +``` + +You can also register both resolvers at the same time if you wish by prepending both. Implementing support for more tricky things is easy and up to you! + +### Resolving callables from a container + +The `Invoker` can be wired to your DI container to resolve the callables. + +For example with an invokable class: + +```php +class MyHandler +{ + public function __invoke() + { + // ... + } +} + +// By default this doesn't work: an instance of the class should be provided +$invoker->call('MyHandler'); + +// If we set up the container to use +$invoker = new Invoker\Invoker(null, $container); +// Now 'MyHandler' is resolved using the container! +$invoker->call('MyHandler'); +``` + +The same works for a class method: + +```php +class WelcomeController +{ + public function home() + { + // ... + } +} + +// By default this doesn't work: home() is not a static method +$invoker->call(['WelcomeController', 'home']); + +// If we set up the container to use +$invoker = new Invoker\Invoker(null, $container); +// Now 'WelcomeController' is resolved using the container! +$invoker->call(['WelcomeController', 'home']); +// Alternatively we can use the Class::method syntax +$invoker->call('WelcomeController::home'); +``` + +That feature can be used as the base building block for a framework's dispatcher. + +Again, any [PSR-11](https://www.php-fig.org/psr/psr-11/) compliant container can be provided. + diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/composer.json b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/composer.json new file mode 100644 index 000000000..3d15969f8 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/composer.json @@ -0,0 +1,32 @@ +{ + "name": "php-di/invoker", + "description": "Generic and extensible callable invoker", + "keywords": ["invoker", "dependency-injection", "dependency", "injection", "callable", "invoke"], + "homepage": "https://github.com/PHP-DI/Invoker", + "license": "MIT", + "type": "library", + "autoload": { + "psr-4": { + "Invoker\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Invoker\\Test\\": "tests/" + } + }, + "require": { + "php": ">=7.3", + "psr/container": "^1.0|^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0 || ^10 || ^11 || ^12", + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/CallableResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/CallableResolver.php new file mode 100644 index 000000000..e3b11b28e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/CallableResolver.php @@ -0,0 +1,124 @@ +container = $container; + } + + /** + * Resolve the given callable into a real PHP callable. + * + * @param callable|string|array $callable + * @return callable Real PHP callable. + * @throws NotCallableException|ReflectionException + */ + public function resolve($callable): callable + { + if (is_string($callable) && strpos($callable, '::') !== false) { + $callable = explode('::', $callable, 2); + } + + $callable = $this->resolveFromContainer($callable); + + if (! is_callable($callable)) { + throw NotCallableException::fromInvalidCallable($callable, true); + } + + return $callable; + } + + /** + * @param callable|string|array $callable + * @return callable|mixed + * @throws NotCallableException|ReflectionException + */ + private function resolveFromContainer($callable) + { + // Shortcut for a very common use case + if ($callable instanceof Closure) { + return $callable; + } + + // If it's already a callable there is nothing to do + if (is_callable($callable)) { + // TODO with PHP 8 that should not be necessary to check this anymore + if (! $this->isStaticCallToNonStaticMethod($callable)) { + return $callable; + } + } + + // The callable is a container entry name + if (is_string($callable)) { + try { + return $this->container->get($callable); + } catch (NotFoundExceptionInterface $e) { + if ($this->container->has($callable)) { + throw $e; + } + throw NotCallableException::fromInvalidCallable($callable, true); + } + } + + // The callable is an array whose first item is a container entry name + // e.g. ['some-container-entry', 'methodToCall'] + if (is_array($callable) && is_string($callable[0])) { + try { + // Replace the container entry name by the actual object + $callable[0] = $this->container->get($callable[0]); + return $callable; + } catch (NotFoundExceptionInterface $e) { + if ($this->container->has($callable[0])) { + throw $e; + } + throw new NotCallableException(sprintf( + 'Cannot call %s() on %s because it is not a class nor a valid container entry', + $callable[1], + $callable[0] + )); + } + } + + // Unrecognized stuff, we let it fail later + return $callable; + } + + /** + * Check if the callable represents a static call to a non-static method. + * + * @param mixed $callable + * @throws ReflectionException + */ + private function isStaticCallToNonStaticMethod($callable): bool + { + if (is_array($callable) && is_string($callable[0])) { + [$class, $method] = $callable; + + if (! method_exists($class, $method)) { + return false; + } + + $reflection = new ReflectionMethod($class, $method); + + return ! $reflection->isStatic(); + } + + return false; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/Exception/InvocationException.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/Exception/InvocationException.php new file mode 100644 index 000000000..90f6585e5 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/Exception/InvocationException.php @@ -0,0 +1,10 @@ +parameterResolver = $parameterResolver ?: $this->createParameterResolver(); + $this->container = $container; + + if ($container) { + $this->callableResolver = new CallableResolver($container); + } + } + + /** + * {@inheritdoc} + */ + public function call($callable, array $parameters = []) + { + if ($this->callableResolver) { + $callable = $this->callableResolver->resolve($callable); + } + + if (! is_callable($callable)) { + throw new NotCallableException(sprintf( + '%s is not a callable', + is_object($callable) ? 'Instance of ' . get_class($callable) : var_export($callable, true) + )); + } + + $callableReflection = CallableReflection::create($callable); + + $args = $this->parameterResolver->getParameters($callableReflection, $parameters, []); + + // Sort by array key because call_user_func_array ignores numeric keys + ksort($args); + + // Check all parameters are resolved + $diff = array_diff_key($callableReflection->getParameters(), $args); + $parameter = reset($diff); + if ($parameter && \assert($parameter instanceof ReflectionParameter) && ! $parameter->isVariadic()) { + throw new NotEnoughParametersException(sprintf( + 'Unable to invoke the callable because no value was given for parameter %d ($%s)', + $parameter->getPosition() + 1, + $parameter->name + )); + } + + return call_user_func_array($callable, $args); + } + + /** + * Create the default parameter resolver. + */ + private function createParameterResolver(): ParameterResolver + { + return new ResolverChain([ + new NumericArrayResolver, + new AssociativeArrayResolver, + new DefaultValueResolver, + ]); + } + + /** + * @return ParameterResolver By default it's a ResolverChain + */ + public function getParameterResolver(): ParameterResolver + { + return $this->parameterResolver; + } + + public function getContainer(): ?ContainerInterface + { + return $this->container; + } + + /** + * @return CallableResolver|null Returns null if no container was given in the constructor. + */ + public function getCallableResolver(): ?CallableResolver + { + return $this->callableResolver; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/InvokerInterface.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/InvokerInterface.php new file mode 100644 index 000000000..06f006441 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/InvokerInterface.php @@ -0,0 +1,25 @@ +call($callable, ['foo' => 'bar'])` will inject the string `'bar'` + * in the parameter named `$foo`. + * + * Parameters that are not indexed by a string are ignored. + */ +class AssociativeArrayResolver implements ParameterResolver +{ + public function getParameters( + ReflectionFunctionAbstract $reflection, + array $providedParameters, + array $resolvedParameters + ): array { + $parameters = $reflection->getParameters(); + + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $parameters = array_diff_key($parameters, $resolvedParameters); + } + + foreach ($parameters as $index => $parameter) { + if (array_key_exists($parameter->name, $providedParameters)) { + $resolvedParameters[$index] = $providedParameters[$parameter->name]; + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php new file mode 100644 index 000000000..126e14cca --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php @@ -0,0 +1,47 @@ +container = $container; + } + + public function getParameters( + ReflectionFunctionAbstract $reflection, + array $providedParameters, + array $resolvedParameters + ): array { + $parameters = $reflection->getParameters(); + + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $parameters = array_diff_key($parameters, $resolvedParameters); + } + + foreach ($parameters as $index => $parameter) { + $name = $parameter->name; + + if ($name && $this->container->has($name)) { + $resolvedParameters[$index] = $this->container->get($name); + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php new file mode 100644 index 000000000..014956e9f --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php @@ -0,0 +1,65 @@ +container = $container; + } + + public function getParameters( + ReflectionFunctionAbstract $reflection, + array $providedParameters, + array $resolvedParameters + ): array { + $parameters = $reflection->getParameters(); + + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $parameters = array_diff_key($parameters, $resolvedParameters); + } + + foreach ($parameters as $index => $parameter) { + $parameterType = $parameter->getType(); + if (! $parameterType) { + // No type + continue; + } + if (! $parameterType instanceof ReflectionNamedType) { + // Union types are not supported + continue; + } + if ($parameterType->isBuiltin()) { + // Primitive types are not supported + continue; + } + + $parameterClass = $parameterType->getName(); + if ($parameterClass === 'self') { + $parameterClass = $parameter->getDeclaringClass()->getName(); + } + + if ($this->container->has($parameterClass)) { + $resolvedParameters[$index] = $this->container->get($parameterClass); + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php new file mode 100644 index 000000000..3fdf7b783 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php @@ -0,0 +1,43 @@ +getParameters(); + + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $parameters = array_diff_key($parameters, $resolvedParameters); + } + + foreach ($parameters as $index => $parameter) { + \assert($parameter instanceof \ReflectionParameter); + if ($parameter->isDefaultValueAvailable()) { + try { + $resolvedParameters[$index] = $parameter->getDefaultValue(); + } catch (ReflectionException $e) { + // Can't get default values from PHP internal classes and functions + } + } else { + $parameterType = $parameter->getType(); + if ($parameterType && $parameterType->allowsNull()) { + $resolvedParameters[$index] = null; + } + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php new file mode 100644 index 000000000..07d8ece55 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php @@ -0,0 +1,37 @@ +call($callable, ['foo', 'bar'])` will simply resolve the parameters + * to `['foo', 'bar']`. + * + * Parameters that are not indexed by a number (i.e. parameter position) + * will be ignored. + */ +class NumericArrayResolver implements ParameterResolver +{ + public function getParameters( + ReflectionFunctionAbstract $reflection, + array $providedParameters, + array $resolvedParameters + ): array { + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $providedParameters = array_diff_key($providedParameters, $resolvedParameters); + } + + foreach ($providedParameters as $key => $value) { + if (is_int($key)) { + $resolvedParameters[$key] = $value; + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/ParameterResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/ParameterResolver.php new file mode 100644 index 000000000..85046c369 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/ParameterResolver.php @@ -0,0 +1,30 @@ +resolvers = $resolvers; + } + + public function getParameters( + ReflectionFunctionAbstract $reflection, + array $providedParameters, + array $resolvedParameters + ): array { + $reflectionParameters = $reflection->getParameters(); + + foreach ($this->resolvers as $resolver) { + $resolvedParameters = $resolver->getParameters( + $reflection, + $providedParameters, + $resolvedParameters + ); + + $diff = array_diff_key($reflectionParameters, $resolvedParameters); + if (empty($diff)) { + // Stop traversing: all parameters are resolved + return $resolvedParameters; + } + } + + return $resolvedParameters; + } + + /** + * Push a parameter resolver after the ones already registered. + */ + public function appendResolver(ParameterResolver $resolver): void + { + $this->resolvers[] = $resolver; + } + + /** + * Insert a parameter resolver before the ones already registered. + */ + public function prependResolver(ParameterResolver $resolver): void + { + array_unshift($this->resolvers, $resolver); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/TypeHintResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/TypeHintResolver.php new file mode 100644 index 000000000..3ccc2e039 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/ParameterResolver/TypeHintResolver.php @@ -0,0 +1,54 @@ +getParameters(); + + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $parameters = array_diff_key($parameters, $resolvedParameters); + } + + foreach ($parameters as $index => $parameter) { + $parameterType = $parameter->getType(); + if (! $parameterType) { + // No type + continue; + } + if (! $parameterType instanceof ReflectionNamedType) { + // Union types are not supported + continue; + } + if ($parameterType->isBuiltin()) { + // Primitive types are not supported + continue; + } + + $parameterClass = $parameterType->getName(); + if ($parameterClass === 'self') { + $parameterClass = $parameter->getDeclaringClass()->getName(); + } + + if (array_key_exists($parameterClass, $providedParameters)) { + $resolvedParameters[$index] = $providedParameters[$parameterClass]; + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/Reflection/CallableReflection.php b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/Reflection/CallableReflection.php new file mode 100644 index 000000000..3f19f33b7 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/invoker/src/Reflection/CallableReflection.php @@ -0,0 +1,56 @@ + DI\factory(function ($host) {...}) + ->parameter('host', DI\get('db.host')), + ]; + ``` + Read the [factories documentation](https://php-di.org/doc/php-definitions.html#factories) to learn more. Feature implemented by [@predakanga](https://github.com/predakanga). + +Improvements: + +- [#429](https://github.com/PHP-DI/PHP-DI/pull/429): performance improvements in definition resolution (by [@mnapoli](https://github.com/mnapoli)) +- [#421](https://github.com/PHP-DI/PHP-DI/issues/421): once a `ContainerBuilder` has built a container, it is locked to prevent confusion when adding new definitions to it (by [@mnapoli](https://github.com/mnapoli)) +- [#423](https://github.com/PHP-DI/PHP-DI/pull/423): improved exception messages (by [@mnapoli](https://github.com/mnapoli)) + +## 5.3 + +Read the [news entry](news/19-php-di-5-3-released.md). + +- release of the [2.0 version](https://github.com/PHP-DI/Symfony-Bridge/releases/tag/2.0.0) of the Symfony bridge (by [@mnapoli](https://github.com/mnapoli)) +- PHP 5.5 or above is now required +- a lot of documentation improvements by 9 different contributors +- [#389](https://github.com/PHP-DI/PHP-DI/pull/389): exception message improvement by [@mopahle](https://github.com/mopahle) +- [#359](https://github.com/PHP-DI/PHP-DI/issues/359), [#411](https://github.com/PHP-DI/PHP-DI/issues/411), [#414](https://github.com/PHP-DI/PHP-DI/pull/414), [#412](https://github.com/PHP-DI/PHP-DI/pull/412): compatibility with ProxyManager 1.* and 2.* (by [@holtkamp](https://github.com/holtkamp) and [@mnapoli](https://github.com/mnapoli)) +- [#416](https://github.com/PHP-DI/PHP-DI/pull/416): dumping definitions was refactored into a more lightweight and simple solution; definition "dumpers" have been removed (internal classes), definitions can now be cast to string directly (by [@mnapoli](https://github.com/mnapoli)) + +## 5.2 + +Read the [news entry](news/17-php-di-5-2-released.md). + +Improvements: + +- [#347](https://github.com/PHP-DI/PHP-DI/pull/347) (includes [#333](https://github.com/PHP-DI/PHP-DI/pull/333) and [#345](https://github.com/PHP-DI/PHP-DI/pull/345)): by [@jdreesen](https://github.com/jdreesen), [@quimcalpe](https://github.com/quimcalpe) and [@mnapoli](https://github.com/mnapoli) + - Allow injection of any container object as factory parameter via type hinting + - Allow injection of a `DI\Factory\RequestedEntry` object to get the requested entry name +- [#272](https://github.com/PHP-DI/PHP-DI/issues/272): Support `"Class::method""` syntax for callables (by [@jdreesen](https://github.com/jdreesen)) +- [#332](https://github.com/PHP-DI/PHP-DI/issues/332): IDE support (plugin and documentation) (by [@pulyaevskiy](https://github.com/pulyaevskiy), [@avant1](https://github.com/avant1) and [@mnapoli](https://github.com/mnapoli)) +- [#326](https://github.com/PHP-DI/PHP-DI/pull/326): Exception messages are simpler and more consistent (by [@mnapoli](https://github.com/mnapoli)) +- [#325](https://github.com/PHP-DI/PHP-DI/pull/325): Add a "Edit this page" button in the website to encourage users to improve the documentation (by [@jdreesen](https://github.com/jdreesen)) + +Bugfixes: + +- [#321](https://github.com/PHP-DI/PHP-DI/pull/321): Allow factory definitions to reference arbitrary container entries as callables (by [@jdreesen](https://github.com/jdreesen)) +- [#335](https://github.com/PHP-DI/PHP-DI/issues/335): Class imports in traits are now considered when parsing annotations (by [@thebigb](https://github.com/thebigb)) + +## 5.1 + +Read the [news entry](news/16-php-di-5-1-released.md). + +Improvements: + +- [Zend Framework 2 integration](https://github.com/PHP-DI/ZF2-Bridge) (by @Rastusik) +- [#308](https://github.com/PHP-DI/PHP-DI/pull/308): Instantiate factories using the container (`DI\factory(['FooFactory', 'create'])`) +- Many performances improvements - some benchmarks show up to 35% performance improvements, real results may vary of course +- Many documentation improvements (@jdreesen, @mindplay-dk, @mnapoli, @holtkamp, @Rastusik) +- [#296](https://github.com/PHP-DI/PHP-DI/issues/296): Provide a faster `ArrayCache` implementation, mostly useful in micro-benchmarks + +Bugfixes: + +- [#257](https://github.com/PHP-DI/PHP-DI/issues/257) & [#274](https://github.com/PHP-DI/PHP-DI/issues/274): Private properties of parent classes are not injected when using annotations +- [#300](https://github.com/PHP-DI/PHP-DI/pull/300): Exception if object definition extends an incompatible definition +- [#306](https://github.com/PHP-DI/PHP-DI/issues/306): Errors when using parameters passed by reference (fixed by @bradynpoulsen) +- [#318](https://github.com/PHP-DI/PHP-DI/issues/318): `Container::call()` ignores parameter's default value + +Internal changes: + +- [#276](https://github.com/PHP-DI/PHP-DI/pull/276): Tests now pass on Windows (@bgaillard) + +## 5.0 + +This is the complete change log. You can also read the [migration guide](doc/migration/5.0.md) for upgrading, or [the news article](news/15-php-di-5-0-released.md) for a nicer introduction to this new version. + +Improvements: + +- Moved to an organization on GitHub: [github.com/PHP-DI/PHP-DI](https://github.com/PHP-DI/PHP-DI) +- The package has been renamed to: from `mnapoli/php-di` to [`php-di/php-di`](https://packagist.org/packages/php-di/php-di) +- New [Silex integration](doc/frameworks/silex.md) +- Lighter package: from 10 to 3 Composer dependencies! +- [#235](https://github.com/PHP-DI/PHP-DI/issues/235): `DI\link()` is now deprecated in favor of `DI\get()`. There is no BC break as `DI\link()` still works. +- [#207](https://github.com/PHP-DI/PHP-DI/issues/207): Support for `DI\link()` in arrays +- [#203](https://github.com/PHP-DI/PHP-DI/issues/203): New `DI\string()` helper ([documentation](doc/php-definitions.md)) +- [#208](https://github.com/PHP-DI/PHP-DI/issues/208): Support for nested definitions +- [#226](https://github.com/PHP-DI/PHP-DI/pull/226): `DI\factory()` can now be omitted with closures: + + ```php + // before + 'My\Class' => DI\factory(function () { ... }) + // now (optional shortcut) + 'My\Class' => function () { ... } + ``` +- [#193](https://github.com/PHP-DI/PHP-DI/issues/193): `DI\object()->method()` now supports calling the same method twice (or more). +- [#248](https://github.com/PHP-DI/PHP-DI/issues/248): New `DI\decorate()` helper to decorate a previously defined entry ([documentation](doc/definition-overriding.md)) +- [#215](https://github.com/PHP-DI/PHP-DI/pull/215): New `DI\add()` helper to add entries to an existing array ([documentation](doc/definition-overriding.md)) +- [#218](https://github.com/PHP-DI/PHP-DI/issues/218): `ContainerBuilder::addDefinitions()` can now take an array of definitions +- [#211](https://github.com/PHP-DI/PHP-DI/pull/211): `ContainerBuilder::addDefinitions()` is now fluent (return `$this`) +- [#250](https://github.com/PHP-DI/PHP-DI/issues/250): `Container::call()` now also accepts parameters not indexed by name as well as embedded definitions ([documentation](doc/container.md)) +- Various performance improvements, e.g. lower the number of files loaded, simpler architecture, … + +BC breaks: + +- PHP-DI now requires a version of PHP >= 5.4.0 +- The package is lighter by default: + - [#251](https://github.com/PHP-DI/PHP-DI/issues/251): Annotations are disabled by default, if you use annotations enable them with `$containerBuilder->useAnnotations(true)`. Additionally the `doctrine/annotations` package isn't required by default anymore, so you also need to run `composer require doctrine/annotations`. + - `doctrine/cache` is not installed by default anymore, you need to require it in `composer.json` (`~1.0`) if you want to configure a cache for PHP-DI + - [#198](https://github.com/PHP-DI/PHP-DI/issues/198): `ocramius/proxy-manager` is not installed by default anymore, you need to require it in `composer.json` (`~1.0`) if you want to use **lazy injection** +- Closures are now converted into factory definitions automatically. If you ever defined a closure as a value (e.g. to have the closure injected in a class), you need to wrap the closure with the new `DI\value()` helper. +- [#223](https://github.com/PHP-DI/PHP-DI/issues/223): `DI\ContainerInterface` was deprecated since v4.1 and has been removed + +Internal changes in case you were replacing/extending some parts: + +- the definition sources architecture has been refactored, if you defined custom definition sources you will need to update your code (it should be much easier now) +- [#252](https://github.com/PHP-DI/PHP-DI/pull/252): `DI\Scope` internal implementation has changed. You are encouraged to use the constants (`DI\Scope::SINGLETON` and `DI\Scope::PROTOTYPE`) instead of the static methods, but backward compatibility is kept (static methods still work). +- [#241](https://github.com/PHP-DI/PHP-DI/issues/241): `Container::call()` now uses the *Invoker* external library + +## 4.4 + +Read the [news entry](news/13-php-di-4-4-released.md). + +- [#185](https://github.com/PHP-DI/PHP-DI/issues/185) Support for invokable objects in `Container::call()` +- [#192](https://github.com/PHP-DI/PHP-DI/pull/192) Support for invokable classes in `Container::call()` (will instantiate the class) +- [#184](https://github.com/PHP-DI/PHP-DI/pull/184) Option to ignore phpdoc errors + +## 4.3 + +Read the [news entry](news/11-php-di-4-3-released.md). + +- [#176](https://github.com/PHP-DI/PHP-DI/pull/176) New definition type for reading environment variables: `DI\env()` +- [#181](https://github.com/PHP-DI/PHP-DI/pull/181) `DI\FactoryInterface` and `DI\InvokerInterface` are now auto-registered inside the container so that you can inject them without any configuration needed +- [#173](https://github.com/PHP-DI/PHP-DI/pull/173) `$container->call(['MyClass', 'method]);` will get `MyClass` from the container if `method()` is not a static method + +## 4.2.2 + +- Fixed [#180](https://github.com/PHP-DI/PHP-DI/pull/180): `Container::call()` with object methods (`[$object, 'method']`) is now supported + +## 4.2.1 + +- Support for PHP 5.3.3, which was previously incomplete because of a bug in the reflection (there is now a workaround for this bug) + +But if you can, seriously avoid this (really old) PHP version and upgrade. + +## 4.2 + +Read the [news entry](news/10-php-di-4-2-released.md). + +**Minor BC-break**: Optional parameters (that were not configured) were injected, they are now ignored, which is what naturally makes sense since they are optional. +Example: + +```php + public function __construct(Bar $bar = null) + { + $this->bar = $bar ?: $this->createDefaultBar(); + } +``` + +Before 4.2, PHP-DI would try to inject a `Bar` instance. From 4.2 and onwards, it will inject `null`. + +Of course, you can still explicitly define an injection for the optional parameters and that will work. + +All changes: + +* [#162](https://github.com/PHP-DI/PHP-DI/pull/162) Added `Container::call()` to call functions with dependency injection +* [#156](https://github.com/PHP-DI/PHP-DI/issues/156) Wildcards (`*`) in definitions +* [#164](https://github.com/PHP-DI/PHP-DI/issues/164) Prototype scope is now available for `factory()` definitions too +* FIXED [#168](https://github.com/PHP-DI/PHP-DI/pull/168) `Container::has()` now returns false for interfaces and abstract classes that are not mapped in the definitions +* FIXED [#171](https://github.com/PHP-DI/PHP-DI/issues/171) Optional parameters are now ignored (not injected) if not set in the definitions (see the BC-break warning above) + +## 4.1 + +Read the [news entry](news/09-php-di-4-1-released.md). + +BC-breaks: None. + +* [#138](https://github.com/PHP-DI/PHP-DI/issues/138) [Container-interop](https://github.com/container-interop/container-interop) compliance +* [#143](https://github.com/PHP-DI/PHP-DI/issues/143) Much more explicit exception messages +* [#157](https://github.com/PHP-DI/PHP-DI/issues/157) HHVM support +* [#158](https://github.com/PHP-DI/PHP-DI/issues/158) Improved the documentation for [Symfony 2 integration](https://php-di.org/doc/frameworks/symfony2.html) + +## 4.0 + +Major changes: + +* The configuration format has changed ([read more here to understand why](news/06-php-di-4-0-new-definitions.md)) + +Read the migration guide if you are using 3.x: [Migration guide from 3.x to 4.0](doc/migration/4.0.md). + +BC-breaks: + +* YAML, XML and JSON definitions have been removed, and the PHP definition format has changed (see above) +* `ContainerSingleton` has been removed +* You cannot configure an injection as lazy anymore, you can only configure a container entry as lazy +* The Container constructor now takes mandatory parameters. Use the ContainerBuilder to create a Container. +* Removed `ContainerBuilder::setDefinitionsValidation()` (no definition validation anymore) +* `ContainerBuilder::useReflection()` is now named: `ContainerBuilder::useAutowiring()` +* `ContainerBuilder::addDefinitionsFromFile()` is now named: `ContainerBuilder::addDefinitions()` +* The `$proxy` parameter in `Container::get($name, $proxy = true)` hase been removed. To get a proxy, you now need to define an entry as "lazy". + +Other changes: + +* Added `ContainerInterface` and `FactoryInterface`, both implemented by the container. +* [#115](https://github.com/PHP-DI/PHP-DI/issues/115) Added `Container::has()` +* [#142](https://github.com/PHP-DI/PHP-DI/issues/142) Added `Container::make()` to resolve an entry +* [#127](https://github.com/PHP-DI/PHP-DI/issues/127) Added support for cases where PHP-DI is wrapped by another container (like Acclimate): PHP-DI can now use the wrapping container to perform injections +* [#128](https://github.com/PHP-DI/PHP-DI/issues/128) Configure entry aliases +* [#110](https://github.com/PHP-DI/PHP-DI/issues/110) XML definitions are not supported anymore +* [#122](https://github.com/PHP-DI/PHP-DI/issues/122) JSON definitions are not supported anymore +* `ContainerSingleton` has finally been removed +* Added `ContainerBuilder::buildDevContainer()` to get started with a default container very easily. +* [#99](https://github.com/PHP-DI/PHP-DI/issues/99) Fixed "`@param` with PHP internal type throws exception" + +## 3.5.1 + +* FIXED [#126](https://github.com/PHP-DI/PHP-DI/issues/126): `Container::set` without effect if a value has already been set and retrieved + +## 3.5 + +Read the [news entry](news/05-php-di-3-5.md). + +* Importing `@Inject` and `@Injectable` annotations is now optional! It means that you don't have to write `use DI\Annotation\Inject` anymore +* FIXED [#124](https://github.com/PHP-DI/PHP-DI/issues/124): `@Injects` annotation conflicts with other annotations + +## 3.4 + +Read the [news entry](news/04-php-di-3-4.md). + +* [#106](https://github.com/PHP-DI/PHP-DI/pull/106) You can now define arrays of values (in YAML, PHP, …) thanks to [@unkind](https://github.com/unkind) +* [#98](https://github.com/PHP-DI/PHP-DI/issues/98) `ContainerBuilder` is now fluent thanks to [@drdamour](https://github.com/drdamour) +* [#101](https://github.com/PHP-DI/PHP-DI/pull/101) Optional parameters are now supported: if you don't define a value to inject, their default value will be used +* XML definitions have been deprecated, there weren't even documented and were not maintained. They will be removed in 4.0. +* FIXED [#100](https://github.com/PHP-DI/PHP-DI/issues/100): bug for lazy injection in constructors + +## 3.3 + +Read the [news entry](news/03-php-di-3-3.md). + +* Inject dependencies on an existing instance with `Container::injectOn` (work from [Jeff Flitton](https://github.com/jflitton): [#89](https://github.com/PHP-DI/PHP-DI/pull/89)). +* [#86](https://github.com/PHP-DI/PHP-DI/issues/86): Optimized definition lookup (faster) +* FIXED [#87](https://github.com/PHP-DI/PHP-DI/issues/87): Rare bug in the `PhpDocParser`, fixed by [drdamour](https://github.com/drdamour) + +## 3.2 + +Read the [news entry](news/02-php-di-3-2.md). + +Small BC-break: PHP-DI 3.0 and 3.1 injected properties before calling the constructor. This was confusing and [not supported for internal classes](https://github.com/PHP-DI/PHP-DI/issues/74). +From 3.2 and on, properties are injected after calling the constructor. + +* **[Lazy injection](doc/lazy-injection.md)**: it is now possible to use lazy injection on properties and methods (setters and constructors). +* Lazy dependencies are now proxies that extend the class they proxy, so type-hinting works. +* Addition of the **`ContainerBuilder`** object, that helps to [create and configure a `Container`](doc/container-configuration.md). +* Some methods for configuring the Container have gone **deprecated** in favor of the `ContainerBuilder`. Fear not, these deprecated methods will remain until next major version (4.0). + * `Container::useReflection`, use ContainerBuilder::useReflection instead + * `Container::useAnnotations`, use ContainerBuilder::useAnnotations instead + * `Container::setDefinitionCache`, use ContainerBuilder::setDefinitionCache instead + * `Container::setDefinitionsValidation`, use ContainerBuilder::setDefinitionsValidation instead +* The container is now auto-registered (as 'DI\Container'). You can now inject the container without registering it. + +## 3.1.1 + +* Value definitions (`$container->set('foo', 80)`) are not cached anymore +* FIXED [#82](https://github.com/PHP-DI/PHP-DI/issues/82): Serialization error when using a cache + +## 3.1 + +Read the [news entry](news/01-php-di-3-1.md). + +* Zend Framework 1 integration through the [PHP-DI-ZF1 project](https://github.com/PHP-DI/PHP-DI-ZF1) +* Fixed the order of priorities when you mix different definition sources (reflection, annotations, files, …). See [Definition overriding](doc/definition-overriding.md) +* Now possible to define null values with `$container->set('foo', null)` (see [#79](https://github.com/PHP-DI/PHP-DI/issues/79)). +* Deprecated usage of `ContainerSingleton`, will be removed in next major version (4.0) + +## 3.0.6 + +* FIXED [#76](https://github.com/PHP-DI/PHP-DI/issues/76): Definition conflict when setting a closure for a class name + +## 3.0.5 + +* FIXED [#70](https://github.com/PHP-DI/PHP-DI/issues/70): Definition conflict when setting a value for a class name + +## 3.0.4 + +* FIXED [#69](https://github.com/PHP-DI/PHP-DI/issues/69): YamlDefinitionFileLoader crashes if YAML file is empty + +## 3.0.3 + +* Fixed over-restrictive dependencies in composer.json + +## 3.0.2 + +* [#64](https://github.com/PHP-DI/PHP-DI/issues/64): Non PHP-DI exceptions are not captured-rethrown anymore when injecting dependencies (cleaner stack trace) + +## 3.0.1 + +* [#62](https://github.com/PHP-DI/PHP-DI/issues/62): When using aliases, definitions are now merged + +## 3.0 + +Major compatibility breaks with 2.x. + +* The container is no longer a Singleton (but `ContainerSingleton::getInstance()` is available for fools who like it) +* Setter injection +* Constructor injection +* Scopes: singleton (share the same instance of the class) or prototype (create a new instance each time it is fetched). Defined at class level. +* Configuration is reworked from scratch. Now every configuration backend can do 100% of the job. +* Provided configuration backends: + * Reflection + * Annotations: @Inject, @Injectable + * PHP code (`Container::set()`) + * PHP array + * YAML file +* As a consequence, annotations are not mandatory anymore, all functionalities can be used with or without annotations. +* Renamed `DI\Annotations\` to `DI\Annotation\` +* `Container` no longer implements ArrayAccess, use only `$container->get($key)` now +* ZF1 integration broken and removed (work in progress for next releases) +* Code now follows PSR1 and PSR2 coding styles +* FIXED: [#58](https://github.com/PHP-DI/PHP-DI/issues/58) Getting a proxy of an alias didn't work + +## 2.1 + +* `use` statements to import classes from other namespaces are now taken into account with the `@var` annotation +* Updated and lightened the dependencies : `doctrine/common` has been replaced with more specific `doctrine/annotations` and `doctrine/cache` + +## 2.0 + +Major compatibility breaks with 1.x. + +* `Container::resolveDependencies()` has been renamed to `Container::injectAll()` +* Dependencies are now injected **before** the constructor is called, and thus are available in the constructor +* Merged `@Value` annotation with `@Inject`: no difference between value and bean injection anymore +* Container implements ArrayAccess for get() and set() (`$container['db.host'] = 'localhost';`) +* Ini configuration files removed: configuration is done in PHP +* Allow to define beans within closures for lazy-loading +* Switched to MIT License + +Warning: + +* If you use PHP 5.3 and __wakeup() methods, they will be called when PHP-DI creates new instances of those classes. + +## 1.1 + +* Caching of annotations based on Doctrine caches + +## 1.0 + +* DependencyManager renamed to Container +* Refactored basic Container usage with `get` and `set` +* Allow named injection `@Inject(name="")` +* Zend Framework integration diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/composer.json b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/composer.json new file mode 100644 index 000000000..0e6adef83 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/composer.json @@ -0,0 +1,45 @@ +{ + "name": "php-di/php-di", + "type": "library", + "description": "The dependency injection container for humans", + "keywords": ["di", "dependency injection", "container", "ioc", "psr-11", "psr11", "container-interop"], + "homepage": "https://php-di.org/", + "license": "MIT", + "autoload": { + "psr-4": { + "DI\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "DI\\Test\\IntegrationTest\\": "tests/IntegrationTest/", + "DI\\Test\\UnitTest\\": "tests/UnitTest/" + } + }, + "scripts": { + "test": "phpunit", + "format-code": "php-cs-fixer fix --allow-risky=yes" + }, + "require": { + "php": ">=8.0", + "psr/container": "^1.1 || ^2.0", + "php-di/invoker": "^2.0", + "laravel/serializable-closure": "^1.0 || ^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^10 || ^11", + "mnapoli/phpunit-easymock": "^1.3", + "friendsofphp/proxy-manager-lts": "^1", + "friendsofphp/php-cs-fixer": "^3", + "vimeo/psalm": "^5|^6" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "suggest": { + "friendsofphp/proxy-manager-lts": "Install it if you want to use lazy injection (version ^1)" + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Attribute/Inject.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Attribute/Inject.php new file mode 100644 index 000000000..6a88f15d2 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Attribute/Inject.php @@ -0,0 +1,74 @@ + + */ +#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)] +class Inject +{ + /** + * Entry name. + */ + private ?string $name = null; + + /** + * Parameters, indexed by the parameter number (index) or name. + * + * Used if the attribute is set on a method + */ + private array $parameters = []; + + /** + * @throws InvalidAttribute + */ + public function __construct(string|array|null $name = null) + { + // #[Inject('foo')] or #[Inject(name: 'foo')] + if (is_string($name)) { + $this->name = $name; + } + + // #[Inject([...])] on a method + if (is_array($name)) { + foreach ($name as $key => $value) { + if (! is_string($value)) { + throw new InvalidAttribute(sprintf( + "#[Inject(['param' => 'value'])] expects \"value\" to be a string, %s given.", + json_encode($value, \JSON_THROW_ON_ERROR) + )); + } + + $this->parameters[$key] = $value; + } + } + } + + /** + * @return string|null Name of the entry to inject + */ + public function getName() : ?string + { + return $this->name; + } + + /** + * @return array Parameters, indexed by the parameter number (index) or name + */ + public function getParameters() : array + { + return $this->parameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Attribute/Injectable.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Attribute/Injectable.php new file mode 100644 index 000000000..af68b4c88 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Attribute/Injectable.php @@ -0,0 +1,34 @@ + + * @author Matthieu Napoli + */ +#[Attribute(Attribute::TARGET_CLASS)] +class Injectable +{ + /** + * @param bool|null $lazy Should the object be lazy-loaded. + */ + public function __construct( + private ?bool $lazy = null, + ) { + } + + public function isLazy() : ?bool + { + return $this->lazy; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/CompiledContainer.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/CompiledContainer.php new file mode 100644 index 000000000..632e5d5b8 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/CompiledContainer.php @@ -0,0 +1,116 @@ + + */ +abstract class CompiledContainer extends Container +{ + /** + * This const is overridden in child classes (compiled containers). + * @var array + */ + protected const METHOD_MAPPING = []; + + private ?InvokerInterface $factoryInvoker = null; + + public function get(string $id) : mixed + { + // Try to find the entry in the singleton map + if (isset($this->resolvedEntries[$id]) || array_key_exists($id, $this->resolvedEntries)) { + return $this->resolvedEntries[$id]; + } + + /** @psalm-suppress UndefinedConstant */ + $method = static::METHOD_MAPPING[$id] ?? null; + + // If it's a compiled entry, then there is a method in this class + if ($method !== null) { + // Check if we are already getting this entry -> circular dependency + if (isset($this->entriesBeingResolved[$id])) { + $idList = implode(' -> ', [...array_keys($this->entriesBeingResolved), $id]); + throw new DependencyException("Circular dependency detected while trying to resolve entry '$id': Dependencies: " . $idList); + } + $this->entriesBeingResolved[$id] = true; + + try { + $value = $this->$method(); + } finally { + unset($this->entriesBeingResolved[$id]); + } + + // Store the entry to always return it without recomputing it + $this->resolvedEntries[$id] = $value; + + return $value; + } + + return parent::get($id); + } + + public function has(string $id) : bool + { + // The parent method is overridden to check in our array, it avoids resolving definitions + /** @psalm-suppress UndefinedConstant */ + if (isset(static::METHOD_MAPPING[$id])) { + return true; + } + + return parent::has($id); + } + + protected function setDefinition(string $name, Definition $definition) : void + { + // It needs to be forbidden because that would mean get() must go through the definitions + // every time, which kinds of defeats the performance gains of the compiled container + throw new \LogicException('You cannot set a definition at runtime on a compiled container. You can either put your definitions in a file, disable compilation or ->set() a raw value directly (PHP object, string, int, ...) instead of a PHP-DI definition.'); + } + + /** + * Invoke the given callable. + */ + protected function resolveFactory($callable, $entryName, array $extraParameters = []) : mixed + { + // Initialize the factory resolver + if (! $this->factoryInvoker) { + $parameterResolver = new ResolverChain([ + new AssociativeArrayResolver, + new FactoryParameterResolver($this->delegateContainer), + new NumericArrayResolver, + new DefaultValueResolver, + ]); + + $this->factoryInvoker = new Invoker($parameterResolver, $this->delegateContainer); + } + + $parameters = [$this->delegateContainer, new RequestedEntryHolder($entryName)]; + + $parameters = array_merge($parameters, $extraParameters); + + try { + return $this->factoryInvoker->call($callable, $parameters); + } catch (NotCallableException $e) { + throw new InvalidDefinition("Entry \"$entryName\" cannot be resolved: factory " . $e->getMessage()); + } catch (NotEnoughParametersException $e) { + throw new InvalidDefinition("Entry \"$entryName\" cannot be resolved: " . $e->getMessage()); + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/Compiler.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/Compiler.php new file mode 100644 index 000000000..4f5e4b173 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/Compiler.php @@ -0,0 +1,402 @@ + + */ +class Compiler +{ + private string $containerClass; + + private string $containerParentClass; + + /** + * Definitions indexed by the entry name. The value can be null if the definition needs to be fetched. + * + * Keys are strings, values are `Definition` objects or null. + */ + private \ArrayIterator $entriesToCompile; + + /** + * Progressive counter for definitions. + * + * Each key in $entriesToCompile is defined as 'SubEntry' + counter + * and each definition has always the same key in the CompiledContainer + * if PHP-DI configuration does not change. + */ + private int $subEntryCounter = 0; + + /** + * Progressive counter for CompiledContainer get methods. + * + * Each CompiledContainer method name is defined as 'get' + counter + * and remains the same after each recompilation + * if PHP-DI configuration does not change. + */ + private int $methodMappingCounter = 0; + + /** + * Map of entry names to method names. + * + * @var string[] + */ + private array $entryToMethodMapping = []; + + /** + * @var string[] + */ + private array $methods = []; + + private bool $autowiringEnabled; + + public function __construct( + private ProxyFactoryInterface $proxyFactory, + ) { + } + + public function getProxyFactory() : ProxyFactoryInterface + { + return $this->proxyFactory; + } + + /** + * Compile the container. + * + * @return string The compiled container file name. + */ + public function compile( + DefinitionSource $definitionSource, + string $directory, + string $className, + string $parentClassName, + bool $autowiringEnabled, + ) : string { + $fileName = rtrim($directory, '/') . '/' . $className . '.php'; + + if (file_exists($fileName)) { + // The container is already compiled + return $fileName; + } + + $this->autowiringEnabled = $autowiringEnabled; + + // Validate that a valid class name was provided + $validClassName = preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $className); + if (!$validClassName) { + throw new InvalidArgumentException("The container cannot be compiled: `$className` is not a valid PHP class name"); + } + + $this->entriesToCompile = new \ArrayIterator($definitionSource->getDefinitions()); + + // We use an ArrayIterator so that we can keep adding new items to the list while we compile entries + foreach ($this->entriesToCompile as $entryName => $definition) { + $silenceErrors = false; + // This is an entry found by reference during autowiring + if (!$definition) { + $definition = $definitionSource->getDefinition($entryName); + // We silence errors for those entries because type-hints may reference interfaces/abstract classes + // which could later be defined, or even not used (we don't want to block the compilation for those) + $silenceErrors = true; + } + if (!$definition) { + // We do not throw a `NotFound` exception here because the dependency + // could be defined at runtime + continue; + } + // Check that the definition can be compiled + $errorMessage = $this->isCompilable($definition); + if ($errorMessage !== true) { + continue; + } + try { + $this->compileDefinition($entryName, $definition); + } catch (InvalidDefinition $e) { + if ($silenceErrors) { + // forget the entry + unset($this->entryToMethodMapping[$entryName]); + } else { + throw $e; + } + } + } + + $this->containerClass = $className; + $this->containerParentClass = $parentClassName; + + ob_start(); + require __DIR__ . '/Template.php'; + $fileContent = ob_get_clean(); + + $fileContent = "createCompilationDirectory(dirname($fileName)); + $this->writeFileAtomic($fileName, $fileContent); + + return $fileName; + } + + private function writeFileAtomic(string $fileName, string $content) : void + { + $tmpFile = @tempnam(dirname($fileName), 'swap-compile'); + if ($tmpFile === false) { + throw new InvalidArgumentException( + sprintf('Error while creating temporary file in %s', dirname($fileName)) + ); + } + @chmod($tmpFile, 0666); + + $written = file_put_contents($tmpFile, $content); + if ($written === false) { + @unlink($tmpFile); + + throw new InvalidArgumentException(sprintf('Error while writing to %s', $tmpFile)); + } + + @chmod($tmpFile, 0666); + $renamed = @rename($tmpFile, $fileName); + if (!$renamed) { + @unlink($tmpFile); + + throw new InvalidArgumentException(sprintf('Error while renaming %s to %s', $tmpFile, $fileName)); + } + } + + /** + * @return string The method name + * @throws DependencyException + * @throws InvalidDefinition + */ + private function compileDefinition(string $entryName, Definition $definition) : string + { + // Generate a unique method name + $methodName = 'get' . (++$this->methodMappingCounter); + $this->entryToMethodMapping[$entryName] = $methodName; + + switch (true) { + case $definition instanceof ValueDefinition: + $value = $definition->getValue(); + $code = 'return ' . $this->compileValue($value) . ';'; + break; + case $definition instanceof Reference: + $targetEntryName = $definition->getTargetEntryName(); + $code = 'return $this->delegateContainer->get(' . $this->compileValue($targetEntryName) . ');'; + // If this method is not yet compiled we store it for compilation + if (!isset($this->entriesToCompile[$targetEntryName])) { + $this->entriesToCompile[$targetEntryName] = null; + } + break; + case $definition instanceof StringDefinition: + $entryName = $this->compileValue($definition->getName()); + $expression = $this->compileValue($definition->getExpression()); + $code = 'return \DI\Definition\StringDefinition::resolveExpression(' . $entryName . ', ' . $expression . ', $this->delegateContainer);'; + break; + case $definition instanceof EnvironmentVariableDefinition: + $variableName = $this->compileValue($definition->getVariableName()); + $isOptional = $this->compileValue($definition->isOptional()); + $defaultValue = $this->compileValue($definition->getDefaultValue()); + $code = <<getVariableName()}' has not been defined"); + } + return $defaultValue; + PHP; + break; + case $definition instanceof ArrayDefinition: + try { + $code = 'return ' . $this->compileValue($definition->getValues()) . ';'; + } catch (\Exception $e) { + throw new DependencyException(sprintf( + 'Error while compiling %s. %s', + $definition->getName(), + $e->getMessage() + ), 0, $e); + } + break; + case $definition instanceof ObjectDefinition: + $compiler = new ObjectCreationCompiler($this); + $code = $compiler->compile($definition); + $code .= "\n return \$object;"; + break; + case $definition instanceof DecoratorDefinition: + $decoratedDefinition = $definition->getDecoratedDefinition(); + if (! $decoratedDefinition instanceof Definition) { + if (! $definition->getName()) { + throw new InvalidDefinition('Decorators cannot be nested in another definition'); + } + throw new InvalidDefinition(sprintf( + 'Entry "%s" decorates nothing: no previous definition with the same name was found', + $definition->getName() + )); + } + $code = sprintf( + 'return call_user_func(%s, %s, $this->delegateContainer);', + $this->compileValue($definition->getCallable()), + $this->compileValue($decoratedDefinition) + ); + break; + case $definition instanceof FactoryDefinition: + $value = $definition->getCallable(); + + // Custom error message to help debugging + $isInvokableClass = is_string($value) && class_exists($value) && method_exists($value, '__invoke'); + if ($isInvokableClass && !$this->autowiringEnabled) { + throw new InvalidDefinition(sprintf( + 'Entry "%s" cannot be compiled. Invokable classes cannot be automatically resolved if autowiring is disabled on the container, you need to enable autowiring or define the entry manually.', + $entryName + )); + } + + $definitionParameters = ''; + if (!empty($definition->getParameters())) { + $definitionParameters = ', ' . $this->compileValue($definition->getParameters()); + } + + $code = sprintf( + 'return $this->resolveFactory(%s, %s%s);', + $this->compileValue($value), + var_export($entryName, true), + $definitionParameters + ); + + break; + default: + // This case should not happen (so it cannot be tested) + throw new \Exception('Cannot compile definition of type ' . $definition::class); + } + + $this->methods[$methodName] = $code; + + return $methodName; + } + + public function compileValue(mixed $value) : string + { + // Check that the value can be compiled + $errorMessage = $this->isCompilable($value); + if ($errorMessage !== true) { + throw new InvalidDefinition($errorMessage); + } + + if ($value instanceof Definition) { + // Give it an arbitrary unique name + $subEntryName = 'subEntry' . (++$this->subEntryCounter); + // Compile the sub-definition in another method + $methodName = $this->compileDefinition($subEntryName, $value); + + // The value is now a method call to that method (which returns the value) + return "\$this->$methodName()"; + } + + if (is_array($value)) { + $value = array_map(function ($value, $key) { + $compiledValue = $this->compileValue($value); + $key = var_export($key, true); + + return " $key => $compiledValue,\n"; + }, $value, array_keys($value)); + $value = implode('', $value); + + return "[\n$value ]"; + } + + if ($value instanceof \Closure) { + return $this->compileClosure($value); + } + + return var_export($value, true); + } + + private function createCompilationDirectory(string $directory) : void + { + if (!is_dir($directory) && !@mkdir($directory, 0777, true) && !is_dir($directory)) { + throw new InvalidArgumentException(sprintf('Compilation directory does not exist and cannot be created: %s.', $directory)); + } + if (!is_writable($directory)) { + throw new InvalidArgumentException(sprintf('Compilation directory is not writable: %s.', $directory)); + } + } + + /** + * @return string|true If true is returned that means that the value is compilable. + */ + private function isCompilable($value) : string|bool + { + if ($value instanceof ValueDefinition) { + return $this->isCompilable($value->getValue()); + } + if (($value instanceof DecoratorDefinition) && empty($value->getName())) { + return 'Decorators cannot be nested in another definition'; + } + // All other definitions are compilable + if ($value instanceof Definition) { + return true; + } + if ($value instanceof \Closure) { + return true; + } + /** @psalm-suppress UndefinedClass */ + if ((\PHP_VERSION_ID >= 80100) && ($value instanceof \UnitEnum)) { + return true; + } + if (is_object($value)) { + return 'An object was found but objects cannot be compiled'; + } + if (is_resource($value)) { + return 'A resource was found but resources cannot be compiled'; + } + + return true; + } + + /** + * @throws InvalidDefinition + */ + private function compileClosure(\Closure $closure) : string + { + $reflector = new ReflectionClosure($closure); + + if ($reflector->getUseVariables()) { + throw new InvalidDefinition('Cannot compile closures which import variables using the `use` keyword'); + } + + if ($reflector->isBindingRequired() || $reflector->isScopeRequired()) { + throw new InvalidDefinition('Cannot compile closures which use $this or self/static/parent references'); + } + + // Force all closures to be static (add the `static` keyword), i.e. they can't use + // $this, which makes sense since their code is copied into another class. + $code = ($reflector->isStatic() ? '' : 'static ') . $reflector->getCode(); + + return trim($code, "\t\n\r;"); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/ObjectCreationCompiler.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/ObjectCreationCompiler.php new file mode 100644 index 000000000..f951cb05f --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/ObjectCreationCompiler.php @@ -0,0 +1,198 @@ + + */ +class ObjectCreationCompiler +{ + public function __construct( + private Compiler $compiler, + ) { + } + + public function compile(ObjectDefinition $definition) : string + { + $this->assertClassIsNotAnonymous($definition); + $this->assertClassIsInstantiable($definition); + /** @var class-string $className At this point we have checked the class is valid */ + $className = $definition->getClassName(); + + // Lazy? + if ($definition->isLazy()) { + return $this->compileLazyDefinition($definition); + } + + try { + $classReflection = new ReflectionClass($className); + $constructorArguments = $this->resolveParameters($definition->getConstructorInjection(), $classReflection->getConstructor()); + $dumpedConstructorArguments = array_map(function ($value) { + return $this->compiler->compileValue($value); + }, $constructorArguments); + + $code = []; + $code[] = sprintf( + '$object = new %s(%s);', + $className, + implode(', ', $dumpedConstructorArguments) + ); + + // Property injections + foreach ($definition->getPropertyInjections() as $propertyInjection) { + $value = $propertyInjection->getValue(); + $value = $this->compiler->compileValue($value); + + $propertyClassName = $propertyInjection->getClassName() ?: $className; + $property = new ReflectionProperty($propertyClassName, $propertyInjection->getPropertyName()); + if ($property->isPublic() && !(\PHP_VERSION_ID >= 80100 && $property->isReadOnly())) { + $code[] = sprintf('$object->%s = %s;', $propertyInjection->getPropertyName(), $value); + } else { + // Private/protected/readonly property + $code[] = sprintf( + '\DI\Definition\Resolver\ObjectCreator::setPrivatePropertyValue(%s, $object, \'%s\', %s);', + var_export($propertyInjection->getClassName(), true), + $propertyInjection->getPropertyName(), + $value + ); + } + } + + // Method injections + foreach ($definition->getMethodInjections() as $methodInjection) { + $methodReflection = new ReflectionMethod($className, $methodInjection->getMethodName()); + $parameters = $this->resolveParameters($methodInjection, $methodReflection); + + $dumpedParameters = array_map(function ($value) { + return $this->compiler->compileValue($value); + }, $parameters); + + $code[] = sprintf( + '$object->%s(%s);', + $methodInjection->getMethodName(), + implode(', ', $dumpedParameters) + ); + } + } catch (InvalidDefinition $e) { + throw InvalidDefinition::create($definition, sprintf( + 'Entry "%s" cannot be compiled: %s', + $definition->getName(), + $e->getMessage() + )); + } + + return implode("\n ", $code); + } + + public function resolveParameters(?MethodInjection $definition, ?ReflectionMethod $method) : array + { + $args = []; + + if (! $method) { + return $args; + } + + $definitionParameters = $definition ? $definition->getParameters() : []; + + foreach ($method->getParameters() as $index => $parameter) { + if (array_key_exists($index, $definitionParameters)) { + // Look in the definition + $value = &$definitionParameters[$index]; + } elseif ($parameter->isOptional()) { + // If the parameter is optional and wasn't specified, we take its default value + $args[] = $this->getParameterDefaultValue($parameter, $method); + continue; + } else { + throw new InvalidDefinition(sprintf( + 'Parameter $%s of %s has no value defined or guessable', + $parameter->getName(), + $this->getFunctionName($method) + )); + } + + $args[] = &$value; + } + + return $args; + } + + private function compileLazyDefinition(ObjectDefinition $definition) : string + { + $subDefinition = clone $definition; + $subDefinition->setLazy(false); + $subDefinition = $this->compiler->compileValue($subDefinition); + + /** @var class-string $className At this point we have checked the class is valid */ + $className = $definition->getClassName(); + + $this->compiler->getProxyFactory()->generateProxyClass($className); + + return <<proxyFactory->createProxy( + '{$definition->getClassName()}', + function () { + return $subDefinition; + } + ); + STR; + } + + /** + * Returns the default value of a function parameter. + * + * @throws InvalidDefinition Can't get default values from PHP internal classes and functions + */ + private function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function) : mixed + { + try { + return $parameter->getDefaultValue(); + } catch (\ReflectionException) { + throw new InvalidDefinition(sprintf( + 'The parameter "%s" of %s has no type defined or guessable. It has a default value, ' + . 'but the default value can\'t be read through Reflection because it is a PHP internal class.', + $parameter->getName(), + $this->getFunctionName($function) + )); + } + } + + private function getFunctionName(ReflectionMethod $method) : string + { + return $method->getName() . '()'; + } + + private function assertClassIsNotAnonymous(ObjectDefinition $definition) : void + { + if (str_contains($definition->getClassName(), '@')) { + throw InvalidDefinition::create($definition, sprintf( + 'Entry "%s" cannot be compiled: anonymous classes cannot be compiled', + $definition->getName() + )); + } + } + + private function assertClassIsInstantiable(ObjectDefinition $definition) : void + { + if ($definition->isInstantiable()) { + return; + } + + $message = ! $definition->classExists() + ? 'Entry "%s" cannot be compiled: the class doesn\'t exist' + : 'Entry "%s" cannot be compiled: the class is not instantiable'; + + throw InvalidDefinition::create($definition, sprintf($message, $definition->getName())); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/RequestedEntryHolder.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/RequestedEntryHolder.php new file mode 100644 index 000000000..2d9cba9bb --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/RequestedEntryHolder.php @@ -0,0 +1,23 @@ + + */ +class RequestedEntryHolder implements RequestedEntry +{ + public function __construct( + private string $name, + ) { + } + + public function getName() : string + { + return $this->name; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/Template.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/Template.php new file mode 100644 index 000000000..b63b9b815 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Compiler/Template.php @@ -0,0 +1,16 @@ +/** + * This class has been auto-generated by PHP-DI. + */ +class containerClass; ?> extends containerParentClass; ?> +{ + const METHOD_MAPPING = entryToMethodMapping); ?>; + +methods as $methodName => $methodContent) { ?> + protected function () + { + + + } + + +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Container.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Container.php new file mode 100644 index 000000000..3bfb28ac3 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Container.php @@ -0,0 +1,404 @@ + + */ +class Container implements ContainerInterface, FactoryInterface, InvokerInterface +{ + /** + * Map of entries that are already resolved. + */ + protected array $resolvedEntries = []; + + private MutableDefinitionSource $definitionSource; + + private DefinitionResolver $definitionResolver; + + /** + * Map of definitions that are already fetched (local cache). + * + * @var array + */ + private array $fetchedDefinitions = []; + + /** + * Array of entries being resolved. Used to avoid circular dependencies and infinite loops. + */ + protected array $entriesBeingResolved = []; + + private ?InvokerInterface $invoker = null; + + /** + * Container that wraps this container. If none, points to $this. + */ + protected ContainerInterface $delegateContainer; + + protected ProxyFactoryInterface $proxyFactory; + + public static function create( + array $definitions, + ) : static { + $source = new SourceChain([new ReflectionBasedAutowiring]); + $source->setMutableDefinitionSource(new DefinitionArray($definitions, new ReflectionBasedAutowiring)); + + return new static($definitions); + } + + /** + * Use `$container = new Container()` if you want a container with the default configuration. + * + * If you want to customize the container's behavior, you are discouraged to create and pass the + * dependencies yourself, the ContainerBuilder class is here to help you instead. + * + * @see ContainerBuilder + * + * @param ContainerInterface $wrapperContainer If the container is wrapped by another container. + */ + public function __construct( + array|MutableDefinitionSource $definitions = [], + ?ProxyFactoryInterface $proxyFactory = null, + ?ContainerInterface $wrapperContainer = null, + ) { + if (is_array($definitions)) { + $this->definitionSource = $this->createDefaultDefinitionSource($definitions); + } else { + $this->definitionSource = $definitions; + } + + $this->delegateContainer = $wrapperContainer ?: $this; + + if ($proxyFactory === null) { + $proxyFactory = (\PHP_VERSION_ID >= 80400) ? new NativeProxyFactory : new ProxyFactory; + } + $this->proxyFactory = $proxyFactory; + $this->definitionResolver = new ResolverDispatcher($this->delegateContainer, $this->proxyFactory); + + // Auto-register the container + $this->resolvedEntries = [ + self::class => $this, + ContainerInterface::class => $this->delegateContainer, + FactoryInterface::class => $this, + InvokerInterface::class => $this, + ]; + } + + /** + * Returns an entry of the container by its name. + * + * @template T + * @param string|class-string $id Entry name or a class name. + * + * @return mixed|T + * @throws DependencyException Error while resolving the entry. + * @throws NotFoundException No entry found for the given name. + */ + public function get(string $id) : mixed + { + // If the entry is already resolved we return it + if (isset($this->resolvedEntries[$id]) || array_key_exists($id, $this->resolvedEntries)) { + return $this->resolvedEntries[$id]; + } + + $definition = $this->getDefinition($id); + if (! $definition) { + throw new NotFoundException("No entry or class found for '$id'"); + } + + $value = $this->resolveDefinition($definition); + + $this->resolvedEntries[$id] = $value; + + return $value; + } + + private function getDefinition(string $name) : ?Definition + { + // Local cache that avoids fetching the same definition twice + if (!array_key_exists($name, $this->fetchedDefinitions)) { + $this->fetchedDefinitions[$name] = $this->definitionSource->getDefinition($name); + } + + return $this->fetchedDefinitions[$name]; + } + + /** + * Build an entry of the container by its name. + * + * This method behave like get() except resolves the entry again every time. + * For example if the entry is a class then a new instance will be created each time. + * + * This method makes the container behave like a factory. + * + * @template T + * @param string|class-string $name Entry name or a class name. + * @param array $parameters Optional parameters to use to build the entry. Use this to force + * specific parameters to specific values. Parameters not defined in this + * array will be resolved using the container. + * + * @return mixed|T + * @throws InvalidArgumentException The name parameter must be of type string. + * @throws DependencyException Error while resolving the entry. + * @throws NotFoundException No entry found for the given name. + */ + public function make(string $name, array $parameters = []) : mixed + { + $definition = $this->getDefinition($name); + if (! $definition) { + // If the entry is already resolved we return it + if (array_key_exists($name, $this->resolvedEntries)) { + return $this->resolvedEntries[$name]; + } + + throw new NotFoundException("No entry or class found for '$name'"); + } + + return $this->resolveDefinition($definition, $parameters); + } + + public function has(string $id) : bool + { + if (array_key_exists($id, $this->resolvedEntries)) { + return true; + } + + $definition = $this->getDefinition($id); + if ($definition === null) { + return false; + } + + return $this->definitionResolver->isResolvable($definition); + } + + /** + * Inject all dependencies on an existing instance. + * + * @template T + * @param object|T $instance Object to perform injection upon + * @return object|T $instance Returns the same instance + * @throws InvalidArgumentException + * @throws DependencyException Error while injecting dependencies + */ + public function injectOn(object $instance) : object + { + $className = $instance::class; + + // If the class is anonymous, don't cache its definition + // Checking for anonymous classes is cleaner via Reflection, but also slower + $objectDefinition = str_contains($className, '@anonymous') + ? $this->definitionSource->getDefinition($className) + : $this->getDefinition($className); + + if (! $objectDefinition instanceof ObjectDefinition) { + return $instance; + } + + $definition = new InstanceDefinition($instance, $objectDefinition); + + $this->definitionResolver->resolve($definition); + + return $instance; + } + + /** + * Call the given function using the given parameters. + * + * Missing parameters will be resolved from the container. + * + * @param callable|array|string $callable Function to call. + * @param array $parameters Parameters to use. Can be indexed by the parameter names + * or not indexed (same order as the parameters). + * The array can also contain DI definitions, e.g. DI\get(). + * + * @return mixed Result of the function. + */ + public function call($callable, array $parameters = []) : mixed + { + return $this->getInvoker()->call($callable, $parameters); + } + + /** + * Define an object or a value in the container. + * + * @param string $name Entry name + * @param mixed|DefinitionHelper $value Value, use definition helpers to define objects + */ + public function set(string $name, mixed $value) : void + { + if ($value instanceof DefinitionHelper) { + $value = $value->getDefinition($name); + } elseif ($value instanceof \Closure) { + $value = new FactoryDefinition($name, $value); + } + + if ($value instanceof ValueDefinition) { + $this->resolvedEntries[$name] = $value->getValue(); + } elseif ($value instanceof Definition) { + $value->setName($name); + $this->setDefinition($name, $value); + } else { + $this->resolvedEntries[$name] = $value; + } + } + + /** + * Get defined container entries. + * + * @return string[] + */ + public function getKnownEntryNames() : array + { + $entries = array_unique(array_merge( + array_keys($this->definitionSource->getDefinitions()), + array_keys($this->resolvedEntries) + )); + sort($entries); + + return $entries; + } + + /** + * Get entry debug information. + * + * @param string $name Entry name + * + * @throws InvalidDefinition + * @throws NotFoundException + */ + public function debugEntry(string $name) : string + { + $definition = $this->definitionSource->getDefinition($name); + if ($definition instanceof Definition) { + return (string) $definition; + } + + if (array_key_exists($name, $this->resolvedEntries)) { + return $this->getEntryType($this->resolvedEntries[$name]); + } + + throw new NotFoundException("No entry or class found for '$name'"); + } + + /** + * Get formatted entry type. + */ + private function getEntryType(mixed $entry) : string + { + if (is_object($entry)) { + return sprintf("Object (\n class = %s\n)", $entry::class); + } + + if (is_array($entry)) { + return preg_replace(['/^array \(/', '/\)$/'], ['[', ']'], var_export($entry, true)); + } + + if (is_string($entry)) { + return sprintf('Value (\'%s\')', $entry); + } + + if (is_bool($entry)) { + return sprintf('Value (%s)', $entry === true ? 'true' : 'false'); + } + + return sprintf('Value (%s)', is_scalar($entry) ? (string) $entry : ucfirst(gettype($entry))); + } + + /** + * Resolves a definition. + * + * Checks for circular dependencies while resolving the definition. + * + * @throws DependencyException Error while resolving the entry. + */ + private function resolveDefinition(Definition $definition, array $parameters = []) : mixed + { + $entryName = $definition->getName(); + + // Check if we are already getting this entry -> circular dependency + if (isset($this->entriesBeingResolved[$entryName])) { + $entryList = implode(' -> ', [...array_keys($this->entriesBeingResolved), $entryName]); + throw new DependencyException("Circular dependency detected while trying to resolve entry '$entryName': Dependencies: " . $entryList); + } + $this->entriesBeingResolved[$entryName] = true; + + // Resolve the definition + try { + $value = $this->definitionResolver->resolve($definition, $parameters); + } finally { + unset($this->entriesBeingResolved[$entryName]); + } + + return $value; + } + + protected function setDefinition(string $name, Definition $definition) : void + { + // Clear existing entry if it exists + if (array_key_exists($name, $this->resolvedEntries)) { + unset($this->resolvedEntries[$name]); + } + $this->fetchedDefinitions = []; // Completely clear this local cache + + $this->definitionSource->addDefinition($definition); + } + + private function getInvoker() : InvokerInterface + { + if (! $this->invoker) { + $parameterResolver = new ResolverChain([ + new DefinitionParameterResolver($this->definitionResolver), + new NumericArrayResolver, + new AssociativeArrayResolver, + new DefaultValueResolver, + new TypeHintContainerResolver($this->delegateContainer), + ]); + + $this->invoker = new Invoker($parameterResolver, $this); + } + + return $this->invoker; + } + + private function createDefaultDefinitionSource(array $definitions) : SourceChain + { + $autowiring = new ReflectionBasedAutowiring; + $source = new SourceChain([$autowiring]); + $source->setMutableDefinitionSource(new DefinitionArray($definitions, $autowiring)); + + return $source; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/ContainerBuilder.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/ContainerBuilder.php new file mode 100644 index 000000000..393c7a044 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/ContainerBuilder.php @@ -0,0 +1,336 @@ +build(); + * + * @api + * + * @since 3.2 + * @author Matthieu Napoli + * + * @psalm-template ContainerClass of Container + */ +class ContainerBuilder +{ + /** + * Name of the container class, used to create the container. + * @var class-string + * @psalm-var class-string + */ + private string $containerClass; + + /** + * Name of the container parent class, used on compiled container. + * @var class-string + * @psalm-var class-string + */ + private string $containerParentClass; + + private bool $useAutowiring = true; + + private bool $useAttributes = false; + + /** + * If set, write the proxies to disk in this directory to improve performances. + */ + private ?string $proxyDirectory = null; + + /** + * If PHP-DI is wrapped in another container, this references the wrapper. + */ + private ?ContainerInterface $wrapperContainer = null; + + /** + * @var DefinitionSource[]|string[]|array[] + */ + private array $definitionSources = []; + + /** + * Whether the container has already been built. + */ + private bool $locked = false; + + private ?string $compileToDirectory = null; + + private bool $sourceCache = false; + + protected string $sourceCacheNamespace = ''; + + /** + * @param class-string $containerClass Name of the container class, used to create the container. + * @psalm-param class-string $containerClass + */ + public function __construct(string $containerClass = Container::class) + { + $this->containerClass = $containerClass; + } + + /** + * Build and return a container. + * + * @return Container + * @psalm-return ContainerClass + */ + public function build() + { + $sources = array_reverse($this->definitionSources); + + if ($this->useAttributes) { + $autowiring = new AttributeBasedAutowiring; + $sources[] = $autowiring; + } elseif ($this->useAutowiring) { + $autowiring = new ReflectionBasedAutowiring; + $sources[] = $autowiring; + } else { + $autowiring = new NoAutowiring; + } + + $sources = array_map(function ($definitions) use ($autowiring) { + if (is_string($definitions)) { + // File + return new DefinitionFile($definitions, $autowiring); + } + if (is_array($definitions)) { + return new DefinitionArray($definitions, $autowiring); + } + + return $definitions; + }, $sources); + $source = new SourceChain($sources); + + // Mutable definition source + $source->setMutableDefinitionSource(new DefinitionArray([], $autowiring)); + + if ($this->sourceCache) { + if (!SourceCache::isSupported()) { + throw new \Exception('APCu is not enabled, PHP-DI cannot use it as a cache'); + } + // Wrap the source with the cache decorator + $source = new SourceCache($source, $this->sourceCacheNamespace); + } + + $proxyFactory = (\PHP_VERSION_ID >= 80400) + ? new NativeProxyFactory() + : new ProxyFactory($this->proxyDirectory); + + $this->locked = true; + + $containerClass = $this->containerClass; + + if ($this->compileToDirectory) { + $compiler = new Compiler($proxyFactory); + $compiledContainerFile = $compiler->compile( + $source, + $this->compileToDirectory, + $containerClass, + $this->containerParentClass, + $this->useAutowiring + ); + // Only load the file if it hasn't been already loaded + // (the container can be created multiple times in the same process) + if (!class_exists($containerClass, false)) { + require $compiledContainerFile; + } + } + + return new $containerClass($source, $proxyFactory, $this->wrapperContainer); + } + + /** + * Compile the container for optimum performances. + * + * Be aware that the container is compiled once and never updated! + * + * Therefore: + * + * - in production you should clear that directory every time you deploy + * - in development you should not compile the container + * + * @see https://php-di.org/doc/performances.html + * + * @psalm-template T of CompiledContainer + * + * @param string $directory Directory in which to put the compiled container. + * @param string $containerClass Name of the compiled class. Customize only if necessary. + * @param class-string $containerParentClass Name of the compiled container parent class. Customize only if necessary. + * @psalm-param class-string $containerParentClass + * + * @psalm-return self + */ + public function enableCompilation( + string $directory, + string $containerClass = 'CompiledContainer', + string $containerParentClass = CompiledContainer::class, + ) : self { + $this->ensureNotLocked(); + + $this->compileToDirectory = $directory; + $this->containerClass = $containerClass; + $this->containerParentClass = $containerParentClass; + + return $this; + } + + /** + * Enable or disable the use of autowiring to guess injections. + * + * Enabled by default. + * + * @return $this + */ + public function useAutowiring(bool $bool) : self + { + $this->ensureNotLocked(); + + $this->useAutowiring = $bool; + + return $this; + } + + /** + * Enable or disable the use of PHP 8 attributes to configure injections. + * + * Disabled by default. + * + * @return $this + */ + public function useAttributes(bool $bool) : self + { + $this->ensureNotLocked(); + + $this->useAttributes = $bool; + + return $this; + } + + /** + * Configure the proxy generation. + * + * For dev environment, use `writeProxiesToFile(false)` (default configuration) + * For production environment, use `writeProxiesToFile(true, 'tmp/proxies')` + * + * @see https://php-di.org/doc/lazy-injection.html + * + * @param bool $writeToFile If true, write the proxies to disk to improve performances + * @param string|null $proxyDirectory Directory where to write the proxies + * @return $this + * @throws InvalidArgumentException when writeToFile is set to true and the proxy directory is null + */ + public function writeProxiesToFile(bool $writeToFile, ?string $proxyDirectory = null) : self + { + $this->ensureNotLocked(); + + if ($writeToFile && $proxyDirectory === null) { + throw new InvalidArgumentException( + 'The proxy directory must be specified if you want to write proxies on disk' + ); + } + $this->proxyDirectory = $writeToFile ? $proxyDirectory : null; + + return $this; + } + + /** + * If PHP-DI's container is wrapped by another container, we can + * set this so that PHP-DI will use the wrapper rather than itself for building objects. + * + * @return $this + */ + public function wrapContainer(ContainerInterface $otherContainer) : self + { + $this->ensureNotLocked(); + + $this->wrapperContainer = $otherContainer; + + return $this; + } + + /** + * Add definitions to the container. + * + * @param string|array|DefinitionSource ...$definitions Can be an array of definitions, the + * name of a file containing definitions + * or a DefinitionSource object. + * @return $this + */ + public function addDefinitions(string|array|DefinitionSource ...$definitions) : self + { + $this->ensureNotLocked(); + + foreach ($definitions as $definition) { + $this->definitionSources[] = $definition; + } + + return $this; + } + + /** + * Enables the use of APCu to cache definitions. + * + * You must have APCu enabled to use it. + * + * Before using this feature, you should try these steps first: + * - enable compilation if not already done (see `enableCompilation()`) + * - if you use autowiring or attributes, add all the classes you are using into your configuration so that + * PHP-DI knows about them and compiles them + * Once this is done, you can try to optimize performances further with APCu. It can also be useful if you use + * `Container::make()` instead of `get()` (`make()` calls cannot be compiled so they are not optimized). + * + * Remember to clear APCu on each deploy else your application will have a stale cache. Do not enable the cache + * in development environment: any change you will make to the code will be ignored because of the cache. + * + * @see https://php-di.org/doc/performances.html + * + * @param string $cacheNamespace use unique namespace per container when sharing a single APC memory pool to prevent cache collisions + * @return $this + */ + public function enableDefinitionCache(string $cacheNamespace = '') : self + { + $this->ensureNotLocked(); + + $this->sourceCache = true; + $this->sourceCacheNamespace = $cacheNamespace; + + return $this; + } + + /** + * Are we building a compiled container? + */ + public function isCompilationEnabled() : bool + { + return (bool) $this->compileToDirectory; + } + + private function ensureNotLocked() : void + { + if ($this->locked) { + throw new \LogicException('The ContainerBuilder cannot be modified after the container has been built'); + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ArrayDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ArrayDefinition.php new file mode 100644 index 000000000..fd0f8b48e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ArrayDefinition.php @@ -0,0 +1,65 @@ + + */ +class ArrayDefinition implements Definition +{ + /** Entry name. */ + private string $name = ''; + + public function __construct( + private array $values, + ) { + } + + public function getName() : string + { + return $this->name; + } + + public function setName(string $name) : void + { + $this->name = $name; + } + + public function getValues() : array + { + return $this->values; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + $this->values = array_map($replacer, $this->values); + } + + public function __toString() : string + { + $str = '[' . \PHP_EOL; + + foreach ($this->values as $key => $value) { + if (is_string($key)) { + $key = "'" . $key . "'"; + } + + $str .= ' ' . $key . ' => '; + + if ($value instanceof Definition) { + $str .= str_replace(\PHP_EOL, \PHP_EOL . ' ', (string) $value); + } else { + $str .= var_export($value, true); + } + + $str .= ',' . \PHP_EOL; + } + + return $str . ']'; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ArrayDefinitionExtension.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ArrayDefinitionExtension.php new file mode 100644 index 000000000..d55f12488 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ArrayDefinitionExtension.php @@ -0,0 +1,39 @@ + + */ +class ArrayDefinitionExtension extends ArrayDefinition implements ExtendsPreviousDefinition +{ + private ?ArrayDefinition $subDefinition = null; + + public function getValues() : array + { + if (! $this->subDefinition) { + return parent::getValues(); + } + + return array_merge($this->subDefinition->getValues(), parent::getValues()); + } + + public function setExtendedDefinition(Definition $definition) : void + { + if (! $definition instanceof ArrayDefinition) { + throw new InvalidDefinition(sprintf( + 'Definition %s tries to add array entries but the previous definition is not an array', + $this->getName() + )); + } + + $this->subDefinition = $definition; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/AutowireDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/AutowireDefinition.php new file mode 100644 index 000000000..25dc3ecbc --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/AutowireDefinition.php @@ -0,0 +1,12 @@ + + */ +class AutowireDefinition extends ObjectDefinition +{ +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/DecoratorDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/DecoratorDefinition.php new file mode 100644 index 000000000..c84ed37d3 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/DecoratorDefinition.php @@ -0,0 +1,36 @@ + + */ +class DecoratorDefinition extends FactoryDefinition implements Definition, ExtendsPreviousDefinition +{ + private ?Definition $decorated = null; + + public function setExtendedDefinition(Definition $definition) : void + { + $this->decorated = $definition; + } + + public function getDecoratedDefinition() : ?Definition + { + return $this->decorated; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + // no nested definitions + } + + public function __toString() : string + { + return 'Decorate(' . $this->getName() . ')'; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Definition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Definition.php new file mode 100644 index 000000000..8ceb9c68b --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Definition.php @@ -0,0 +1,37 @@ + + */ +interface Definition extends RequestedEntry, \Stringable +{ + /** + * Returns the name of the entry in the container. + */ + public function getName() : string; + + /** + * Set the name of the entry in the container. + */ + public function setName(string $name) : void; + + /** + * Apply a callable that replaces the definitions nested in this definition. + */ + public function replaceNestedDefinitions(callable $replacer) : void; + + /** + * Definitions can be cast to string for debugging information. + */ + public function __toString() : string; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Dumper/ObjectDefinitionDumper.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Dumper/ObjectDefinitionDumper.php new file mode 100644 index 000000000..c59c51000 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Dumper/ObjectDefinitionDumper.php @@ -0,0 +1,144 @@ + + */ +class ObjectDefinitionDumper +{ + /** + * Returns the definition as string representation. + */ + public function dump(ObjectDefinition $definition) : string + { + $className = $definition->getClassName(); + $classExist = class_exists($className) || interface_exists($className); + + // Class + if (! $classExist) { + $warning = '#UNKNOWN# '; + } else { + $class = new \ReflectionClass($className); + $warning = $class->isInstantiable() ? '' : '#NOT INSTANTIABLE# '; + } + $str = sprintf(' class = %s%s', $warning, $className); + + // Lazy + $str .= \PHP_EOL . ' lazy = ' . var_export($definition->isLazy(), true); + + if ($classExist) { + // Constructor + $str .= $this->dumpConstructor($className, $definition); + + // Properties + $str .= $this->dumpProperties($definition); + + // Methods + $str .= $this->dumpMethods($className, $definition); + } + + return sprintf('Object (' . \PHP_EOL . '%s' . \PHP_EOL . ')', $str); + } + + /** + * @param class-string $className + */ + private function dumpConstructor(string $className, ObjectDefinition $definition) : string + { + $str = ''; + + $constructorInjection = $definition->getConstructorInjection(); + + if ($constructorInjection !== null) { + $parameters = $this->dumpMethodParameters($className, $constructorInjection); + + $str .= sprintf(\PHP_EOL . ' __construct(' . \PHP_EOL . ' %s' . \PHP_EOL . ' )', $parameters); + } + + return $str; + } + + private function dumpProperties(ObjectDefinition $definition) : string + { + $str = ''; + + foreach ($definition->getPropertyInjections() as $propertyInjection) { + $value = $propertyInjection->getValue(); + $valueStr = $value instanceof Definition ? (string) $value : var_export($value, true); + + $str .= sprintf(\PHP_EOL . ' $%s = %s', $propertyInjection->getPropertyName(), $valueStr); + } + + return $str; + } + + /** + * @param class-string $className + */ + private function dumpMethods(string $className, ObjectDefinition $definition) : string + { + $str = ''; + + foreach ($definition->getMethodInjections() as $methodInjection) { + $parameters = $this->dumpMethodParameters($className, $methodInjection); + + $str .= sprintf(\PHP_EOL . ' %s(' . \PHP_EOL . ' %s' . \PHP_EOL . ' )', $methodInjection->getMethodName(), $parameters); + } + + return $str; + } + + /** + * @param class-string $className + */ + private function dumpMethodParameters(string $className, MethodInjection $methodInjection) : string + { + $methodReflection = new \ReflectionMethod($className, $methodInjection->getMethodName()); + + $args = []; + + $definitionParameters = $methodInjection->getParameters(); + + foreach ($methodReflection->getParameters() as $index => $parameter) { + if (array_key_exists($index, $definitionParameters)) { + $value = $definitionParameters[$index]; + $valueStr = $value instanceof Definition ? (string) $value : var_export($value, true); + + $args[] = sprintf('$%s = %s', $parameter->getName(), $valueStr); + + continue; + } + + // If the parameter is optional and wasn't specified, we take its default value + if ($parameter->isOptional()) { + try { + $value = $parameter->getDefaultValue(); + + $args[] = sprintf( + '$%s = (default value) %s', + $parameter->getName(), + var_export($value, true) + ); + continue; + } catch (ReflectionException) { + // The default value can't be read through Reflection because it is a PHP internal class + } + } + + $args[] = sprintf('$%s = #UNDEFINED#', $parameter->getName()); + } + + return implode(\PHP_EOL . ' ', $args); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/EnvironmentVariableDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/EnvironmentVariableDefinition.php new file mode 100644 index 000000000..5b4209675 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/EnvironmentVariableDefinition.php @@ -0,0 +1,87 @@ + + */ +class EnvironmentVariableDefinition implements Definition +{ + /** Entry name. */ + private string $name = ''; + + /** + * @param string $variableName The name of the environment variable + * @param bool $isOptional Whether or not the environment variable definition is optional. If true and the environment variable given by $variableName has not been defined, $defaultValue is used. + * @param mixed $defaultValue The default value to use if the environment variable is optional and not provided + */ + public function __construct( + private string $variableName, + private bool $isOptional = false, + private mixed $defaultValue = null, + ) { + } + + public function getName() : string + { + return $this->name; + } + + public function setName(string $name) : void + { + $this->name = $name; + } + + /** + * @return string The name of the environment variable + */ + public function getVariableName() : string + { + return $this->variableName; + } + + /** + * @return bool Whether or not the environment variable definition is optional + */ + public function isOptional() : bool + { + return $this->isOptional; + } + + /** + * @return mixed The default value to use if the environment variable is optional and not provided + */ + public function getDefaultValue() : mixed + { + return $this->defaultValue; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + $this->defaultValue = $replacer($this->defaultValue); + } + + public function __toString() : string + { + $str = ' variable = ' . $this->variableName . \PHP_EOL + . ' optional = ' . ($this->isOptional ? 'yes' : 'no'); + + if ($this->isOptional) { + if ($this->defaultValue instanceof Definition) { + $nestedDefinition = (string) $this->defaultValue; + $defaultValueStr = str_replace(\PHP_EOL, \PHP_EOL . ' ', $nestedDefinition); + } else { + $defaultValueStr = var_export($this->defaultValue, true); + } + + $str .= \PHP_EOL . ' default = ' . $defaultValueStr; + } + + return sprintf('Environment variable (' . \PHP_EOL . '%s' . \PHP_EOL . ')', $str); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Exception/InvalidAttribute.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Exception/InvalidAttribute.php new file mode 100644 index 000000000..4504f18ab --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Exception/InvalidAttribute.php @@ -0,0 +1,14 @@ + + */ +class InvalidAttribute extends InvalidDefinition +{ +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Exception/InvalidDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Exception/InvalidDefinition.php new file mode 100644 index 000000000..f88aa69ea --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Exception/InvalidDefinition.php @@ -0,0 +1,25 @@ + + */ +class InvalidDefinition extends \Exception implements ContainerExceptionInterface +{ + public static function create(Definition $definition, string $message, ?\Exception $previous = null) : self + { + return new self(sprintf( + '%s' . \PHP_EOL . 'Full definition:' . \PHP_EOL . '%s', + $message, + (string) $definition + ), 0, $previous); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ExtendsPreviousDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ExtendsPreviousDefinition.php new file mode 100644 index 000000000..ac2a7ab60 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ExtendsPreviousDefinition.php @@ -0,0 +1,15 @@ + + */ +interface ExtendsPreviousDefinition extends Definition +{ + public function setExtendedDefinition(Definition $definition) : void; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/FactoryDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/FactoryDefinition.php new file mode 100644 index 000000000..5044c910b --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/FactoryDefinition.php @@ -0,0 +1,78 @@ + + */ +class FactoryDefinition implements Definition +{ + /** + * Entry name. + */ + private string $name; + + /** + * Callable that returns the value. + * @var callable + */ + private $factory; + + /** + * Factory parameters. + * @var mixed[] + */ + private array $parameters; + + /** + * @param string $name Entry name + * @param callable|array|string $factory Callable that returns the value associated to the entry name. + * @param array $parameters Parameters to be passed to the callable + */ + public function __construct(string $name, callable|array|string $factory, array $parameters = []) + { + $this->name = $name; + $this->factory = $factory; + $this->parameters = $parameters; + } + + public function getName() : string + { + return $this->name; + } + + public function setName(string $name) : void + { + $this->name = $name; + } + + /** + * @return callable|array|string Callable that returns the value associated to the entry name. + */ + public function getCallable() : callable|array|string + { + return $this->factory; + } + + /** + * @return array Array containing the parameters to be passed to the callable, indexed by name. + */ + public function getParameters() : array + { + return $this->parameters; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + $this->parameters = array_map($replacer, $this->parameters); + } + + public function __toString() : string + { + return 'Factory'; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/AutowireDefinitionHelper.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/AutowireDefinitionHelper.php new file mode 100644 index 000000000..01b59e8fe --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/AutowireDefinitionHelper.php @@ -0,0 +1,72 @@ + + */ +class AutowireDefinitionHelper extends CreateDefinitionHelper +{ + public const DEFINITION_CLASS = AutowireDefinition::class; + + /** + * Defines a value for a specific argument of the constructor. + * + * This method is usually used together with attributes or autowiring, when a parameter + * is not (or cannot be) type-hinted. Using this method instead of constructor() allows to + * avoid defining all the parameters (letting them being resolved using attributes or autowiring) + * and only define one. + * + * @param string|int $parameter Parameter name of position for which the value will be given. + * @param mixed $value Value to give to this parameter. + * + * @return $this + */ + public function constructorParameter(string|int $parameter, mixed $value) : self + { + $this->constructor[$parameter] = $value; + + return $this; + } + + /** + * Defines a method to call and a value for a specific argument. + * + * This method is usually used together with attributes or autowiring, when a parameter + * is not (or cannot be) type-hinted. Using this method instead of method() allows to + * avoid defining all the parameters (letting them being resolved using attributes or + * autowiring) and only define one. + * + * If multiple calls to the method have been configured already (e.g. in a previous definition) + * then this method only overrides the parameter for the *first* call. + * + * @param string $method Name of the method to call. + * @param string|int $parameter Parameter name of position for which the value will be given. + * @param mixed $value Value to give to this parameter. + * + * @return $this + */ + public function methodParameter(string $method, string|int $parameter, mixed $value) : self + { + // Special case for the constructor + if ($method === '__construct') { + $this->constructor[$parameter] = $value; + + return $this; + } + + if (! isset($this->methods[$method])) { + $this->methods[$method] = [0 => []]; + } + + $this->methods[$method][0][$parameter] = $value; + + return $this; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/CreateDefinitionHelper.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/CreateDefinitionHelper.php new file mode 100644 index 000000000..5d2190512 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/CreateDefinitionHelper.php @@ -0,0 +1,189 @@ + + */ +class CreateDefinitionHelper implements DefinitionHelper +{ + private const DEFINITION_CLASS = ObjectDefinition::class; + + private ?string $className; + + private ?bool $lazy = null; + + /** + * Array of constructor parameters. + */ + protected array $constructor = []; + + /** + * Array of properties and their value. + */ + private array $properties = []; + + /** + * Array of methods and their parameters. + */ + protected array $methods = []; + + /** + * Helper for defining an object. + * + * @param string|null $className Class name of the object. + * If null, the name of the entry (in the container) will be used as class name. + */ + public function __construct(?string $className = null) + { + $this->className = $className; + } + + /** + * Define the entry as lazy. + * + * A lazy entry is created only when it is used, a proxy is injected instead. + * + * @return $this + */ + public function lazy() : self + { + $this->lazy = true; + + return $this; + } + + /** + * Defines the arguments to use to call the constructor. + * + * This method takes a variable number of arguments, example: + * ->constructor($param1, $param2, $param3) + * + * @param mixed ...$parameters Parameters to use for calling the constructor of the class. + * + * @return $this + */ + public function constructor(mixed ...$parameters) : self + { + $this->constructor = $parameters; + + return $this; + } + + /** + * Defines a value to inject in a property of the object. + * + * @param string $property Entry in which to inject the value. + * @param mixed $value Value to inject in the property. + * + * @return $this + */ + public function property(string $property, mixed $value) : self + { + $this->properties[$property] = $value; + + return $this; + } + + /** + * Defines a method to call and the arguments to use. + * + * This method takes a variable number of arguments after the method name, example: + * + * ->method('myMethod', $param1, $param2) + * + * Can be used multiple times to declare multiple calls. + * + * @param string $method Name of the method to call. + * @param mixed ...$parameters Parameters to use for calling the method. + * + * @return $this + */ + public function method(string $method, mixed ...$parameters) : self + { + if (! isset($this->methods[$method])) { + $this->methods[$method] = []; + } + + $this->methods[$method][] = $parameters; + + return $this; + } + + public function getDefinition(string $entryName) : ObjectDefinition + { + $class = $this::DEFINITION_CLASS; + /** @var ObjectDefinition $definition */ + $definition = new $class($entryName, $this->className); + + if ($this->lazy !== null) { + $definition->setLazy($this->lazy); + } + + if (! empty($this->constructor)) { + $parameters = $this->fixParameters($definition, '__construct', $this->constructor); + $constructorInjection = MethodInjection::constructor($parameters); + $definition->setConstructorInjection($constructorInjection); + } + + if (! empty($this->properties)) { + foreach ($this->properties as $property => $value) { + $definition->addPropertyInjection( + new PropertyInjection($property, $value) + ); + } + } + + if (! empty($this->methods)) { + foreach ($this->methods as $method => $calls) { + foreach ($calls as $parameters) { + $parameters = $this->fixParameters($definition, $method, $parameters); + $methodInjection = new MethodInjection($method, $parameters); + $definition->addMethodInjection($methodInjection); + } + } + } + + return $definition; + } + + /** + * Fixes parameters indexed by the parameter name -> reindex by position. + * + * This is necessary so that merging definitions between sources is possible. + * + * @throws InvalidDefinition + */ + private function fixParameters(ObjectDefinition $definition, string $method, array $parameters) : array + { + $fixedParameters = []; + + foreach ($parameters as $index => $parameter) { + // Parameter indexed by the parameter name, we reindex it with its position + if (is_string($index)) { + $callable = [$definition->getClassName(), $method]; + + try { + $reflectionParameter = new \ReflectionParameter($callable, $index); + } catch (\ReflectionException $e) { + throw InvalidDefinition::create($definition, sprintf("Parameter with name '%s' could not be found. %s.", $index, $e->getMessage())); + } + + $index = $reflectionParameter->getPosition(); + } + + $fixedParameters[$index] = $parameter; + } + + return $fixedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/DefinitionHelper.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/DefinitionHelper.php new file mode 100644 index 000000000..327167e66 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/DefinitionHelper.php @@ -0,0 +1,20 @@ + + */ +interface DefinitionHelper +{ + /** + * @param string $entryName Container entry name + */ + public function getDefinition(string $entryName) : Definition; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/FactoryDefinitionHelper.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/FactoryDefinitionHelper.php new file mode 100644 index 000000000..85e1ba525 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Helper/FactoryDefinitionHelper.php @@ -0,0 +1,63 @@ + + */ +class FactoryDefinitionHelper implements DefinitionHelper +{ + /** + * @var callable + */ + private $factory; + + private bool $decorate; + + private array $parameters = []; + + /** + * @param bool $decorate Is the factory decorating a previous definition? + */ + public function __construct(callable|array|string $factory, bool $decorate = false) + { + $this->factory = $factory; + $this->decorate = $decorate; + } + + public function getDefinition(string $entryName) : FactoryDefinition + { + if ($this->decorate) { + return new DecoratorDefinition($entryName, $this->factory, $this->parameters); + } + + return new FactoryDefinition($entryName, $this->factory, $this->parameters); + } + + /** + * Defines arguments to pass to the factory. + * + * Because factory methods do not yet support attributes or autowiring, this method + * should be used to define all parameters except the ContainerInterface and RequestedEntry. + * + * Multiple calls can be made to the method to override individual values. + * + * @param string $parameter Name or index of the parameter for which the value will be given. + * @param mixed $value Value to give to this parameter. + * + * @return $this + */ + public function parameter(string $parameter, mixed $value) : self + { + $this->parameters[$parameter] = $value; + + return $this; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/InstanceDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/InstanceDefinition.php new file mode 100644 index 000000000..51bf42e8c --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/InstanceDefinition.php @@ -0,0 +1,54 @@ + + */ +class InstanceDefinition implements Definition +{ + /** + * @param object $instance Instance on which to inject dependencies. + */ + public function __construct( + private object $instance, + private ObjectDefinition $objectDefinition, + ) { + } + + public function getName() : string + { + // Name are superfluous for instance definitions + return ''; + } + + public function setName(string $name) : void + { + // Name are superfluous for instance definitions + } + + public function getInstance() : object + { + return $this->instance; + } + + public function getObjectDefinition() : ObjectDefinition + { + return $this->objectDefinition; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + $this->objectDefinition->replaceNestedDefinitions($replacer); + } + + public function __toString() : string + { + return 'Instance'; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition.php new file mode 100644 index 000000000..bf571b463 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition.php @@ -0,0 +1,242 @@ + + */ +class ObjectDefinition implements Definition +{ + /** + * Entry name (most of the time, same as $classname). + */ + private string $name; + + /** + * Class name (if null, then the class name is $name). + */ + protected ?string $className = null; + + protected ?MethodInjection $constructorInjection = null; + + protected array $propertyInjections = []; + + /** + * Method calls. + * @var MethodInjection[][] + */ + protected array $methodInjections = []; + + protected ?bool $lazy = null; + + /** + * Store if the class exists. Storing it (in cache) avoids recomputing this. + */ + private bool $classExists; + + /** + * Store if the class is instantiable. Storing it (in cache) avoids recomputing this. + */ + private bool $isInstantiable; + + /** + * @param string $name Entry name + */ + public function __construct(string $name, ?string $className = null) + { + $this->name = $name; + $this->setClassName($className); + } + + public function getName() : string + { + return $this->name; + } + + public function setName(string $name) : void + { + $this->name = $name; + } + + public function setClassName(?string $className) : void + { + $this->className = $className; + + $this->updateCache(); + } + + public function getClassName() : string + { + return $this->className ?? $this->name; + } + + public function getConstructorInjection() : ?MethodInjection + { + return $this->constructorInjection; + } + + public function setConstructorInjection(MethodInjection $constructorInjection) : void + { + $this->constructorInjection = $constructorInjection; + } + + public function completeConstructorInjection(MethodInjection $injection) : void + { + if ($this->constructorInjection !== null) { + // Merge + $this->constructorInjection->merge($injection); + } else { + // Set + $this->constructorInjection = $injection; + } + } + + /** + * @return PropertyInjection[] Property injections + */ + public function getPropertyInjections() : array + { + return $this->propertyInjections; + } + + public function addPropertyInjection(PropertyInjection $propertyInjection) : void + { + $className = $propertyInjection->getClassName(); + if ($className) { + // Index with the class name to avoid collisions between parent and + // child private properties with the same name + $key = $className . '::' . $propertyInjection->getPropertyName(); + } else { + $key = $propertyInjection->getPropertyName(); + } + + $this->propertyInjections[$key] = $propertyInjection; + } + + /** + * @return MethodInjection[] Method injections + */ + public function getMethodInjections() : array + { + // Return array leafs + $injections = []; + array_walk_recursive($this->methodInjections, function ($injection) use (&$injections) { + $injections[] = $injection; + }); + + return $injections; + } + + public function addMethodInjection(MethodInjection $methodInjection) : void + { + $method = $methodInjection->getMethodName(); + if (! isset($this->methodInjections[$method])) { + $this->methodInjections[$method] = []; + } + $this->methodInjections[$method][] = $methodInjection; + } + + public function completeFirstMethodInjection(MethodInjection $injection) : void + { + $method = $injection->getMethodName(); + + if (isset($this->methodInjections[$method][0])) { + // Merge + $this->methodInjections[$method][0]->merge($injection); + } else { + // Set + $this->addMethodInjection($injection); + } + } + + public function setLazy(?bool $lazy = null) : void + { + $this->lazy = $lazy; + } + + public function isLazy() : bool + { + if ($this->lazy !== null) { + return $this->lazy; + } + + // Default value + return false; + } + + public function classExists() : bool + { + return $this->classExists; + } + + public function isInstantiable() : bool + { + return $this->isInstantiable; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + array_walk($this->propertyInjections, function (PropertyInjection $propertyInjection) use ($replacer) { + $propertyInjection->replaceNestedDefinition($replacer); + }); + + $this->constructorInjection?->replaceNestedDefinitions($replacer); + + array_walk($this->methodInjections, function ($injectionArray) use ($replacer) { + array_walk($injectionArray, function (MethodInjection $methodInjection) use ($replacer) { + $methodInjection->replaceNestedDefinitions($replacer); + }); + }); + } + + /** + * Replaces all the wildcards in the string with the given replacements. + * + * @param string[] $replacements + */ + public function replaceWildcards(array $replacements) : void + { + $className = $this->getClassName(); + + foreach ($replacements as $replacement) { + $pos = strpos($className, DefinitionArray::WILDCARD); + if ($pos !== false) { + $className = substr_replace($className, $replacement, $pos, 1); + } + } + + $this->setClassName($className); + } + + public function __toString() : string + { + return (new ObjectDefinitionDumper)->dump($this); + } + + private function updateCache() : void + { + $className = $this->getClassName(); + + $this->classExists = class_exists($className) || interface_exists($className); + + if (! $this->classExists) { + $this->isInstantiable = false; + + return; + } + + /** @var class-string $className */ + $class = new ReflectionClass($className); + $this->isInstantiable = $class->isInstantiable(); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition/MethodInjection.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition/MethodInjection.php new file mode 100644 index 000000000..72b8b5c50 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition/MethodInjection.php @@ -0,0 +1,76 @@ + + */ +class MethodInjection implements Definition +{ + /** + * @param mixed[] $parameters + */ + public function __construct( + private string $methodName, + private array $parameters = [], + ) { + } + + public static function constructor(array $parameters = []) : self + { + return new self('__construct', $parameters); + } + + public function getMethodName() : string + { + return $this->methodName; + } + + /** + * @return mixed[] + */ + public function getParameters() : array + { + return $this->parameters; + } + + /** + * Replace the parameters of the definition by a new array of parameters. + */ + public function replaceParameters(array $parameters) : void + { + $this->parameters = $parameters; + } + + public function merge(self $definition) : void + { + // In case of conflicts, the current definition prevails. + $this->parameters += $definition->parameters; + } + + public function getName() : string + { + return ''; + } + + public function setName(string $name) : void + { + // The name does not matter for method injections + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + $this->parameters = array_map($replacer, $this->parameters); + } + + public function __toString() : string + { + return sprintf('method(%s)', $this->methodName); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition/PropertyInjection.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition/PropertyInjection.php new file mode 100644 index 000000000..9457986e6 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ObjectDefinition/PropertyInjection.php @@ -0,0 +1,61 @@ + + */ +class PropertyInjection +{ + private string $propertyName; + + /** + * Value that should be injected in the property. + */ + private mixed $value; + + /** + * Use for injecting in properties of parent classes: the class name + * must be the name of the parent class because private properties + * can be attached to the parent classes, not the one we are resolving. + */ + private ?string $className; + + /** + * @param string $propertyName Property name + * @param mixed $value Value that should be injected in the property + */ + public function __construct(string $propertyName, mixed $value, ?string $className = null) + { + $this->propertyName = $propertyName; + $this->value = $value; + $this->className = $className; + } + + public function getPropertyName() : string + { + return $this->propertyName; + } + + /** + * @return mixed Value that should be injected in the property + */ + public function getValue() : mixed + { + return $this->value; + } + + public function getClassName() : ?string + { + return $this->className; + } + + public function replaceNestedDefinition(callable $replacer) : void + { + $this->value = $replacer($this->value); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Reference.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Reference.php new file mode 100644 index 000000000..839e7aa2a --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Reference.php @@ -0,0 +1,64 @@ + + */ +class Reference implements Definition, SelfResolvingDefinition +{ + /** Entry name. */ + private string $name = ''; + + /** + * @param string $targetEntryName Name of the target entry + */ + public function __construct( + private string $targetEntryName, + ) { + } + + public function getName() : string + { + return $this->name; + } + + public function setName(string $name) : void + { + $this->name = $name; + } + + public function getTargetEntryName() : string + { + return $this->targetEntryName; + } + + public function resolve(ContainerInterface $container) : mixed + { + return $container->get($this->getTargetEntryName()); + } + + public function isResolvable(ContainerInterface $container) : bool + { + return $container->has($this->getTargetEntryName()); + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + // no nested definitions + } + + public function __toString() : string + { + return sprintf( + 'get(%s)', + $this->targetEntryName + ); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ArrayResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ArrayResolver.php new file mode 100644 index 000000000..2c6a1c374 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ArrayResolver.php @@ -0,0 +1,76 @@ + + * + * @since 5.0 + * @author Matthieu Napoli + */ +class ArrayResolver implements DefinitionResolver +{ + /** + * @param DefinitionResolver $definitionResolver Used to resolve nested definitions. + */ + public function __construct( + private DefinitionResolver $definitionResolver, + ) { + } + + /** + * {@inheritDoc} + * + * Resolve an array definition to a value. + * + * An array definition can contain simple values or references to other entries. + * + * @param ArrayDefinition $definition + */ + public function resolve(Definition $definition, array $parameters = []) : array + { + $values = $definition->getValues(); + + // Resolve nested definitions + array_walk_recursive($values, function (& $value, $key) use ($definition) { + if ($value instanceof Definition) { + $value = $this->resolveDefinition($value, $definition, $key); + } + }); + + return $values; + } + + public function isResolvable(Definition $definition, array $parameters = []) : bool + { + return true; + } + + /** + * @throws DependencyException + */ + private function resolveDefinition(Definition $value, ArrayDefinition $definition, int|string $key) : mixed + { + try { + return $this->definitionResolver->resolve($value); + } catch (DependencyException $e) { + throw $e; + } catch (Exception $e) { + throw new DependencyException(sprintf( + 'Error while resolving %s[%s]. %s', + $definition->getName(), + $key, + $e->getMessage() + ), 0, $e); + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/DecoratorResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/DecoratorResolver.php new file mode 100644 index 000000000..945f6be94 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/DecoratorResolver.php @@ -0,0 +1,74 @@ + + * + * @since 5.0 + * @author Matthieu Napoli + */ +class DecoratorResolver implements DefinitionResolver +{ + /** + * The resolver needs a container. This container will be passed to the factory as a parameter + * so that the factory can access other entries of the container. + * + * @param DefinitionResolver $definitionResolver Used to resolve nested definitions. + */ + public function __construct( + private ContainerInterface $container, + private DefinitionResolver $definitionResolver, + ) { + } + + /** + * Resolve a decorator definition to a value. + * + * This will call the callable of the definition and pass it the decorated entry. + * + * @param DecoratorDefinition $definition + */ + public function resolve(Definition $definition, array $parameters = []) : mixed + { + $callable = $definition->getCallable(); + + if (! is_callable($callable)) { + throw new InvalidDefinition(sprintf( + 'The decorator "%s" is not callable', + $definition->getName() + )); + } + + $decoratedDefinition = $definition->getDecoratedDefinition(); + + if (! $decoratedDefinition instanceof Definition) { + if (! $definition->getName()) { + throw new InvalidDefinition('Decorators cannot be nested in another definition'); + } + + throw new InvalidDefinition(sprintf( + 'Entry "%s" decorates nothing: no previous definition with the same name was found', + $definition->getName() + )); + } + + $decorated = $this->definitionResolver->resolve($decoratedDefinition, $parameters); + + return $callable($decorated, $this->container); + } + + public function isResolvable(Definition $definition, array $parameters = []) : bool + { + return true; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/DefinitionResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/DefinitionResolver.php new file mode 100644 index 000000000..54d50bd56 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/DefinitionResolver.php @@ -0,0 +1,42 @@ + + * + * @template T of Definition + */ +interface DefinitionResolver +{ + /** + * Resolve a definition to a value. + * + * @param Definition $definition Object that defines how the value should be obtained. + * @psalm-param T $definition + * @param array $parameters Optional parameters to use to build the entry. + * @return mixed Value obtained from the definition. + * + * @throws InvalidDefinition If the definition cannot be resolved. + * @throws DependencyException + */ + public function resolve(Definition $definition, array $parameters = []) : mixed; + + /** + * Check if a definition can be resolved. + * + * @param Definition $definition Object that defines how the value should be obtained. + * @psalm-param T $definition + * @param array $parameters Optional parameters to use to build the entry. + */ + public function isResolvable(Definition $definition, array $parameters = []) : bool; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/EnvironmentVariableResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/EnvironmentVariableResolver.php new file mode 100644 index 000000000..e328bf3f2 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/EnvironmentVariableResolver.php @@ -0,0 +1,69 @@ + + * + * @author James Harris + */ +class EnvironmentVariableResolver implements DefinitionResolver +{ + /** @var callable */ + private $variableReader; + + public function __construct( + private DefinitionResolver $definitionResolver, + $variableReader = null, + ) { + $this->variableReader = $variableReader ?? [$this, 'getEnvVariable']; + } + + /** + * Resolve an environment variable definition to a value. + * + * @param EnvironmentVariableDefinition $definition + */ + public function resolve(Definition $definition, array $parameters = []) : mixed + { + $value = call_user_func($this->variableReader, $definition->getVariableName()); + + if (false !== $value) { + return $value; + } + + if (!$definition->isOptional()) { + throw new InvalidDefinition(sprintf( + "The environment variable '%s' has not been defined", + $definition->getVariableName() + )); + } + + $value = $definition->getDefaultValue(); + + // Nested definition + if ($value instanceof Definition) { + return $this->definitionResolver->resolve($value); + } + + return $value; + } + + public function isResolvable(Definition $definition, array $parameters = []) : bool + { + return true; + } + + protected function getEnvVariable(string $variableName) + { + return $_ENV[$variableName] ?? $_SERVER[$variableName] ?? getenv($variableName); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/FactoryResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/FactoryResolver.php new file mode 100644 index 000000000..d08bc5d8a --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/FactoryResolver.php @@ -0,0 +1,112 @@ + + * + * @since 4.0 + * @author Matthieu Napoli + */ +class FactoryResolver implements DefinitionResolver +{ + private ?Invoker $invoker = null; + + /** + * The resolver needs a container. This container will be passed to the factory as a parameter + * so that the factory can access other entries of the container. + */ + public function __construct( + private ContainerInterface $container, + private DefinitionResolver $resolver, + ) { + } + + /** + * Resolve a factory definition to a value. + * + * This will call the callable of the definition. + * + * @param FactoryDefinition $definition + */ + public function resolve(Definition $definition, array $parameters = []) : mixed + { + if (! $this->invoker) { + $parameterResolver = new ResolverChain([ + new AssociativeArrayResolver, + new FactoryParameterResolver($this->container), + new NumericArrayResolver, + new DefaultValueResolver, + ]); + + $this->invoker = new Invoker($parameterResolver, $this->container); + } + + $callable = $definition->getCallable(); + + try { + $providedParams = [$this->container, $definition]; + $extraParams = $this->resolveExtraParams($definition->getParameters()); + $providedParams = array_merge($providedParams, $extraParams, $parameters); + + return $this->invoker->call($callable, $providedParams); + } catch (NotCallableException $e) { + // Custom error message to help debugging + if (is_string($callable) && class_exists($callable) && method_exists($callable, '__invoke')) { + throw new InvalidDefinition(sprintf( + 'Entry "%s" cannot be resolved: factory %s. Invokable classes cannot be automatically resolved if autowiring is disabled on the container, you need to enable autowiring or define the entry manually.', + $definition->getName(), + $e->getMessage() + )); + } + + throw new InvalidDefinition(sprintf( + 'Entry "%s" cannot be resolved: factory %s', + $definition->getName(), + $e->getMessage() + )); + } catch (NotEnoughParametersException $e) { + throw new InvalidDefinition(sprintf( + 'Entry "%s" cannot be resolved: %s', + $definition->getName(), + $e->getMessage() + )); + } + } + + public function isResolvable(Definition $definition, array $parameters = []) : bool + { + return true; + } + + private function resolveExtraParams(array $params) : array + { + $resolved = []; + foreach ($params as $key => $value) { + // Nested definitions + if ($value instanceof Definition) { + $value = $this->resolver->resolve($value); + } + $resolved[$key] = $value; + } + + return $resolved; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/InstanceInjector.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/InstanceInjector.php new file mode 100644 index 000000000..81c3f63da --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/InstanceInjector.php @@ -0,0 +1,50 @@ + + * + * @since 5.0 + * @author Matthieu Napoli + */ +class InstanceInjector extends ObjectCreator implements DefinitionResolver +{ + /** + * Injects dependencies on an existing instance. + * + * @param InstanceDefinition $definition + * @psalm-suppress ImplementedParamTypeMismatch + */ + public function resolve(Definition $definition, array $parameters = []) : ?object + { + /** @psalm-suppress InvalidCatch */ + try { + $this->injectMethodsAndProperties($definition->getInstance(), $definition->getObjectDefinition()); + } catch (NotFoundExceptionInterface $e) { + $message = sprintf( + 'Error while injecting dependencies into %s: %s', + get_class($definition->getInstance()), + $e->getMessage() + ); + + throw new DependencyException($message, 0, $e); + } + + return $definition; + } + + public function isResolvable(Definition $definition, array $parameters = []) : bool + { + return true; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ObjectCreator.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ObjectCreator.php new file mode 100644 index 000000000..3d4738caf --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ObjectCreator.php @@ -0,0 +1,203 @@ + + * + * @since 4.0 + * @author Matthieu Napoli + */ +class ObjectCreator implements DefinitionResolver +{ + private ParameterResolver $parameterResolver; + + /** + * @param DefinitionResolver $definitionResolver Used to resolve nested definitions. + * @param ProxyFactoryInterface $proxyFactory Used to create proxies for lazy injections. + */ + public function __construct( + private DefinitionResolver $definitionResolver, + private ProxyFactoryInterface $proxyFactory, + ) { + $this->parameterResolver = new ParameterResolver($definitionResolver); + } + + /** + * Resolve a class definition to a value. + * + * This will create a new instance of the class using the injections points defined. + * + * @param ObjectDefinition $definition + */ + public function resolve(Definition $definition, array $parameters = []) : ?object + { + // Lazy? + if ($definition->isLazy()) { + return $this->createProxy($definition, $parameters); + } + + return $this->createInstance($definition, $parameters); + } + + /** + * The definition is not resolvable if the class is not instantiable (interface or abstract) + * or if the class doesn't exist. + * + * @param ObjectDefinition $definition + */ + public function isResolvable(Definition $definition, array $parameters = []) : bool + { + return $definition->isInstantiable(); + } + + /** + * Returns a proxy instance. + */ + private function createProxy(ObjectDefinition $definition, array $parameters) : object + { + /** @var class-string $className */ + $className = $definition->getClassName(); + + return $this->proxyFactory->createProxy( + $className, + function () use ($definition, $parameters) { + return $this->createInstance($definition, $parameters); + } + ); + } + + /** + * Creates an instance of the class and injects dependencies.. + * + * @param array $parameters Optional parameters to use to create the instance. + * + * @throws DependencyException + * @throws InvalidDefinition + */ + private function createInstance(ObjectDefinition $definition, array $parameters) : object + { + // Check that the class is instantiable + if (! $definition->isInstantiable()) { + // Check that the class exists + if (! $definition->classExists()) { + throw InvalidDefinition::create($definition, sprintf( + 'Entry "%s" cannot be resolved: the class doesn\'t exist', + $definition->getName() + )); + } + + throw InvalidDefinition::create($definition, sprintf( + 'Entry "%s" cannot be resolved: the class is not instantiable', + $definition->getName() + )); + } + + /** @psalm-var class-string $classname */ + $classname = $definition->getClassName(); + $classReflection = new ReflectionClass($classname); + + $constructorInjection = $definition->getConstructorInjection(); + + /** @psalm-suppress InvalidCatch */ + try { + $args = $this->parameterResolver->resolveParameters( + $constructorInjection, + $classReflection->getConstructor(), + $parameters + ); + + $object = new $classname(...$args); + + $this->injectMethodsAndProperties($object, $definition); + } catch (NotFoundExceptionInterface $e) { + throw new DependencyException(sprintf( + 'Error while injecting dependencies into %s: %s', + $classReflection->getName(), + $e->getMessage() + ), 0, $e); + } catch (InvalidDefinition $e) { + throw InvalidDefinition::create($definition, sprintf( + 'Entry "%s" cannot be resolved: %s', + $definition->getName(), + $e->getMessage() + )); + } + + return $object; + } + + protected function injectMethodsAndProperties(object $object, ObjectDefinition $objectDefinition) : void + { + // Property injections + foreach ($objectDefinition->getPropertyInjections() as $propertyInjection) { + $this->injectProperty($object, $propertyInjection); + } + + // Method injections + foreach ($objectDefinition->getMethodInjections() as $methodInjection) { + $methodReflection = new \ReflectionMethod($object, $methodInjection->getMethodName()); + $args = $this->parameterResolver->resolveParameters($methodInjection, $methodReflection); + + $methodReflection->invokeArgs($object, $args); + } + } + + /** + * Inject dependencies into properties. + * + * @param object $object Object to inject dependencies into + * @param PropertyInjection $propertyInjection Property injection definition + * + * @throws DependencyException + */ + private function injectProperty(object $object, PropertyInjection $propertyInjection) : void + { + $propertyName = $propertyInjection->getPropertyName(); + + $value = $propertyInjection->getValue(); + + if ($value instanceof Definition) { + try { + $value = $this->definitionResolver->resolve($value); + } catch (DependencyException $e) { + throw $e; + } catch (Exception $e) { + throw new DependencyException(sprintf( + 'Error while injecting in %s::%s. %s', + $object::class, + $propertyName, + $e->getMessage() + ), 0, $e); + } + } + + self::setPrivatePropertyValue($propertyInjection->getClassName(), $object, $propertyName, $value); + } + + public static function setPrivatePropertyValue(?string $className, $object, string $propertyName, mixed $propertyValue) : void + { + $className = $className ?: $object::class; + + $property = new ReflectionProperty($className, $propertyName); + if (! $property->isPublic() && \PHP_VERSION_ID < 80100) { + $property->setAccessible(true); + } + $property->setValue($object, $propertyValue); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ParameterResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ParameterResolver.php new file mode 100644 index 000000000..2f46d1f7e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ParameterResolver.php @@ -0,0 +1,106 @@ + + */ +class ParameterResolver +{ + /** + * @param DefinitionResolver $definitionResolver Will be used to resolve nested definitions. + */ + public function __construct( + private DefinitionResolver $definitionResolver, + ) { + } + + /** + * @return array Parameters to use to call the function. + * @throws InvalidDefinition A parameter has no value defined or guessable. + */ + public function resolveParameters( + ?MethodInjection $definition = null, + ?ReflectionMethod $method = null, + array $parameters = [], + ) : array { + $args = []; + + if (! $method) { + return $args; + } + + $definitionParameters = $definition ? $definition->getParameters() : []; + + foreach ($method->getParameters() as $index => $parameter) { + if (array_key_exists($parameter->getName(), $parameters)) { + // Look in the $parameters array + $value = &$parameters[$parameter->getName()]; + } elseif (array_key_exists($index, $definitionParameters)) { + // Look in the definition + $value = &$definitionParameters[$index]; + } else { + // If the parameter is optional and wasn't specified, we take its default value + if ($parameter->isDefaultValueAvailable() || $parameter->isOptional()) { + $args[] = $this->getParameterDefaultValue($parameter, $method); + continue; + } + + throw new InvalidDefinition(sprintf( + 'Parameter $%s of %s has no value defined or guessable', + $parameter->getName(), + $this->getFunctionName($method) + )); + } + + // Nested definitions + if ($value instanceof Definition) { + // If the container cannot produce the entry, we can use the default parameter value + if ($parameter->isOptional() && ! $this->definitionResolver->isResolvable($value)) { + $value = $this->getParameterDefaultValue($parameter, $method); + } else { + $value = $this->definitionResolver->resolve($value); + } + } + + $args[] = &$value; + } + + return $args; + } + + /** + * Returns the default value of a function parameter. + * + * @throws InvalidDefinition Can't get default values from PHP internal classes and functions + */ + private function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function) : mixed + { + try { + return $parameter->getDefaultValue(); + } catch (\ReflectionException) { + throw new InvalidDefinition(sprintf( + 'The parameter "%s" of %s has no type defined or guessable. It has a default value, ' + . 'but the default value can\'t be read through Reflection because it is a PHP internal class.', + $parameter->getName(), + $this->getFunctionName($function) + )); + } + } + + private function getFunctionName(ReflectionMethod $method) : string + { + return $method->getName() . '()'; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ResolverDispatcher.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ResolverDispatcher.php new file mode 100644 index 000000000..70e2bfc7e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Resolver/ResolverDispatcher.php @@ -0,0 +1,125 @@ + + * + * @psalm-suppress MissingTemplateParam + */ +class ResolverDispatcher implements DefinitionResolver +{ + private ?ArrayResolver $arrayResolver = null; + private ?FactoryResolver $factoryResolver = null; + private ?DecoratorResolver $decoratorResolver = null; + private ?ObjectCreator $objectResolver = null; + private ?InstanceInjector $instanceResolver = null; + private ?EnvironmentVariableResolver $envVariableResolver = null; + + public function __construct( + private ContainerInterface $container, + private ProxyFactoryInterface $proxyFactory, + ) { + } + + /** + * Resolve a definition to a value. + * + * @param Definition $definition Object that defines how the value should be obtained. + * @param array $parameters Optional parameters to use to build the entry. + * + * @return mixed Value obtained from the definition. + * @throws InvalidDefinition If the definition cannot be resolved. + */ + public function resolve(Definition $definition, array $parameters = []) : mixed + { + // Special case, tested early for speed + if ($definition instanceof SelfResolvingDefinition) { + return $definition->resolve($this->container); + } + + $definitionResolver = $this->getDefinitionResolver($definition); + + return $definitionResolver->resolve($definition, $parameters); + } + + public function isResolvable(Definition $definition, array $parameters = []) : bool + { + // Special case, tested early for speed + if ($definition instanceof SelfResolvingDefinition) { + return $definition->isResolvable($this->container); + } + + $definitionResolver = $this->getDefinitionResolver($definition); + + return $definitionResolver->isResolvable($definition, $parameters); + } + + /** + * Returns a resolver capable of handling the given definition. + * + * @throws \RuntimeException No definition resolver was found for this type of definition. + */ + private function getDefinitionResolver(Definition $definition) : DefinitionResolver + { + switch (true) { + case $definition instanceof ObjectDefinition: + if (! $this->objectResolver) { + $this->objectResolver = new ObjectCreator($this, $this->proxyFactory); + } + + return $this->objectResolver; + case $definition instanceof DecoratorDefinition: + if (! $this->decoratorResolver) { + $this->decoratorResolver = new DecoratorResolver($this->container, $this); + } + + return $this->decoratorResolver; + case $definition instanceof FactoryDefinition: + if (! $this->factoryResolver) { + $this->factoryResolver = new FactoryResolver($this->container, $this); + } + + return $this->factoryResolver; + case $definition instanceof ArrayDefinition: + if (! $this->arrayResolver) { + $this->arrayResolver = new ArrayResolver($this); + } + + return $this->arrayResolver; + case $definition instanceof EnvironmentVariableDefinition: + if (! $this->envVariableResolver) { + $this->envVariableResolver = new EnvironmentVariableResolver($this); + } + + return $this->envVariableResolver; + case $definition instanceof InstanceDefinition: + if (! $this->instanceResolver) { + $this->instanceResolver = new InstanceInjector($this, $this->proxyFactory); + } + + return $this->instanceResolver; + default: + throw new \RuntimeException('No definition resolver was configured for definition of type ' . $definition::class); + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/SelfResolvingDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/SelfResolvingDefinition.php new file mode 100644 index 000000000..be2f274b3 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/SelfResolvingDefinition.php @@ -0,0 +1,25 @@ + + */ +interface SelfResolvingDefinition +{ + /** + * Resolve the definition and return the resulting value. + */ + public function resolve(ContainerInterface $container) : mixed; + + /** + * Check if a definition can be resolved. + */ + public function isResolvable(ContainerInterface $container) : bool; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/AttributeBasedAutowiring.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/AttributeBasedAutowiring.php new file mode 100644 index 000000000..7ef5ca9f6 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/AttributeBasedAutowiring.php @@ -0,0 +1,263 @@ + + */ +class AttributeBasedAutowiring implements DefinitionSource, Autowiring +{ + /** + * @throws InvalidAttribute + */ + public function autowire(string $name, ?ObjectDefinition $definition = null) : ?ObjectDefinition + { + $className = $definition ? $definition->getClassName() : $name; + + if (!class_exists($className) && !interface_exists($className)) { + return $definition; + } + + $definition = $definition ?: new ObjectDefinition($name); + + $class = new ReflectionClass($className); + + $this->readInjectableAttribute($class, $definition); + + // Browse the class properties looking for annotated properties + $this->readProperties($class, $definition); + + // Browse the object's methods looking for annotated methods + $this->readMethods($class, $definition); + + return $definition; + } + + /** + * @throws InvalidAttribute + * @throws InvalidArgumentException The class doesn't exist + */ + public function getDefinition(string $name) : ?ObjectDefinition + { + return $this->autowire($name); + } + + /** + * Autowiring cannot guess all existing definitions. + */ + public function getDefinitions() : array + { + return []; + } + + /** + * Browse the class properties looking for annotated properties. + */ + private function readProperties(ReflectionClass $class, ObjectDefinition $definition) : void + { + foreach ($class->getProperties() as $property) { + $this->readProperty($property, $definition); + } + + // Read also the *private* properties of the parent classes + /** @noinspection PhpAssignmentInConditionInspection */ + while ($class = $class->getParentClass()) { + foreach ($class->getProperties(ReflectionProperty::IS_PRIVATE) as $property) { + $this->readProperty($property, $definition, $class->getName()); + } + } + } + + /** + * @throws InvalidAttribute + */ + private function readProperty(ReflectionProperty $property, ObjectDefinition $definition, ?string $classname = null) : void + { + if ($property->isStatic() || $property->isPromoted()) { + return; + } + + // Look for #[Inject] attribute + try { + $attribute = $property->getAttributes(Inject::class)[0] ?? null; + if (! $attribute) { + return; + } + /** @var Inject $inject */ + $inject = $attribute->newInstance(); + } catch (Throwable $e) { + throw new InvalidAttribute(sprintf( + '#[Inject] annotation on property %s::%s is malformed. %s', + $property->getDeclaringClass()->getName(), + $property->getName(), + $e->getMessage() + ), 0, $e); + } + + // Try to #[Inject("name")] or look for the property type + $entryName = $inject->getName(); + + // Try using typed properties + $propertyType = $property->getType(); + if ($entryName === null && $propertyType instanceof ReflectionNamedType) { + if (! class_exists($propertyType->getName()) && ! interface_exists($propertyType->getName())) { + throw new InvalidAttribute(sprintf( + '#[Inject] found on property %s::%s but unable to guess what to inject, the type of the property does not look like a valid class or interface name', + $property->getDeclaringClass()->getName(), + $property->getName() + )); + } + $entryName = $propertyType->getName(); + } + + if ($entryName === null) { + throw new InvalidAttribute(sprintf( + '#[Inject] found on property %s::%s but unable to guess what to inject, please add a type to the property', + $property->getDeclaringClass()->getName(), + $property->getName() + )); + } + + $definition->addPropertyInjection( + new PropertyInjection($property->getName(), new Reference($entryName), $classname) + ); + } + + /** + * Browse the object's methods looking for annotated methods. + */ + private function readMethods(ReflectionClass $class, ObjectDefinition $objectDefinition) : void + { + // This will look in all the methods, including those of the parent classes + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if ($method->isStatic()) { + continue; + } + + $methodInjection = $this->getMethodInjection($method); + + if (! $methodInjection) { + continue; + } + + if ($method->isConstructor()) { + $objectDefinition->completeConstructorInjection($methodInjection); + } else { + $objectDefinition->completeFirstMethodInjection($methodInjection); + } + } + } + + private function getMethodInjection(ReflectionMethod $method) : ?MethodInjection + { + // Look for #[Inject] attribute + $attribute = $method->getAttributes(Inject::class)[0] ?? null; + + if ($attribute) { + /** @var Inject $inject */ + $inject = $attribute->newInstance(); + $annotationParameters = $inject->getParameters(); + } elseif ($method->isConstructor()) { + // #[Inject] on constructor is implicit, we continue + $annotationParameters = []; + } else { + return null; + } + + $parameters = []; + foreach ($method->getParameters() as $index => $parameter) { + $entryName = $this->getMethodParameter($index, $parameter, $annotationParameters); + + if ($entryName !== null) { + $parameters[$index] = new Reference($entryName); + } + } + + if ($method->isConstructor()) { + return MethodInjection::constructor($parameters); + } + + return new MethodInjection($method->getName(), $parameters); + } + + /** + * @return string|null Entry name or null if not found. + */ + private function getMethodParameter(int $parameterIndex, ReflectionParameter $parameter, array $annotationParameters) : ?string + { + // Let's check if this parameter has an #[Inject] attribute + $attribute = $parameter->getAttributes(Inject::class)[0] ?? null; + if ($attribute) { + /** @var Inject $inject */ + $inject = $attribute->newInstance(); + + return $inject->getName(); + } + + // #[Inject] has definition for this parameter (by index, or by name) + if (isset($annotationParameters[$parameterIndex])) { + return $annotationParameters[$parameterIndex]; + } + if (isset($annotationParameters[$parameter->getName()])) { + return $annotationParameters[$parameter->getName()]; + } + + // Skip optional parameters if not explicitly defined + if ($parameter->isOptional()) { + return null; + } + + // Look for the property type + $parameterType = $parameter->getType(); + if ($parameterType instanceof ReflectionNamedType && !$parameterType->isBuiltin()) { + return $parameterType->getName(); + } + + return null; + } + + /** + * @throws InvalidAttribute + */ + private function readInjectableAttribute(ReflectionClass $class, ObjectDefinition $definition) : void + { + try { + $attribute = $class->getAttributes(Injectable::class)[0] ?? null; + if (! $attribute) { + return; + } + $attribute = $attribute->newInstance(); + } catch (Throwable $e) { + throw new InvalidAttribute(sprintf( + 'Error while reading #[Injectable] on %s: %s', + $class->getName(), + $e->getMessage() + ), 0, $e); + } + + if ($attribute->isLazy() !== null) { + $definition->setLazy($attribute->isLazy()); + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/Autowiring.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/Autowiring.php new file mode 100644 index 000000000..e5392f3de --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/Autowiring.php @@ -0,0 +1,23 @@ + + */ +interface Autowiring +{ + /** + * Autowire the given definition. + * + * @throws InvalidDefinition An invalid definition was found. + */ + public function autowire(string $name, ?ObjectDefinition $definition = null) : ?ObjectDefinition; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionArray.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionArray.php new file mode 100644 index 000000000..150f2b7b9 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionArray.php @@ -0,0 +1,112 @@ + + */ +class DefinitionArray implements DefinitionSource, MutableDefinitionSource +{ + public const WILDCARD = '*'; + /** + * Matches anything except "\". + */ + private const WILDCARD_PATTERN = '([^\\\\]+)'; + + /** DI definitions in a PHP array. */ + private array $definitions; + + /** Cache of wildcard definitions. */ + private ?array $wildcardDefinitions = null; + + private DefinitionNormalizer $normalizer; + + public function __construct(array $definitions = [], ?Autowiring $autowiring = null) + { + if (isset($definitions[0])) { + throw new \Exception('The PHP-DI definition is not indexed by an entry name in the definition array'); + } + + $this->definitions = $definitions; + + $this->normalizer = new DefinitionNormalizer($autowiring ?: new NoAutowiring); + } + + /** + * @param array $definitions DI definitions in a PHP array indexed by the definition name. + */ + public function addDefinitions(array $definitions) : void + { + if (isset($definitions[0])) { + throw new \Exception('The PHP-DI definition is not indexed by an entry name in the definition array'); + } + + // The newly added data prevails + // "for keys that exist in both arrays, the elements from the left-hand array will be used" + $this->definitions = $definitions + $this->definitions; + + // Clear cache + $this->wildcardDefinitions = null; + } + + public function addDefinition(Definition $definition) : void + { + $this->definitions[$definition->getName()] = $definition; + + // Clear cache + $this->wildcardDefinitions = null; + } + + public function getDefinition(string $name) : ?Definition + { + // Look for the definition by name + if (array_key_exists($name, $this->definitions)) { + $definition = $this->definitions[$name]; + + return $this->normalizer->normalizeRootDefinition($definition, $name); + } + + // Build the cache of wildcard definitions + if ($this->wildcardDefinitions === null) { + $this->wildcardDefinitions = []; + foreach ($this->definitions as $key => $definition) { + if (str_contains($key, self::WILDCARD)) { + $this->wildcardDefinitions[$key] = $definition; + } + } + } + + // Look in wildcards definitions + foreach ($this->wildcardDefinitions as $key => $definition) { + // Turn the pattern into a regex + $key = preg_quote($key, '#'); + $key = '#^' . str_replace('\\' . self::WILDCARD, self::WILDCARD_PATTERN, $key) . '#'; + if (preg_match($key, $name, $matches) === 1) { + array_shift($matches); + + return $this->normalizer->normalizeRootDefinition($definition, $name, $matches); + } + } + + return null; + } + + public function getDefinitions() : array + { + // Return all definitions except wildcard definitions + $definitions = []; + foreach ($this->definitions as $key => $definition) { + if (! str_contains($key, self::WILDCARD)) { + $definitions[$key] = $definition; + } + } + + return $definitions; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionFile.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionFile.php new file mode 100644 index 000000000..99e4d8a55 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionFile.php @@ -0,0 +1,62 @@ + + */ +class DefinitionFile extends DefinitionArray +{ + private bool $initialized = false; + + /** + * @param string $file File in which the definitions are returned as an array. + */ + public function __construct( + private string $file, + ?Autowiring $autowiring = null, + ) { + // Lazy-loading to improve performances + parent::__construct([], $autowiring); + } + + public function getDefinition(string $name) : ?Definition + { + $this->initialize(); + + return parent::getDefinition($name); + } + + public function getDefinitions() : array + { + $this->initialize(); + + return parent::getDefinitions(); + } + + /** + * Lazy-loading of the definitions. + */ + private function initialize() : void + { + if ($this->initialized === true) { + return; + } + + $definitions = require $this->file; + + if (! is_array($definitions)) { + throw new \Exception("File $this->file should return an array of definitions"); + } + + $this->addDefinitions($definitions); + + $this->initialized = true; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionNormalizer.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionNormalizer.php new file mode 100644 index 000000000..8f89095d7 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionNormalizer.php @@ -0,0 +1,112 @@ + + */ +class DefinitionNormalizer +{ + public function __construct( + private Autowiring $autowiring, + ) { + } + + /** + * Normalize a definition that is *not* nested in another one. + * + * This is usually a definition declared at the root of a definition array. + * + * @param string $name The definition name. + * @param string[] $wildcardsReplacements Replacements for wildcard definitions. + * + * @throws InvalidDefinition + */ + public function normalizeRootDefinition(mixed $definition, string $name, ?array $wildcardsReplacements = null) : Definition + { + if ($definition instanceof DefinitionHelper) { + $definition = $definition->getDefinition($name); + } elseif (is_array($definition)) { + $definition = new ArrayDefinition($definition); + } elseif ($definition instanceof \Closure) { + $definition = new FactoryDefinition($name, $definition); + } elseif (! $definition instanceof Definition) { + $definition = new ValueDefinition($definition); + } + + // For a class definition, we replace * in the class name with the matches + // *Interface -> *Impl => FooInterface -> FooImpl + if ($wildcardsReplacements && $definition instanceof ObjectDefinition) { + $definition->replaceWildcards($wildcardsReplacements); + } + + if ($definition instanceof AutowireDefinition) { + /** @var AutowireDefinition $definition */ + $definition = $this->autowiring->autowire($name, $definition); + } + + $definition->setName($name); + + try { + $definition->replaceNestedDefinitions([$this, 'normalizeNestedDefinition']); + } catch (InvalidDefinition $e) { + throw InvalidDefinition::create($definition, sprintf( + 'Definition "%s" contains an error: %s', + $definition->getName(), + $e->getMessage() + ), $e); + } + + return $definition; + } + + /** + * Normalize a definition that is nested in another one. + * + * @throws InvalidDefinition + */ + public function normalizeNestedDefinition(mixed $definition) : mixed + { + $name = ''; + + if ($definition instanceof DefinitionHelper) { + $definition = $definition->getDefinition($name); + } elseif (is_array($definition)) { + $definition = new ArrayDefinition($definition); + } elseif ($definition instanceof \Closure) { + $definition = new FactoryDefinition($name, $definition); + } + + if ($definition instanceof DecoratorDefinition) { + throw new InvalidDefinition('Decorators cannot be nested in another definition'); + } + + if ($definition instanceof AutowireDefinition) { + $definition = $this->autowiring->autowire($name, $definition); + } + + if ($definition instanceof Definition) { + $definition->setName($name); + + // Recursively traverse nested definitions + $definition->replaceNestedDefinitions([$this, 'normalizeNestedDefinition']); + } + + return $definition; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionSource.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionSource.php new file mode 100644 index 000000000..2595195c8 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/DefinitionSource.php @@ -0,0 +1,28 @@ + + */ +interface DefinitionSource +{ + /** + * Returns the DI definition for the entry name. + * + * @throws InvalidDefinition An invalid definition was found. + */ + public function getDefinition(string $name) : ?Definition; + + /** + * @return array Definitions indexed by their name. + */ + public function getDefinitions() : array; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/MutableDefinitionSource.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/MutableDefinitionSource.php new file mode 100644 index 000000000..28b41d3c5 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/MutableDefinitionSource.php @@ -0,0 +1,17 @@ + + */ +interface MutableDefinitionSource extends DefinitionSource +{ + public function addDefinition(Definition $definition) : void; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/NoAutowiring.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/NoAutowiring.php new file mode 100644 index 000000000..42ee81e36 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/NoAutowiring.php @@ -0,0 +1,24 @@ + + */ +class NoAutowiring implements Autowiring +{ + public function autowire(string $name, ?ObjectDefinition $definition = null) : ?ObjectDefinition + { + throw new InvalidDefinition(sprintf( + 'Cannot autowire entry "%s" because autowiring is disabled', + $name + )); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/ReflectionBasedAutowiring.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/ReflectionBasedAutowiring.php new file mode 100644 index 000000000..5b0e112c2 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/ReflectionBasedAutowiring.php @@ -0,0 +1,85 @@ + + */ +class ReflectionBasedAutowiring implements DefinitionSource, Autowiring +{ + public function autowire(string $name, ?ObjectDefinition $definition = null) : ?ObjectDefinition + { + $className = $definition ? $definition->getClassName() : $name; + + if (!class_exists($className) && !interface_exists($className)) { + return $definition; + } + + $definition = $definition ?: new ObjectDefinition($name); + + // Constructor + $class = new \ReflectionClass($className); + $constructor = $class->getConstructor(); + if ($constructor && $constructor->isPublic()) { + $constructorInjection = MethodInjection::constructor($this->getParametersDefinition($constructor)); + $definition->completeConstructorInjection($constructorInjection); + } + + return $definition; + } + + public function getDefinition(string $name) : ?ObjectDefinition + { + return $this->autowire($name); + } + + /** + * Autowiring cannot guess all existing definitions. + */ + public function getDefinitions() : array + { + return []; + } + + /** + * Read the type-hinting from the parameters of the function. + */ + private function getParametersDefinition(\ReflectionFunctionAbstract $constructor) : array + { + $parameters = []; + + foreach ($constructor->getParameters() as $index => $parameter) { + // Skip optional parameters + if ($parameter->isOptional()) { + continue; + } + + $parameterType = $parameter->getType(); + if (!$parameterType) { + // No type + continue; + } + if (!$parameterType instanceof ReflectionNamedType) { + // Union types are not supported + continue; + } + if ($parameterType->isBuiltin()) { + // Primitive types are not supported + continue; + } + + $parameters[$index] = new Reference($parameterType->getName()); + } + + return $parameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/SourceCache.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/SourceCache.php new file mode 100644 index 000000000..245f9d782 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/SourceCache.php @@ -0,0 +1,77 @@ + + */ +class SourceCache implements DefinitionSource, MutableDefinitionSource +{ + public const CACHE_KEY = 'php-di.definitions.'; + + public function __construct( + private DefinitionSource $cachedSource, + private string $cacheNamespace = '', + ) { + } + + public function getDefinition(string $name) : ?Definition + { + $definition = apcu_fetch($this->getCacheKey($name)); + + if ($definition === false) { + $definition = $this->cachedSource->getDefinition($name); + + // Update the cache + if ($this->shouldBeCached($definition)) { + apcu_store($this->getCacheKey($name), $definition); + } + } + + return $definition; + } + + /** + * Used only for the compilation so we can skip the cache safely. + */ + public function getDefinitions() : array + { + return $this->cachedSource->getDefinitions(); + } + + public static function isSupported() : bool + { + return function_exists('apcu_fetch') + && ini_get('apc.enabled') + && ! ('cli' === \PHP_SAPI && ! ini_get('apc.enable_cli')); + } + + public function getCacheKey(string $name) : string + { + return self::CACHE_KEY . $this->cacheNamespace . $name; + } + + public function addDefinition(Definition $definition) : void + { + throw new \LogicException('You cannot set a definition at runtime on a container that has caching enabled. Doing so would risk caching the definition for the next execution, where it might be different. You can either put your definitions in a file, remove the cache or ->set() a raw value directly (PHP object, string, int, ...) instead of a PHP-DI definition.'); + } + + private function shouldBeCached(?Definition $definition = null) : bool + { + return + // Cache missing definitions + ($definition === null) + // Object definitions are used with `make()` + || ($definition instanceof ObjectDefinition) + // Autowired definitions cannot be all compiled and are used with `make()` + || ($definition instanceof AutowireDefinition); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/SourceChain.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/SourceChain.php new file mode 100644 index 000000000..a636ce638 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/Source/SourceChain.php @@ -0,0 +1,90 @@ + + */ +class SourceChain implements DefinitionSource, MutableDefinitionSource +{ + private ?MutableDefinitionSource $mutableSource; + + /** + * @param list $sources + */ + public function __construct( + private array $sources, + ) { + } + + /** + * @param int $startIndex Use this parameter to start looking from a specific + * point in the source chain. + */ + public function getDefinition(string $name, int $startIndex = 0) : ?Definition + { + $count = count($this->sources); + for ($i = $startIndex; $i < $count; ++$i) { + $source = $this->sources[$i]; + + $definition = $source->getDefinition($name); + + if ($definition) { + if ($definition instanceof ExtendsPreviousDefinition) { + $this->resolveExtendedDefinition($definition, $i); + } + + return $definition; + } + } + + return null; + } + + public function getDefinitions() : array + { + $allDefinitions = array_merge(...array_map(fn ($source) => $source->getDefinitions(), $this->sources)); + + /** @var string[] $allNames */ + $allNames = array_keys($allDefinitions); + + $allValues = array_filter(array_map(fn ($name) => $this->getDefinition($name), $allNames)); + + return array_combine($allNames, $allValues); + } + + public function addDefinition(Definition $definition) : void + { + if (! $this->mutableSource) { + throw new \LogicException("The container's definition source has not been initialized correctly"); + } + + $this->mutableSource->addDefinition($definition); + } + + private function resolveExtendedDefinition(ExtendsPreviousDefinition $definition, int $currentIndex) + { + // Look in the next sources only (else infinite recursion, and we can only extend + // entries defined in the previous definition files - a previous == next here because + // the array was reversed ;) ) + $subDefinition = $this->getDefinition($definition->getName(), $currentIndex + 1); + + if ($subDefinition) { + $definition->setExtendedDefinition($subDefinition); + } + } + + public function setMutableDefinitionSource(MutableDefinitionSource $mutableSource) : void + { + $this->mutableSource = $mutableSource; + + array_unshift($this->sources, $mutableSource); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/StringDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/StringDefinition.php new file mode 100644 index 000000000..1031be863 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/StringDefinition.php @@ -0,0 +1,90 @@ + + */ +class StringDefinition implements Definition, SelfResolvingDefinition +{ + /** Entry name. */ + private string $name = ''; + + public function __construct( + private string $expression, + ) { + } + + public function getName() : string + { + return $this->name; + } + + public function setName(string $name) : void + { + $this->name = $name; + } + + public function getExpression() : string + { + return $this->expression; + } + + public function resolve(ContainerInterface $container) : string + { + return self::resolveExpression($this->name, $this->expression, $container); + } + + public function isResolvable(ContainerInterface $container) : bool + { + return true; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + // no nested definitions + } + + public function __toString() : string + { + return $this->expression; + } + + /** + * Resolve a string expression. + */ + public static function resolveExpression( + string $entryName, + string $expression, + ContainerInterface $container, + ) : string { + $callback = function (array $matches) use ($entryName, $container) { + /** @psalm-suppress InvalidCatch */ + try { + return $container->get($matches[1]); + } catch (NotFoundExceptionInterface $e) { + throw new DependencyException(sprintf( + "Error while parsing string expression for entry '%s': %s", + $entryName, + $e->getMessage() + ), 0, $e); + } + }; + + $result = preg_replace_callback('#\{([^{}]+)}#', $callback, $expression); + if ($result === null) { + throw new \RuntimeException(sprintf('An unknown error occurred while parsing the string definition: \'%s\'', $expression)); + } + + return $result; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ValueDefinition.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ValueDefinition.php new file mode 100644 index 000000000..7d8f23c16 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Definition/ValueDefinition.php @@ -0,0 +1,60 @@ + + */ +class ValueDefinition implements Definition, SelfResolvingDefinition +{ + /** + * Entry name. + */ + private string $name = ''; + + public function __construct( + private mixed $value, + ) { + } + + public function getName() : string + { + return $this->name; + } + + public function setName(string $name) : void + { + $this->name = $name; + } + + public function getValue() : mixed + { + return $this->value; + } + + public function resolve(ContainerInterface $container) : mixed + { + return $this->getValue(); + } + + public function isResolvable(ContainerInterface $container) : bool + { + return true; + } + + public function replaceNestedDefinitions(callable $replacer) : void + { + // no nested definitions + } + + public function __toString() : string + { + return sprintf('Value (%s)', var_export($this->value, true)); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/DependencyException.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/DependencyException.php new file mode 100644 index 000000000..63b5fdd45 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/DependencyException.php @@ -0,0 +1,14 @@ + + */ +interface RequestedEntry +{ + /** + * Returns the name of the entry that was requested by the container. + */ + public function getName() : string; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/FactoryInterface.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/FactoryInterface.php new file mode 100644 index 000000000..3486b900b --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/FactoryInterface.php @@ -0,0 +1,30 @@ + + */ +interface FactoryInterface +{ + /** + * Resolves an entry by its name. If given a class name, it will return a new instance of that class. + * + * @param string $name Entry name or a class name. + * @param array $parameters Optional parameters to use to build the entry. Use this to force specific + * parameters to specific values. Parameters not defined in this array will + * be automatically resolved. + * + * @throws \InvalidArgumentException The name parameter must be of type string. + * @throws DependencyException Error while resolving the entry. + * @throws NotFoundException No entry or class found for the given name. + */ + public function make(string $name, array $parameters = []) : mixed; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Invoker/DefinitionParameterResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Invoker/DefinitionParameterResolver.php new file mode 100644 index 000000000..6e7e52f0e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Invoker/DefinitionParameterResolver.php @@ -0,0 +1,64 @@ + + */ +class DefinitionParameterResolver implements ParameterResolver +{ + public function __construct( + private DefinitionResolver $definitionResolver, + ) { + } + + public function getParameters( + ReflectionFunctionAbstract $reflection, + array $providedParameters, + array $resolvedParameters, + ) : array { + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $providedParameters = array_diff_key($providedParameters, $resolvedParameters); + } + + foreach ($providedParameters as $key => $value) { + if ($value instanceof DefinitionHelper) { + $value = $value->getDefinition(''); + } + + if (! $value instanceof Definition) { + continue; + } + + $value = $this->definitionResolver->resolve($value); + + if (is_int($key)) { + // Indexed by position + $resolvedParameters[$key] = $value; + } else { + // Indexed by parameter name + // TODO optimize? + $reflectionParameters = $reflection->getParameters(); + foreach ($reflectionParameters as $reflectionParameter) { + if ($key === $reflectionParameter->name) { + $resolvedParameters[$reflectionParameter->getPosition()] = $value; + } + } + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Invoker/FactoryParameterResolver.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Invoker/FactoryParameterResolver.php new file mode 100644 index 000000000..d32580063 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Invoker/FactoryParameterResolver.php @@ -0,0 +1,69 @@ + + * @author Matthieu Napoli + */ +class FactoryParameterResolver implements ParameterResolver +{ + public function __construct( + private ContainerInterface $container, + ) { + } + + public function getParameters( + ReflectionFunctionAbstract $reflection, + array $providedParameters, + array $resolvedParameters, + ) : array { + $parameters = $reflection->getParameters(); + + // Skip parameters already resolved + if (! empty($resolvedParameters)) { + $parameters = array_diff_key($parameters, $resolvedParameters); + } + + foreach ($parameters as $index => $parameter) { + $parameterType = $parameter->getType(); + if (!$parameterType) { + // No type + continue; + } + if (!$parameterType instanceof ReflectionNamedType) { + // Union types are not supported + continue; + } + if ($parameterType->isBuiltin()) { + // Primitive types are not supported + continue; + } + + $parameterClass = $parameterType->getName(); + + if ($parameterClass === 'Psr\Container\ContainerInterface') { + $resolvedParameters[$index] = $this->container; + } elseif ($parameterClass === 'DI\Factory\RequestedEntry') { + // By convention the second parameter is the definition + $resolvedParameters[$index] = $providedParameters[1]; + } elseif ($this->container->has($parameterClass)) { + $resolvedParameters[$index] = $this->container->get($parameterClass); + } + } + + return $resolvedParameters; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/NotFoundException.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/NotFoundException.php new file mode 100644 index 000000000..67dbacd21 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/NotFoundException.php @@ -0,0 +1,14 @@ + + */ +class NativeProxyFactory implements ProxyFactoryInterface +{ + /** + * Creates a new lazy proxy instance of the given class with + * the given initializer. + * + * {@inheritDoc} + */ + public function createProxy(string $className, \Closure $createFunction) : object + { + if (\PHP_VERSION_ID < 80400) { + throw new LogicException('Lazy loading proxies require PHP 8.4 or higher.'); + } + + $reflector = new \ReflectionClass($className); + + return $reflector->newLazyProxy($createFunction); + } + + public function generateProxyClass(string $className) : void + { + // Noop for this type. + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Proxy/ProxyFactory.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Proxy/ProxyFactory.php new file mode 100644 index 000000000..83adb681e --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Proxy/ProxyFactory.php @@ -0,0 +1,91 @@ + + */ +class ProxyFactory implements ProxyFactoryInterface +{ + private ?LazyLoadingValueHolderFactory $proxyManager = null; + + /** + * @param string|null $proxyDirectory If set, write the proxies to disk in this directory to improve performances. + */ + public function __construct( + private ?string $proxyDirectory = null, + ) { + } + + /** + * Creates a new lazy proxy instance of the given class with + * the given initializer. + * + * {@inheritDoc} + */ + public function createProxy(string $className, \Closure $createFunction): object + { + return $this->proxyManager()->createProxy( + $className, + function (& $wrappedObject, $proxy, $method, $params, & $initializer) use ($createFunction) { + $wrappedObject = $createFunction(); + $initializer = null; // turning off further lazy initialization + + return true; + } + ); + } + + /** + * Generates and writes the proxy class to file. + * + * @param class-string $className name of the class to be proxied + */ + public function generateProxyClass(string $className) : void + { + // If proxy classes a written to file then we pre-generate the class + // If they are not written to file then there is no point to do this + if ($this->proxyDirectory) { + $this->createProxy($className, function () {}); + } + } + + private function proxyManager() : LazyLoadingValueHolderFactory + { + if ($this->proxyManager === null) { + if (! class_exists(Configuration::class)) { + throw new \RuntimeException('The ocramius/proxy-manager library is not installed. Lazy injection requires that library to be installed with Composer in order to work. Run "composer require ocramius/proxy-manager:~2.0".'); + } + + $config = new Configuration(); + + if ($this->proxyDirectory) { + $config->setProxiesTargetDir($this->proxyDirectory); + $config->setGeneratorStrategy(new FileWriterGeneratorStrategy(new FileLocator($this->proxyDirectory))); + // @phpstan-ignore-next-line + spl_autoload_register($config->getProxyAutoloader()); + } else { + $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); + } + + $this->proxyManager = new LazyLoadingValueHolderFactory($config); + } + + return $this->proxyManager; + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Proxy/ProxyFactoryInterface.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Proxy/ProxyFactoryInterface.php new file mode 100644 index 000000000..f541f127c --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/Proxy/ProxyFactoryInterface.php @@ -0,0 +1,32 @@ + + */ +interface ProxyFactoryInterface +{ + /** + * Creates a new lazy proxy instance of the given class with + * the given initializer. + * + * @param class-string $className name of the class to be proxied + * @param \Closure $createFunction initializer to be passed to the proxy initializer to be passed to the proxy + */ + public function createProxy(string $className, \Closure $createFunction) : object; + + /** + * If the proxy generator depends on a filesystem component, + * this step writes the proxy for that class to file. Otherwise, + * it is a no-op. + * + * @param class-string $className name of the class to be proxied + */ + public function generateProxyClass(string $className) : void; +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/functions.php b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/functions.php new file mode 100644 index 000000000..43aa9d93b --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/src/functions.php @@ -0,0 +1,154 @@ + decorate(function ($foo, $container) { + * return new CachedFoo($foo, $container->get('cache')); + * }) + * + * @param callable $callable The callable takes the decorated object as first parameter and + * the container as second. + */ + function decorate(callable|array|string $callable) : FactoryDefinitionHelper + { + return new FactoryDefinitionHelper($callable, true); + } +} + +if (! function_exists('DI\get')) { + /** + * Helper for referencing another container entry in an object definition. + */ + function get(string $entryName) : Reference + { + return new Reference($entryName); + } +} + +if (! function_exists('DI\env')) { + /** + * Helper for referencing environment variables. + * + * @param string $variableName The name of the environment variable. + * @param mixed $defaultValue The default value to be used if the environment variable is not defined. + */ + function env(string $variableName, mixed $defaultValue = null) : EnvironmentVariableDefinition + { + // Only mark as optional if the default value was *explicitly* provided. + $isOptional = 2 === func_num_args(); + + return new EnvironmentVariableDefinition($variableName, $isOptional, $defaultValue); + } +} + +if (! function_exists('DI\add')) { + /** + * Helper for extending another definition. + * + * Example: + * + * 'log.backends' => DI\add(DI\get('My\Custom\LogBackend')) + * + * or: + * + * 'log.backends' => DI\add([ + * DI\get('My\Custom\LogBackend') + * ]) + * + * @param mixed|array $values A value or an array of values to add to the array. + * + * @since 5.0 + */ + function add($values) : ArrayDefinitionExtension + { + if (! is_array($values)) { + $values = [$values]; + } + + return new ArrayDefinitionExtension($values); + } +} + +if (! function_exists('DI\string')) { + /** + * Helper for concatenating strings. + * + * Example: + * + * 'log.filename' => DI\string('{app.path}/app.log') + * + * @param string $expression A string expression. Use the `{}` placeholders to reference other container entries. + * + * @since 5.0 + */ + function string(string $expression) : StringDefinition + { + return new StringDefinition($expression); + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/php-di/php-di/support.md b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/support.md new file mode 100644 index 000000000..22dd39ae5 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/php-di/php-di/support.md @@ -0,0 +1,42 @@ +--- +layout: documentation +current_menu: enterprise-support +title: Enterprise support for PHP-DI +--- + +# PHP-DI for Enterprise + +> *Available as part of the Tidelift Subscription* + +Tidelift is working with the maintainers of PHP-DI and thousands of other +open source projects to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. + +#### [Learn more](https://tidelift.com/subscription/pkg/packagist-php-di-php-di?utm_source=packagist-php-di-php-di&utm_medium=referral&utm_campaign=enterprise) | [**Request a demo**](https://tidelift.com/subscription/request-a-demo?utm_source=packagist-php-di-php-di&utm_medium=referral&utm_campaign=enterprise) + +## Enterprise-ready open source software—managed for you + +The Tidelift Subscription is a managed open source subscription for application dependencies covering millions of open source projects across JavaScript, Python, Java, PHP, Ruby, .NET, and more. + +Your subscription includes: + +- **Security updates** + Tidelift’s security response team coordinates patches for new breaking security vulnerabilities and alerts immediately through a private channel, so your software supply chain is always secure. + +- **Licensing verification and indemnification** + Tidelift verifies license information to enable easy policy enforcement and adds intellectual property indemnification to cover creators and users in case something goes wrong. You always have a 100% up-to-date bill of materials for your dependencies to share with your legal team, customers, or partners. + +- **Maintenance and code improvement** + Tidelift ensures the software you rely on keeps working as long as you need it to work. Your managed dependencies are actively maintained and we recruit additional maintainers where required. + +- **Package selection and version guidance** + We help you choose the best open source packages from the start—and then guide you through updates to stay on the best releases as new issues arise. + +- **Roadmap input** + Take a seat at the table with the creators behind the software you use. Tidelift’s participating maintainers earn more income as their software is used by more subscribers, so they’re interested in knowing what you need. + +- **Tooling and cloud integration** + Tidelift works with GitHub, GitLab, BitBucket, and more. We support every cloud platform (and other deployment targets, too). + +The end result? All of the capabilities you expect from commercial-grade software, for the full breadth of open source you use. That means less time grappling with esoteric open source trivia, and more time building your own applications—and your business. + +[Learn more](https://tidelift.com/subscription/pkg/packagist-php-di-php-di?utm_source=packagist-php-di-php-di&utm_medium=referral&utm_campaign=enterprise) | [**Request a demo**](https://tidelift.com/subscription/request-a-demo?utm_source=packagist-php-di-php-di&utm_medium=referral&utm_campaign=enterprise) diff --git a/wp-content/plugins/pattern-sync/vendor/psr/container/.gitignore b/wp-content/plugins/pattern-sync/vendor/psr/container/.gitignore new file mode 100644 index 000000000..b2395aa05 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/psr/container/.gitignore @@ -0,0 +1,3 @@ +composer.lock +composer.phar +/vendor/ diff --git a/wp-content/plugins/pattern-sync/vendor/psr/container/LICENSE b/wp-content/plugins/pattern-sync/vendor/psr/container/LICENSE new file mode 100644 index 000000000..2877a4894 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/psr/container/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 container-interop +Copyright (c) 2016 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wp-content/plugins/pattern-sync/vendor/psr/container/README.md b/wp-content/plugins/pattern-sync/vendor/psr/container/README.md new file mode 100644 index 000000000..1b9d9e570 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/psr/container/README.md @@ -0,0 +1,13 @@ +Container interface +============== + +This repository holds all interfaces related to [PSR-11 (Container Interface)][psr-url]. + +Note that this is not a Container implementation of its own. It is merely abstractions that describe the components of a Dependency Injection Container. + +The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist. + +[psr-url]: https://www.php-fig.org/psr/psr-11/ +[package-url]: https://packagist.org/packages/psr/container +[implementation-url]: https://packagist.org/providers/psr/container-implementation + diff --git a/wp-content/plugins/pattern-sync/vendor/psr/container/composer.json b/wp-content/plugins/pattern-sync/vendor/psr/container/composer.json new file mode 100644 index 000000000..baf6cd1a0 --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/psr/container/composer.json @@ -0,0 +1,27 @@ +{ + "name": "psr/container", + "type": "library", + "description": "Common Container Interface (PHP FIG PSR-11)", + "keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"], + "homepage": "https://github.com/php-fig/container", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=7.4.0" + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/wp-content/plugins/pattern-sync/vendor/psr/container/src/ContainerExceptionInterface.php b/wp-content/plugins/pattern-sync/vendor/psr/container/src/ContainerExceptionInterface.php new file mode 100644 index 000000000..0f213f2fe --- /dev/null +++ b/wp-content/plugins/pattern-sync/vendor/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,12 @@ +