-
-
Notifications
You must be signed in to change notification settings - Fork 0
v1.0.0 Release - PHP 8.2+ with Full Feature Parity #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MarketDataApp
wants to merge
121
commits into
main
Choose a base branch
from
update/php-8.2-8.5
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- Update composer.json PHP requirement from ^8.1 to ^8.2 - Update test matrix in run-tests.yml to [8.4, 8.3, 8.2] - Update phpdoc.yml PHP version to 8.2 - Update actions/checkout to v4 and create-pull-request to v7 in phpdoc.yml
Updates the requirements on [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) to permit the latest version. - [Release notes](https://github.com/sebastianbergmann/phpunit/releases) - [Changelog](https://github.com/sebastianbergmann/phpunit/blob/11.4.0/ChangeLog-11.4.md) - [Commits](sebastianbergmann/phpunit@10.3.2...11.4.0) --- updated-dependencies: - dependency-name: phpunit/phpunit dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com>
- Fix earnings currency: Make Earning::$currency nullable (string|null) and update Earnings.php to use null coalescing for currency to handle API returning null for future earnings reports - Fix expired option tests: Update option symbol to AAPL281215C00400000 (expires 2028-12-15) and expiration date to 2028-12-15 for long-term test validity - Remove rho property: Remove rho from all option models (Quote, OptionChainStrike) and all tests as it's no longer supported by the API - Fix utilities service count: Change assertion from assertCount(4) to assertGreaterThanOrEqual(4) since API now returns 12 services - Update all integration tests: Update setUp() methods to use MARKETDATA_TOKEN environment variable and skip tests if not set - Update StocksTest: Update currency assertion to handle nullable type - Update OptionsTest: Remove rho assertions and update to use valid option contracts - Update Unit/OptionsTest: Remove rho from mocks and assertions All tests now pass (82 tests, 814 assertions)
- Remove bulkQuotes() method from Stocks endpoint - Delete BulkQuotes and BulkQuote response classes - Remove all bulkQuotes test methods from unit and integration tests - Update README.md to remove bulkQuotes example - Update CHANGELOG.md to document bulkQuotes removal BREAKING CHANGE: The bulkQuotes endpoint has been removed as it is no longer supported by the API.
Retry Logic Implementation: - Added RetryConfig class with configurable retry attempts and exponential backoff - Implemented retry logic in ClientBase for both sync (execute) and async (async) requests - Added RequestError and BadStatusCodeError exception classes - Retry logic matches Python SDK behavior: - 3 max retry attempts - Exponential backoff: 0.5s → 1s → 2s (max 5s) - Retries on status codes > 500 and network errors - No retry on 4xx errors (except 404 special case) Test Fixes: - Modified testParallelRetryOnServerError_mixedResults to verify behavior (all symbols present) rather than implementation details (response ordering) - Test now uses order-independent symbol verification to avoid race conditions with MockHandler's FIFO queue - Fixed testExceptionHandling_throwsGuzzleException to account for retry logic requiring 3 mock responses to exhaust retries - Added comprehensive retry test suite (21 tests covering sync, async, and parallel scenarios) All tests passing (85/85)
- Add RateLimits value object for reusable rate limit data structure - Add extractRateLimitsFromResponse() method to ClientBase for modular header extraction - Add User response class wrapping RateLimits - Add user() method to Utilities class calling /user/ endpoint - Add makeRawRequest() helper method to ClientBase for accessing response headers - Add comprehensive unit tests covering edge cases and failure paths: - Missing headers, partial headers, invalid values, empty values - Case-insensitive matching, numeric formats, invalid timestamps, boundary values - Add integration tests: - Test to determine if /user/ endpoint consumes requests - Test verifying rate limits after real SPY quote API call - Update documentation to clarify consumed is per-request (not cumulative) - All 97 tests passing with 727 assertions
- Create UnauthorizedException class extending BadStatusCodeError - Update ClientBase to detect 401 status codes and throw UnauthorizedException - Add unit tests for 401 UnauthorizedException handling - Add integration tests for /user/ endpoint with invalid token - Add integration test for SPY quote with no token - Update makeRawRequest() to handle 401 errors for /user/ endpoint All tests passing (103 tests, 753 assertions)
- Add public rate_limits property to ClientBase that automatically tracks rate limits - Initialize rate limits during client construction via /user/ endpoint - Automatically update rate limits after every successful API request - Gracefully handle missing rate limit headers (no update, no error) - Add comprehensive unit tests (8 tests) with mocked responses - Add comprehensive integration tests (6 tests) with real API calls - All 117 existing tests pass, no regressions
- Change rate limit property names to match API header names: - credits_limit -> limit (x-api-ratelimit-limit) - credits_remaining -> remaining (x-api-ratelimit-remaining) - credits_reset -> reset (x-api-ratelimit-reset) - credits_consumed -> consumed (x-api-ratelimit-consumed) - Update all documentation to clarify that rate limits track credits, not requests - Add examples directory with rate_limit_tracking.php example demonstrating automatic rate limit tracking - Add examples/README.md with documentation for examples - Update all tests to use new property names - All 117 tests pass, no regressions
- Prevent client initialization with invalid tokens by validating via /user endpoint - Allow empty token for free symbols like AAPL (skips validation) - Throw UnauthorizedException during construction if token is invalid - Update all unit tests to use empty tokens (they use mocks anyway) - Add comprehensive integration tests for token validation scenarios - Update existing tests to reflect new behavior All tests pass (123 tests, 874 assertions)
- Add vlucas/phpdotenv dependency for .env file support - Create Settings class with automatic token resolution from: 1. Explicit token (constructor parameter) - highest priority 2. MARKETDATA_TOKEN environment variable 3. .env file (MARKETDATA_TOKEN) 4. Empty string fallback (for free symbols) - Make token parameter optional in Client and ClientBase constructors - Update documentation with Configuration section matching Python SDK - Update examples to show automatic env var support - Add comprehensive tests for token resolution and env var support - Maintain backward compatibility (explicit tokens still work) Matches Python SDK developer experience for token configuration.
- Updated GitHub Actions workflow to test PHP 8.2, 8.3, 8.4, and 8.5 - Updated README badge to include PHP 8.5 - Created TESTING_STRATEGY.md with comprehensive testing documentation - Updated CHANGELOG.md with PHP 8.5 support (v0.8.0-beta) - Fixed integration test skipping issue (environment variable cleanup in SettingsTest) - All PHP 8.5 compatibility fixes from 8.5-tests branch included - Renamed branch from update/php-8.2-8.4 to update/php-8.2-8.5 All tests pass on PHP 8.5.2: 164 tests, 1165 assertions, 0 skipped
- Add script to run GitHub Actions tests locally using act - Support testing all PHP versions (default) or a specific version - Quick test mode: when a PHP version is specified, runs only 1 job (prefer-stable) - Full test mode: when no version is specified, runs all 8 jobs (4 versions × 2 stability options) - Includes Docker caching optimization (--pull=false) for faster subsequent runs - Validates test results and fails if any tests are skipped - Properly passes MARKETDATA_TOKEN environment variable for integration tests
- Add Mode enum with LIVE, CACHED, DELAYED values - Add mode parameter to Parameters class (nullable, optional) - Update UniversalParameters trait to handle mode in execute() and execute_in_parallel() - Add unit tests for mode parameter (5 tests) - Add integration tests for mode parameter using stockquote endpoint (3 tests) - Update README with documentation for PHP version testing script - All tests pass on PHP 8.2, 8.3, 8.4, and 8.5 Implements medium priority feature from universal parameters comparison document.
- Add DateFormat enum with TIMESTAMP, UNIX, and SPREADSHEET values - Add date_format parameter to Parameters class (CSV-only restriction) - Update UniversalParameters trait to pass dateformat query parameter - Add comprehensive unit tests for parameter validation - Add integration tests for all date formats across endpoints - All 208 tests passing (1314 assertions)
- Updated Parameters class validation to allow date_format with both CSV and HTML formats - Updated UniversalParameters trait to pass dateformat parameter for HTML format - Added unit tests to validate HTML format with date_format - Updated all error messages to reflect CSV/HTML support All 210 tests passing (1323 assertions)
- Add columns parameter to Parameters class with CSV/HTML-only validation - Update UniversalParameters trait to pass columns parameter to API requests - Add comprehensive unit tests for columns parameter validation - Add integration tests using quote endpoint to verify CSV column filtering - All 225 tests passing (21 new unit tests, 3 new integration tests)
- Add add_headers parameter to Parameters class with CSV/HTML-only validation - Update UniversalParameters trait to pass headers parameter to API in both execute() and execute_in_parallel() methods - Fix execute_in_parallel() to properly handle CSV/HTML responses (was incorrectly trying to json_decode plain text) - Fix Quotes class to handle null responses gracefully and initialize quotes array - Add comprehensive unit tests for add_headers parameter validation (7 tests) - Add integration tests for CSV output with add_headers=true/false using stocks/quotes endpoint (4 tests) - All tests passing (236 tests, 1422 assertions, 0 skipped) This fix also enables parallel requests to work correctly with CSV/HTML formats, which was previously broken due to incorrect response parsing.
- Add optional filename parameter to Parameters class - Validates filename extension matches format (.csv or .html) - Validates that at least one parent directory exists (nested subdirectories created automatically) - Prevents overwriting existing files - Disallows filename with parallel requests (throws exception) - Implements file writing in processResponse when filename provided - Adds saveToFile() convenience method to ResponseBase - Maintains backward compatibility (filename is optional) - Returns response object with CSV/HTML string even when file is saved - Supports both relative and absolute paths - Handles multi-level nested directory creation - Comprehensive unit and integration test coverage All tests pass (257 tests, 1484 assertions, 0 skipped)
- Add Prices response class supporting JSON, CSV, HTML, and human-readable formats - Add prices() method to Stocks class supporting single symbol (path) and multiple symbols (query) formats - Support extended parameter (boolean, defaults to true) - Add comprehensive unit tests (8 tests) covering all scenarios - Add integration tests (6 tests) making real API calls - All 271 tests pass with no regressions
- Created ValidatesInputs trait with reusable validation methods - Added date range validation (only when both dates are parseable) - Added numeric range validation (positive integers, min/max) - Added string/array validation (non-empty, symbols) - Added format validation (resolution, country codes) - Follows Python SDK approach: allows relative dates and option expiration dates - Added validation to all endpoint methods (Stocks, Options, Markets, MutualFunds) - Added comprehensive test coverage (38 trait tests + 25 endpoint tests) - All 334 tests passing, no regressions - Backward compatible, no breaking changes
- Add default_params property to ClientBase for client-level parameter defaults - Implement three-level configuration hierarchy: env vars → client defaults → method params - Add Settings::getDefaultParameters() to read universal params from environment - Update UniversalParameters trait with mergeParameters() and validation - Add comprehensive test suite (77 tests, 113 assertions) with environment isolation - Validate CSV-only parameters cannot be used with JSON format after merging - Support .env file loading with parent directory search (up to 5 levels) - Maintain backward compatibility with existing code patterns
- Introduced a new `test.sh` script to streamline test execution, allowing for unit and integration tests to be run with configurable options. - Updated `phpunit.xml.dist` to define separate test suites for Unit, Integration, and All tests, enhancing clarity and control over test execution. - The new script supports logging, PHP version selection, and provides real-time output to both console and log files. All tests remain passing with the new structure in place.
- Add VERSION constant (0.8.0) to ClientBase class - Update headers() method to include User-Agent: marketdata-sdk-php/0.8.0 - Follow RFC 7231 format: product/product-version (with slash separator) - Add comprehensive test suite (8 tests) for User-Agent functionality - All tests passing (325 tests total) This implementation uses the correct RFC 7231 format, unlike the Python SDK which uses marketdata-sdk-py-1.1.0 (missing slash separator).
…ecking - Add ApiStatusResult enum (ONLINE, OFFLINE, UNKNOWN) - Add online boolean field to ServiceStatus class - Update ApiStatus to parse online field from API response - Create ApiStatusData class with smart caching: * 5-minute cache validity * 4min30sec refresh trigger window * Async refresh in refresh window * Blocking refresh when cache is stale - Add getServiceStatus() method to check specific service status - Add refreshApiStatus() method for manual cache refresh - Add constants for refresh interval and cache validity - Add comprehensive unit and integration tests - Failed refreshes don't overwrite existing cache - Backward compatible with missing online field
- Add getServicePath() helper method with hardcoded mapping from method paths to service paths - Integrate API status checking into execute() and async() retry logic - Skip retries when service is known to be offline (matches Python SDK behavior) - Continue retries when service is online or unknown - Handle status endpoint special case (skip checking its own status) - Add unit tests for intelligent retry behavior with different service statuses - Add integration test for real API service status detection - Optimize getApiStatus() to skip blocking refresh during retry logic This matches the Python SDK's @api_error_handler decorator functionality.
- Update actions/checkout from v4 to v6 in all workflows - Update codecov/codecov-action from v4 to v5 in run-tests.yml - Update peter-evans/create-pull-request from v7 to v8 in phpdoc.yml - Update stefanzweifel/git-auto-commit-action from v5 to v7 in update-changelog.yml - Remove deprecated ReflectionMethod::setAccessible() call in ValidatesInputsTest.php (deprecated in PHP 8.5, no longer needed since PHP 8.1)
Changed adjust_splits and adjust_dividends parameters from bool to nullable bool (?bool) in candles(), candlesConcurrent(), and bulkCandles() methods. This allows callers to explicitly override the API default (true for daily candles) by passing false. Previously, passing adjust_splits=false was indistinguishable from not passing the parameter at all, so the SDK never sent adjustsplits=false to the API. Fixes bug #10.
When automatic date range splitting is triggered for intraday candles, the splitDateRangeIntoYearChunks() method was using startOfDay() and endOfDay(), stripping the time-of-day from the original from/to values. Now preserves the original timestamps: - First chunk's 'from' uses the original from value - Last chunk's 'to' uses the original to value - Intermediate chunk boundaries use date-only strings
bulkCandles was not validating the symbols array, allowing empty strings to pass through and create malformed query strings like "symbols=,AAPL" instead of rejecting them with a validation error. Now calls validateSymbols() when symbols are provided, consistent with other stock endpoints like quotes() and prices().
Convert Format enum to its string value when passed to execute(),
execute_in_parallel(), and async() methods. This allows users to use
the SDK's own Format enum (e.g., Format::CSV) instead of requiring
string values ('csv').
Fixes bug-014: Client::execute Format enum rejection.
Single-symbol methods like quote(), candles(), prices(), etc. were not trimming whitespace from symbols before using them in URL paths. Passing a symbol like "AAPL " would result in encoded spaces (%20) in the path. Now all single-symbol endpoints in Stocks and Options trim the symbol after validation. Fixes bug 017.
Tests for bug 015 (options quotes CSV multi-symbol) and bug 016 (candles CSV splitting): - HTML format throws for multi-symbol/split requests - CSV format properly combines responses with headers on first only - User add_headers=false setting is respected
Bug 019: The mutual funds candles endpoint validated non-empty symbols but didn't trim whitespace before building the URL, causing encoded spaces (%20) in the request path.
The REST API supports an `extended` parameter for stock quotes, but the SDK's quote() and quotes() methods did not expose it. This prevented users from controlling extended-hours behavior. Added `bool $extended = true` parameter to both methods. When false, sends `extended=false` to the API to return only primary session quotes.
The API returns recent news when no date parameters are provided, but the SDK incorrectly required either 'from' or 'countback' + 'to'. This fix aligns the SDK with the API behavior.
The API returns recent/upcoming earnings when no date parameters are provided, but the SDK incorrectly required either 'from' or 'countback' + 'to'. This fix aligns the SDK with the API behavior.
The extended parameter was added between fifty_two_week and parameters, breaking tests that passed Parameters as the third positional argument. Updated tests to use named parameters for clarity and correctness. Also fixed nested directory tests to create full path before testing since SDK no longer creates directories automatically (bug-008 fix).
- Add tests for CSV candles with extended, adjust_splits, adjust_dividends params - Add test for CSV candles when all responses are empty (no data) - Add test for CSV candles with filename (throws exception) - Add test for options quotes CSV when all requests fail
Add two new tests to cover defensive code paths: - testCandles_automaticConcurrent_csvFormatAll401Failures: covers line 661 where all requests fail with non-404 HTTP errors - testCandles_automaticConcurrent_csvFormatEmptyAndFailure: covers line 701 where some responses are empty and some fail Mark unreachable defensive code in UniversalParameters.php (lines 176-181) with @codeCoverageIgnore since callers validate filename before calling execute_in_parallel.
Add support for the maxage universal parameter which sets a maximum
acceptable age for cached data when using mode=CACHED. If cached data
is older than maxage, the API returns 204 (no content) with no credit
charge, enabling cost-efficient fallback strategies.
Parameter accepts int (seconds), DateInterval, or CarbonInterval:
- maxage: 300 (5 minutes)
- maxage: new DateInterval('PT5M')
- maxage: CarbonInterval::minutes(5)
Includes 21 unit tests covering all input types and validation.
The option_chain method had an incorrect parameter name (minBidAskSpread) that was being silently ignored by the API. Renamed to maxBidAskSpread which is the actual REST API parameter. Added am and pm boolean parameters for filtering AM-settled vs PM-settled index options (e.g., SPX vs SPXW). These parameters only have an effect on index options - the API silently ignores them on regular equities. Changes: - Renamed min_bid_ask_spread to max_bid_ask_spread in option_chain() - Added am parameter for AM-settled index options filtering - Added pm parameter for PM-settled index options filtering - Added unit tests for all new parameters - Updated endpoint parameter audit to reflect fixes - Added issue document for API silent parameter handling
Adds a section explaining design decisions for intentionally unsupported REST API features: token query parameter (security) and limit/offset pagination (SDK uses concurrent parallel requests instead).
Adds a new test helper method that captures HTTP request history during mocked test runs, useful for verifying request parameters.
The API documentation describes a 'report' parameter for filtering by quarter (e.g., 2023-Q4), but testing confirms the API silently ignores both 'report' and 'datekey' parameters. Removing until the API implements this feature.
PHPUnit 12 deprecates doc-comment metadata in favor of PHP 8 attributes. Updated 6 test methods to use #[DataProvider()] attribute instead.
…s from candles These parameters were documented in the SDK but are not supported by the Market Data API stock candles endpoint. The adjust_dividends docstring even noted it was "planned for the future, but not yet implemented." Removes parameters from: - candles() method - candlesConcurrent() method - candlesConcurrentCsv() method Also removes related tests that verified these unsupported parameters.
Add validation rule across all endpoints: when 'to' is provided, it must be accompanied by either 'from' OR 'countback', but not both together. Valid: from+to, to+countback, from only, countback only, neither Invalid: to only, from+to+countback Also add docstring to Parameters.php documenting supported and intentionally unsupported universal parameters.
…rrent requests - Updated copyright year in LICENSE.md from 2024 to 2026. - Added a new test method `testCandles_automaticConcurrent_withMaxage` in CandlesConcurrentTest.php to verify that the maxage parameter is correctly applied in parallel requests using CACHED mode.
ResponseBase::isJson(), isCsv(), and isHtml() now use isset() instead
of empty() to detect response format. This fixes BUG-001 where empty
CSV/HTML bodies caused TypeError because empty('') returns true.
Extract _filename from arguments before building the query params in ClientBase::execute() and async(). The _filename is an internal SDK feature for saving CSV/HTML responses to files and should never be sent to the API.
Options::quotesMultipleCsv() now filters out JSON error responses before concatenating CSV output. When a symbol returns a JSON error (e.g., 404), it's excluded from the combined CSV rather than being concatenated as invalid CSV data. If all responses are errors, an ApiException is thrown.
ClientBase::processResponse() now returns a structured 'no_data' object for 204 responses and empty JSON bodies instead of returning null which violated the object return type. Quote response class now handles 'no_data' status without attempting to parse missing data fields.
ResponseBase::getCsv() and getHtml() now throw InvalidArgumentException with a clear message when called on responses of the wrong format, instead of causing a PHP Error from uninitialized typed properties.
Options::quotesMultipleCsv() now validates that filename parameter is not provided, throwing InvalidArgumentException with a clear message instead of silently ignoring it. This matches the behavior of Stocks::candlesConcurrentCsv() for parallel requests.
API returns JSON error bodies even when CSV/HTML format is requested. Previously these were silently returned as CSV/HTML content (or written to files). Now processResponse() detects JSON error payloads and throws ApiException before returning or writing files.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
v1.0.0 Release - PHP 8.2+ with Full Feature Parity
Summary
This PR prepares the PHP SDK for its 1.0.0 stable release. This is a major upgrade from v0.6.2-beta representing 172 commits, 245 files changed, and ~30,000 lines of new code. The SDK now has full feature parity with the Python SDK and 100% test coverage.
TL;DR for Review
Key files to review:
CHANGELOG.md- Release notes and migration guideSDK_FEATURE_COMPARISON.md- Feature parity documentationBreaking Changes (from v0.6.2-beta)
1. PHP Version Requirement
2. Removed: bulkQuotes Method
3. Options Quote Class Consolidation
QuoteandOptionChainStrikeclasses merged into unifiedOptionQuoteclass4. Client Constructor Changes
MARKETDATA_TOKENenv var or.envfile)UnauthorizedExceptionduring construction (not on first API call)$loggerparameter for custom PSR-3 logger injectionNew Features
Core Infrastructure
DefaultLogger,LoggerFactory, configurable viaMARKETDATA_LOGGING_LEVEL.envfile support (searches up to 5 directories)RetryConfigwith exponential backoff (3 attempts, 0.5-5s)$client->rate_limitsafter each requestNew Source Files Added (17 files)
New Endpoints/Methods
$client->stocks->prices()- SmartMid model prices$client->options->quotes()- Now supports multiple symbols with concurrent fetching$client->utilities->user()- User account informationNew Exception Hierarchy
Response Enhancements
__toString()for human-readable outputFormatsForDisplaytrait for currency, percent, volume formattingValidatesInputstrait for comprehensive input validationOptionChains Convenience Methods
Test Coverage Summary
The test suite now includes comprehensive tests for:
__toString()methodsChanges by Category
src/)tests/)Checklist
Post-Merge Release Steps
v1.0.0on main branchcomposer require marketdataapp/sdk-php ^1.0Questions for Your Review