diff --git a/.distignore b/.distignore new file mode 100644 index 0000000..aca5ad9 --- /dev/null +++ b/.distignore @@ -0,0 +1,26 @@ +# Version control +.git +.github +.gitignore +.distignore + +# Development +tests +phpunit.xml +composer.json +composer.lock +DEVELOPMENT.md +API.md + +# Build tools +node_modules +.env +.env.* + +# IDE +.vscode +.idea +.DS_Store + +# Logs +*.log diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c8a4517 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "ci" + include: "scope" + open-pull-requests-limit: 5 + + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "deps" + include: "scope" + open-pull-requests-limit: 5 diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index de5d44d..2c44796 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -1,39 +1,52 @@ -name: PHP Composer +name: CI on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] permissions: contents: read jobs: - build: - + test: + name: Test (PHP ${{ matrix.php }}) runs-on: ubuntu-latest + strategy: + matrix: + php: ["8.2", "8.3", "8.4", "8.5"] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Install dependencies + run: composer install --prefer-dist --no-progress - - name: Validate composer.json and composer.lock - run: composer validate --strict + - name: Run tests + run: ./vendor/bin/pest - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v3 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- + format-check: + name: Check Code Style + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 - - name: Install dependencies - run: composer install --prefer-dist --no-progress + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.2" - # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" - # Docs: https://getcomposer.org/doc/articles/scripts.md + - name: Install dependencies + run: composer install --prefer-dist --no-progress - - name: Run test suite - run: ./vendor/bin/pest + - name: Check formatting + run: composer format:check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1e99454 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,33 @@ +name: Release + +on: + push: + tags: + - "v*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.2" + + - name: Install production dependencies + run: composer install --no-dev --optimize-autoloader + + - name: Create release zip + run: | + mkdir -p release + rsync -av --exclude-from=.distignore . release/hellotext-wordpress/ + cd release + zip -r hellotext-wordpress.zip hellotext-wordpress + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: release/hellotext-wordpress.zip + generate_release_notes: true diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache new file mode 100644 index 0000000..32b6755 --- /dev/null +++ b/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.4.8","version":"3.92.5:v3.92.5#260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":{"default":"single_space"},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"classes_opening_brace":"same_line","functions_opening_brace":"same_line"},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"modifier_keywords":true,"new_with_parentheses":{"anonymous_class":true},"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["extra","throw","use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":{"space_before":"none"},"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":{"closure_fn_spacing":"one"},"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"trailing_comma_in_multiline":true,"single_quote":true,"no_unused_imports":true,"concat_space":{"spacing":"one"},"method_chaining_indentation":true,"no_spaces_around_offset":true,"no_whitespace_before_comma_in_array":true,"whitespace_after_comma_in_array":true,"phpdoc_scalar":true,"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"phpdoc_align":{"align":"left"},"standardize_not_equals":true,"no_unneeded_control_parentheses":true,"no_unneeded_braces":true},"ruleCustomisationPolicyVersion":"null-policy","hashes":{"src\/Misc\/Settings.php":"5e71bd024792e74350c4f1fabcab8065","src\/Misc\/Scripts.php":"e6cad7cd6433d891904071e795d7b0b5","src\/Constants.php":"d7e09100a92074fca817f5e422cf49fb","src\/Adapters\/RefundAdapter.php":"b28b6632847c1a62156d56b54f9f7c21","src\/Adapters\/ProductAdapter.php":"cc0aca0113a5011e83c87ffee4e76b2f","src\/Adapters\/PriceAdapter.php":"f745ecde0eb052560f2db758e1c3122e","src\/Adapters\/OrderAdapter.php":"3ca1bd395d356e3b75e0ceea176cb524","src\/Api\/Event.php":"d3b4144a54d899c86095fb0d9e33e7b6","src\/Api\/Webchat.php":"ef03b3ca436f233ea7ffd4e1bff0f576","src\/Api\/Client.php":"b7c6f6762ab21fcd3fd3639420901b37","src\/Events\/CartUpdates.php":"72e4d163177b381fabc2d683fd104542","src\/Events\/OrderPlaced.php":"1b39dc7198082d8189ce509041f18757","src\/Events\/OrderStatus.php":"6d120b5401afb4f186e9fac1da425bf5","src\/Events\/RefundReceived.php":"a84421a12bf84435de14aeae1442a4a7","src\/Events\/AppInstalled.php":"058e8a7e9aa362b58aa6322f562c30fe","src\/Events\/UserRegistered.php":"f228476df530692be96fb1bb89fd9a79","src\/Events\/CustomOptionsUpdated.php":"cdd61280578691aa394a98d5ad953779","src\/Events\/ProductViewed.php":"8b467111ef300987a48a227a3f8b2a81","src\/Events\/CouponRedeemed.php":"63a56657cfe882ab05a943ae455fe74d","src\/Events\/AppRemoved.php":"993fd99a136255b9261dc8c6460f175f","src\/Services\/Session.php":"5f112c07423d60c7d5f6ece58fbceed0","src\/Services\/CreateProfile.php":"2c3cc60eb9f6e4681e6b931ebdff5a40"}} \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..ee7ebd6 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,68 @@ +in(__DIR__ . '/src') + ->name('*.php'); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PSR12' => true, + + // Array/Hash syntax (like Ruby's modern syntax) + 'array_syntax' => ['syntax' => 'short'], + 'trailing_comma_in_multiline' => true, + + // String literals (single quotes like Ruby) + 'single_quote' => true, + + // Imports/Use statements (alphabetically ordered) + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'no_unused_imports' => true, + + // Spacing rules (match Ruby spacing preferences) + 'binary_operator_spaces' => ['default' => 'single_space'], + 'concat_space' => ['spacing' => 'one'], + 'method_chaining_indentation' => true, + 'no_spaces_around_offset' => true, + 'no_whitespace_before_comma_in_array' => true, + 'whitespace_after_comma_in_array' => true, + + // Empty lines and whitespace + 'no_extra_blank_lines' => [ + 'tokens' => [ + 'extra', + 'throw', + 'use', + ], + ], + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'single_blank_line_at_eof' => true, + + // Braces (your preference) + 'braces_position' => [ + 'classes_opening_brace' => 'same_line', + 'functions_opening_brace' => 'same_line', + ], + + // Method/Function definitions + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + ], + 'return_type_declaration' => ['space_before' => 'none'], + + // PHPDoc + 'phpdoc_scalar' => true, + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'phpdoc_align' => ['align' => 'left'], + + // Operators + 'not_operator_with_successor_space' => false, + 'standardize_not_equals' => true, + + // Control structures + 'no_unneeded_control_parentheses' => true, + 'no_unneeded_braces' => true, + ]) + ->setFinder($finder); diff --git a/API.md b/API.md new file mode 100644 index 0000000..d5f689d --- /dev/null +++ b/API.md @@ -0,0 +1,470 @@ +# Hellotext WordPress Plugin - API Documentation + +## Overview + +This document provides comprehensive API documentation for the Hellotext WordPress plugin. The plugin integrates WooCommerce with Hellotext's customer engagement platform, tracking customer activities and synchronizing profiles. + +## Table of Contents + +- [Core Concepts](#core-concepts) +- [HTTP Client](#http-client) +- [Event Tracking](#event-tracking) +- [Adapters](#adapters) +- [Services](#services) +- [Constants Reference](#constants-reference) +- [WordPress Hooks](#wordpress-hooks) + +## Core Concepts + +### Architecture + +The plugin follows a clean architecture pattern with distinct layers: + +- **Api/** - HTTP clients for external communication +- **Adapters/** - Transform WooCommerce data to Hellotext payloads +- **Events/** - Event handlers for WooCommerce hooks +- **Services/** - Business logic for profile and session management +- **Misc/** - WordPress integration (settings, scripts) + +### Session Management + +The plugin uses a cookie-based session system (`hello_session`) to track anonymous users and associate their activities with Hellotext profiles. + +## HTTP Client + +### `Hellotext\Api\Client` + +The main HTTP client for communicating with the Hellotext API. + +#### Static Methods + +##### `request(string $method, string $path, array $data = []): array` + +Makes an HTTP request to the Hellotext API. + +**Parameters:** +- `$method` - HTTP method (GET, POST, PATCH, PUT, DELETE) +- `$path` - API endpoint path +- `$data` - Request payload + +**Returns:** +```php +[ + 'request' => [ + 'method' => string, + 'path' => string, + 'data' => array + ], + 'status' => int, // HTTP status code + 'body' => array|null // Decoded JSON response +] +``` + +**Example:** +```php +use Hellotext\Api\Client; +use Hellotext\Constants; + +$response = Client::post(Constants::API_ENDPOINT_PROFILES, [ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'email' => 'john@example.com' +]); +``` + +##### Convenience Methods + +- `get(string $path, ?array $data = null): array` +- `post(string $path, ?array $data = null): array` +- `patch(string $path, ?array $data = null): array` +- `put(string $path, ?array $data = null): array` +- `delete(string $path, ?array $data = null): array` + +##### `with_sufix(string $sufix = ''): self` + +Creates a client instance with a custom API suffix. Useful for testing. + +**Example:** +```php +$client = Client::with_sufix('/v2'); +``` + +## Event Tracking + +### `Hellotext\Api\Event` + +Tracks customer activities and sends them to Hellotext. + +#### Constructor + +```php +public function __construct(?string $session = null) +``` + +Creates a new event tracker. If no session is provided, it attempts to retrieve it from the `hello_session` cookie. + +#### Methods + +##### `track(string $action, array $payload): void` + +Tracks an event with the given action and payload. + +**Parameters:** +- `$action` - Event action name (use constants from `Constants::EVENT_*`) +- `$payload` - Event data + +**Example:** +```php +use Hellotext\Api\Event; +use Hellotext\Constants; + +$event = new Event(); +$event->track(Constants::EVENT_PRODUCT_VIEWED, [ + 'object_parameters' => [ + 'reference' => '123', + 'name' => 'Product Name', + 'price' => ['amount' => 2999, 'currency' => 'USD'] + ] +]); +``` + +## Adapters + +Adapters transform WooCommerce objects into Hellotext-compatible payloads. + +### `Hellotext\Adapters\ProductAdapter` + +Transforms WooCommerce products. + +#### Constructor + +```php +public function __construct(int|\WC_Product $product) +``` + +Accepts either a product ID or a `WC_Product` instance. + +#### Methods + +##### `get(): array` + +Returns the adapted product payload. + +**Returns:** +```php +[ + 'reference' => int, // Product ID + 'source' => 'woo', + 'name' => string, + 'categories' => string[], + 'price' => [ + 'amount' => int, // Price in cents + 'currency' => string + ], + 'tags' => string[], + 'image_url' => string, + 'url' => string // Product permalink +] +``` + +**Example:** +```php +use Hellotext\Adapters\ProductAdapter; + +$adapter = new ProductAdapter($product_id); +$payload = $adapter->get(); +``` + +### `Hellotext\Adapters\OrderAdapter` + +Transforms WooCommerce orders. + +#### Constructor + +```php +public function __construct(\WC_Order $order) +``` + +#### Methods + +##### `get(): array` + +Returns the adapted order payload. + +**Returns:** +```php +[ + 'reference' => int, // Order ID + 'source' => 'woo', + 'status' => string, // Order status + 'subtotal' => ['amount' => int, 'currency' => string], + 'total' => ['amount' => int, 'currency' => string], + 'tax' => ['amount' => int, 'currency' => string], + 'shipping' => ['amount' => int, 'currency' => string], + 'discount' => ['amount' => int, 'currency' => string], + 'items' => array[], // Order items + 'url' => string +] +``` + +### `Hellotext\Adapters\RefundAdapter` + +Transforms WooCommerce refunds. + +#### Constructor + +```php +public function __construct(\WC_Order_Refund $refund) +``` + +#### Methods + +##### `get(): array` + +Returns the adapted refund payload. + +### `Hellotext\Adapters\PriceAdapter` + +Converts prices to Hellotext format (cents-based). + +#### Constructor + +```php +public function __construct(float|string|null $price) +``` + +#### Methods + +##### `get(): array` + +Returns price in cents with currency. + +**Returns:** +```php +[ + 'amount' => int, // Price in cents + 'currency' => string // e.g., 'USD' +] +``` + +## Services + +### `Hellotext\Services\CreateProfile` + +Manages Hellotext profile creation and association. + +#### Constructor + +```php +public function __construct(?int $user_id, array $billing = []) +``` + +**Parameters:** +- `$user_id` - WordPress user ID (null for guest checkout) +- `$billing` - Billing data from checkout + +#### Methods + +##### `process(): void` + +Executes the profile creation/association flow: +1. Checks if profile exists +2. Creates new profile if needed +3. Associates profile with session +4. Updates session metadata + +**Example:** +```php +use Hellotext\Services\CreateProfile; + +$service = new CreateProfile($user->ID); +$service->process(); + +// For guest checkout with billing data +$service = new CreateProfile(null, [ + 'first_name' => 'Jane', + 'last_name' => 'Smith', + 'email' => 'jane@example.com', + 'phone' => '+1234567890' +]); +$service->process(); +``` + +### `Hellotext\Services\Session` + +Manages session encryption and cookies. + +#### Static Methods + +##### `create(): string` + +Creates a new session identifier. + +**Returns:** UUID session string + +##### `encrypt(string $session): string` + +Encrypts a session string using AES-256-CBC. + +##### `decrypt(string $encrypted_session): string` + +Decrypts an encrypted session string. + +##### `set_cookie(string $session): void` + +Sets the `hello_session` cookie with proper expiration and security flags. + +**Example:** +```php +use Hellotext\Services\Session; + +$session = Session::create(); +Session::set_cookie($session); + +// Later, encrypt for storage +$encrypted = Session::encrypt($session); +add_post_meta($order_id, 'hellotext_session', $encrypted); +``` + +## Constants Reference + +### `Hellotext\Constants` + +All plugin constants are centralized in this class. + +#### API Configuration + +```php +Constants::API_VERSION // 'v1' +Constants::API_ENDPOINT_TRACK // '/v1/track/events' +Constants::API_ENDPOINT_PROFILES // '/profiles' +Constants::API_ENDPOINT_SESSIONS // '/sessions' +Constants::API_ENDPOINT_WEBCHATS // '/v1/wordpress/webchats' +Constants::API_ENDPOINT_INTEGRATIONS_WOO // '/integrations/woo' +``` + +#### Session & Encryption + +```php +Constants::ENCRYPTION_METHOD // 'aes-256-cbc' +Constants::SESSION_COOKIE_NAME // 'hello_session' +``` + +#### WordPress Options + +```php +Constants::OPTION_BUSINESS_ID // 'hellotext_business_id' +Constants::OPTION_ACCESS_TOKEN // 'hellotext_access_token' +Constants::OPTION_WEBCHAT_ID // 'hellotext_webchat_id' +Constants::OPTION_WEBCHAT_PLACEMENT // 'hellotext_webchat_placement' +Constants::OPTION_WEBCHAT_BEHAVIOUR // 'hellotext_webchat_behaviour' +``` + +#### User Meta Keys + +```php +Constants::META_PROFILE_ID // 'hellotext_profile_id' +Constants::META_SESSION // 'hellotext_session' +``` + +#### Event Names + +```php +Constants::EVENT_ORDER_PLACED // 'order.placed' +Constants::EVENT_ORDER_CONFIRMED // 'order.confirmed' +Constants::EVENT_ORDER_CANCELLED // 'order.cancelled' +Constants::EVENT_ORDER_DELIVERED // 'order.delivered' +Constants::EVENT_PRODUCT_PURCHASED // 'product.purchased' +Constants::EVENT_PRODUCT_VIEWED // 'product.viewed' +Constants::EVENT_CART_ADDED // 'cart.added' +Constants::EVENT_CART_REMOVED // 'cart.removed' +Constants::EVENT_REFUND_RECEIVED // 'refund.received' +Constants::EVENT_COUPON_REDEEMED // 'coupon.redeemed' +``` + +## WordPress Hooks + +### Actions + +#### `hellotext_create_profile` + +Triggers profile creation/association. + +**Parameters:** +- `$payload` - Either user ID (int) or billing data (array) + +**Usage:** +```php +// For logged-in user +do_action('hellotext_create_profile', $user_id); + +// For guest with billing data +do_action('hellotext_create_profile', [ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'email' => 'john@example.com', + 'phone' => '+1234567890' +]); +``` + +### WooCommerce Integration Hooks + +The plugin automatically hooks into these WooCommerce actions: + +- `woocommerce_after_order_details` - Track order placement +- `woocommerce_order_status_changed` - Track order status updates +- `woocommerce_order_refunded` - Track refunds +- `woocommerce_add_to_cart` - Track cart additions +- `woocommerce_cart_item_removed` - Track cart removals +- `woocommerce_applied_coupon` - Track coupon usage +- `wp_footer` - Inject tracking scripts and webchat + +## Testing + +For testing information, see [DEVELOPMENT.md](DEVELOPMENT.md). + +## Environment Variables + +The plugin respects the following environment variables: + +- `APP_ENV` - Environment mode: `production`, `development`, or `test` +- `HELLOTEXT_API_URL` - Override API URL (development only) + +**Example (.htaccess):** +```apache +SetEnv APP_ENV development +SetEnv HELLOTEXT_API_URL https://api-dev.hellotext.com +``` + +## Error Handling + +Most methods that interact with external APIs will throw exceptions on failure. Always wrap critical API calls in try-catch blocks: + +```php +try { + $adapter = new ProductAdapter($product_id); + $payload = $adapter->get(); +} catch (\Exception $e) { + error_log('Hellotext: Failed to adapt product - ' . $e->getMessage()); +} +``` + +## Security + +### Authentication + +All API requests include an `Authorization: Bearer` header with either: +- **Business ID** for event tracking +- **Access Token** for profile/session management + +### Session Encryption + +Sessions are encrypted using AES-256-CBC before storage in the database. The encryption key is derived from WordPress security keys. + +### Data Sanitization + +All user input is sanitized using WordPress functions (`sanitize_text_field()`, etc.) before processing. + +## License + +This plugin is licensed under GPL v2. See [LICENSE](LICENSE) for details. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..10af1bb --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,492 @@ +# Hellotext WordPress Plugin - Development Guide + +## Overview + +This guide covers development setup, testing, code standards, and contribution guidelines for the Hellotext WordPress plugin. + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Development Setup](#development-setup) +- [Project Structure](#project-structure) +- [Code Standards](#code-standards) +- [Testing](#testing) +- [Debugging](#debugging) +- [Build & Release](#build--release) +- [Contributing](#contributing) + +## Prerequisites + +### Required Software + +- **PHP**: 8.2.12 or higher +- **Composer**: Latest version +- **WordPress**: 5.0 or higher +- **WooCommerce**: 5.0 or higher +- **Local Development Environment**: + - Local by Flywheel + - MAMP/XAMPP + - Docker (wp-env) + - Or similar + +## Development Setup + +### 1. Clone the Repository + +```bash +cd wp-content/plugins/ +git clone https://github.com/hellotext/hellotext-wordpress.git +cd hellotext-wordpress +``` + +### 2. Install Dependencies + +```bash +composer install +``` + +This installs: +- Pest (testing framework) +- Mockery (mocking library) +- WordPress & WooCommerce stubs (for IDE autocomplete) + +### 3. Configure Environment + +Create or modify your WordPress configuration to set environment variables. + +**Option A: wp-config.php** + +```php +// Add before "That's all, stop editing!" +$_ENV['APP_ENV'] = 'development'; +$_ENV['HELLOTEXT_API_URL'] = 'https://api-dev.hellotext.com'; +``` + +**Option B: .htaccess** + +```apache +SetEnv APP_ENV development +SetEnv HELLOTEXT_API_URL https://api-dev.hellotext.com +``` + +**Option C: Server Configuration** + +For Local by Flywheel, add to site configuration or use `.env` file if supported. + +### 4. Activate Plugin + +1. Navigate to WordPress admin → Plugins +2. Activate "Hellotext" +3. Configure with development Business ID and Access Token + +### 5. Enable Debugging + +In `wp-config.php`: + +```php +define('WP_DEBUG', true); +define('WP_DEBUG_LOG', true); +define('WP_DEBUG_DISPLAY', false); +``` + +This logs errors to `wp-content/debug.log`. + +## Code Standards + +### PHP Standards + +- **PHP Version**: 8.2+ +- **Namespace**: `Hellotext\` +- **Coding Style**: WordPress Coding Standards with modern PHP + +### Type Hints + +All methods must have type hints for parameters and return types: + +```php +public function process(?int $user_id, array $data = []): void +{ + // Implementation +} +``` + +### PHPDoc Comments + +All classes and public methods must have PHPDoc comments: + +```php +/** + * Create a Hellotext profile. + * + * @param int $user_id WordPress user ID. + * @param array $data Additional profile data. + * @return array Profile response from API. + * @throws \Exception If user not found. + */ +public function create(int $user_id, array $data = []): array +{ + // Implementation +} +``` + +### Constants Usage + +Always use constants from the `Constants` class instead of magic strings: + +```php +// ✅ Good +$session = $_COOKIE[Constants::SESSION_COOKIE_NAME]; +$response = Client::post(Constants::API_ENDPOINT_PROFILES, $data); + +// ❌ Bad +$session = $_COOKIE['hello_session']; +$response = Client::post('/profiles', $data); +``` + +### Error Handling + +Use exceptions for error conditions and log appropriately: + +```php +try { + $adapter = new ProductAdapter($product_id); + $payload = $adapter->get(); +} catch (\Exception $e) { + error_log('Hellotext: ' . $e->getMessage()); + return; +} +``` + +## Testing + +The project uses [Pest](https://pestphp.com/) for testing. + +### Running Tests + +```bash +# Run all tests +./vendor/bin/pest + +# Run specific test file +./vendor/bin/pest tests/Unit/Adapters/ProductAdapterTest.php + +# Run with coverage (requires Xdebug) +./vendor/bin/pest --coverage + +# Run in parallel +./vendor/bin/paratest +``` + +### Writing Tests + +#### Unit Tests + +Unit tests are located in `tests/Unit/`. Each test file corresponds to a source file. + +**Example: Testing an Adapter** + +```php +product = Mockery::mock('WC_Product'); + $this->product->shouldReceive('get_id')->andReturn(123); + $this->product->shouldReceive('get_name')->andReturn('Test Product'); + // ... more mocks +}); + +test('ProductAdapter transforms product correctly', function () { + $adapter = new ProductAdapter($this->product); + $result = $adapter->get(); + + expect($result) + ->toHaveKey('reference', 123) + ->toHaveKey('name', 'Test Product') + ->toHaveKey('source', 'woo'); +}); + +test('ProductAdapter throws exception for invalid product', function () { + $adapter = new ProductAdapter(99999); + $adapter->get(); // Should throw +})->throws(\Exception::class); +``` + +#### Test Structure + +Use Pest's modern syntax: + +```php +// Arrange +$data = ['key' => 'value']; + +// Act +$result = (new Service())->process($data); + +// Assert +expect($result)->toBe('expected'); +``` + +#### Mocking + +Use Mockery for mocking WordPress and WooCommerce functions: + +```php +beforeEach(function () { + // Mock WordPress functions + Mockery::mock('function:get_option') + ->shouldReceive('get_option') + ->with('hellotext_business_id') + ->andReturn('test_business_id'); +}); + +afterEach(function () { + Mockery::close(); +}); +``` + +### Test Coverage Goals + +- **Unit Tests**: All Adapters and Services +- **Integration Tests**: Key user flows (order placement, profile creation) +- **Coverage Target**: 80%+ for critical paths + +## Debugging + +### Debug Logging + +Enable WordPress debug logging and use `error_log()`: + +```php +error_log('Hellotext Debug: ' . print_r($data, true)); +``` + +Logs appear in `wp-content/debug.log`. + +### API Debugging + +To inspect API requests/responses: + +```php +$response = Client::post('/profiles', $data); +error_log('API Response: ' . print_r($response, true)); +``` + +### Event Tracking Debugging + +To verify events are being tracked: + +```php +add_filter('hellotext_event_payload', function($payload) { + error_log('Event Payload: ' . print_r($payload, true)); + return $payload; +}); +``` + +### WordPress Hooks Debug + +Use `add_action()` to monitor hook execution: + +```php +add_action('all', function($hook) { + if (strpos($hook, 'woocommerce') !== false) { + error_log('Hook fired: ' . $hook); + } +}); +``` + +## Build & Release + +### Version Bump + +1. Update version in `hellotext.php` header comment +2. Update `changelog.txt` with changes +3. Commit changes + +```php +/** + * Version: 1.3.0 + */ +``` + +### Pre-Release Checklist + +- [ ] Run all tests: `./vendor/bin/pest` +- [ ] Check for PHP errors/warnings +- [ ] Test on fresh WordPress installation +- [ ] Test with WooCommerce latest version +- [ ] Verify settings page functionality +- [ ] Test event tracking in Hellotext dashboard +- [ ] Update documentation if API changed +- [ ] Update changelog.txt + +### Creating a Release + +1. **Tag the release:** +```bash +git tag -a v1.3.0 -m "Release version 1.3.0" +git push origin v1.3.0 +``` + +2. **Build release package:** +```bash +# Remove dev dependencies +composer install --no-dev + +# Create zip +cd .. +zip -r hellotext-wordpress-1.3.0.zip hellotext-wordpress \ + -x "hellotext-wordpress/.git/*" \ + -x "hellotext-wordpress/tests/*" \ + -x "hellotext-wordpress/node_modules/*" +``` + +3. **Create GitHub release:** + - Go to Releases → Draft new release + - Select the tag + - Upload the zip file + - Add release notes from changelog + +### Post-Release + +1. Reinstall dev dependencies: `composer install` +2. Announce release to team +3. Monitor error logs for issues + +## Contributing + +### Workflow + +1. **Fork & Clone** +```bash +git clone https://github.com/YOUR_USERNAME/hellotext-wordpress.git +cd hellotext-wordpress +composer install +``` + +2. **Create Feature Branch** +```bash +git checkout -b feature/my-new-feature +``` + +3. **Make Changes** + - Write code + - Add tests + - Update documentation + +4. **Run Tests** +```bash +./vendor/bin/pest +``` + +5. **Commit** +```bash +git add . +git commit -m "feat: add new feature" +``` + +Use [Conventional Commits](https://www.conventionalcommits.org/): +- `feat:` - New feature +- `fix:` - Bug fix +- `docs:` - Documentation +- `test:` - Tests +- `refactor:` - Code refactoring + +6. **Push & PR** +```bash +git push origin feature/my-new-feature +``` + +Then create a Pull Request on GitHub. + +### Code Review Checklist + +- [ ] Code follows WordPress coding standards +- [ ] All functions have type hints +- [ ] PHPDoc comments present and accurate +- [ ] Tests written and passing +- [ ] No hardcoded strings (use Constants) +- [ ] Proper error handling +- [ ] WordPress/WooCommerce hooks used correctly +- [ ] Security best practices followed + +## Common Development Tasks + +### Adding a New Event + +1. Create event file in `src/Events/` +2. Hook into appropriate WooCommerce action +3. Use `Event` class to track +4. Add event constant to `Constants.php` +5. Write test in `tests/Unit/Events/` + +**Example:** + +```php +track(Constants::EVENT_NEW_ACTION, [ + 'object_parameters' => $data + ]); +} +``` + +### Adding a New Adapter + +1. Create adapter file in `src/Adapters/` +2. Implement `get(): array` method +3. Write comprehensive test +4. Document in API.md + +### Adding Configuration Option + +1. Add constant to `Constants.php` +2. Add field to `Settings.php` admin page +3. Use `get_option(Constants::OPTION_NAME)` to retrieve + +## Troubleshooting + +### Tests Failing + +**Issue**: Mockery errors or WordPress function not found + +**Solution**: Ensure WordPress and WooCommerce stubs are installed: +```bash +composer require --dev php-stubs/wordpress-stubs php-stubs/woocommerce-stubs +``` + +### Session Cookie Not Setting + +**Issue**: `hello_session` cookie not being created + +**Solution**: Check that `session_start()` is called early in plugin load and cookie settings (domain, path) are correct. + +### API Requests Failing + +**Issue**: 401 Unauthorized errors + +**Solution**: Verify Business ID and Access Token are set correctly in WordPress admin → Extensions → Hellotext. + +## Resources + +- [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/) +- [WooCommerce Developer Documentation](https://woocommerce.github.io/code-reference/) +- [Pest Documentation](https://pestphp.com/docs/) +- [Hellotext API Documentation](https://docs.hellotext.com/) + +## License + +GPL v2 - See [LICENSE](LICENSE) file. + +## Support + +- **Issues**: [GitHub Issues](https://github.com/hellotext/hellotext-wordpress/issues) +- **Email**: support@hellotext.com +- **Documentation**: [API.md](API.md) diff --git a/README.md b/README.md index a9a96c3..9f0a551 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Once installed, click on *Activate Plugin* to activate it. -Your plugin is now activated. You can check that it has been correctly installed and activated from *Plugins* by clicking on *Installed Plugins*. +Your plugin is now activated. You can check that it has been correctly installed and activated from *Plugins* by clicking on *Installed Plugins*. You should see Hellotext as shown in the image below. @@ -55,9 +55,9 @@ You should see Hellotext as shown in the image below. With the Hellotext plugin already installed, let's configure it. -The first thing you need to do is obtain your business identifier in Hellotext. +The first thing you need to do is obtain your business identifier in Hellotext. -From your Hellotext business panel, visit the *Settings* section and you will find it below your business name. +From your Hellotext business panel, visit the *Settings* section and you will find it below your business name. Select and copy this identifier as you will need to add it to your WooCommerce site. @@ -83,7 +83,7 @@ Once created, click on the indicated icon to copy your created token. -Now, go back to your WooCommerce panel and paste the authorization token into the *Access Token* field. +Now, go back to your WooCommerce panel and paste the authorization token into the *Access Token* field. Click on *Save Changes* to save the changes. @@ -93,12 +93,44 @@ Done! Your WooCommerce store is now connected to Hellotext. ## Next Steps -From now on, you will start seeing your customers' activity reflected in the Audience section or from the Inbox. +From now on, you will start seeing your customers' activity reflected in the Audience section or from the Inbox. -You can click on each profile to see the activity history they perform in your eCommerce. +You can click on each profile to see the activity history they perform in your eCommerce. With this information, you can create segments for sending campaigns and automations from the Routes section. +## Tracked Events + +The plugin automatically tracks the following customer activities: + +- **Product Viewed** - When a customer views a product page +- **Cart Added/Removed** - Items added to or removed from cart +- **Coupon Redeemed** - When a customer applies a coupon code +- **Order Placed** - When an order is successfully created +- **Order Status Updates** - Order confirmation, cancellation, delivery +- **Refund Received** - When orders are refunded +- **User Registration** - When new customers register + +## Developer Documentation + +For developers looking to integrate, extend, or contribute to this plugin: + +- **[API Documentation](API.md)** - Complete API reference for classes, methods, and hooks +- **[Development Guide](DEVELOPMENT.md)** - Setup, testing, and contribution guidelines +### Requirements + +- PHP 8.2 or higher +- WordPress 5.0 or higher +- WooCommerce 5.0 or higher + +## Support + +Need help? + +- **Documentation**: See [API.md](API.md) and [DEVELOPMENT.md](DEVELOPMENT.md) +- **Issues**: [GitHub Issues](https://github.com/hellotext/hellotext-wordpress/issues) +- **Website**: [www.hellotext.com](https://www.hellotext.com) + ## License This plugin is licensed under the [GPLv2](https://www.gnu.org/licenses/gpl-2.0.html) License. diff --git a/changelog.txt b/changelog.txt index 3f36e9b..613c7c6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,14 @@ ** Hellotext for WooCommerce Changelog ** +2026-01-18 - version 1.3.0 + +* Add comprehensive type hints to all source files for better IDE support and type safety. +* Add PHPDoc comments to all classes and methods for improved documentation. +* Create centralized Constants.php file to eliminate magic strings. +* Expand test coverage with new unit tests for adapters and services. +* Add comprehensive developer documentation (API.md and DEVELOPMENT.md). +* Improve code quality and maintainability across the codebase. + 2025-04-10 - version 1.2.2 * The generated Hellotext API key has read/write access to allow Hellotext to subscribe to webhooks. diff --git a/composer.json b/composer.json index f6ea664..e1d05ad 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,11 @@ "name": "hellotext/wordpress-plugin", "description": "We help eCommerce and retail brands improve their revenues and simplify their marketing.", "license": "proprietary", + "autoload": { + "psr-4": { + "Hellotext\\": "src/" + } + }, "config": { "allow-plugins": { "alleyinteractive/composer-wordpress-autoloader": true, @@ -12,9 +17,16 @@ } }, "require-dev": { - "pestphp/pest": "*", + "pestphp/pest": "^3.8", "mockery/mockery": "^1.6", - "php-stubs/wordpress-stubs": "^5.0@stable", - "php-stubs/woocommerce-stubs": "^5.0@stable" + "php-stubs/wordpress-stubs": "^6.9", + "php-stubs/woocommerce-stubs": "^10.4", + "friendsofphp/php-cs-fixer": "^3.92" + }, + "scripts": { + "test": "vendor/bin/pest", + "format": "vendor/bin/php-cs-fixer fix", + "format:check": "vendor/bin/php-cs-fixer fix --dry-run --diff", + "build": "composer install --no-dev --optimize-autoloader" } } diff --git a/composer.lock b/composer.lock index 6bbcf18..d2d7787 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,21 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "42a2924fc95410633bc83d2cc5bcde7a", + "content-hash": "4185a3feadc6889097776b6b9c8b1be3", "packages": [], "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.4.3", + "version": "v7.8.4", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec" + "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/64fcfd0e28a6b8078a19dbf9127be2ee645b92ec", - "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/130a9bf0e269ee5f5b320108f794ad03e275cad4", + "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4", "shasum": "" }, "require": { @@ -26,31 +26,30 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.1.0", - "jean85/pretty-package-versions": "^2.0.5", - "php": "~8.2.0 || ~8.3.0", - "phpunit/php-code-coverage": "^10.1.11 || ^11.0.0", - "phpunit/php-file-iterator": "^4.1.0 || ^5.0.0", - "phpunit/php-timer": "^6.0.0 || ^7.0.0", - "phpunit/phpunit": "^10.5.9 || ^11.0.3", - "sebastian/environment": "^6.0.1 || ^7.0.0", - "symfony/console": "^6.4.3 || ^7.0.3", - "symfony/process": "^6.4.3 || ^7.0.3" + "fidry/cpu-core-counter": "^1.2.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-timer": "^7.0.1", + "phpunit/phpunit": "^11.5.24", + "sebastian/environment": "^7.2.1", + "symfony/console": "^6.4.22 || ^7.3.0", + "symfony/process": "^6.4.20 || ^7.3.0" }, "require-dev": { "doctrine/coding-standard": "^12.0.0", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^1.10.58", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "squizlabs/php_codesniffer": "^3.9.0", - "symfony/filesystem": "^6.4.3 || ^7.0.3" + "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.6", + "phpstan/phpstan-strict-rules": "^2.0.4", + "squizlabs/php_codesniffer": "^3.13.2", + "symfony/filesystem": "^6.4.13 || ^7.3.0" }, "bin": [ "bin/paratest", - "bin/paratest.bat", "bin/paratest_for_phpstorm" ], "type": "library", @@ -87,7 +86,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.4.3" + "source": "https://github.com/paratestphp/paratest/tree/v7.8.4" }, "funding": [ { @@ -99,87 +98,111 @@ "type": "paypal" } ], - "time": "2024-02-20T07:24:02+00:00" + "time": "2025-06-23T06:07:21+00:00" }, { - "name": "doctrine/deprecations", - "version": "1.1.3", + "name": "clue/ndjson-react", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": ">=5.3", + "react/stream": "^1.2" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + "Clue\\React\\NDJson\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", + "keywords": [ + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" + ], "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" }, - "time": "2024-01-30T19:34:25+00:00" + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" }, { - "name": "fidry/cpu-core-counter", - "version": "1.1.0", + "name": "composer/pcre", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "fidry/makefile": "^0.2.0", - "fidry/php-cs-fixer-config": "^1.1.2", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^8.5.31 || ^9.5.26", - "webmozarts/strict-phpunit": "^7.5" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" + "Composer\\Pcre\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -188,63 +211,68 @@ ], "authors": [ { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Tiny utility to get the number of CPU cores.", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "CPU", - "core" + "PCRE", + "preg", + "regex", + "regular expression" ], "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" }, "funding": [ { - "url": "https://github.com/theofidry", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2024-02-07T09:43:46+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { - "name": "filp/whoops", - "version": "2.15.4", + "name": "composer/semver", + "version": "3.4.4", "source": { "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Whoops\\": "src/Whoops/" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -253,277 +281,687 @@ ], "authors": [ { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.4" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { - "url": "https://github.com/denis-sokolov", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" } ], - "time": "2023-11-03T12:00:00+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { - "name": "hamcrest/hamcrest-php", - "version": "v2.0.1", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "php": "^5.3|^7.0|^8.0" - }, - "replace": { - "cordoval/hamcrest-php": "*", - "davedevelopment/hamcrest-php": "*", - "kodova/hamcrest-php": "*" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, "autoload": { - "classmap": [ - "hamcrest" - ] + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "This is the PHP port of Hamcrest Matchers", + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", "keywords": [ - "test" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, - "time": "2020-07-09T08:09:16+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" }, { - "name": "jean85/pretty-package-versions", - "version": "2.0.6", + "name": "doctrine/deprecations", + "version": "1.1.5", "source": { "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", "shasum": "" }, "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, + "type": "library", "autoload": { "psr-4": { - "Jean85\\": "src/" + "Doctrine\\Deprecations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" }, - "time": "2024-03-08T09:58:59+00:00" + "time": "2025-04-07T20:06:18+00:00" }, { - "name": "mockery/mockery", - "version": "1.6.12", + "name": "evenement/evenement", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "^2.0.1", - "lib-pcre": ">=7.0", - "php": ">=7.3" - }, - "conflict": { - "phpunit/phpunit": "<8.0" + "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.17", - "symplify/easy-coding-standard": "^12.1.14" + "phpunit/phpunit": "^9 || ^6" }, "type": "library", "autoload": { - "files": [ - "library/helpers.php", - "library/Mockery.php" - ], "psr-4": { - "Mockery\\": "library/Mockery" + "Evenement\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Pádraic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "https://github.com/padraic", - "role": "Author" - }, - { - "name": "Dave Marshall", - "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "https://davedevelopment.co.uk", - "role": "Developer" - }, - { - "name": "Nathanael Esayeas", - "email": "nathanael.esayeas@protonmail.com", - "homepage": "https://github.com/ghostwriter", - "role": "Lead Developer" + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" } ], - "description": "Mockery is a simple yet flexible PHP mock object framework", - "homepage": "https://github.com/mockery/mockery", + "description": "Événement is a very simple event dispatching library for PHP", "keywords": [ - "BDD", - "TDD", - "library", - "mock", - "mock objects", - "mockery", - "stub", - "test", - "test double", - "testing" + "event-dispatcher", + "event-emitter" ], "support": { - "docs": "https://docs.mockery.io/", - "issues": "https://github.com/mockery/mockery/issues", - "rss": "https://github.com/mockery/mockery/releases.atom", - "security": "https://github.com/mockery/mockery/security/advisories", - "source": "https://github.com/mockery/mockery" + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" }, - "time": "2024-05-16T03:13:13+00:00" + "time": "2023-08-08T05:53:35+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.12.0", + "name": "fidry/cpu-core-counter", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" + "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" }, "type": "library", "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Fidry\\CpuCoreCounter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "CPU", + "core" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.92.5", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58", + "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58", + "shasum": "" + }, + "require": { + "clue/ndjson-react": "^1.3", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.5", + "ext-filter": "*", + "ext-hash": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.3", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.6", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3.1 || ^2.7", + "infection/infection": "^0.31", + "justinrainbow/json-schema": "^6.6", + "keradus/cli-executor": "^2.3", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.9", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.46", + "symfony/polyfill-php85": "^1.33", + "symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0", + "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/**/Internal/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.5" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2026-01-08T21:57:37+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -531,45 +969,1316 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.8.3", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", + "php": "^8.2.0", + "symfony/console": "^7.3.0" + }, + "conflict": { + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2 || ^4.0.0", + "sebastian/environment": "^7.2.1 || ^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-11-20T02:55:25+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.3", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.3.6" + }, + "require-dev": { + "illuminate/console": "^11.46.1", + "laravel/pint": "^1.25.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2025-11-20T02:34:59+00:00" + }, + { + "name": "pestphp/pest", + "version": "v3.8.4", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "72cf695554420e21858cda831d5db193db102574" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/72cf695554420e21858cda831d5db193db102574", + "reference": "72cf695554420e21858cda831d5db193db102574", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.8.4", + "nunomaduro/collision": "^8.8.2", + "nunomaduro/termwind": "^2.3.1", + "pestphp/pest-plugin": "^3.0.0", + "pestphp/pest-plugin-arch": "^3.1.1", + "pestphp/pest-plugin-mutate": "^3.0.5", + "php": "^8.2.0", + "phpunit/phpunit": "^11.5.33" + }, + "conflict": { + "filp/whoops": "<2.16.0", + "phpunit/phpunit": ">11.5.33", + "sebastian/exporter": "<6.0.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^3.4.0", + "pestphp/pest-plugin-type-coverage": "^3.6.1", + "symfony/process": "^7.3.0" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v3.8.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-20T19:12:42+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e79b26c65bc11c41093b10150c1341cc5cdbea83", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.2" + }, + "conflict": { + "pestphp/pest": "<3.0.0" + }, + "require-dev": { + "composer/composer": "^2.7.9", + "pestphp/pest": "^3.0.0", + "pestphp/pest-dev-tools": "^3.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2024-09-08T23:21:41+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/db7bd9cb1612b223e16618d85475c6f63b9c8daa", + "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "ta-tikoma/phpunit-architecture-test": "^0.8.4" + }, + "require-dev": { + "pestphp/pest": "^3.8.1", + "pestphp/pest-dev-tools": "^3.4.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-04-16T22:59:48+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v3.0.5", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.2.0", + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^3.0.8", + "pestphp/pest-dev-tools": "^3.0.0", + "pestphp/pest-plugin-type-coverage": "^3.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v3.0.5" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-09-22T07:54:40+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "php-stubs/woocommerce-stubs", + "version": "v10.4.2", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/woocommerce-stubs.git", + "reference": "b1222d510f791a92bf89905708c2995854de85b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/b1222d510f791a92bf89905708c2995854de85b6", + "reference": "b1222d510f791a92bf89905708c2995854de85b6", + "shasum": "" + }, + "require": { + "php-stubs/wordpress-stubs": "^5.3 || ^6.0" + }, + "require-dev": { + "php": "~7.1 || ~8.0", + "php-stubs/generator": "^0.8.0" + }, + "suggest": { + "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WooCommerce function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/woocommerce-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "woocommerce", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", + "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v10.4.2" + }, + "time": "2025-12-14T04:41:11+00:00" + }, + { + "name": "php-stubs/wordpress-stubs", + "version": "v6.9.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-stubs.git", + "reference": "5171cb6650e6c583a96943fd6ea0dfa3e1089a8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/5171cb6650e6c583a96943fd6ea0dfa3e1089a8a", + "reference": "5171cb6650e6c583a96943fd6ea0dfa3e1089a8a", + "shasum": "" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "nikic/php-parser": "^5.5", + "php": "^7.4 || ^8.0", + "php-stubs/generator": "^0.8.3", + "phpdocumentor/reflection-docblock": "^5.4.1", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.5", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "paragonie/sodium_compat": "Pure PHP implementation of libsodium", + "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wordpress-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-stubs/issues", + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.0" + }, + "time": "2025-12-03T23:06:24+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.6", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6" + }, + "time": "2025-12-22T21:13:58+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" + }, + "time": "2025-11-21T15:09:14+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/16dbf9937da8d4528ceb2145c9c7c0bd29e26374", + "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.1" + }, + "time": "2026-01-12T11:33:04+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.12", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.1", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.3.1" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.46" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-12-24T07:01:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" }, { - "name": "nikic/php-parser", - "version": "v5.1.0", + "name": "phpunit/php-timer", + "version": "7.0.1", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", "shasum": "" }, "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" + "php": ">=8.2" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^11.0" }, - "bin": [ - "bin/php-parse" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -577,164 +2286,164 @@ ], "authors": [ { - "name": "Nikita Popov" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A PHP parser written in PHP", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "parser", - "php" + "timer" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" }, - "time": "2024-07-01T20:03:41+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" }, { - "name": "nunomaduro/collision", - "version": "v8.3.0", + "name": "phpunit/phpunit", + "version": "11.5.33", "source": { "type": "git", - "url": "https://github.com/nunomaduro/collision.git", - "reference": "b49f5b2891ce52726adfd162841c69d4e4c84229" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "5965e9ff57546cb9137c0ff6aa78cb7442b05cf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/b49f5b2891ce52726adfd162841c69d4e4c84229", - "reference": "b49f5b2891ce52726adfd162841c69d4e4c84229", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5965e9ff57546cb9137c0ff6aa78cb7442b05cf6", + "reference": "5965e9ff57546cb9137c0ff6aa78cb7442b05cf6", "shasum": "" }, "require": { - "filp/whoops": "^2.15.4", - "nunomaduro/termwind": "^2.0.1", - "php": "^8.2.0", - "symfony/console": "^7.1.2" - }, - "conflict": { - "laravel/framework": "<11.0.0 || >=12.0.0", - "phpunit/phpunit": "<10.5.1 || >=12.0.0" + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" }, - "require-dev": { - "larastan/larastan": "^2.9.8", - "laravel/framework": "^11.16.0", - "laravel/pint": "^1.16.2", - "laravel/sail": "^1.30.2", - "laravel/sanctum": "^4.0.2", - "laravel/tinker": "^2.9.0", - "orchestra/testbench-core": "^9.2.1", - "pestphp/pest": "^2.34.9 || ^3.0.0", - "sebastian/environment": "^6.1.0 || ^7.0.0" + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { - "laravel": { - "providers": [ - "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" - ] - }, "branch-alias": { - "dev-8.x": "8.x-dev" + "dev-main": "11.5-dev" } }, "autoload": { "files": [ - "./src/Adapters/Phpunit/Autoload.php" + "src/Framework/Assert/Functions.php" ], - "psr-4": { - "NunoMaduro\\Collision\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Cli error handling for console/command-line PHP applications.", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "artisan", - "cli", - "command-line", - "console", - "error", - "handling", - "laravel", - "laravel-zero", - "php", - "symfony" + "phpunit", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/nunomaduro/collision/issues", - "source": "https://github.com/nunomaduro/collision" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.33" }, "funding": [ { - "url": "https://www.paypal.com/paypalme/enunomaduro", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { - "url": "https://github.com/nunomaduro", + "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2024-07-16T22:41:01+00:00" + "time": "2025-08-16T05:19:02+00:00" }, { - "name": "nunomaduro/termwind", - "version": "v2.0.1", + "name": "psr/container", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/nunomaduro/termwind.git", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a" + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/58c4c58cf23df7f498daeb97092e34f5259feb6a", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": "^8.2", - "symfony/console": "^7.0.4" - }, - "require-dev": { - "ergebnis/phpstan-rules": "^2.2.0", - "illuminate/console": "^11.0.0", - "laravel/pint": "^1.14.0", - "mockery/mockery": "^1.6.7", - "pestphp/pest": "^2.34.1", - "phpstan/phpstan": "^1.10.59", - "phpstan/phpstan-strict-rules": "^1.5.2", - "symfony/var-dumper": "^7.0.4", - "thecodingmachine/phpstan-strict-rules": "^1.0.0" + "php": ">=7.4.0" }, "type": "library", "extra": { - "laravel": { - "providers": [ - "Termwind\\Laravel\\TermwindServiceProvider" - ] - }, "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "files": [ - "src/Functions.php" - ], "psr-4": { - "Termwind\\": "src/" + "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -743,110 +2452,51 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Its like Tailwind CSS, but for the console.", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "cli", - "console", - "css", - "package", - "php", - "style" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], "support": { - "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.0.1" + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://github.com/xiCO2k", - "type": "github" - } - ], - "time": "2024-03-06T16:17:14+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { - "name": "pestphp/pest", - "version": "v2.34.9", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/pestphp/pest.git", - "reference": "ef120125e036bf84c9e46a9e62219702f5b92e16" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/ef120125e036bf84c9e46a9e62219702f5b92e16", - "reference": "ef120125e036bf84c9e46a9e62219702f5b92e16", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "brianium/paratest": "^7.3.1", - "nunomaduro/collision": "^7.10.0|^8.1.1", - "nunomaduro/termwind": "^1.15.1|^2.0.1", - "pestphp/pest-plugin": "^2.1.1", - "pestphp/pest-plugin-arch": "^2.7.0", - "php": "^8.1.0", - "phpunit/phpunit": "^10.5.17" - }, - "conflict": { - "phpunit/phpunit": ">10.5.17", - "sebastian/exporter": "<5.1.0", - "webmozart/assert": "<1.11.0" + "php": ">=7.2.0" }, - "require-dev": { - "pestphp/pest-dev-tools": "^2.16.0", - "pestphp/pest-plugin-type-coverage": "^2.8.4", - "symfony/process": "^6.4.0|^7.1.1" - }, - "bin": [ - "bin/pest" - ], "type": "library", "extra": { - "pest": { - "plugins": [ - "Pest\\Plugins\\Bail", - "Pest\\Plugins\\Cache", - "Pest\\Plugins\\Coverage", - "Pest\\Plugins\\Init", - "Pest\\Plugins\\Environment", - "Pest\\Plugins\\Help", - "Pest\\Plugins\\Memory", - "Pest\\Plugins\\Only", - "Pest\\Plugins\\Printer", - "Pest\\Plugins\\ProcessIsolation", - "Pest\\Plugins\\Profile", - "Pest\\Plugins\\Retry", - "Pest\\Plugins\\Snapshot", - "Pest\\Plugins\\Verbose", - "Pest\\Plugins\\Version", - "Pest\\Plugins\\Parallel" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] + "branch-alias": { + "dev-master": "1.0.x-dev" } }, "autoload": { - "files": [ - "src/Functions.php", - "src/Pest.php" - ], "psr-4": { - "Pest\\": "src/" + "Psr\\EventDispatcher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -855,411 +2505,446 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "The elegant PHP Testing Framework.", + "description": "Standard interfaces for event handling.", "keywords": [ - "framework", - "pest", - "php", - "test", - "testing", - "unit" + "events", + "psr", + "psr-14" ], "support": { - "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v2.34.9" + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2024-07-11T08:36:26+00:00" + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "pestphp/pest-plugin", - "version": "v2.1.1", + "name": "psr/log", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/pestphp/pest-plugin.git", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b" + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { - "composer-plugin-api": "^2.0.0", - "composer-runtime-api": "^2.2.2", - "php": "^8.1" - }, - "conflict": { - "pestphp/pest": "<2.2.3" - }, - "require-dev": { - "composer/composer": "^2.5.8", - "pestphp/pest": "^2.16.0", - "pestphp/pest-dev-tools": "^2.16.0" + "php": ">=8.0.0" }, - "type": "composer-plugin", + "type": "library", "extra": { - "class": "Pest\\Plugin\\Manager" + "branch-alias": { + "dev-master": "3.x-dev" + } }, "autoload": { "psr-4": { - "Pest\\Plugin\\": "src/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "The Pest plugin manager", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "framework", - "manager", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" + "log", + "psr", + "psr-3" ], "support": { - "source": "https://github.com/pestphp/pest-plugin/tree/v2.1.1" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "funding": [ - { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], - "time": "2023-08-22T08:40:06+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { - "name": "pestphp/pest-plugin-arch", - "version": "v2.7.0", + "name": "psr/simple-cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda" + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/d23b2d7498475354522c3818c42ef355dca3fcda", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "nunomaduro/collision": "^7.10.0|^8.1.0", - "pestphp/pest-plugin": "^2.1.1", - "php": "^8.1", - "ta-tikoma/phpunit-architecture-test": "^0.8.4" - }, - "require-dev": { - "pestphp/pest": "^2.33.0", - "pestphp/pest-dev-tools": "^2.16.0" + "php": ">=8.0.0" }, "type": "library", "extra": { - "pest": { - "plugins": [ - "Pest\\Arch\\Plugin" - ] + "branch-alias": { + "dev-master": "3.0.x-dev" } }, "autoload": { - "files": [ - "src/Autoload.php" - ], "psr-4": { - "Pest\\Arch\\": "src/" + "Psr\\SimpleCache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "The Arch plugin for Pest PHP.", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", "keywords": [ - "arch", - "architecture", - "framework", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.7.0" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2024-01-26T09:46:42+00:00" + "time": "2021-10-29T13:26:27+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.4", + "name": "react/cache", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Cache\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" }, "funding": [ { - "url": "https://github.com/theseer", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2024-03-03T12:33:53+00:00" + "time": "2022-11-30T15:59:55+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "react/child-process", + "version": "v0.6.7", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/reactphp/child-process.git", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\ChildProcess\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Library for handling version information and constraints", + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.7" }, - "time": "2022-02-21T01:04:05+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-12-23T15:25:20+00:00" }, { - "name": "php-stubs/woocommerce-stubs", - "version": "v5.9.1", + "name": "react/dns", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/php-stubs/woocommerce-stubs.git", - "reference": "486ccff117badfab94c404065d37a77d632d7db5" + "url": "https://github.com/reactphp/dns.git", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/486ccff117badfab94c404065d37a77d632d7db5", - "reference": "486ccff117badfab94c404065d37a77d632d7db5", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", "shasum": "" }, "require": { - "php-stubs/wordpress-stubs": "^5.3.0" + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" }, "require-dev": { - "php": "~7.1", - "php-stubs/generator": "^0.8.0" - }, - "suggest": { - "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" }, "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "WooCommerce function and class declaration stubs for static analysis.", - "homepage": "https://github.com/php-stubs/woocommerce-stubs", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", "keywords": [ - "PHPStan", - "static analysis", - "woocommerce", - "wordpress" + "async", + "dns", + "dns-resolver", + "reactphp" ], "support": { - "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", - "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v5.9.1" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, - "time": "2022-04-30T06:35:48+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-18T19:34:28+00:00" }, { - "name": "php-stubs/wordpress-stubs", - "version": "v5.9.9", + "name": "react/event-loop", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "06c51c4863659ea9e9f4c2a23293728a677cb059" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/06c51c4863659ea9e9f4c2a23293728a677cb059", - "reference": "06c51c4863659ea9e9f4c2a23293728a677cb059", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, + "require": { + "php": ">=5.3.0" + }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "nikic/php-parser": "^4.13", - "php": "^7.4 || ~8.0.0", - "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "5.3", - "phpstan/phpstan": "^1.10.49", - "phpunit/phpunit": "^9.5", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.11" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "suggest": { - "paragonie/sodium_compat": "Pure PHP implementation of libsodium", - "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" }, "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "WordPress function and class declaration stubs for static analysis.", - "homepage": "https://github.com/php-stubs/wordpress-stubs", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", "keywords": [ - "PHPStan", - "static analysis", - "wordpress" + "asynchronous", + "event-loop" ], "support": { - "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v5.9.9" + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, - "time": "2024-04-14T17:16:00+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-17T20:46:25+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "react/promise", + "version": "v3.3.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } + "require-dev": { + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" }, + "type": "library", "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "phpDocumentor\\Reflection\\": "src/" + "React\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1268,66 +2953,75 @@ ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "promise", + "promises" ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, - "time": "2020-06-27T09:03:43+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "name": "react/socket", + "version": "v1.17.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "url": "https://github.com/reactphp/socket.git", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7", - "webmozart/assert": "^1.9.1" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { - "mockery/mockery": "~1.3.5", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.13" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "React\\Socket\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1336,60 +3030,73 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, - "time": "2024-05-21T05:55:05+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-19T20:47:34+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "React\\Stream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1398,104 +3105,130 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, - "time": "2024-02-23T11:10:43+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" }, { - "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "name": "sebastian/cli-parser", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=8.2" }, "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" + "phpunit/phpunit": "^11.0" }, "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" }, - "time": "2024-05-31T08:52:43+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "10.1.15", + "name": "sebastian/code-unit", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.1" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "phpunit/phpunit": "^11.5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1514,17 +3247,12 @@ "role": "lead" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -1532,27 +3260,27 @@ "type": "github" } ], - "time": "2024-06-29T08:25:15+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "4.1.0", + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { @@ -1572,20 +3300,15 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" }, "funding": [ { @@ -1593,36 +3316,39 @@ "type": "github" } ], - "time": "2023-08-31T06:24:48+00:00" + "time": "2024-07-03T04:45:54+00:00" }, { - "name": "phpunit/php-invoker", - "version": "4.0.0", + "name": "sebastian/comparator", + "version": "6.3.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", "shasum": "" }, "require": { - "php": ">=8.1" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" }, "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.4" }, "suggest": { - "ext-pcntl": "*" + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -1637,51 +3363,78 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ - "process" + "comparator", + "compare", + "equality" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2023-02-03T06:56:09+00:00" + "time": "2025-08-10T08:07:46+00:00" }, { - "name": "phpunit/php-text-template", - "version": "3.0.1", + "name": "sebastian/complexity", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", "shasum": "" }, "require": { - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1700,15 +3453,12 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" }, "funding": [ { @@ -1716,27 +3466,28 @@ "type": "github" } ], - "time": "2023-08-31T14:07:24+00:00" + "time": "2024-07-03T04:49:50+00:00" }, { - "name": "phpunit/php-timer", - "version": "6.0.0", + "name": "sebastian/diff", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { @@ -1756,18 +3507,25 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "timer" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" }, "funding": [ { @@ -1775,66 +3533,38 @@ "type": "github" } ], - "time": "2023-02-03T06:57:52+00:00" + "time": "2024-07-03T04:53:05+00:00" }, { - "name": "phpunit/phpunit", - "version": "10.5.17", + "name": "sebastian/environment", + "version": "7.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c1f736a473d21957ead7e94fcc029f571895abf5" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c1f736a473d21957ead7e94fcc029f571895abf5", - "reference": "c1f736a473d21957ead7e94fcc029f571895abf5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" }, "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "ext-posix": "*" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "7.2-dev" } }, "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], "classmap": [ "src/" ] @@ -1846,165 +3576,158 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ - "phpunit", - "testing", - "xunit" + "Xdebug", + "environment", + "hhvm" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.17" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, { "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", "type": "tidelift" } ], - "time": "2024-04-05T04:39:01+00:00" + "time": "2025-05-21T11:55:47+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "sebastian/exporter", + "version": "6.3.2", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { - "php": ">=7.4.0" + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "6.3-dev" } }, "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "export", + "exporter" ], "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" - }, - { - "name": "psr/log", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" - }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { - "name": "sebastian/cli-parser", - "version": "2.0.1", + "name": "sebastian/global-state", + "version": "7.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "ext-dom": "*", + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2019,16 +3742,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" }, "funding": [ { @@ -2036,32 +3761,33 @@ "type": "github" } ], - "time": "2024-03-02T07:12:49+00:00" + "time": "2024-07-03T04:57:36+00:00" }, { - "name": "sebastian/code-unit", - "version": "2.0.0", + "name": "sebastian/lines-of-code", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", "shasum": "" }, "require": { - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2080,11 +3806,12 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" }, "funding": [ { @@ -2092,32 +3819,34 @@ "type": "github" } ], - "time": "2023-02-03T06:58:43+00:00" + "time": "2024-07-03T04:58:38+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", + "name": "sebastian/object-enumerator", + "version": "6.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2135,11 +3864,12 @@ "email": "sebastian@phpunit.de" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" }, "funding": [ { @@ -2147,36 +3877,32 @@ "type": "github" } ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2024-07-03T05:00:13+00:00" }, { - "name": "sebastian/comparator", - "version": "5.0.1", + "name": "sebastian/object-reflector", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -2188,35 +3914,18 @@ "license": [ "BSD-3-Clause" ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, + "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" }, "funding": [ { @@ -2224,33 +3933,32 @@ "type": "github" } ], - "time": "2023-08-14T13:18:12+00:00" + "time": "2024-07-03T05:01:32+00:00" }, { - "name": "sebastian/complexity", - "version": "3.2.0", + "name": "sebastian/recursion-context", + "version": "6.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2265,45 +3973,63 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { - "name": "sebastian/diff", - "version": "5.1.1", + "name": "sebastian/type", + "version": "5.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -2323,61 +4049,58 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { - "name": "sebastian/environment", - "version": "6.1.0", + "name": "sebastian/version", + "version": "5.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-posix": "*" + "php": ">=8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2392,20 +4115,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { @@ -2413,541 +4132,632 @@ "type": "github" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { - "name": "sebastian/exporter", - "version": "5.1.2", + "name": "staabm/side-effects-detector", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-dev" - } - }, "autoload": { "classmap": [ - "src/" + "lib/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } + "MIT" ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "description": "A static analysis tool to detect side effects in PHP code", "keywords": [ - "export", - "exporter" + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/staabm", "type": "github" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { - "name": "sebastian/global-state", - "version": "6.0.2", + "name": "symfony/console", + "version": "v7.4.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "url": "https://github.com/symfony/console.git", + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^10.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", "keywords": [ - "global state" + "cli", + "command-line", + "console", + "terminal" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "source": "https://github.com/symfony/console/tree/v7.4.3" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2025-12-23T14:50:43+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "2.0.2", + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "3.6-dev" } }, "autoload": { - "classmap": [ - "src/" + "files": [ + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "5.0.0", + "name": "symfony/event-dispatcher", + "version": "v7.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T07:08:32+00:00" + "time": "2025-10-28T09:38:46+00:00" }, { - "name": "sebastian/object-reflector", - "version": "3.0.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" + "php": ">=8.1", + "psr/event-dispatcher": "^1" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.6-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { - "name": "sebastian/recursion-context", - "version": "5.0.0", + "name": "symfony/filesystem", + "version": "v7.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "url": "https://github.com/symfony/filesystem.git", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "source": "https://github.com/symfony/filesystem/tree/v7.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { - "name": "sebastian/type", - "version": "4.0.0", + "name": "symfony/finder", + "version": "v7.4.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "url": "https://github.com/symfony/finder.git", + "reference": "fffe05569336549b20a1be64250b40516d6e8d06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", + "reference": "fffe05569336549b20a1be64250b40516d6e8d06", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "source": "https://github.com/symfony/finder/tree/v7.4.3" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2025-12-23T14:50:43+00:00" }, { - "name": "sebastian/version", - "version": "4.0.1", + "name": "symfony/options-resolver", + "version": "v7.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "b38026df55197f9e39a44f3215788edf83187b80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b38026df55197f9e39a44f3215788edf83187b80", + "reference": "b38026df55197f9e39a44f3215788edf83187b80", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "source": "https://github.com/symfony/options-resolver/tree/v7.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2025-11-12T15:39:26+00:00" }, { - "name": "symfony/console", - "version": "v7.1.2", + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "0aa29ca177f432ab68533432db0de059f39c92ae" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0aa29ca177f432ab68533432db0de059f39c92ae", - "reference": "0aa29ca177f432ab68533432db0de059f39c92ae", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "php": ">=7.2" }, "provide": { - "psr/log-implementation": "1.0|2.0|3.0" + "ext-ctype": "*" }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2955,24 +4765,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ - "cli", - "command-line", - "console", - "terminal" + "compatibility", + "ctype", + "polyfill", + "portable" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.2" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -2983,44 +4793,51 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { "files": [ - "function.php" - ] + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3036,10 +4853,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "A generic function and convention to trigger deprecation notices", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -3050,40 +4875,53 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { - "name": "symfony/finder", - "version": "v7.1.1", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fbb0ba67688b780efbc886c1a0a0948dcf7205d6", - "reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=7.2" }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Finder\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3092,18 +4930,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Finds files and directories via an intuitive fluent interface", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.1" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -3114,41 +4960,46 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { - "ext-ctype": "*" + "ext-mbstring": "*" }, "suggest": { - "ext-ctype": "For best performance" + "ext-mbstring": "For best performance" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3156,7 +5007,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -3165,24 +5016,25 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "ctype", + "mbstring", "polyfill", - "portable" + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -3193,38 +5045,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "name": "symfony/polyfill-php80", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3232,14 +5085,21 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -3249,18 +5109,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "grapheme", - "intl", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -3271,38 +5129,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "name": "symfony/polyfill-php81", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3310,7 +5169,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, "classmap": [ "Resources/stubs" @@ -3330,18 +5189,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "intl", - "normalizer", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -3352,41 +5209,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "name": "symfony/polyfill-php84", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3394,8 +5249,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3411,17 +5269,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -3432,25 +5289,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { "name": "symfony/process", - "version": "v7.1.1", + "version": "v7.4.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "febf90124323a093c7ee06fdb30e765ca3c20028" + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/febf90124323a093c7ee06fdb30e765ca3c20028", - "reference": "febf90124323a093c7ee06fdb30e765ca3c20028", + "url": "https://api.github.com/repos/symfony/process/zipball/2f8e1a6cdf590ca63715da4d3a7a3327404a523f", + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f", "shasum": "" }, "require": { @@ -3482,7 +5343,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.1" + "source": "https://github.com/symfony/process/tree/v7.4.3" }, "funding": [ { @@ -3493,25 +5354,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2025-12-19T10:00:43+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -3524,12 +5389,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -3565,7 +5430,73 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T11:30:57+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "8a24af0a2e8a872fb745047180649b8418303084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/8a24af0a2e8a872fb745047180649b8418303084", + "reference": "8a24af0a2e8a872fb745047180649b8418303084", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.4.0" }, "funding": [ { @@ -3576,31 +5507,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2025-08-04T07:05:15+00:00" }, { "name": "symfony/string", - "version": "v7.1.2", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8" + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/14221089ac66cf82e3cf3d1c1da65de305587ff8", - "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8", + "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.33", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, @@ -3608,12 +5544,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3652,7 +5587,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.2" + "source": "https://github.com/symfony/string/tree/v7.4.0" }, "funding": [ { @@ -3663,32 +5598,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-06-28T09:27:18+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", - "version": "0.8.4", + "version": "0.8.5", "source": { "type": "git", "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", - "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636" + "reference": "cf6fb197b676ba716837c886baca842e4db29005" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/89f0dea1cb0f0d5744d3ec1764a286af5e006636", - "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/cf6fb197b676ba716837c886baca842e4db29005", + "reference": "cf6fb197b676ba716837c886baca842e4db29005", "shasum": "" }, "require": { "nikic/php-parser": "^4.18.0 || ^5.0.0", "php": "^8.1.0", "phpdocumentor/reflection-docblock": "^5.3.0", - "phpunit/phpunit": "^10.5.5 || ^11.0.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0", "symfony/finder": "^6.4.0 || ^7.0.0" }, "require-dev": { @@ -3725,22 +5664,22 @@ ], "support": { "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", - "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.4" + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.5" }, - "time": "2024-01-05T14:10:56+00:00" + "time": "2025-04-20T20:23:40+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -3769,7 +5708,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -3777,37 +5716,37 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", "shasum": "" }, "require": { "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-feature/2-0": "2.0-dev" } }, "autoload": { @@ -3823,6 +5762,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -3833,21 +5776,18 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/2.1.2" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2026-01-13T14:02:24+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "php-stubs/wordpress-stubs": 0, - "php-stubs/woocommerce-stubs": 0 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "platform-overrides": { "php": "8.2.12" }, diff --git a/hellotext.php b/hellotext.php index 077a2cd..4fb9c48 100644 --- a/hellotext.php +++ b/hellotext.php @@ -8,7 +8,7 @@ * Plugin Name: Hellotext * Plugin URI: https://github.com/hellotext/hellotext-wordpress * Description: Integrates Hellotext tracking to WooCommerce. - * Version: 1.2.0 + * Version: 1.3.0 * Author: Hellotext * Author URI: https://www.hellotext.com * License: GPL v2 @@ -17,6 +17,8 @@ * Domain Path: /languages */ +use Hellotext\Constants; + // TODO: Refactor this to use the APP_ENV variable if (! isset($_ENV['APP_ENV'])) { $_ENV['APP_ENV'] = 'production'; @@ -29,71 +31,104 @@ : 'https://api.hellotext.com'; -session_start(); +if (session_status() === PHP_SESSION_NONE) { + session_start(); +} -$paths = [ - 'Adapters', - 'Api', - 'Events', - 'Misc', - 'Services', -]; +// Load Composer autoloader (handles all classes) +require_once __DIR__ . '/vendor/autoload.php'; -foreach ($paths as $current_path) { - $scan = scandir(plugin_dir_path( __FILE__ ) . 'src/' . $current_path . '/'); +// Load event handlers (contain functions, not classes) +$event_files = glob(__DIR__ . '/src/Events/*.php'); +foreach ($event_files as $file) { + require_once $file; +} - foreach ($scan as $file) { - if (strpos($file, '.php') !== false) { - include('src/' . $current_path . '/' . $file); - } - } +// Load misc files (Settings, Scripts contain functions) +$misc_files = glob(__DIR__ . '/src/Misc/*.php'); +foreach ($misc_files as $file) { + require_once $file; } // Function on Events/AppRemoved.php register_deactivation_hook( __FILE__, 'hellotext_deactivate' ); -// New Version Check -function version_check() { - $releases_api_url = 'https://api.github.com/repos/hellotext/hellotext-wordpress/releases'; - $releases_url = 'https://github.com/hellotext/hellotext-wordpress/releases'; - $plugin_slug = plugin_basename( __FILE__ ); - $plugin_data = get_plugin_data( __FILE__ ); +/** + * Check for plugin updates from GitHub. + * Caches result for 24 hours to avoid rate limiting. + * + * @return void + */ +function hellotext_version_check() { + if (!is_admin()) { + return; + } - // Check if this plugin is at least partially ours - if ( isset( $_GET['page'] ) && $_GET['page'] === 'your-plugin-settings-page' ) { - return; // Don't show the notice on your plugin settings page - } + $cache_key = 'hellotext_version_check'; + $cached = get_transient($cache_key); - $response = wp_remote_get( $releases_api_url ); - - if ( ! is_wp_error( $response ) ) { - $body = wp_remote_retrieve_body( $response ); - $json = json_decode( $body, true ); - if ( is_array( $json ) ) { - $current_version = $plugin_data['Version']; - $latest_version = preg_replace('/[a-zA-Z]/', '', $json[0]['tag_name']); - if ( version_compare( $current_version, $latest_version, '<' ) ) { - $message = sprintf( __( 'There is a new version of %1$s available. View version %2$s details or update now.' ), $plugin_data['Name'], $latest_version, $releases_url ); - echo '

' . $message . '

'; - } - } - } + if (false !== $cached) { + if (isset($cached['message'])) { + echo '

' . wp_kses_post($cached['message']) . '

'; + } + return; + } + + $response = wp_remote_get( + 'https://api.github.com/repos/hellotext/hellotext-wordpress/releases/latest', + ['timeout' => 5] + ); + + $cache_data = []; + + if (!is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response)) { + $release = json_decode(wp_remote_retrieve_body($response), true); + + if (isset($release['tag_name'])) { + $current = get_plugin_data(__FILE__)['Version']; + $latest = ltrim($release['tag_name'], 'v'); + + if (version_compare($current, $latest, '<')) { + $cache_data['message'] = sprintf( + 'New version of Hellotext available: %s. View details', + $latest, + $release['html_url'] + ); + } + } + } + + set_transient($cache_key, $cache_data, DAY_IN_SECONDS); + + if (isset($cache_data['message'])) { + echo '

' . wp_kses_post($cache_data['message']) . '

'; + } } -add_action( 'admin_notices', 'version_check' ); +add_action('admin_notices', 'hellotext_version_check'); +/** + * Load plugin text domain. + * + * @return void + */ function hellotext_load_textdomain() { load_plugin_textdomain( 'hellotext', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); } add_action( 'plugins_loaded', 'hellotext_load_textdomain' ); +/** + * Uninstall handler for cleanup. + * + * @return void + */ function uninstall() { global $wpdb; - delete_option('hellotext_business_id'); - delete_option('hellotext_webchat_id'); - delete_option('hellotext_webchat_placement'); - delete_option('hellotext_webchat_behaviour'); - delete_option('hellotext_access_token'); + delete_option(Constants::OPTION_BUSINESS_ID); + delete_option(Constants::OPTION_WEBCHAT_ID); + delete_option(Constants::OPTION_WEBCHAT_PLACEMENT); + delete_option(Constants::OPTION_WEBCHAT_BEHAVIOUR); + delete_option(Constants::OPTION_ACCESS_TOKEN); $api_keys_table = $wpdb->prefix . 'woocommerce_api_keys'; if ($wpdb->get_var("SHOW TABLES LIKE '$api_keys_table'") === $api_keys_table) { diff --git a/src/Adapters/OrderAdapter.php b/src/Adapters/OrderAdapter.php index 92047bc..072daa9 100644 --- a/src/Adapters/OrderAdapter.php +++ b/src/Adapters/OrderAdapter.php @@ -2,45 +2,77 @@ namespace Hellotext\Adapters; -use Hellotext\Adapters\ProductAdapter; -use Hellotext\Adapters\PriceAdapter; - +/** + * OrderAdapter + * + * Transforms WooCommerce orders into Hellotext payloads. + * + * @package Hellotext\Adapters + */ class OrderAdapter { - public $order; // WooCommerce Order - public $products; // Order items + /** + * WooCommerce order instance. + * + * @var \WC_Order|false + */ + public \WC_Order|false $order; + + /** + * Adapted order items. + * + * @var array + */ + public array $products; - public function __construct ($order, $products = []) { - $this->order = is_numeric($order) ? wc_get_order( $order ) : $order; - $this->products = $products; - } + /** + * Create a new adapter instance. + * + * @param int|\WC_Order $order Order ID or order instance. + * @param array $products Pre-adapted products list. + */ + public function __construct(int|\WC_Order $order, array $products = []) { + $this->order = is_numeric($order) ? wc_get_order($order) : $order; + $this->products = $products; + } - public function get () { - if (!$this->order) { - throw new \Exception('Order not found'); - } + /** + * Get the adapted order payload. + * + * @return array + * @throws \Exception When order is not found. + */ + public function get(): array { + if (!$this->order) { + throw new \Exception('Order not found'); + } - return array( + return [ 'source' => 'woo', 'delivery' => 'deliver', - 'reference' => $this->order->get_id(), - 'items' => $this->adapted_products(), - 'total' => ( new PriceAdapter($this->order->get_total(), $this->order->get_currency()) )->get(), - ); - } + 'reference' => $this->order->get_id(), + 'items' => $this->adapted_products(), + 'total' => (new PriceAdapter($this->order->get_total(), $this->order->get_currency()))->get(), + ]; + } - public function adapted_products () { - $items = $this->order->get_items(); + /** + * Adapt order items to product payloads. + * + * @return array + */ + public function adapted_products(): array { + $items = $this->order->get_items(); - foreach ($items as $item) { - $product = $item->get_product(); + foreach ($items as $item) { + $product = $item->get_product(); $this->products[] = [ - 'product' => ( new ProductAdapter($product) )->get(), + 'product' => (new ProductAdapter($product))->get(), 'quantity' => $item->get_quantity(), ]; - } + } - return $this->products; - } + return $this->products; + } } diff --git a/src/Adapters/PriceAdapter.php b/src/Adapters/PriceAdapter.php index efc9f05..30b2bfb 100644 --- a/src/Adapters/PriceAdapter.php +++ b/src/Adapters/PriceAdapter.php @@ -2,21 +2,50 @@ namespace Hellotext\Adapters; +/** + * PriceAdapter + * + * Normalizes price values to Hellotext payload structure. + * + * @package Hellotext\Adapters + */ class PriceAdapter { - public $price; - public $currency; + /** + * Price amount. + * + * @var float|string + */ + public float|string $price; - public function __construct ($price, $currency = null) { - $this->price = $price; - $this->currency = is_null($currency) ? get_woocommerce_currency() : $currency; - } + /** + * Currency code. + * + * @var string + */ + public string $currency; - public function get () { - return array( - 'amount' => $this->price, - 'currency' => $this->currency, - 'converted_amount' => $this->price, - 'converted_currency' => $this->currency, - ); - } + /** + * Create a new adapter instance. + * + * @param float|string $price Raw price amount. + * @param string|null $currency Currency code or null to use store default. + */ + public function __construct(float|string $price, ?string $currency = null) { + $this->price = $price; + $this->currency = is_null($currency) ? get_woocommerce_currency() : $currency; + } + + /** + * Get the adapted price payload. + * + * @return array + */ + public function get(): array { + return [ + 'amount' => $this->price, + 'currency' => $this->currency, + 'converted_amount' => $this->price, + 'converted_currency' => $this->currency, + ]; + } } diff --git a/src/Adapters/ProductAdapter.php b/src/Adapters/ProductAdapter.php index d42d810..0a6b06b 100644 --- a/src/Adapters/ProductAdapter.php +++ b/src/Adapters/ProductAdapter.php @@ -2,36 +2,57 @@ namespace Hellotext\Adapters; -use Hellotext\Adapters\PriceAdapter; - +/** + * ProductAdapter + * + * Transforms WooCommerce products into Hellotext payloads. + * + * @package Hellotext\Adapters + */ class ProductAdapter { - public $product; // WooCommerce product - - public function __construct ($product) { - $this->product = is_numeric($product) ? wc_get_product( $product ) : $product; - } - - public function get () { - if (!$this->product) { - throw new \Exception('Product not found'); - } - - $response = array( - 'reference' => $this->product->get_id(), - 'source' => 'woo', - 'name' => $this->product->get_name(), - 'categories' => wp_get_post_terms( $this->product->get_id(), 'product_cat', array( 'fields' => 'names' ) ), - 'price' => ( new PriceAdapter($this->product->get_price()) )->get(), - 'tags' => wp_get_post_terms( $this->product->get_id(), 'product_tag', array( 'fields' => 'names' ) ), - 'image_url' => wp_get_attachment_url( $this->product->get_image_id() ), - 'url' => get_permalink( $this->product->get_id() ), - ); - - $response = array_filter($response, function ($value) { - return null != $value && [] != $value; - }); - - return $response; - } + /** + * WooCommerce product instance. + * + * @var \WC_Product|false + */ + public \WC_Product|false $product; + + /** + * Create a new adapter instance. + * + * @param int|\WC_Product $product Product ID or product instance. + */ + public function __construct(int|\WC_Product $product) { + $this->product = is_numeric($product) ? wc_get_product($product) : $product; + } + + /** + * Get the adapted product payload. + * + * @return array + * @throws \Exception When product is not found. + */ + public function get(): array { + if (!$this->product) { + throw new \Exception('Product not found'); + } + + $response = [ + 'reference' => $this->product->get_id(), + 'source' => 'woo', + 'name' => $this->product->get_name(), + 'categories' => wp_get_post_terms($this->product->get_id(), 'product_cat', [ 'fields' => 'names' ]), + 'price' => (new PriceAdapter($this->product->get_price()))->get(), + 'tags' => wp_get_post_terms($this->product->get_id(), 'product_tag', [ 'fields' => 'names' ]), + 'image_url' => wp_get_attachment_url($this->product->get_image_id()), + 'url' => get_permalink($this->product->get_id()), + ]; + + $response = array_filter($response, function ($value) { + return null != $value && [] != $value; + }); + + return $response; + } } diff --git a/src/Adapters/RefundAdapter.php b/src/Adapters/RefundAdapter.php index 475c396..964c7fc 100644 --- a/src/Adapters/RefundAdapter.php +++ b/src/Adapters/RefundAdapter.php @@ -2,32 +2,52 @@ namespace Hellotext\Adapters; -use Hellotext\Adapters\PriceAdapter; - +/** + * RefundAdapter + * + * Transforms WooCommerce refunds into Hellotext payloads. + * + * @package Hellotext\Adapters + */ class RefundAdapter { - public $refund; // WooCommerce Refund - - public function __construct ($refund, $order) { - $this->refund = $refund; - $this->order = $order; - } - - public function get () { - if (!$this->refund) { - throw new \Exception('Refund not found'); - } - - if (!$this->order) { - throw new \Exception('Order not found'); - } - - return array( - 'reference' => $this->refund->get_id(), - 'source' => 'woo', - 'amount' => $this->refund->get_amount(), - 'currency' => $this->refund->get_currency(), - 'total' => ( new PriceAdapter($this->order->get_total(), $this->order->get_currency()) )->get(), - ); - } + /** + * WooCommerce refund instance. + * + * @var \WC_Order_Refund + */ + public \WC_Order_Refund $refund; + + /** + * WooCommerce order instance. + * + * @var \WC_Order + */ + public \WC_Order $order; + + /** + * Create a new adapter instance. + * + * @param \WC_Order_Refund $refund Refund instance. + * @param \WC_Order $order Order instance. + */ + public function __construct(\WC_Order_Refund $refund, \WC_Order $order) { + $this->refund = $refund; + $this->order = $order; + } + + /** + * Get the adapted refund payload. + * + * @return array + */ + public function get(): array { + return [ + 'reference' => $this->refund->get_id(), + 'source' => 'woo', + 'amount' => $this->refund->get_amount(), + 'currency' => $this->refund->get_currency(), + 'total' => (new PriceAdapter($this->order->get_total(), $this->order->get_currency()))->get(), + ]; + } } diff --git a/src/Api/Client.php b/src/Api/Client.php index cf1434b..e0db10b 100644 --- a/src/Api/Client.php +++ b/src/Api/Client.php @@ -2,73 +2,180 @@ namespace Hellotext\Api; +use Hellotext\Constants; + +/** + * Client + * + * HTTP client for communicating with the Hellotext API. + * + * @package Hellotext\Api + */ class Client { - public static $sufix = '/v1'; - - public static function with_sufix ($sufix = '') { - if (0 < strlen($sufix) && '/' !== $sufix[0]) { - $sufix = '/' . $sufix; - } - - self::$sufix = $sufix; - - return new self(); - } - - public static function request ($method = 'GET', $path = '/', $data = []) { - $request_url = self::get_api_url() . $path; - $curl = curl_init($request_url); - self::set_curl_options($curl, $method); - - curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data)); - - $result = curl_exec($curl); - - return array( - 'request' => array( - 'method' => $method, - 'path' => $request_url, - 'data' => $data, - ), - 'status' => curl_getinfo($curl, CURLINFO_HTTP_CODE), - 'body' => json_decode($result, true) - ); - } - - public static function get ($path = '/', $data = null) { - return self::request('GET', $path, $data); - } - - public static function post ($path = '/', $data = null) { - return self::request('POST', $path, $data); - } - - public static function patch ($path = '/', $data = null) { - return self::request('PATCH', $path, $data); - } - - public static function put ($path = '/', $data = null) { - return self::request('PUT', $path, $data); - } - - public static function delete ($path = '/', $data = null) { - return self::request('DELETE', $path, $data); - } - - private static function get_api_url () { - global $HELLOTEXT_API_URL; - - return $HELLOTEXT_API_URL . self::$sufix; - } - - private static function set_curl_options ($curl, $method) { - $hellotext_access_token = get_option('hellotext_access_token'); - - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'Content-Type: application/json', - 'Authorization: Bearer ' . $hellotext_access_token, - )); - } + /** + * API URL suffix. + * + * @var string + */ + public static string $sufix = '/' . Constants::API_VERSION; + + /** + * Set a custom API suffix and return a client instance. + * + * @param string $sufix Suffix to append to API URL. + * @return self + */ + public static function with_sufix(string $sufix = ''): self { + if (0 < strlen($sufix) && '/' !== $sufix[0]) { + $sufix = '/' . $sufix; + } + + self::$sufix = $sufix; + + return new self(); + } + + /** + * Make an HTTP request to the Hellotext API. + * + * @param string $method HTTP method. + * @param string $path API endpoint path. + * @param array $data Request payload. + * @return array + */ + public static function request(string $method = 'GET', string $path = '/', array $data = []): array { + $request_url = self::get_api_url() . $path; + $access_token = get_option(Constants::OPTION_ACCESS_TOKEN); + + $args = [ + 'method' => strtoupper($method), + 'timeout' => 15, + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $access_token, + ], + 'sslverify' => true, + ]; + + // Add body for non-GET requests + if (!empty($data) && 'GET' !== $method) { + $args['body'] = json_encode($data); + } + + $response = wp_remote_request($request_url, $args); + + // Handle errors + if (is_wp_error($response)) { + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log(sprintf( + '[Hellotext] API request failed: %s %s - Error: %s', + $method, + $request_url, + $response->get_error_message() + )); + } + + return [ + 'request' => [ + 'method' => $method, + 'path' => $request_url, + 'data' => $data, + ], + 'status' => 0, + 'body' => null, + 'error' => $response->get_error_message(), + ]; + } + + $status_code = wp_remote_retrieve_response_code($response); + $body_raw = wp_remote_retrieve_body($response); + + // Log non-2xx responses + if (defined('WP_DEBUG') && WP_DEBUG && ($status_code < 200 || $status_code >= 300)) { + error_log(sprintf( + '[Hellotext] API request returned %d: %s %s', + $status_code, + $method, + $request_url + )); + } + + return [ + 'request' => [ + 'method' => $method, + 'path' => $request_url, + 'data' => $data, + ], + 'status' => $status_code, + 'body' => !empty($body_raw) ? json_decode($body_raw, true) : null, + 'error' => null, + ]; + } + + /** + * Make a GET request. + * + * @param string $path API endpoint path. + * @param array|null $data Request payload. + * @return array + */ + public static function get(string $path = '/', ?array $data = null): array { + return self::request('GET', $path, $data ?? []); + } + + /** + * Make a POST request. + * + * @param string $path API endpoint path. + * @param array|null $data Request payload. + * @return array + */ + public static function post(string $path = '/', ?array $data = null): array { + return self::request('POST', $path, $data ?? []); + } + + /** + * Make a PATCH request. + * + * @param string $path API endpoint path. + * @param array|null $data Request payload. + * @return array + */ + public static function patch(string $path = '/', ?array $data = null): array { + return self::request('PATCH', $path, $data ?? []); + } + + /** + * Make a PUT request. + * + * @param string $path API endpoint path. + * @param array|null $data Request payload. + * @return array + */ + public static function put(string $path = '/', ?array $data = null): array { + return self::request('PUT', $path, $data ?? []); + } + + /** + * Make a DELETE request. + * + * @param string $path API endpoint path. + * @param array|null $data Request payload. + * @return array + */ + public static function delete(string $path = '/', ?array $data = null): array { + return self::request('DELETE', $path, $data ?? []); + } + + /** + * Get base API URL. + * + * @return string + */ + private static function get_api_url(): string { + global $HELLOTEXT_API_URL; + + return $HELLOTEXT_API_URL . self::$sufix; + } + } diff --git a/src/Api/Event.php b/src/Api/Event.php index 99492ad..1e33b3a 100644 --- a/src/Api/Event.php +++ b/src/Api/Event.php @@ -2,54 +2,101 @@ namespace Hellotext\Api; +use Hellotext\Constants; + +/** + * Event + * + * Tracks events by posting to the Hellotext API. + * + * @package Hellotext\Api + */ class Event { - public function __construct ($session = null) { - $this->hellotext_business_id = get_option('hellotext_business_id'); - $this->session = $session; - $this->curl = curl_init($this->get_api_url()); - $this->set_curl_options(); - } - - public function track ($action, $payload) { - $body = array_merge( - array( - 'action' => $action, - 'session' => ( isset($this->session) && $this->session ) - ? $this->session - : $this->browser_session(), - ), - $payload - ); - - curl_setopt($this->curl, CURLOPT_POSTFIELDS, json_encode($body)); - - $result = curl_exec($this->curl); - - curl_close($this->curl); - } - - private function set_curl_options () { - curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'POST'); - - // Headers - curl_setopt($this->curl, CURLOPT_HTTPHEADER, array( - 'Content-Type: application/json', - 'Authorization: Bearer ' . $this->hellotext_business_id, - )); - } - - private function get_api_url () { - global $HELLOTEXT_API_URL; - - return $HELLOTEXT_API_URL . '/v1/track/events'; - } - - private function browser_session () { - if (isset($_COOKIE['hello_session'])) { - return sanitize_text_field($_COOKIE['hello_session']); - } - - return null; - } + /** + * Hellotext business ID token. + * + * @var string + */ + private string $hellotext_business_id; + + /** + * Session identifier. + * + * @var string|null + */ + private ?string $session; + + /** + * Create a new event tracker. + * + * @param string|null $session Optional session identifier. + */ + public function __construct(?string $session = null) { + $this->hellotext_business_id = get_option(Constants::OPTION_BUSINESS_ID); + $this->session = $session; + } + + /** + * Track an event. + * + * @param string $action Event action name. + * @param array $payload Event payload data. + * @return void + */ + public function track(string $action, array $payload): void { + $body = array_merge( + [ + 'action' => $action, + 'session' => $this->session ?? $this->browser_session(), + ], + $payload + ); + + $response = wp_remote_post( + $this->get_api_url(), + [ + 'timeout' => 10, + 'blocking' => false, + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->hellotext_business_id, + ], + 'body' => json_encode($body), + 'sslverify' => true, + ] + ); + + // Only log if there's an error and debug is enabled + if (is_wp_error($response) && defined('WP_DEBUG') && WP_DEBUG) { + error_log(sprintf( + '[Hellotext] Event tracking failed for action "%s": %s', + $action, + $response->get_error_message() + )); + } + } + + /** + * Get the event tracking API URL. + * + * @return string + */ + private function get_api_url(): string { + global $HELLOTEXT_API_URL; + + return $HELLOTEXT_API_URL . Constants::API_ENDPOINT_TRACK; + } + + /** + * Retrieve session from browser cookie. + * + * @return string|null + */ + private function browser_session(): ?string { + if (isset($_COOKIE[Constants::SESSION_COOKIE_NAME])) { + return sanitize_text_field($_COOKIE[Constants::SESSION_COOKIE_NAME]); + } + + return null; + } } diff --git a/src/Api/Webchat.php b/src/Api/Webchat.php index eeda2e8..a07782e 100644 --- a/src/Api/Webchat.php +++ b/src/Api/Webchat.php @@ -2,17 +2,29 @@ namespace Hellotext\Api; -use Hellotext\Api\Client; +use Hellotext\Constants; +/** + * Webchat + * + * Retrieves webchat configuration from the Hellotext API. + * + * @package Hellotext\Api + */ class Webchat { - public static function index() { - $hellotext_access_token = get_option('hellotext_access_token'); + /** + * Fetch webchat IDs for the current business. + * + * @return array + */ + public static function index(): array { + $hellotext_access_token = get_option(Constants::OPTION_ACCESS_TOKEN); - if(!$hellotext_access_token) { + if (!$hellotext_access_token) { return []; } - $body = Client::with_sufix()->get('/v1/wordpress/webchats'); + $body = Client::with_sufix()->get(Constants::API_ENDPOINT_WEBCHATS); return is_array($body['body']) && isset($body['body']['ids']) ? $body['body']['ids'] : []; } } diff --git a/src/Constants.php b/src/Constants.php new file mode 100644 index 0000000..9c79959 --- /dev/null +++ b/src/Constants.php @@ -0,0 +1,53 @@ +prefix . 'woocommerce_api_keys'; - $api_keys = $wpdb->get_row("SELECT * FROM $api_keys_table WHERE description = 'Hellotext'"); - if (!$api_keys) { - // Create a new API key - $api_keys = (object) [ - 'consumer_key' => 'ck_' . wc_rand_hash(), - 'consumer_secret' => 'cs_' . wc_rand_hash(), - ]; - - // wc_api_hash will hash the $api_keys->consumer_key in place. - $conusmer_key = $api_keys->consumer_key; - - $wpdb->insert($api_keys_table, [ - 'user_id' => get_current_user_id(), - 'description' => 'Hellotext', - 'permissions' => 'read_write', - 'consumer_key' => wc_api_hash($conusmer_key), - 'consumer_secret' => $api_keys->consumer_secret, - 'truncated_key' => substr($api_keys->consumer_key, -7), - ]); +/** + * Create WooCommerce integration in Hellotext. + * + * @param mixed $business_id Business ID token. + * @return void + */ +add_action('hellotext_create_integration', function (mixed $business_id): void { + if (!$business_id) { + $business_id = get_option(Constants::OPTION_BUSINESS_ID); + } + + global $wpdb; + $api_keys_table = $wpdb->prefix . 'woocommerce_api_keys'; + $api_keys = $wpdb->get_row("SELECT * FROM $api_keys_table WHERE description = 'Hellotext'"); + if (!$api_keys) { + // Create a new API key + $api_keys = (object) [ + 'consumer_key' => 'ck_' . wc_rand_hash(), + 'consumer_secret' => 'cs_' . wc_rand_hash(), + ]; + + // wc_api_hash will hash the $api_keys->consumer_key in place. + $conusmer_key = $api_keys->consumer_key; + + $wpdb->insert($api_keys_table, [ + 'user_id' => get_current_user_id(), + 'description' => 'Hellotext', + 'permissions' => 'read_write', + 'consumer_key' => wc_api_hash($conusmer_key), + 'consumer_secret' => $api_keys->consumer_secret, + 'truncated_key' => substr($api_keys->consumer_key, -7), + ]); Client::with_sufix() - ->post('/integrations/woo', [ + ->post(Constants::API_ENDPOINT_INTEGRATIONS_WOO, [ 'shop' => [ 'name' => get_bloginfo('name'), 'url' => get_bloginfo('url'), @@ -48,38 +59,62 @@ function hellotext_activate () { 'consumer_key' => $api_keys->consumer_key, 'consumer_secret' => $api_keys->consumer_secret, 'currency' => get_woocommerce_currency(), - ] + ], ]); delete_transient('hellotext_integration_triggered'); - } + } }); -function after_business_id_save($old_value, $new_value) { +/** + * Handle business ID update. + * + * @param mixed $old_value Previous value. + * @param mixed $new_value New value. + * @return void + */ +function after_business_id_save(mixed $old_value, mixed $new_value): void { if ($old_value !== $new_value) { maybe_trigger_integration($new_value); } } -function after_business_id_set($value) { +/** + * Handle business ID creation. + * + * @param mixed $value Business ID value. + * @return void + */ +function after_business_id_set(mixed $value): void { maybe_trigger_integration($value); } -function maybe_trigger_integration($business_id) { +/** + * Trigger integration creation when possible. + * + * @param mixed $business_id Business ID value. + * @return void + */ +function maybe_trigger_integration(mixed $business_id): void { $integration_flag = get_transient('hellotext_integration_triggered'); if ($integration_flag) { return; } - $hellotext_access_token = get_option('hellotext_access_token'); + $hellotext_access_token = get_option(Constants::OPTION_ACCESS_TOKEN); if ($hellotext_access_token && $business_id) { set_transient('hellotext_integration_triggered', true, 10); do_action('hellotext_create_integration'); } else { - add_action('shutdown', function () { + /** + * Attempt integration creation during shutdown. + * + * @return void + */ + add_action('shutdown', function (): void { $integration_flag = get_transient('hellotext_integration_triggered'); - $hellotext_access_token = get_option('hellotext_access_token'); + $hellotext_access_token = get_option(Constants::OPTION_ACCESS_TOKEN); if ($hellotext_access_token && !$integration_flag) { set_transient('hellotext_integration_triggered', true, 10); // Expires in 10 seconds. @@ -89,7 +124,7 @@ function maybe_trigger_integration($business_id) { } } -add_action('add_option_hellotext_business_id', 'after_business_id_set', 10, 1); -add_action('add_option_hellotext_access_token', 'maybe_trigger_integration', 10, 3); -add_action('update_option_hellotext_business_id', 'after_business_id_save', 10, 3); -add_action('update_option_hellotext_access_token', 'maybe_trigger_integration', 10, 4); +add_action('add_option_' . Constants::OPTION_BUSINESS_ID, 'after_business_id_set', 10, 1); +add_action('add_option_' . Constants::OPTION_ACCESS_TOKEN, 'maybe_trigger_integration', 10, 3); +add_action('update_option_' . Constants::OPTION_BUSINESS_ID, 'after_business_id_save', 10, 3); +add_action('update_option_' . Constants::OPTION_ACCESS_TOKEN, 'maybe_trigger_integration', 10, 4); diff --git a/src/Events/AppRemoved.php b/src/Events/AppRemoved.php index 37c3b56..d431bc3 100644 --- a/src/Events/AppRemoved.php +++ b/src/Events/AppRemoved.php @@ -1,25 +1,37 @@ delete('/integrations/woo', [ - 'shop' => [ - 'business_id' => $business_id, - ] - ]); +/** + * Remove WooCommerce integration from Hellotext. + * + * @param mixed $business_id Business ID token. + * @return void + */ +add_action('hellotext_remove_integration', function (mixed $business_id): void { + Client::with_sufix() + ->delete(Constants::API_ENDPOINT_INTEGRATIONS_WOO, [ + 'shop' => [ + 'business_id' => $business_id, + ], + ]); }); diff --git a/src/Events/CartUpdates.php b/src/Events/CartUpdates.php index d56e59b..324e5d8 100644 --- a/src/Events/CartUpdates.php +++ b/src/Events/CartUpdates.php @@ -2,37 +2,48 @@ use Hellotext\Adapters\ProductAdapter; use Hellotext\Api\Event; +use Hellotext\Constants; // We could listen for woocommerce_cart_updated event but this event is // triggered too many times per cart update. Instead, we listen for the // cart page load and trigger our own event. -add_action( 'woocommerce_after_cart', 'hellotext_trigger_cart_updated' ); -add_action( 'woocommerce_add_to_cart', 'hellotext_trigger_cart_updated' ); -add_action( 'woocommerce_cart_item_removed', 'hellotext_trigger_cart_updated' ); -add_action( 'woocommerce_after_cart_item_quantity_update', 'hellotext_trigger_cart_updated' ); - -function hellotext_trigger_cart_updated () { - do_action('hellotext_create_profile'); - do_action('hellotext_woocommerce_cart_updated'); +add_action('woocommerce_after_cart', 'hellotext_trigger_cart_updated'); +add_action('woocommerce_add_to_cart', 'hellotext_trigger_cart_updated'); +add_action('woocommerce_cart_item_removed', 'hellotext_trigger_cart_updated'); +add_action('woocommerce_after_cart_item_quantity_update', 'hellotext_trigger_cart_updated'); + +/** + * Trigger cart update tracking actions. + * + * @return void + */ +function hellotext_trigger_cart_updated(): void { + do_action('hellotext_create_profile'); + do_action('hellotext_woocommerce_cart_updated'); } add_action('hellotext_woocommerce_cart_updated', 'hellotext_cart_updated'); -function hellotext_cart_updated() { +/** + * Track cart item add/remove changes. + * + * @return void + */ +function hellotext_cart_updated(): void { wc_load_cart(); - $changes = array( - 'added' => array(), - 'removed' => array() - ); + $changes = [ + 'added' => [], + 'removed' => [], + ]; // Set previous cart items and current cart items - $previous_cart_items = isset($_SESSION['hellotext_cart_items']) - ? json_decode(sanitize_text_field($_SESSION['hellotext_cart_items']), true) - : array(); + $previous_cart_items = isset($_SESSION[Constants::SESSION_CART_ITEMS]) + ? json_decode(sanitize_text_field($_SESSION[Constants::SESSION_CART_ITEMS]), true) + : []; $current_cart_items = WC()->cart->get_cart(); - $cart_items = array(); + $cart_items = []; foreach ($current_cart_items as $key => $cart_item) { $product = $cart_item['data']; // WC_Product object @@ -44,57 +55,66 @@ function hellotext_cart_updated() { } // Save current cart items to session - $_SESSION['hellotext_cart_items'] = json_encode($cart_items); + $_SESSION[Constants::SESSION_CART_ITEMS] = json_encode($cart_items); // Calculate total cart value $cart_total = WC()->cart->get_cart_contents_total(); $currency = get_woocommerce_currency(); // Get current page URL - $current_url = home_url(add_query_arg(array(), $GLOBALS['wp']->request)); + $current_url = home_url(add_query_arg([], $GLOBALS['wp']->request)); foreach ($cart_items as $cart_item) { - $match = array_filter( - $previous_cart_items, - fn($item) => $item['product']['reference'] == $cart_item['product']['reference'] - ); + $match = array_filter( + $previous_cart_items, + fn ($item) => $item['product']['reference'] == $cart_item['product']['reference'] + ); - $previous_item = count($match) > 0 ? array_shift($match) : null; + $previous_item = count($match) > 0 ? array_shift($match) : null; - if (!$previous_item || $previous_item['quantity'] < $cart_item['quantity']) { - $changes['added'][] = $cart_item; - } + if (!$previous_item || $previous_item['quantity'] < $cart_item['quantity']) { + $changes['added'][] = $cart_item; + } } // Add items that were removed from the cart foreach ($previous_cart_items as $previous_item) { - $match = array_filter( - $cart_items, - fn($item) => $item['product']['reference'] == $previous_item['product']['reference'] - ); + $match = array_filter( + $cart_items, + fn ($item) => $item['product']['reference'] == $previous_item['product']['reference'] + ); - $cart_item = count($match) > 0 ? array_shift($match) : null; + $cart_item = count($match) > 0 ? array_shift($match) : null; - if (!$cart_item || $previous_item['quantity'] > $cart_item['quantity']) { - $changes['removed'][] = $previous_item; // Use previous_item here as it's the removed item - } + if (!$cart_item || $previous_item['quantity'] > $cart_item['quantity']) { + $changes['removed'][] = $previous_item; // Use previous_item here as it's the removed item + } } // Trigger events, one for added and one for removed items + $event_map = [ + 'added' => Constants::EVENT_CART_ADDED, + 'removed' => Constants::EVENT_CART_REMOVED, + ]; + foreach ($changes as $event => $items) { - if (0 == count($items)) { - continue; - } - - $event_data = array( - 'amount' => $cart_total, - 'currency' => $currency, - 'url' => $current_url, - 'object_parameters' => array( - 'items' => $items - ) - ); - - (new Event())->track("cart.{$event}", $event_data); + if (0 == count($items)) { + continue; + } + + if (!isset($event_map[$event])) { + continue; + } + + $event_data = [ + 'amount' => $cart_total, + 'currency' => $currency, + 'url' => $current_url, + 'object_parameters' => [ + 'items' => $items, + ], + ]; + + (new Event())->track($event_map[$event], $event_data); } } diff --git a/src/Events/CouponRedeemed.php b/src/Events/CouponRedeemed.php index 47afe87..d5217b4 100644 --- a/src/Events/CouponRedeemed.php +++ b/src/Events/CouponRedeemed.php @@ -1,27 +1,33 @@ is_coupon_valid($coupon); + $valid = $discounts->is_coupon_valid($coupon); - - if ($valid) { - ( new Event() )->track('coupon.redeemed', [ - 'object_parameters' => [ - 'type' => 'coupon', - 'reference' => $coupon->get_id(), - 'code' => $code, - 'description' => $coupon->get_description(), - 'destination_url' => site_url('/cart'), - ] - ]); - } + if ($valid) { + (new Event())->track(Constants::EVENT_COUPON_REDEEMED, [ + 'object_parameters' => [ + 'type' => 'coupon', + 'reference' => $coupon->get_id(), + 'code' => $code, + 'description' => $coupon->get_description(), + 'destination_url' => site_url('/cart'), + ], + ]); + } } diff --git a/src/Events/CustomOptionsUpdated.php b/src/Events/CustomOptionsUpdated.php index 02cdffc..e805cdf 100644 --- a/src/Events/CustomOptionsUpdated.php +++ b/src/Events/CustomOptionsUpdated.php @@ -1,12 +1,22 @@ get_user_id(); - $userId = $userId > 0 ? $userId : $order->data['billing']; +/** + * Track order placement. + * + * @param \WC_Order $order WooCommerce order instance. + * @return void + */ +function hellotext_order_placed(\WC_Order $order): void { + $userId = $order->get_user_id(); + $userId = $userId > 0 ? $userId : $order->data['billing']; - do_action('hellotext_create_profile', $userId ?? $order->data['billing']); + do_action('hellotext_create_profile', $userId ?? $order->data['billing']); - $event = new Event(); - $parsedOrder = ( new OrderAdapter($order) )->get(); + $event = new Event(); + $parsedOrder = (new OrderAdapter($order))->get(); - $session = isset($_COOKIE['hello_session']) - ? sanitize_text_field($_COOKIE['hello_session']) - : null; - $encrypted_session = Session::encrypt($session); - add_post_meta($order->get_id(), 'hellotext_session', $encrypted_session); + $session = isset($_COOKIE[Constants::SESSION_COOKIE_NAME]) + ? sanitize_text_field($_COOKIE[Constants::SESSION_COOKIE_NAME]) + : null; + $encrypted_session = Session::encrypt($session); + add_post_meta($order->get_id(), Constants::META_SESSION, $encrypted_session); - $event->track('order.placed', array( - 'object_parameters' => $parsedOrder, - )); + $event->track(Constants::EVENT_ORDER_PLACED, [ + 'object_parameters' => $parsedOrder, + ]); } diff --git a/src/Events/OrderStatus.php b/src/Events/OrderStatus.php index 13f975a..c40fad1 100644 --- a/src/Events/OrderStatus.php +++ b/src/Events/OrderStatus.php @@ -1,37 +1,47 @@ get_user_id()); - - switch ($new_status) { - case 'processing': - $event->track('order.confirmed', array( - 'object_parameters' => $orderAdapter->get(), - )); - break; - - case 'cancelled': - $event->track('order.cancelled', array( - 'object_parameters' => $orderAdapter->get(), - )); - break; - - case 'completed': - $event->track('order.delivered', array( - 'object_parameters' => $orderAdapter->get(), - )); - break; - } +/** + * Track order status transitions. + * + * @param int $order_id Order ID. + * @param string $old_status Previous status. + * @param string $new_status New status. + * @param \WC_Order $order WooCommerce order instance. + * @return void + */ +function track_order_status(int $order_id, string $old_status, string $new_status, \WC_Order $order): void { + $encrypted_session = get_post_meta($order_id, Constants::META_SESSION, true); + $session = Session::decrypt($encrypted_session); + + $orderAdapter = new OrderAdapter($order); + $event = new Event($session); + + do_action('hellotext_create_profile', $order->get_user_id()); + + switch ($new_status) { + case 'processing': + $event->track(Constants::EVENT_ORDER_CONFIRMED, [ + 'object_parameters' => $orderAdapter->get(), + ]); + break; + + case 'cancelled': + $event->track(Constants::EVENT_ORDER_CANCELLED, [ + 'object_parameters' => $orderAdapter->get(), + ]); + break; + + case 'completed': + $event->track(Constants::EVENT_ORDER_DELIVERED, [ + 'object_parameters' => $orderAdapter->get(), + ]); + break; + } } diff --git a/src/Events/ProductViewed.php b/src/Events/ProductViewed.php index ac4cf5c..5b92b59 100644 --- a/src/Events/ProductViewed.php +++ b/src/Events/ProductViewed.php @@ -2,15 +2,21 @@ use Hellotext\Adapters\ProductAdapter; use Hellotext\Api\Event; +use Hellotext\Constants; add_action('woocommerce_after_single_product', 'hellotext_product_viewed'); -function hellotext_product_viewed() { - global $product; +/** + * Track product view event. + * + * @return void + */ +function hellotext_product_viewed(): void { + global $product; - do_action('hellotext_create_profile'); + do_action('hellotext_create_profile'); - ( new Event() )->track('product.viewed', array( - 'object_parameters' => ( new ProductAdapter($product) )->get() - )); + (new Event())->track(Constants::EVENT_PRODUCT_VIEWED, [ + 'object_parameters' => (new ProductAdapter($product))->get(), + ]); } diff --git a/src/Events/RefundReceived.php b/src/Events/RefundReceived.php index 116b0c5..666e3ed 100644 --- a/src/Events/RefundReceived.php +++ b/src/Events/RefundReceived.php @@ -1,21 +1,29 @@ get_user_id()); + do_action('hellotext_create_profile', $order->get_user_id()); - $encrypted_session = get_post_meta($order_id, 'hellotext_session', true); - $session = Session::decrypt($encrypted_session); + $encrypted_session = get_post_meta($order_id, Constants::META_SESSION, true); + $session = Session::decrypt($encrypted_session); - ( new Event($session) )->track('refund.received', array( - 'object_parameters' => ( new RefundAdapter($refund, $order) )->get(), - )); + (new Event($session))->track(Constants::EVENT_REFUND_RECEIVED, [ + 'object_parameters' => (new RefundAdapter($refund, $order))->get(), + ]); } diff --git a/src/Events/UserRegistered.php b/src/Events/UserRegistered.php index e4e4932..7ecc2a8 100644 --- a/src/Events/UserRegistered.php +++ b/src/Events/UserRegistered.php @@ -2,9 +2,15 @@ use Hellotext\Services\CreateProfile; -add_action( 'user_register', 'hellotext_user_registered', 10, 1 ); +add_action('user_register', 'hellotext_user_registered', 10, 1); -function hellotext_user_registered ($user_id) { - $service = new CreateProfile($user_id); - $service->process(); +/** + * Create a Hellotext profile when a user registers. + * + * @param int $user_id WordPress user ID. + * @return void + */ +function hellotext_user_registered(int $user_id): void { + $service = new CreateProfile($user_id); + $service->process(); } diff --git a/src/Misc/Scripts.php b/src/Misc/Scripts.php index 3bde4a8..21c5d0e 100644 --- a/src/Misc/Scripts.php +++ b/src/Misc/Scripts.php @@ -1,10 +1,15 @@ ' . wp_kses( __( 'description.paragraphs.one', 'hellotext' ), array( 'a' => array( 'href' => array(), 'target' => array(), 'style' => array() ) ) ) . '

'; + echo '

' . wp_kses(__('description.paragraphs.one', 'hellotext'), [ 'a' => [ 'href' => [], 'target' => [], 'style' => [] ] ]) . '

'; } if (!$access_token) { - echo '

' . wp_kses( __( 'description.paragraphs.two', 'hellotext' ), array( 'a' => array( 'href' => array(), 'target' => array(), 'style' => array() ) ) ) . '

'; + echo '

' . wp_kses(__('description.paragraphs.two', 'hellotext'), [ 'a' => [ 'href' => [], 'target' => [], 'style' => [] ] ]) . '

'; } } -function hellotext_business_id_field() { +/** + * Render the business ID field. + * + * @return void + */ +function hellotext_business_id_field(): void { ?> - - + ' . __('webchat_unavailable', 'hellotext') . '

'; @@ -102,7 +128,9 @@ function hellotext_webchat_id_field() { } ?> - @@ -114,55 +142,83 @@ function hellotext_webchat_id_field() { - + - - - - + + +/** + * Initialize Hellotext admin UI hooks. + * + * @return void + */ +function init_hellotext(): void { + /** + * Register the WooCommerce submenu for Hellotext. + * + * @return void + */ + function custom_woocommerce_menu(): void { + add_submenu_page( + 'woocommerce', + 'Hellotext', + 'Hellotext', + 'manage_options', + 'wc-hellotext', + 'hellotext_submenu_page_callback' + ); + } + add_action('admin_menu', 'custom_woocommerce_menu'); + + /** + * Render the Hellotext settings page. + * + * @return void + */ + function hellotext_submenu_page_callback(): void { + ?>

hellotext @@ -171,28 +227,28 @@ function hellotext_submenu_page_callback () {

Important: Please select any Permalink structure other than "Plain" in Settings > Permalinks. Otherwise, the plugin will not work.
HTML; } - ?> + ?>
'background-color: #FF4C00; color: #FFFFFF; border: none;') - ); - ?> + settings_fields('hellotext-form'); + do_settings_sections('hellotext-form'); + submit_button( + __('settings.submit', 'hellotext'), + null, + null, + false, + ['style' => 'background-color: #FF4C00; color: #FFFFFF; border: none;'] + ); + ?>
user_id = $user_id; - $this->session = isset($_COOKIE['hello_session']) ? sanitize_text_field($_COOKIE['hello_session']) : null; - $this->client = Client::class; - $this->billing = $billing; - } - - public function process () { - if (isset($this->billing) && !empty($this->billing)) { - $this->create_hellotext_profile(); - $this->attach_profile_to_session(); - return; - } - - if (! $this->user_id) { - return; - } - - if (! $this->verify_if_profile_exists()) { - $this->get_user(); - $this->create_hellotext_profile(); - $this->attach_profile_to_session(); - } - - if ($this->session_changed()) { - $this->attach_profile_to_session(); - } - } - - private function get_user () { - $this->user = get_user_by('id', $this->user_id); - - if (!$this->user) { - throw new \Exception("User with id {$this->user_id} not found"); - } - } - - private function verify_if_profile_exists () { - $hellotext_profile_id = get_user_meta($this->user_id ?? $this->session, 'hellotext_profile_id', true); - - return false != $hellotext_profile_id && '' != $hellotext_profile_id; - } - - private function session_changed () { - return get_user_meta($this->user_id, 'hellotext_session', true) != $this->session; - } - - public function create_hellotext_profile () { - $profile = get_user_meta($this->user_id ?? $this->session, 'hellotext_profile_id', true); - - if ($profile) { - if (isset($this->user)) { - update_user_meta($this->user->ID, 'hellotext_profile_id', $profile); - } - - $this->client::patch("/sessions/{$this->session}", array( + /** + * WordPress user instance. + * + * @var \WP_User|false|null + */ + public \WP_User|false|null $user = null; + + /** + * Hellotext profile ID. + * + * @var string|null + */ + public ?string $hellotext_profile_id = null; + + /** + * API client class name. + * + * @var string + */ + public string $client; + + /** + * Billing payload data. + * + * @var array + */ + public array $billing; + + /** + * WordPress user ID. + * + * @var int|null + */ + public ?int $user_id; + + /** + * Session identifier. + * + * @var string|null + */ + public ?string $session; + + /** + * Create a new profile service instance. + * + * @param int|null $user_id WordPress user ID. + * @param array $billing Billing payload data. + */ + public function __construct(?int $user_id, array $billing = []) { + $this->user_id = $user_id; + $this->session = isset($_COOKIE[Constants::SESSION_COOKIE_NAME]) + ? sanitize_text_field($_COOKIE[Constants::SESSION_COOKIE_NAME]) + : null; + $this->client = Client::class; + $this->billing = $billing; + } + + /** + * Process profile creation/association flow. + * + * @return void + */ + public function process(): void { + if (isset($this->billing) && !empty($this->billing)) { + $this->create_hellotext_profile(); + $this->attach_profile_to_session(); + return; + } + + if (! $this->user_id) { + return; + } + + if (! $this->verify_if_profile_exists()) { + $this->get_user(); + $this->create_hellotext_profile(); + $this->attach_profile_to_session(); + } + + if ($this->session_changed()) { + $this->attach_profile_to_session(); + } + } + + /** + * Load the WordPress user by ID. + * + * @return void + * @throws \Exception When user is not found. + */ + private function get_user(): void { + $this->user = get_user_by('id', $this->user_id); + + if (!$this->user) { + throw new \Exception("User with id {$this->user_id} not found"); + } + } + + /** + * Check if a Hellotext profile already exists. + * + * @return bool + */ + private function verify_if_profile_exists(): bool { + $hellotext_profile_id = get_user_meta($this->user_id ?? $this->session, Constants::META_PROFILE_ID, true); + + return false != $hellotext_profile_id && '' != $hellotext_profile_id; + } + + /** + * Determine if the session value has changed. + * + * @return bool + */ + private function session_changed(): bool { + return get_user_meta($this->user_id, Constants::META_SESSION, true) != $this->session; + } + + /** + * Create a Hellotext profile or reuse existing one. + * + * @return void + */ + public function create_hellotext_profile(): void { + $profile = get_user_meta($this->user_id ?? $this->session, Constants::META_PROFILE_ID, true); + + if ($profile) { + if (isset($this->user)) { + update_user_meta($this->user->ID, Constants::META_PROFILE_ID, $profile); + } + + $this->client::patch(Constants::API_ENDPOINT_SESSIONS . "/{$this->session}", [ 'session' => $this->session, 'profile' => $profile, - )); + ]); - return; - } + return; + } - $phone = get_user_meta($this->user_id, 'billing_phone', true); + $phone = get_user_meta($this->user_id, 'billing_phone', true); - $response = $this->client::post('/profiles', array_filter(array( - 'session' => $this->session, - 'reference' => isset($this->user) ? $this->user->ID : null, - 'first_name' => $this->user->nickname ?? $this->billing['first_name'], - 'last_name' => $this->user->last_name ?? $this->billing['last_name'], - 'email' => $this->user->user_email ?? $this->billing['email'], - 'phone' => empty($phone) ? $this->billing['phone'] : $phone, - 'lists' => array('WooCommerce'), - ))); + $response = $this->client::post(Constants::API_ENDPOINT_PROFILES, array_filter([ + 'session' => $this->session, + 'reference' => isset($this->user) ? $this->user->ID : null, + 'first_name' => $this->user->nickname ?? $this->billing['first_name'], + 'last_name' => $this->user->last_name ?? $this->billing['last_name'], + 'email' => $this->user->user_email ?? $this->billing['email'], + 'phone' => empty($phone) ? $this->billing['phone'] : $phone, + 'lists' => ['WooCommerce'], + ])); - add_user_meta( $this->user_id ?? $this->session, 'hellotext_profile_id', $response['body']['id'], true ); + add_user_meta($this->user_id ?? $this->session, Constants::META_PROFILE_ID, $response['body']['id'], true); - $this->client::patch("/sessions/{$this->session}", array( + $this->client::patch(Constants::API_ENDPOINT_SESSIONS . "/{$this->session}", [ 'session' => $this->session, 'profile' => $response['body']['id'], - )); - } - - private function attach_profile_to_session () { - $profile_id = get_user_meta($this->user_id ?? $this->session, 'hellotext_profile_id', true); - - $response = $this->client::patch("/sessions/{$this->session}", array( - 'session' => $this->session, - 'profile' => $profile_id, - )); - } + ]); + } + + /** + * Attach profile ID to current session. + * + * @return void + */ + private function attach_profile_to_session(): void { + $profile_id = get_user_meta($this->user_id ?? $this->session, Constants::META_PROFILE_ID, true); + + $response = $this->client::patch(Constants::API_ENDPOINT_SESSIONS . "/{$this->session}", [ + 'session' => $this->session, + 'profile' => $profile_id, + ]); + } } -add_action('hellotext_create_profile', function ($payload = null) { - if (is_array($payload)) { - ( new CreateProfile(null, $payload) )->process(); - } - - if (!is_user_logged_in() && !isset($user_id)) { - return; - } - - $user = ( null != $user_id ) - ? get_user_by('id', $user_id) - : wp_get_current_user(); - - if (!$user) { - return; - } - - ( new CreateProfile($user->ID) )->process(); +/** + * Handle Hellotext profile creation action. + * + * @param mixed $payload Billing payload data. + * @return void + */ +add_action('hellotext_create_profile', function (mixed $payload = null): void { + if (is_array($payload)) { + (new CreateProfile(null, $payload))->process(); + } + + if (!is_user_logged_in() && !isset($user_id)) { + return; + } + + $user = (null != $user_id) + ? get_user_by('id', $user_id) + : wp_get_current_user(); + + if (!$user) { + return; + } + + (new CreateProfile($user->ID))->process(); }, 10, 1); diff --git a/src/Services/Session.php b/src/Services/Session.php index 5312294..da85a0d 100644 --- a/src/Services/Session.php +++ b/src/Services/Session.php @@ -2,25 +2,55 @@ namespace Hellotext\Services; +use Hellotext\Constants; + +/** + * Session + * + * Handles encryption and decryption for session identifiers. + * + * @package Hellotext\Services + */ class Session { - const METHOD = 'aes-256-cbc'; - - public static function encrypt ($session = null) { - $key = get_option('hellotext_business_id'); - - // Generate an initialization vector (IV) - $iv_length = openssl_cipher_iv_length(self::METHOD); - $iv = openssl_random_pseudo_bytes($iv_length); - - $encrypted = openssl_encrypt($session, self::METHOD, $key, 0, $iv); - - return base64_encode($encrypted . '::' . $iv); - } - - public static function decrypt ($encrypted_data = null) { - $key = get_option('hellotext_business_id'); - $parts = explode('::', base64_decode($encrypted_data)); - - return openssl_decrypt($parts[0], self::METHOD, $key, 0, $parts[1]); - } + /** + * Encrypt a session identifier. + * + * @param string|null $session Session identifier. + * @return string + */ + public static function encrypt(?string $session = null): string { + $key = get_option(Constants::OPTION_BUSINESS_ID); + + // Use empty string as fallback if key is not set + if (!$key) { + $key = ''; + } + + // Generate an initialization vector (IV) + $iv_length = openssl_cipher_iv_length(Constants::ENCRYPTION_METHOD); + $iv = openssl_random_pseudo_bytes($iv_length); + + $encrypted = openssl_encrypt($session ?? '', Constants::ENCRYPTION_METHOD, $key, 0, $iv); + + return base64_encode($encrypted . '::' . $iv); + } + + /** + * Decrypt an encrypted session identifier. + * + * @param string|null $encrypted_data Encrypted session data. + * @return string|false + */ + public static function decrypt(?string $encrypted_data = null): string|false { + $key = get_option(Constants::OPTION_BUSINESS_ID); + + // Use empty string as fallback if key is not set + if (!$key) { + $key = ''; + } + + $parts = explode('::', base64_decode($encrypted_data ?? '')); + + return openssl_decrypt($parts[0], Constants::ENCRYPTION_METHOD, $key, 0, $parts[1]); + } } diff --git a/tests/Mocks.php b/tests/Mocks.php index 11e9aad..f677503 100644 --- a/tests/Mocks.php +++ b/tests/Mocks.php @@ -12,10 +12,30 @@ function wc_create_refund ($args = array()) { // Mock WooCommerce wc_get_product function function wc_get_product ($id = 0) { + // If already a WC_Product, return it + if ($id instanceof WC_Product) { + return $id; + } + + // If invalid ID, return false + if (!is_numeric($id) || $id <= 0) { + return false; + } + return new WC_Product(); } function wc_get_order ($id = 0) { + // If already a WC_Order, return it + if ($id instanceof WC_Order) { + return $id; + } + + // If invalid ID, return false + if (!is_numeric($id) || $id <= 0) { + return false; + } + return new WC_Order(); } @@ -61,6 +81,7 @@ public function save () { class WC_Order_Refund { public $amount; + public $order_id; public function __construct ($args) { $this->amount = $args['amount']; @@ -150,3 +171,43 @@ public function get_id () { return $this->ID; } } + +// Mock WordPress HTTP API functions +if (!function_exists('wp_remote_request')) { + function wp_remote_request ($url, $args = array()) { + return array( + 'response' => array('code' => 200), + 'body' => json_encode(array('success' => true)), + ); + } +} + +if (!function_exists('wp_remote_post')) { + function wp_remote_post ($url, $args = array()) { + return wp_remote_request($url, $args); + } +} + +if (!function_exists('wp_remote_get')) { + function wp_remote_get ($url, $args = array()) { + return wp_remote_request($url, $args); + } +} + +if (!function_exists('is_wp_error')) { + function is_wp_error ($thing) { + return $thing instanceof WP_Error; + } +} + +if (!function_exists('wp_remote_retrieve_response_code')) { + function wp_remote_retrieve_response_code ($response) { + return $response['response']['code'] ?? 200; + } +} + +if (!function_exists('wp_remote_retrieve_body')) { + function wp_remote_retrieve_body ($response) { + return $response['body'] ?? ''; + } +} diff --git a/tests/Unit/Adapters/OrderAdapterTest.php b/tests/Unit/Adapters/OrderAdapterTest.php index 0eb475b..fb0ca26 100644 --- a/tests/Unit/Adapters/OrderAdapterTest.php +++ b/tests/Unit/Adapters/OrderAdapterTest.php @@ -20,7 +20,7 @@ }); test('throws an exception when order is not found', function () { - (new OrderAdapter(null))->get(); + (new OrderAdapter(0))->get(); })->throws(\Exception::class, 'Order not found'); test('returns the correct structure', function () { diff --git a/tests/Unit/Adapters/ProductAdapterTest.php b/tests/Unit/Adapters/ProductAdapterTest.php index 743012f..b28e6bc 100644 --- a/tests/Unit/Adapters/ProductAdapterTest.php +++ b/tests/Unit/Adapters/ProductAdapterTest.php @@ -13,7 +13,7 @@ }); test('throws an exception when product is not found', function () { - (new ProductAdapter(null))->get(); + (new ProductAdapter(0))->get(); })->throws(\Exception::class, 'Product not found'); test('returns the correct structure', function () { diff --git a/tests/Unit/Adapters/RefundAdapterTest.php b/tests/Unit/Adapters/RefundAdapterTest.php index 1c7e0c0..5cf6a31 100644 --- a/tests/Unit/Adapters/RefundAdapterTest.php +++ b/tests/Unit/Adapters/RefundAdapterTest.php @@ -21,14 +21,6 @@ ]); }); -test('throws an exception when refund is not found', function () { - (new RefundAdapter(null, null))->get(); -})->throws(\Exception::class, 'Refund not found'); - -test('throws an exception when order is not found', function () { - (new RefundAdapter($this->refund, null))->get(); -})->throws(\Exception::class, 'Order not found'); - test('returns the correct structure', function () { $result = (new RefundAdapter($this->refund, $this->order))->get();