From a454ed38570766986601dd22c6fcd47401f9d3bc Mon Sep 17 00:00:00 2001 From: "statsig-kong[bot]" Date: Tue, 17 Dec 2024 22:39:16 +0000 Subject: [PATCH] chore: bump version to 0.0.1-beta.135 --- .gitignore | 144 ++ composer.json | 29 + composer.lock | 1765 +++++++++++++++++ post-install.php | 124 ++ src/EvaluationTypes/BaseEvaluation.php | 53 + src/EvaluationTypes/DynamicConfig.php | 19 + src/EvaluationTypes/Experiment.php | 19 + src/EvaluationTypes/FeatureGate.php | 15 + src/EvaluationTypes/Layer.php | 37 + src/Statsig.php | 85 + src/StatsigEventData.php | 17 + src/StatsigFFI.php | 84 + src/StatsigLocalFileEventLoggingAdapter.php | 34 + src/StatsigLocalFileSpecsAdapter.php | 30 + src/StatsigOptions.php | 33 + src/StatsigUser.php | 36 + tests/MockServer.php | 69 + tests/MockServerTest.php | 52 + tests/StatsigAdapterUsageTest.php | 88 + tests/StatsigLocalFileSpecsAdapterTest.php | 60 + tests/StatsigOptionsTest.php | 21 + ...tatsigScheduledEventLoggingAdapterTest.php | 83 + tests/StatsigTest.php | 169 ++ tests/TestHelpers.php | 16 + 24 files changed, 3082 insertions(+) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 post-install.php create mode 100644 src/EvaluationTypes/BaseEvaluation.php create mode 100644 src/EvaluationTypes/DynamicConfig.php create mode 100644 src/EvaluationTypes/Experiment.php create mode 100644 src/EvaluationTypes/FeatureGate.php create mode 100644 src/EvaluationTypes/Layer.php create mode 100644 src/Statsig.php create mode 100644 src/StatsigEventData.php create mode 100644 src/StatsigFFI.php create mode 100644 src/StatsigLocalFileEventLoggingAdapter.php create mode 100644 src/StatsigLocalFileSpecsAdapter.php create mode 100644 src/StatsigOptions.php create mode 100644 src/StatsigUser.php create mode 100644 tests/MockServer.php create mode 100644 tests/MockServerTest.php create mode 100644 tests/StatsigAdapterUsageTest.php create mode 100644 tests/StatsigLocalFileSpecsAdapterTest.php create mode 100644 tests/StatsigOptionsTest.php create mode 100644 tests/StatsigScheduledEventLoggingAdapterTest.php create mode 100644 tests/StatsigTest.php create mode 100644 tests/TestHelpers.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f93c3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,144 @@ +.DS_Store +.idea/ +resources/ + +# Created by https://www.toptal.com/developers/gitignore/api/phpunit,phpstorm,composer +# Edit at https://www.toptal.com/developers/gitignore?templates=phpunit,phpstorm,composer + +### Composer ### +composer.phar +/vendor/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock + +### PhpStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PhpStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### PHPUnit ### +# Covers PHPUnit +# Reference: https://phpunit.de/ + +# Generated files +.phpunit.result.cache +.phpunit.cache + +# PHPUnit +/app/phpunit.xml +/phpunit.xml + +# Build data +/build/ + +# End of https://www.toptal.com/developers/gitignore/api/phpunit,phpstorm,composer \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..945ebe1 --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "sigstat/sigstat", + "type": "library", + "license": "ISC", + "description": "Statsig PHP SDK", + "autoload": { + "psr-4": { + "Statsig\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Statsig\\Tests\\": "tests/" + } + }, + "require": {}, + "require-dev": { + "phpunit/phpunit": "^10", + "donatj/mock-webserver": "^2.7.2", + "ext-zlib": "*" + }, + "scripts": { + "test": "phpunit tests --testdox --colors=always", + "test:verbose": "phpunit tests --testdox --colors=always --debug", + "post-install-cmd": [ + "php post-install.php" + ] + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..9c46010 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1765 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "f442e0998119746f0932a1df3709476a", + "packages": [], + "packages-dev": [ + { + "name": "donatj/mock-webserver", + "version": "v2.7.2", + "source": { + "type": "git", + "url": "https://github.com/donatj/mock-webserver.git", + "reference": "1887dcbd8b4a52dde23a43f0d303c4509a065e83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/donatj/mock-webserver/zipball/1887dcbd8b4a52dde23a43f0d303c4509a065e83", + "reference": "1887dcbd8b4a52dde23a43f0d303c4509a065e83", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-sockets": "*", + "php": ">=7.1", + "ralouphie/getallheaders": "~2.0 || ~3.0" + }, + "require-dev": { + "corpus/coding-standard": "^0.6.0 || ^0.8.0", + "donatj/drop": "^1.0", + "ext-curl": "*", + "friendsofphp/php-cs-fixer": "^3.1", + "phpunit/phpunit": "~7|~9", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "donatj\\MockWebServer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jesse G. Donat", + "email": "donatj@gmail.com", + "homepage": "https://donatstudios.com", + "role": "Lead" + } + ], + "description": "Simple mock web server for unit testing", + "keywords": [ + "dev", + "http", + "mock", + "phpunit", + "testing", + "unit testing", + "webserver" + ], + "support": { + "issues": "https://github.com/donatj/mock-webserver/issues", + "source": "https://github.com/donatj/mock-webserver/tree/v2.7.2" + }, + "funding": [ + { + "url": "https://www.paypal.me/donatj/15", + "type": "custom" + }, + { + "url": "https://github.com/donatj", + "type": "github" + }, + { + "url": "https://ko-fi.com/donatj", + "type": "ko_fi" + } + ], + "time": "2024-01-22T20:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "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.12.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-11-08T17:47:46+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.3.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "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.0-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.3.1" + }, + "time": "2024-10-08T18:51:32+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": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "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" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.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/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "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": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.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": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.39", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "4e89eff200b801db58f3d580ad7426431949eaa9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4e89eff200b801db58f3d580ad7426431949eaa9", + "reference": "4e89eff200b801db58f3d580ad7426431949eaa9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.3", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.39" + }, + "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", + "type": "tidelift" + } + ], + "time": "2024-12-11T10:51:07+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "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" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "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" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-18T14:56:07+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-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 for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "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" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "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" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "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" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "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" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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": "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/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" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "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": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": { + "ext-zlib": "*" + }, + "plugin-api-version": "2.6.0" +} diff --git a/post-install.php b/post-install.php new file mode 100644 index 0000000..da82cb4 --- /dev/null +++ b/post-install.php @@ -0,0 +1,124 @@ + "statsig-ffi-x86_64-apple-darwin.zip", + "macos-aarch64" => "statsig-ffi-aarch64-apple-darwin.zip", + "linux-aarch64" => "statsig-ffi-aarch64-unknown-linux-gnu.zip", + "linux-x86_64" => "statsig-ffi-x86_64-unknown-linux-gnu.zip", + "windows-x86_64" => "statsig-ffi-x86_64-pc-windows-msvc.zip", + "windows-aarch64" => "statsig-ffi-aarch64-pc-windows-msvc.zip", + ]; + + $binary_file = $binary_map[$system_info[0] . "-" . $system_info[1]] ?? null; + + if ($binary_file === null) { + echo "No binary found for: {$system_info[0]} {$system_info[1]}\n"; + exit(1); + } + + $url = "https://github.com/statsig-io/sigstat-php/releases/download/" . VERSION . "/" . $binary_file; + + echo "Downloading binary from $url\n"; + + $output_path = OUTPUT_DIR . "/" . $binary_file; + + file_put_contents($output_path, file_get_contents($url)); + + return $output_path; +} + +function unzip_binary($zip_file_path) +{ + echo "Unzipping binary\n"; + $zip = new ZipArchive(); + if ($zip->open($zip_file_path) === TRUE) { + for ($i = 0; $i < $zip->numFiles; $i++) { + $filename = $zip->getNameIndex($i); + if (in_array($filename, ['libstatsig_ffi.dylib', 'statsig_ffi.dll', 'libstatsig_ffi.so'])) { + $zip->extractTo(OUTPUT_DIR, $filename); + echo "Extracted $filename\n"; + } + } + $zip->close(); + echo "Binary unzipped to " . OUTPUT_DIR . "\n"; + } else { + echo "Failed to open zip file\n"; + exit(1); + } + + echo "Binary unzipped to " . OUTPUT_DIR . "\n"; + + echo "Deleting zip file\n"; + if (unlink($zip_file_path)) { + echo "Successfully deleted $zip_file_path\n"; + } else { + echo "Failed to delete $zip_file_path\n"; + } +} + +function download_header() +{ + $url = "https://github.com/statsig-io/sigstat-php/releases/download/" . VERSION . "/statsig_ffi.h"; + echo "Downloading header from $url\n"; + + $output_path = OUTPUT_DIR . "/statsig_ffi.h"; + file_put_contents($output_path, file_get_contents($url)); +} + + +$system_info = get_system_info(); +ensure_bin_dir_exists(); +$zip_file_path = download_binary($system_info); +unzip_binary($zip_file_path); +download_header(); diff --git a/src/EvaluationTypes/BaseEvaluation.php b/src/EvaluationTypes/BaseEvaluation.php new file mode 100644 index 0000000..c44a8e0 --- /dev/null +++ b/src/EvaluationTypes/BaseEvaluation.php @@ -0,0 +1,53 @@ +__raw_result = $raw_result; + + if (!is_array($result)) { + $this->error = 'Invalid JSON input'; + $result = []; + } else { + $this->error = null; + } + + $this->__evaluation = $result['__evaluation'] ?? []; + + $this->details = $result['details'] ?? []; + $this->id_type = (string)($result['id_type'] ?? ''); + $this->name = (string)($result['name'] ?? ''); + $this->rule_id = (string)($result['rule_id'] ?? ''); + } + + protected function getValueImpl(array $value, string $param_name, $fallback, $exposure_func) + { + if (!array_key_exists($param_name, $value)) { + return $fallback; + } + + $val = $value[$param_name]; + + if ($fallback !== null && gettype($val) !== gettype($fallback)) { + return $fallback; + } + + if ($exposure_func !== null) { + $exposure_func($param_name); + } + + return $val; + } +} diff --git a/src/EvaluationTypes/DynamicConfig.php b/src/EvaluationTypes/DynamicConfig.php new file mode 100644 index 0000000..4cdcbd6 --- /dev/null +++ b/src/EvaluationTypes/DynamicConfig.php @@ -0,0 +1,19 @@ +value = $result['value'] ?? []; + } + + public function get(string $param_name, $fallback) { + return $this->getValueImpl($this->value, $param_name, $fallback, null); + } +} \ No newline at end of file diff --git a/src/EvaluationTypes/Experiment.php b/src/EvaluationTypes/Experiment.php new file mode 100644 index 0000000..bc32a07 --- /dev/null +++ b/src/EvaluationTypes/Experiment.php @@ -0,0 +1,19 @@ +value = $result['value'] ?? []; + } + + public function get(string $param_name, $fallback) { + return $this->getValueImpl($this->value, $param_name, $fallback, null); + } +} \ No newline at end of file diff --git a/src/EvaluationTypes/FeatureGate.php b/src/EvaluationTypes/FeatureGate.php new file mode 100644 index 0000000..3086db6 --- /dev/null +++ b/src/EvaluationTypes/FeatureGate.php @@ -0,0 +1,15 @@ +value = (bool)($result['value'] ?? false); + } +} \ No newline at end of file diff --git a/src/EvaluationTypes/Layer.php b/src/EvaluationTypes/Layer.php new file mode 100644 index 0000000..9d92392 --- /dev/null +++ b/src/EvaluationTypes/Layer.php @@ -0,0 +1,37 @@ +__statsig_ref = $__statsig_ref; + $this->__value = $result['__value'] ?? []; + } + + public function get(string $param_name, $fallback) + { + return $this->getValueImpl( + $this->__value, + $param_name, + $fallback, + function ($param_name) { + $this->logParameterExposure($param_name); + } + ); + } + + private function logParameterExposure(string $param_name): void + { + StatsigFFI::get()->statsig_log_layer_param_exposure($this->__statsig_ref, $this->__raw_result, $param_name); + } +} diff --git a/src/Statsig.php b/src/Statsig.php new file mode 100644 index 0000000..f65e77c --- /dev/null +++ b/src/Statsig.php @@ -0,0 +1,85 @@ +__ref : (new StatsigOptions)->__ref; + + $ffi = StatsigFFI::get(); + $this->__ref = $ffi->statsig_create($sdk_key, $options_ref); + } + + public function __destruct() + { + if (is_null($this->__ref)) { + return; + } + + StatsigFFI::get()->statsig_release($this->__ref); + $this->__ref = null; + } + + public function initialize($callback): void + { + StatsigFFI::get()->statsig_initialize($this->__ref, $callback); + } + + public function flushEvents($callback): void + { + StatsigFFI::get()->statsig_flush_events($this->__ref, $callback); + } + + public function checkGate(string $name, StatsigUser $user): bool + { + return StatsigFFI::get()->statsig_check_gate($this->__ref, $user->__ref, $name); + } + + public function getFeatureGate(string $name, StatsigUser $user): FeatureGate + { + $raw_result = StatsigFFI::get()->statsig_get_feature_gate($this->__ref, $user->__ref, $name); + return new FeatureGate($raw_result); + } + + public function getDynamicConfig(string $name, StatsigUser $user): DynamicConfig + { + $raw_result = StatsigFFI::get()->statsig_get_dynamic_config($this->__ref, $user->__ref, $name); + return new DynamicConfig($raw_result); + } + + public function getExperiment(string $name, StatsigUser $user): Experiment + { + $raw_result = StatsigFFI::get()->statsig_get_experiment($this->__ref, $user->__ref, $name); + return new Experiment($raw_result); + } + + public function getLayer(string $name, StatsigUser $user): Layer + { + $raw_result = StatsigFFI::get()->statsig_get_layer($this->__ref, $user->__ref, $name); + return new Layer($raw_result, $this->__ref); + } + + public function getClientInitializeResponse(StatsigUser $user): string + { + return StatsigFFI::get()->statsig_get_client_init_response($this->__ref, $user->__ref); + } + + public function logEvent( + StatsigEventData $event_data, + StatsigUser $user, + ): void { + $data = json_encode($event_data); + StatsigFFI::get()->statsig_log_event($this->__ref, $user->__ref, $data); + } +} diff --git a/src/StatsigEventData.php b/src/StatsigEventData.php new file mode 100644 index 0000000..5113942 --- /dev/null +++ b/src/StatsigEventData.php @@ -0,0 +1,17 @@ +name = $name; + $this->value = $value; + $this->metadata = $metadata; + } +} diff --git a/src/StatsigFFI.php b/src/StatsigFFI.php new file mode 100644 index 0000000..3742e6b --- /dev/null +++ b/src/StatsigFFI.php @@ -0,0 +1,84 @@ + 'libstatsig_ffi.dylib', + 'Windows' => 'statsig_ffi.dll', + default => 'libstatsig_ffi.so', + }; + + $path = $dir . '/' . $file_name; + + if (file_exists($path)) { + return $path; + } + + return null; + } + + private static function find_header_file_in_dir(string $dir): ?string + { + $path = $dir . '/statsig_ffi.h'; + + if (file_exists($path)) { + return $path; + } + + return null; + } +} diff --git a/src/StatsigLocalFileEventLoggingAdapter.php b/src/StatsigLocalFileEventLoggingAdapter.php new file mode 100644 index 0000000..a4fc01e --- /dev/null +++ b/src/StatsigLocalFileEventLoggingAdapter.php @@ -0,0 +1,34 @@ +__ref = StatsigFFI::get()->statsig_local_file_event_logging_adapter_create( + $sdk_key, + $output_directory, + $log_event_url + ); + } + + public function __destruct() + { + if (!is_null($this->__ref)) { + StatsigFFI::get()->statsig_local_file_event_logging_adapter_release($this->__ref); + } + + $this->__ref = null; + } + + public function send_pending_events(): void + { + StatsigFFI::get()->statsig_local_file_event_logging_adapter_send_pending_events($this->__ref); + } +} diff --git a/src/StatsigLocalFileSpecsAdapter.php b/src/StatsigLocalFileSpecsAdapter.php new file mode 100644 index 0000000..480bd73 --- /dev/null +++ b/src/StatsigLocalFileSpecsAdapter.php @@ -0,0 +1,30 @@ +__ref = StatsigFFI::get()->statsig_local_file_specs_adapter_create($sdk_key, $output_directory, $specs_url); + } + + public function __destruct() + { + if (!is_null($this->__ref)) { + StatsigFFI::get()->statsig_local_file_specs_adapter_release($this->__ref); + } + + $this->__ref = null; + } + + public function sync_specs_from_network(): void + { + StatsigFFI::get()->statsig_local_file_specs_adapter_fetch_and_write_to_file($this->__ref); + } +} diff --git a/src/StatsigOptions.php b/src/StatsigOptions.php new file mode 100644 index 0000000..f6b7126 --- /dev/null +++ b/src/StatsigOptions.php @@ -0,0 +1,33 @@ +__ref = $ffi->statsig_options_create( + $specs_url, + $log_event_url, + is_null($specs_adapter) ? null : $specs_adapter->__ref, + is_null($event_logging_adapter) ? null : $event_logging_adapter->__ref + ); + } + + public function __destruct() + { + if (is_null($this->__ref)) { + return; + } + + StatsigFFI::get()->statsig_options_release($this->__ref); + $this->__ref = null; + } +} diff --git a/src/StatsigUser.php b/src/StatsigUser.php new file mode 100644 index 0000000..72589c2 --- /dev/null +++ b/src/StatsigUser.php @@ -0,0 +1,36 @@ +__ref = $ffi->statsig_user_create( + $user_id, + null, // custom_ids + $email, + null, // ip + null, // user_agent + null, // country + null, // locale + null, // app_version + null, // custom_json + null, // private_attributes_json + ); + } + + public function __destruct() + { + if (is_null($this->__ref)) { + return; + } + + StatsigFFI::get()->statsig_user_release($this->__ref); + $this->__ref = null; + } +} diff --git a/tests/MockServer.php b/tests/MockServer.php new file mode 100644 index 0000000..3460bf3 --- /dev/null +++ b/tests/MockServer.php @@ -0,0 +1,69 @@ +server = new MockWebServer; + $this->server->setDefaultResponse(new NotFoundResponse); + + $this->server->start(); + } + + public function stop(): void + { + $this->server->stop(); + } + + public function getUrl(): string + { + return $this->server->getServerRoot(); + } + + public function mock($path, $response, $options = []): void + { + $status = $options['status'] ?? 200; + + $this->server->setResponseOfPath( + $path, + new Response( + $response, + ['Cache-Control' => 'no-cache'], + $status + ) + ); + } + + public function getRequests(): array + { + $requests = []; + + for ($i = 0; $i < 999; $i++) { + $request = $this->server->getRequestByOffset($i); + if ($request) { + $requests[] = [ + 'uri' => $request->getRequestUri(), + 'method' => $request->getRequestMethod(), + 'params' => $request->getGet(), + 'body' => $request->getInput(), + 'headers' => $request->getHeaders(), + 'path' => $request->getParsedUri()['path'] + ]; + } else { + break; + } + } + + return $requests; + } +} diff --git a/tests/MockServerTest.php b/tests/MockServerTest.php new file mode 100644 index 0000000..b7ec0cb --- /dev/null +++ b/tests/MockServerTest.php @@ -0,0 +1,52 @@ +server = new MockServer(); + $this->server->mock('/foo', 'RESULT'); + } + + protected function tearDown(): void + { + $this->server->stop(); + } + + public function testGetRequest() + { + file_get_contents($this->server->getUrl() . '/foo?buzz=1'); + + $request = $this->server->getRequests()[0]; + $this->assertEquals('/foo', $request['path']); + $this->assertEquals('GET', $request['method']); + $this->assertEquals(['buzz' => '1'], $request['params']); + } + + public function testPostRequest() + { + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => 'Content-Type: application/json', + 'content' => json_encode(['buzz' => '1']) + ] + ]); + file_get_contents($this->server->getUrl() . '/foo', false, $context); + + $request = $this->server->getRequests()[0]; + $this->assertEquals('/foo', $request['path']); + $this->assertEquals('POST', $request['method']); + $this->assertEquals('{"buzz":"1"}', $request['body']); + $this->assertArrayHasKey('Content-Type', $request['headers']); + $this->assertEquals('application/json', $request['headers']['Content-Type']); + } +} diff --git a/tests/StatsigAdapterUsageTest.php b/tests/StatsigAdapterUsageTest.php new file mode 100644 index 0000000..4987f29 --- /dev/null +++ b/tests/StatsigAdapterUsageTest.php @@ -0,0 +1,88 @@ +server = new MockServer(); + $this->server->mock('/v2/download_config_specs/secret-key.json', $data); + $this->server->mock('/v2/download_config_specs/server-event-logging-usage-test.json', $data); + } + + public function testLocalFileSpecsAdapterUsage() + { + $adapter = new StatsigLocalFileSpecsAdapter( + "secret-key", + "/tmp", + $this->server->getUrl() . "/v2/download_config_specs" + ); + $adapter->sync_specs_from_network(); + + $options = new StatsigOptions( + null, + null, + $adapter + ); + + $statsig = new Statsig("secret-key", $options); + $statsig->initialize(function () use (&$callback_fired) { + $callback_fired = true; + }); + + TestHelpers::waitUntilTrue($this, function () use (&$callback_fired) { + return $callback_fired; + }); + + $user = new StatsigUser("a-user"); + $gate = $statsig->getFeatureGate("test_50_50", $user); + $this->assertTrue($gate->value); + } + + public function testEventLogging() + { + $sdk_key = "server-event-logging-usage-test"; + $specs_adapter = new StatsigLocalFileSpecsAdapter( + $sdk_key, + "/tmp", + $this->server->getUrl() . "/v2/download_config_specs" + ); + + $specs_adapter->sync_specs_from_network(); + + $event_adapter = new StatsigLocalFileEventLoggingAdapter($sdk_key, "/tmp"); + + $statsig = new Statsig($sdk_key, new StatsigOptions(null, null, $specs_adapter, $event_adapter)); + + $statsig->initialize(function () use (&$callback_fired) { + // $callback_fired = true; + }); + +// $statsig->flushEvents(function () use (&$callback_fired) { +// // $callback_fired = true; +// }); + + $this->assertTrue(true); + } +} diff --git a/tests/StatsigLocalFileSpecsAdapterTest.php b/tests/StatsigLocalFileSpecsAdapterTest.php new file mode 100644 index 0000000..9baf203 --- /dev/null +++ b/tests/StatsigLocalFileSpecsAdapterTest.php @@ -0,0 +1,60 @@ +server = new MockServer(); + $this->server->mock('/v2/download_config_specs/'.self::SDK_KEY.'.json', $data); + } + + public function testCreateAndRelease() + { + $adapter = new StatsigLocalFileSpecsAdapter(self::SDK_KEY, "/tmp"); + $this->assertNotNull($adapter->__ref); + + $adapter->__destruct(); + + $this->assertNull($adapter->__ref); + } + + public function testFetchingFromNetwork() + { + $adapter = new StatsigLocalFileSpecsAdapter( + self::SDK_KEY, + "/tmp", + $this->server->getUrl() . "/v2/download_config_specs" + ); + + $adapter->sync_specs_from_network(); + + $json = json_decode(file_get_contents("/tmp/".self::FILE_NAME), true); + $this->assertArrayHasKey("dynamic_configs", $json); + $this->assertArrayHasKey("layer_configs", $json); + $this->assertArrayHasKey("feature_gates", $json); + } +} diff --git a/tests/StatsigOptionsTest.php b/tests/StatsigOptionsTest.php new file mode 100644 index 0000000..b8fe185 --- /dev/null +++ b/tests/StatsigOptionsTest.php @@ -0,0 +1,21 @@ +assertNotNull($options->__ref); + + $options->__destruct(); + + $this->assertNull($options->__ref); + } +} diff --git a/tests/StatsigScheduledEventLoggingAdapterTest.php b/tests/StatsigScheduledEventLoggingAdapterTest.php new file mode 100644 index 0000000..34cfd58 --- /dev/null +++ b/tests/StatsigScheduledEventLoggingAdapterTest.php @@ -0,0 +1,83 @@ +server = new MockServer(); + $this->server->mock('/v1/log_event', '{"success": true}'); + } + + public function testCreateAndRelease() + { + $adapter = new StatsigLocalFileEventLoggingAdapter(self::SDK_KEY, "/tmp"); + $this->assertNotNull($adapter->__ref); + + $adapter->__destruct(); + + $this->assertNull($adapter->__ref); + } + + public function testSendingEvents() + { + $request_json = json_encode([ + "requests" => [ + [ + "payload" => [ + "events" => [ + [ + "eventName" => "my_custom_event", + "metadata" => null, + "secondaryExposures" => null, + "time" => 1730831508904, + "user" => [ + "statsigEnvironment" => null, + "userID" => "a-user", + ], + "value" => null, + ], + ], + "statsigMetadata" => [ + "sdkType" => "statsig-server-core", + "sdkVersion" => "0.0.1", + "sessionId" => "1ff863ed-a9ab-4785-bb0e-1a7b0140c040", + ], + ], + "eventCount" => 1,] + ] + ]); + + file_put_contents(self::FILE_PATH, $request_json); + + $adapter = new StatsigLocalFileEventLoggingAdapter( + self::SDK_KEY, + "/tmp", + $this->server->getUrl() . "/v1/log_event" + ); + + $adapter->send_pending_events(); + + $request = $this->server->getRequests()[0]; + $this->assertEquals('/v1/log_event', $request['path']); + } +} diff --git a/tests/StatsigTest.php b/tests/StatsigTest.php new file mode 100644 index 0000000..a681a44 --- /dev/null +++ b/tests/StatsigTest.php @@ -0,0 +1,169 @@ +user = new StatsigUser("a-user"); + + $dir = dirname(__FILE__); + $data = file_get_contents($dir . '/../../statsig-lib/tests/data/eval_proj_dcs.json'); + + $this->server = new MockServer(); + $this->server->mock('/v2/download_config_specs/secret-key.json', $data); + $this->server->mock('/v1/log_event', '{ "success": true }', ['status' => 202]); + } + + protected function tearDown(): void + { + $this->server->stop(); + } + + public function testCreateAndRelease() + { + $statsig = new Statsig("secret-key"); + $this->assertNotNull($statsig->__ref); + + $statsig->__destruct(); + + $this->assertNull($statsig->__ref); + } + + public function testDoubleRelease() + { + $statsig = new Statsig("secret-key"); + $statsig->__destruct(); + $statsig->__destruct(); + + $this->assertNull($statsig->__ref); + } + + function getInitializedStatsig(): Statsig + { + $options = new StatsigOptions( + $this->server->getUrl() . "/v2/download_config_specs", + $this->server->getUrl() . "/v1/log_event" + ); + $statsig = new Statsig("secret-key", $options); + + $callback_fired = false; + + $statsig->initialize(function () use (&$callback_fired) { + $callback_fired = true; + }); + + TestHelpers::waitUntilTrue($this, function () use (&$callback_fired) { + return $callback_fired; + }); + + return $statsig; + } + + public function testInitialization() + { + $statsig = $this->getInitializedStatsig(); + $this->assertEquals(Statsig::class, get_class($statsig)); + + $request = $this->server->getRequests()[0]; + $this->assertEquals('/v2/download_config_specs/secret-key.json', $request['path']); + } + + public function testCheckGate() + { + $statsig = $this->getInitializedStatsig(); + $this->assertTrue($statsig->checkGate("test_public", $this->user)); + } + + public function testGetFeatureGate() + { + $statsig = $this->getInitializedStatsig(); + + $gate = $statsig->getFeatureGate("test_50_50", $this->user); + $this->assertTrue($gate->value); + } + + public function testGetDynamicConfig() + { + $statsig = $this->getInitializedStatsig(); + + $config = $statsig->getDynamicConfig("test_email_config", $this->user); + $this->assertEquals("everyone else", $config->get("header_text", "err")); + } + + public function testGetExperiment() + { + $statsig = $this->getInitializedStatsig(); + + $experiment = $statsig->getExperiment("exp_with_obj_and_array", $this->user); + $this->assertEquals(["group" => "test"], $experiment->get("obj_param", ["fallback" => ""])); + } + + public function testGetLayer() + { + $statsig = $this->getInitializedStatsig(); + + $layer = $statsig->getLayer("layer_with_many_params", $this->user); + $this->assertEquals("layer", $layer->get("a_string", "err")); + } + + public function testExposureLogs() + { + $statsig = $this->getInitializedStatsig(); + + $statsig->getFeatureGate("test_50_50", $this->user); + $statsig->getDynamicConfig("test_email_config", $this->user); + $statsig->getExperiment("exp_with_obj_and_array", $this->user); + $statsig->getLayer("layer_with_many_params", $this->user)->get("a_string", ""); + + $this->waitUntilEventsFlushed($statsig); + + $request = $this->server->getRequests()[1]; + $this->assertEquals('/v1/log_event', $request['path']); + + $bytes = $request['body']; + $json = gzdecode($bytes); + $body = json_decode($json, true); + $events = array_filter($body['events'], function ($event) { + return $event['eventName'] !== "statsig::diagnostics"; + }); + $this->assertCount(4, $events); + } + + public function testGcir() + { + $statsig = $this->getInitializedStatsig(); + + $raw_result = $statsig->getClientInitializeResponse($this->user); + $result = json_decode($raw_result, true); + + $this->assertCount(62, $result["dynamic_configs"]); + $this->assertCount(65, $result["feature_gates"]); + $this->assertCount(12, $result["layer_configs"]); + } + + protected function waitUntilEventsFlushed(Statsig $statsig): void + { + $callback_fired = false; + $statsig->flushEvents(function () use (&$callback_fired) { + $callback_fired = true; + }); + + TestHelpers::waitUntilTrue($this, function () use (&$callback_fired) { + return $callback_fired; + }); + } +} diff --git a/tests/TestHelpers.php b/tests/TestHelpers.php new file mode 100644 index 0000000..d328844 --- /dev/null +++ b/tests/TestHelpers.php @@ -0,0 +1,16 @@ +fail("Timed out waiting for callback"); + } + } +}