diff --git a/.gitignore b/.gitignore
index b9122934..a4f98fdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@ phpunit.xml
composer.phar
.DS_STORE
.idea
-.php_cs.cache
+.phpcs-cache
diff --git a/.php_cs b/.php_cs
deleted file mode 100644
index 8ff87b69..00000000
--- a/.php_cs
+++ /dev/null
@@ -1,16 +0,0 @@
-notPath('vendor')
- ->in(__DIR__)
- ->name('*.php');
-
-return PhpCsFixer\Config::create()
- ->setRules([
- '@PSR2' => true,
- 'array_syntax' => ['syntax' => 'short'],
- 'ordered_imports' => ['sortAlgorithm' => 'alpha'],
- 'no_unused_imports' => true,
- ])
- ->setFinder($finder);
-
diff --git a/.travis.yml b/.travis.yml
index c0772c38..7b126a87 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,9 +10,10 @@ before_script:
- composer install --prefer-dist --no-interaction --no-suggest
script:
- - vendor/bin/php-cs-fixer fix -v --dry-run --stop-on-violation --using-cache=no
- - vendor/bin/phpunit --configuration phpunit.xml.dist
+ - vendor/bin/phpcs
+ - vendor/bin/psalm
+ - vendor/bin/phpunit
notifications:
- on_success: never
- on_failure: always
+ on_success: never
+ on_failure: always
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index c540f4f0..2278755d 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -1,21 +1,35 @@
-Dev Guide
-=========
+# Dev Guide
-Setup
------
+## Setup
- composer install
+```bash
+composer install
+```
+## Running tests
-Running tests
--------------
+```bash
+vendor/bin/phpunit
+```
- vendor/bin/phpunit
+## Writing Code
+### Checking for static analysis issues
-Writing Code
-------------
+```bash
+vendor/bin/psalm
+```
-We have an automated style fixer called php-cs-fixer. Style is checked on TravisCI as well.
+### Checking coding standards are met
- vendor/bin/php-cs-fixer fix
+```bash
+vendor/bin/phpcs
+```
+
+### Fixing coding standards automatically
+
+We have an automated style fixer called PHP Code Sniffer. Style is checked on TravisCI as well.
+
+```bash
+vendor/bin/phpcbf
+```
diff --git a/README.md b/README.md
index ae801dd7..25f92696 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,9 @@
Email us at support@ScoutAPM.com to get on the beta invite list!
-Monitor the performance of PHP Laravel apps with Scout's
-[PHP APM Agent](https://www.scoutapm.com).
-Detailed performance metrics and transaction traces are collected once the
-agent is installed and configured.
+Monitor the performance of PHP apps with Scout's [PHP APM Agent](https://www.scoutapm.com).
+Detailed performance metrics and transaction traces are collected once the agent is installed and configured.
## Requirements
@@ -15,15 +13,36 @@ PHP Versions: 7.1+
## Quick Start
This package is the base library for the various framework-specific packages.
-To install the ScoutAPM Agent for a specific framework, use the specific
-package instead.
-* [Laravel](https://github.com/scoutapp/scout-apm-laravel)
+### Laravel
+
+To install the ScoutAPM Agent for a specific framework, use the specific package instead.
+
+ * [Laravel](https://github.com/scoutapp/scout-apm-laravel)
+
+### Using the base library directly
+
+```php
+use Scoutapm\Agent;
+use Scoutapm\Config;
+
+$agent = Agent::fromConfig(Config::fromArray([
+ 'name' => 'Your application name',
+ 'key' => 'your scout key',
+ 'monitor' => true,
+]));
+// If the core agent is not already running, this will download and run it (from /tmp by default)
+$agent->connect();
+
+// Use $agent to record `webTransaction`, `backgroundTransaction`, `instrument` or `tagRequest` as necessary
+
+// Nothing is sent to Scout until you call this - so call this at the end of your request
+$agent->send();
+```
## Documentation
-For full installation and troubleshooting documentation, visit our
-[help site](http://docs.scoutapm.com/#php-agent).
+For full installation and troubleshooting documentation, visit our [help site](http://docs.scoutapm.com/#php-agent).
## Support
diff --git a/composer.json b/composer.json
index b4d29e2a..96c4ea13 100644
--- a/composer.json
+++ b/composer.json
@@ -14,9 +14,11 @@
"ramsey/uuid": "^3.7"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^2.15",
+ "doctrine/coding-standard": "^6.0",
+ "monolog/monolog": "^1.24",
"phpunit/phpunit": "^7.5",
- "spatie/phpunit-watcher": "^1.8"
+ "spatie/phpunit-watcher": "^1.8",
+ "vimeo/psalm": "^3.4"
},
"suggest": {
"ext-xdebug": "Required for processing of request headers"
diff --git a/composer.lock b/composer.lock
index 24f8203b..e096871e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "4f6bef77962a1e3da9a993c240fa5ef1",
+ "content-hash": "7452964e8617f3edf8baae1657d4ad27",
"packages": [
{
"name": "paragonie/random_compat",
@@ -280,6 +280,142 @@
}
],
"packages-dev": [
+ {
+ "name": "amphp/amp",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/amp.git",
+ "reference": "c6a775a6c9fdd9ca4c909647a19b02d2d11a0568"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/c6a775a6c9fdd9ca4c909647a19b02d2d11a0568",
+ "reference": "c6a775a6c9fdd9ca4c909647a19b02d2d11a0568",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "dev-master",
+ "amphp/phpunit-util": "^1",
+ "ext-json": "*",
+ "phpstan/phpstan": "^0.8.5",
+ "phpunit/phpunit": "^6.0.9",
+ "react/promise": "^2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Amp\\": "lib"
+ },
+ "files": [
+ "lib/functions.php",
+ "lib/Internal/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A non-blocking concurrency framework for PHP applications.",
+ "homepage": "http://amphp.org/amp",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "awaitable",
+ "concurrency",
+ "event",
+ "event-loop",
+ "future",
+ "non-blocking",
+ "promise"
+ ],
+ "time": "2019-08-02T20:37:42+00:00"
+ },
+ {
+ "name": "amphp/byte-stream",
+ "version": "v1.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/byte-stream.git",
+ "reference": "47908f8e8bb2da8af4e59028200758477bc927ea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/byte-stream/zipball/47908f8e8bb2da8af4e59028200758477bc927ea",
+ "reference": "47908f8e8bb2da8af4e59028200758477bc927ea",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "dev-master",
+ "amphp/phpunit-util": "^1",
+ "friendsofphp/php-cs-fixer": "^2.3",
+ "infection/infection": "^0.9.3",
+ "phpunit/phpunit": "^6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Amp\\ByteStream\\": "lib"
+ },
+ "files": [
+ "lib/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A stream abstraction to make working with non-blocking I/O simple.",
+ "homepage": "http://amphp.org/byte-stream",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "io",
+ "non-blocking",
+ "stream"
+ ],
+ "time": "2019-07-26T21:22:49+00:00"
+ },
{
"name": "clue/stdio-react",
"version": "v2.2.0",
@@ -451,35 +587,30 @@
"time": "2017-07-06T07:43:22+00:00"
},
{
- "name": "composer/semver",
- "version": "1.5.0",
+ "name": "composer/xdebug-handler",
+ "version": "1.3.3",
"source": {
"type": "git",
- "url": "https://github.com/composer/semver.git",
- "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e"
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
- "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f",
+ "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f",
"shasum": ""
},
"require": {
- "php": "^5.3.2 || ^7.0"
+ "php": "^5.3.2 || ^7.0",
+ "psr/log": "^1.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.5 || ^5.0.5",
- "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.x-dev"
- }
- },
"autoload": {
"psr-4": {
- "Composer\\Semver\\": "src"
+ "Composer\\XdebugHandler\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -488,55 +619,48 @@
],
"authors": [
{
- "name": "Nils Adermann",
- "email": "naderman@naderman.de",
- "homepage": "http://www.naderman.de"
- },
- {
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "http://seld.be"
- },
- {
- "name": "Rob Bast",
- "email": "rob.bast@gmail.com",
- "homepage": "http://robbast.nl"
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
}
],
- "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "description": "Restarts a process without xdebug.",
"keywords": [
- "semantic",
- "semver",
- "validation",
- "versioning"
+ "Xdebug",
+ "performance"
],
- "time": "2019-03-19T17:25:45+00:00"
+ "time": "2019-05-27T17:52:04+00:00"
},
{
- "name": "composer/xdebug-handler",
- "version": "1.3.3",
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v0.5.0",
"source": {
"type": "git",
- "url": "https://github.com/composer/xdebug-handler.git",
- "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f"
+ "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
+ "reference": "e749410375ff6fb7a040a68878c656c2e610b132"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f",
- "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f",
+ "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e749410375ff6fb7a040a68878c656c2e610b132",
+ "reference": "e749410375ff6fb7a040a68878c656c2e610b132",
"shasum": ""
},
"require": {
- "php": "^5.3.2 || ^7.0",
- "psr/log": "^1.0"
+ "composer-plugin-api": "^1.0",
+ "php": "^5.3|^7",
+ "squizlabs/php_codesniffer": "^2|^3"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
+ "composer/composer": "*",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "sensiolabs/security-checker": "^4.1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
- "type": "library",
"autoload": {
"psr-4": {
- "Composer\\XdebugHandler\\": "src"
+ "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -545,48 +669,62 @@
],
"authors": [
{
- "name": "John Stevenson",
- "email": "john-stevenson@blueyonder.co.uk"
+ "name": "Franck Nijhof",
+ "role": "Developer / IT Manager",
+ "email": "franck.nijhof@dealerdirect.com",
+ "homepage": "http://www.frenck.nl"
}
],
- "description": "Restarts a process without xdebug.",
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "homepage": "http://www.dealerdirect.com",
"keywords": [
- "Xdebug",
- "performance"
- ],
- "time": "2019-05-27T17:52:04+00:00"
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "time": "2018-10-26T13:21:45+00:00"
},
{
- "name": "doctrine/annotations",
- "version": "v1.7.0",
+ "name": "doctrine/coding-standard",
+ "version": "6.0.0",
"source": {
"type": "git",
- "url": "https://github.com/doctrine/annotations.git",
- "reference": "fa4c4e861e809d6a1103bd620cce63ed91aedfeb"
+ "url": "https://github.com/doctrine/coding-standard.git",
+ "reference": "d33f69eb98b25aa51ffe3a909f0ec77000af4701"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/annotations/zipball/fa4c4e861e809d6a1103bd620cce63ed91aedfeb",
- "reference": "fa4c4e861e809d6a1103bd620cce63ed91aedfeb",
+ "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/d33f69eb98b25aa51ffe3a909f0ec77000af4701",
+ "reference": "d33f69eb98b25aa51ffe3a909f0ec77000af4701",
"shasum": ""
},
"require": {
- "doctrine/lexer": "1.*",
- "php": "^7.1"
- },
- "require-dev": {
- "doctrine/cache": "1.*",
- "phpunit/phpunit": "^7.5@dev"
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
+ "php": "^7.1",
+ "slevomat/coding-standard": "^5.0",
+ "squizlabs/php_codesniffer": "^3.4.0"
},
- "type": "library",
+ "type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
- "dev-master": "1.7.x-dev"
+ "dev-master": "6.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+ "Doctrine\\Sniffs\\": "lib/Doctrine/Sniffs"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -594,35 +732,30 @@
"MIT"
],
"authors": [
- {
- "name": "Guilherme Blanco",
- "email": "guilhermeblanco@gmail.com"
- },
- {
- "name": "Roman Borschel",
- "email": "roman@code-factory.org"
- },
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
- "name": "Jonathan Wage",
- "email": "jonwage@gmail.com"
- },
- {
- "name": "Johannes Schmitt",
- "email": "schmittjoh@gmail.com"
+ "name": "Steve Müller",
+ "email": "st.mueller@dzh-online.de"
}
],
- "description": "Docblock Annotations Parser",
- "homepage": "http://www.doctrine-project.org",
+ "description": "The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.",
+ "homepage": "https://www.doctrine-project.org/projects/coding-standard.html",
"keywords": [
- "annotations",
- "docblock",
- "parser"
- ],
- "time": "2019-08-08T18:11:40+00:00"
+ "checks",
+ "code",
+ "coding",
+ "cs",
+ "doctrine",
+ "rules",
+ "sniffer",
+ "sniffs",
+ "standard",
+ "style"
+ ],
+ "time": "2019-03-15T12:45:47+00:00"
},
{
"name": "doctrine/instantiator",
@@ -681,34 +814,29 @@
"time": "2019-03-17T17:37:11+00:00"
},
{
- "name": "doctrine/lexer",
- "version": "1.0.2",
+ "name": "evenement/evenement",
+ "version": "v3.0.1",
"source": {
"type": "git",
- "url": "https://github.com/doctrine/lexer.git",
- "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8"
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8",
- "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"shasum": ""
},
"require": {
- "php": ">=5.3.2"
+ "php": ">=7.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.5"
+ "phpunit/phpunit": "^6.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
"autoload": {
- "psr-4": {
- "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+ "psr-0": {
+ "Evenement": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -717,159 +845,104 @@
],
"authors": [
{
- "name": "Roman Borschel",
- "email": "roman@code-factory.org"
- },
- {
- "name": "Guilherme Blanco",
- "email": "guilhermeblanco@gmail.com"
- },
- {
- "name": "Johannes Schmitt",
- "email": "schmittjoh@gmail.com"
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
}
],
- "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
- "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+ "description": "Événement is a very simple event dispatching library for PHP",
"keywords": [
- "annotations",
- "docblock",
- "lexer",
- "parser",
- "php"
+ "event-dispatcher",
+ "event-emitter"
],
- "time": "2019-06-08T11:03:04+00:00"
+ "time": "2017-07-23T21:35:13+00:00"
},
{
- "name": "evenement/evenement",
- "version": "v3.0.1",
+ "name": "felixfbecker/advanced-json-rpc",
+ "version": "v3.0.3",
"source": {
"type": "git",
- "url": "https://github.com/igorw/evenement.git",
- "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
+ "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git",
+ "reference": "241c470695366e7b83672be04ea0e64d8085a551"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
- "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/241c470695366e7b83672be04ea0e64d8085a551",
+ "reference": "241c470695366e7b83672be04ea0e64d8085a551",
"shasum": ""
},
"require": {
- "php": ">=7.0"
+ "netresearch/jsonmapper": "^1.0",
+ "php": ">=7.0",
+ "phpdocumentor/reflection-docblock": "^4.0.0"
},
"require-dev": {
- "phpunit/phpunit": "^6.0"
+ "phpunit/phpunit": "^6.0.0"
},
"type": "library",
"autoload": {
- "psr-0": {
- "Evenement": "src"
+ "psr-4": {
+ "AdvancedJsonRpc\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "ISC"
],
"authors": [
{
- "name": "Igor Wiedler",
- "email": "igor@wiedler.ch"
+ "name": "Felix Becker",
+ "email": "felix.b@outlook.com"
}
],
- "description": "Événement is a very simple event dispatching library for PHP",
- "keywords": [
- "event-dispatcher",
- "event-emitter"
- ],
- "time": "2017-07-23T21:35:13+00:00"
+ "description": "A more advanced JSONRPC implementation",
+ "time": "2018-09-10T08:58:41+00:00"
},
{
- "name": "friendsofphp/php-cs-fixer",
- "version": "v2.15.1",
+ "name": "felixfbecker/language-server-protocol",
+ "version": "v1.4.0",
"source": {
"type": "git",
- "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
- "reference": "20064511ab796593a3990669eff5f5b535001f7c"
+ "url": "https://github.com/felixfbecker/php-language-server-protocol.git",
+ "reference": "378801f6139bb74ac215d81cca1272af61df9a9f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/20064511ab796593a3990669eff5f5b535001f7c",
- "reference": "20064511ab796593a3990669eff5f5b535001f7c",
+ "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/378801f6139bb74ac215d81cca1272af61df9a9f",
+ "reference": "378801f6139bb74ac215d81cca1272af61df9a9f",
"shasum": ""
},
"require": {
- "composer/semver": "^1.4",
- "composer/xdebug-handler": "^1.2",
- "doctrine/annotations": "^1.2",
- "ext-json": "*",
- "ext-tokenizer": "*",
- "php": "^5.6 || ^7.0",
- "php-cs-fixer/diff": "^1.3",
- "symfony/console": "^3.4.17 || ^4.1.6",
- "symfony/event-dispatcher": "^3.0 || ^4.0",
- "symfony/filesystem": "^3.0 || ^4.0",
- "symfony/finder": "^3.0 || ^4.0",
- "symfony/options-resolver": "^3.0 || ^4.0",
- "symfony/polyfill-php70": "^1.0",
- "symfony/polyfill-php72": "^1.4",
- "symfony/process": "^3.0 || ^4.0",
- "symfony/stopwatch": "^3.0 || ^4.0"
+ "php": "^7.0"
},
"require-dev": {
- "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0",
- "justinrainbow/json-schema": "^5.0",
- "keradus/cli-executor": "^1.2",
- "mikey179/vfsstream": "^1.6",
- "php-coveralls/php-coveralls": "^2.1",
- "php-cs-fixer/accessible-object": "^1.0",
- "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1",
- "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1",
- "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1",
- "phpunitgoodpractices/traits": "^1.8",
- "symfony/phpunit-bridge": "^4.3"
- },
- "suggest": {
- "ext-mbstring": "For handling non-UTF8 characters in cache signature.",
- "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.",
- "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.",
- "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible."
+ "phpstan/phpstan": "*",
+ "phpunit/phpunit": "^6.3",
+ "squizlabs/php_codesniffer": "^3.1"
},
- "bin": [
- "php-cs-fixer"
- ],
- "type": "application",
+ "type": "library",
"autoload": {
"psr-4": {
- "PhpCsFixer\\": "src/"
- },
- "classmap": [
- "tests/Test/AbstractFixerTestCase.php",
- "tests/Test/AbstractIntegrationCaseFactory.php",
- "tests/Test/AbstractIntegrationTestCase.php",
- "tests/Test/Assert/AssertTokensTrait.php",
- "tests/Test/IntegrationCase.php",
- "tests/Test/IntegrationCaseFactory.php",
- "tests/Test/IntegrationCaseFactoryInterface.php",
- "tests/Test/InternalIntegrationCaseFactory.php",
- "tests/TestCase.php"
- ]
+ "LanguageServerProtocol\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "ISC"
],
"authors": [
{
- "name": "Dariusz Rumiński",
- "email": "dariusz.ruminski@gmail.com"
- },
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "name": "Felix Becker",
+ "email": "felix.b@outlook.com"
}
],
- "description": "A tool to automatically fix PHP code style",
- "time": "2019-06-01T10:32:12+00:00"
+ "description": "PHP classes for the Language Server Protocol",
+ "keywords": [
+ "language",
+ "microsoft",
+ "php",
+ "server"
+ ],
+ "time": "2019-06-23T21:03:50+00:00"
},
{
"name": "jolicode/jolinotif",
@@ -927,6 +1000,84 @@
],
"time": "2019-02-26T18:10:50+00:00"
},
+ {
+ "name": "monolog/monolog",
+ "version": "1.24.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
+ "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "psr/log": "~1.0"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "graylog2/gelf-php": "~1.0",
+ "jakub-onderka/php-parallel-lint": "0.9",
+ "php-amqplib/php-amqplib": "~2.4",
+ "php-console/php-console": "^3.1.3",
+ "phpunit/phpunit": "~4.5",
+ "phpunit/phpunit-mock-objects": "2.3.0",
+ "ruflin/elastica": ">=0.90 <3.0",
+ "sentry/sentry": "^0.13",
+ "swiftmailer/swiftmailer": "^5.3|^6.0"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-mongo": "Allow sending log messages to a MongoDB server",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "php-console/php-console": "Allow sending log messages to Google Chrome",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
+ "sentry/sentry": "Allow sending log messages to a Sentry server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "http://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "time": "2018-11-05T09:00:11+00:00"
+ },
{
"name": "myclabs/deep-copy",
"version": "1.9.3",
@@ -976,12 +1127,208 @@
"time": "2019-08-09T12:45:53+00:00"
},
{
- "name": "phar-io/manifest",
- "version": "1.0.3",
+ "name": "netresearch/jsonmapper",
+ "version": "v1.6.0",
"source": {
"type": "git",
- "url": "https://github.com/phar-io/manifest.git",
- "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
+ "url": "https://github.com/cweiske/jsonmapper.git",
+ "reference": "0d4d1b48d682a93b6bfedf60b88c7750e9cb0b06"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/0d4d1b48d682a93b6bfedf60b88c7750e9cb0b06",
+ "reference": "0d4d1b48d682a93b6bfedf60b88c7750e9cb0b06",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-spl": "*",
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4",
+ "squizlabs/php_codesniffer": "~1.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "JsonMapper": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "OSL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Christian Weiske",
+ "role": "Developer",
+ "email": "cweiske@cweiske.de",
+ "homepage": "http://github.com/cweiske/jsonmapper/"
+ }
+ ],
+ "description": "Map nested JSON structures onto PHP classes",
+ "time": "2019-08-15T19:41:25+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "e612609022e935f3d0337c1295176505b41188c8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/e612609022e935f3d0337c1295176505b41188c8",
+ "reference": "e612609022e935f3d0337c1295176505b41188c8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-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"
+ ],
+ "time": "2019-08-12T20:17:41+00:00"
+ },
+ {
+ "name": "ocramius/package-versions",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Ocramius/PackageVersions.git",
+ "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
+ "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0.0",
+ "php": "^7.1.0"
+ },
+ "require-dev": {
+ "composer/composer": "^1.6.3",
+ "doctrine/coding-standard": "^5.0.1",
+ "ext-zip": "*",
+ "infection/infection": "^0.7.1",
+ "phpunit/phpunit": "^7.0.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PackageVersions\\Installer",
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PackageVersions\\": "src/PackageVersions"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
+ "time": "2019-02-21T12:16:21+00:00"
+ },
+ {
+ "name": "openlss/lib-array2xml",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nullivex/lib-array2xml.git",
+ "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
+ "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "LSS": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Bryan Tong",
+ "email": "bryan@nullivex.com",
+ "homepage": "https://www.nullivex.com"
+ },
+ {
+ "name": "Tony Butler",
+ "email": "spudz76@gmail.com",
+ "homepage": "https://www.nullivex.com"
+ }
+ ],
+ "description": "Array2XML conversion library credit to lalit.org",
+ "homepage": "https://www.nullivex.com",
+ "keywords": [
+ "array",
+ "array conversion",
+ "xml",
+ "xml conversion"
+ ],
+ "time": "2019-03-29T20:06:56+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
},
"dist": {
"type": "zip",
@@ -1077,57 +1424,6 @@
"description": "Library for handling version information and constraints",
"time": "2018-07-08T19:19:57+00:00"
},
- {
- "name": "php-cs-fixer/diff",
- "version": "v1.3.0",
- "source": {
- "type": "git",
- "url": "https://github.com/PHP-CS-Fixer/diff.git",
- "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756",
- "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756",
- "shasum": ""
- },
- "require": {
- "php": "^5.6 || ^7.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^5.7.23 || ^6.4.3",
- "symfony/process": "^3.3"
- },
- "type": "library",
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Kore Nordmann",
- "email": "mail@kore-nordmann.de"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "SpacePossum"
- }
- ],
- "description": "sebastian/diff v2 backport support for PHP5.6",
- "homepage": "https://github.com/PHP-CS-Fixer",
- "keywords": [
- "diff"
- ],
- "time": "2018-02-15T16:58:55+00:00"
- },
{
"name": "phpdocumentor/reflection-common",
"version": "1.0.1",
@@ -1343,6 +1639,53 @@
],
"time": "2019-06-13T12:50:23+00:00"
},
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "0.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "8c4ef2aefd9788238897b678a985e1d5c8df6db4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/8c4ef2aefd9788238897b678a985e1d5c8df6db4",
+ "reference": "8c4ef2aefd9788238897b678a985e1d5c8df6db4",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~7.1"
+ },
+ "require-dev": {
+ "consistence/coding-standard": "^3.5",
+ "jakub-onderka/php-parallel-lint": "^0.9.2",
+ "phing/phing": "^2.16.0",
+ "phpstan/phpstan": "^0.10",
+ "phpunit/phpunit": "^6.3",
+ "slevomat/coding-standard": "^4.7.2",
+ "squizlabs/php_codesniffer": "^3.3.2",
+ "symfony/process": "^3.4 || ^4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\PhpDocParser\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "time": "2019-06-07T19:13:52+00:00"
+ },
{
"name": "phpunit/php-code-coverage",
"version": "6.1.4",
@@ -2333,6 +2676,46 @@
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2016-10-03T07:35:21+00:00"
},
+ {
+ "name": "slevomat/coding-standard",
+ "version": "5.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/slevomat/coding-standard.git",
+ "reference": "287ac3347c47918c0bf5e10335e36197ea10894c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/287ac3347c47918c0bf5e10335e36197ea10894c",
+ "reference": "287ac3347c47918c0bf5e10335e36197ea10894c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1",
+ "phpstan/phpdoc-parser": "^0.3.1",
+ "squizlabs/php_codesniffer": "^3.4.1"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "1.0.0",
+ "phing/phing": "2.16.1",
+ "phpstan/phpstan": "0.11.4",
+ "phpstan/phpstan-phpunit": "0.11",
+ "phpstan/phpstan-strict-rules": "0.11",
+ "phpunit/phpunit": "8.0.5"
+ },
+ "type": "phpcodesniffer-standard",
+ "autoload": {
+ "psr-4": {
+ "SlevomatCodingStandard\\": "SlevomatCodingStandard"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+ "time": "2019-03-22T19:10:53+00:00"
+ },
{
"name": "spatie/phpunit-watcher",
"version": "1.10.1",
@@ -2388,6 +2771,57 @@
],
"time": "2019-07-22T12:59:15+00:00"
},
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
+ "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "time": "2019-04-10T23:49:02+00:00"
+ },
{
"name": "symfony/console",
"version": "v3.4.30",
@@ -2517,125 +2951,12 @@
"time": "2019-07-23T08:39:19+00:00"
},
{
- "name": "symfony/event-dispatcher",
+ "name": "symfony/finder",
"version": "v3.4.30",
"source": {
"type": "git",
- "url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "f18fdd6cc7006441865e698420cee26bac94741f"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f18fdd6cc7006441865e698420cee26bac94741f",
- "reference": "f18fdd6cc7006441865e698420cee26bac94741f",
- "shasum": ""
- },
- "require": {
- "php": "^5.5.9|>=7.0.8"
- },
- "conflict": {
- "symfony/dependency-injection": "<3.3"
- },
- "require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "~2.8|~3.0|~4.0",
- "symfony/dependency-injection": "~3.3|~4.0",
- "symfony/expression-language": "~2.8|~3.0|~4.0",
- "symfony/stopwatch": "~2.8|~3.0|~4.0"
- },
- "suggest": {
- "symfony/dependency-injection": "",
- "symfony/http-kernel": ""
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\EventDispatcher\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony EventDispatcher Component",
- "homepage": "https://symfony.com",
- "time": "2019-06-25T07:45:31+00:00"
- },
- {
- "name": "symfony/filesystem",
- "version": "v3.4.30",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/filesystem.git",
- "reference": "70adda061ef83bb7def63a17953dc41f203308a7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/70adda061ef83bb7def63a17953dc41f203308a7",
- "reference": "70adda061ef83bb7def63a17953dc41f203308a7",
- "shasum": ""
- },
- "require": {
- "php": "^5.5.9|>=7.0.8",
- "symfony/polyfill-ctype": "~1.8"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Filesystem\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony Filesystem Component",
- "homepage": "https://symfony.com",
- "time": "2019-06-23T09:29:17+00:00"
- },
- {
- "name": "symfony/finder",
- "version": "v3.4.30",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/finder.git",
- "reference": "1e762fdf73ace6ceb42ba5a6ca280be86082364a"
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "1e762fdf73ace6ceb42ba5a6ca280be86082364a"
},
"dist": {
"type": "zip",
@@ -2678,60 +2999,6 @@
"homepage": "https://symfony.com",
"time": "2019-06-28T08:02:59+00:00"
},
- {
- "name": "symfony/options-resolver",
- "version": "v3.4.30",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/options-resolver.git",
- "reference": "ed3b397f9c07c8ca388b2a1ef744403b4d4ecc44"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ed3b397f9c07c8ca388b2a1ef744403b4d4ecc44",
- "reference": "ed3b397f9c07c8ca388b2a1ef744403b4d4ecc44",
- "shasum": ""
- },
- "require": {
- "php": "^5.5.9|>=7.0.8"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\OptionsResolver\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony OptionsResolver Component",
- "homepage": "https://symfony.com",
- "keywords": [
- "config",
- "configuration",
- "options"
- ],
- "time": "2019-04-10T16:00:48+00:00"
- },
{
"name": "symfony/polyfill-mbstring",
"version": "v1.12.0",
@@ -2792,38 +3059,34 @@
"time": "2019-08-06T08:03:45+00:00"
},
{
- "name": "symfony/polyfill-php70",
- "version": "v1.12.0",
+ "name": "symfony/process",
+ "version": "v3.4.30",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php70.git",
- "reference": "54b4c428a0054e254223797d2713c31e08610831"
+ "url": "https://github.com/symfony/process.git",
+ "reference": "d129c017e8602507688ef2c3007951a16c1a8407"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831",
- "reference": "54b4c428a0054e254223797d2713c31e08610831",
+ "url": "https://api.github.com/repos/symfony/process/zipball/d129c017e8602507688ef2c3007951a16c1a8407",
+ "reference": "d129c017e8602507688ef2c3007951a16c1a8407",
"shasum": ""
},
"require": {
- "paragonie/random_compat": "~1.0|~2.0|~9.99",
- "php": ">=5.3.3"
+ "php": "^5.5.9|>=7.0.8"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.12-dev"
+ "dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
- "Symfony\\Polyfill\\Php70\\": ""
+ "Symfony\\Component\\Process\\": ""
},
- "files": [
- "bootstrap.php"
- ],
- "classmap": [
- "Resources/stubs"
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -2832,53 +3095,57 @@
],
"authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+ "description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
- "time": "2019-08-06T08:03:45+00:00"
+ "time": "2019-05-30T15:47:52+00:00"
},
{
- "name": "symfony/polyfill-php72",
- "version": "v1.12.0",
+ "name": "symfony/yaml",
+ "version": "v3.4.30",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php72.git",
- "reference": "04ce3335667451138df4307d6a9b61565560199e"
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e",
- "reference": "04ce3335667451138df4307d6a9b61565560199e",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/051d045c684148060ebfc9affb7e3f5e0899d40b",
+ "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": "^5.5.9|>=7.0.8",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "conflict": {
+ "symfony/console": "<3.4"
+ },
+ "require-dev": {
+ "symfony/console": "~3.4|~4.0"
+ },
+ "suggest": {
+ "symfony/console": "For validating YAML files using the lint command"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.12-dev"
+ "dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
- "Symfony\\Polyfill\\Php72\\": ""
+ "Symfony\\Component\\Yaml\\": ""
},
- "files": [
- "bootstrap.php"
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -2887,102 +3154,126 @@
],
"authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
- "time": "2019-08-06T08:03:45+00:00"
+ "time": "2019-07-24T13:01:31+00:00"
},
{
- "name": "symfony/process",
- "version": "v3.4.30",
+ "name": "theseer/tokenizer",
+ "version": "1.1.3",
"source": {
"type": "git",
- "url": "https://github.com/symfony/process.git",
- "reference": "d129c017e8602507688ef2c3007951a16c1a8407"
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/d129c017e8602507688ef2c3007951a16c1a8407",
- "reference": "d129c017e8602507688ef2c3007951a16c1a8407",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+ "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
"shasum": ""
},
"require": {
- "php": "^5.5.9|>=7.0.8"
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.4-dev"
- }
- },
"autoload": {
- "psr-4": {
- "Symfony\\Component\\Process\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
+ "classmap": [
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
}
],
- "description": "Symfony Process Component",
- "homepage": "https://symfony.com",
- "time": "2019-05-30T15:47:52+00:00"
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "time": "2019-06-13T22:48:21+00:00"
},
{
- "name": "symfony/stopwatch",
- "version": "v3.4.30",
+ "name": "vimeo/psalm",
+ "version": "3.4.11",
"source": {
"type": "git",
- "url": "https://github.com/symfony/stopwatch.git",
- "reference": "2a651c2645c10bbedd21170771f122d935e0dd58"
+ "url": "https://github.com/vimeo/psalm.git",
+ "reference": "85c9b6bb442039c773120059ae35a31f8ef9d488"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58",
- "reference": "2a651c2645c10bbedd21170771f122d935e0dd58",
+ "url": "https://api.github.com/repos/vimeo/psalm/zipball/85c9b6bb442039c773120059ae35a31f8ef9d488",
+ "reference": "85c9b6bb442039c773120059ae35a31f8ef9d488",
"shasum": ""
},
"require": {
- "php": "^5.5.9|>=7.0.8"
+ "amphp/amp": "^2.1",
+ "amphp/byte-stream": "^1.5",
+ "composer/xdebug-handler": "^1.1",
+ "felixfbecker/advanced-json-rpc": "^3.0.3",
+ "felixfbecker/language-server-protocol": "^1.4",
+ "netresearch/jsonmapper": "^1.0",
+ "nikic/php-parser": "^4.2",
+ "ocramius/package-versions": "^1.2",
+ "openlss/lib-array2xml": "^1.0",
+ "php": "^7.1",
+ "sebastian/diff": "^3.0",
+ "symfony/console": "^3.3||^4.0",
+ "webmozart/glob": "^4.1",
+ "webmozart/path-util": "^2.3"
},
+ "provide": {
+ "psalm/psalm": "self.version"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.2",
+ "friendsofphp/php-cs-fixer": "^2.15",
+ "phpmyadmin/sql-parser": "^5.0",
+ "phpunit/phpunit": "^7.5 || ^8.0",
+ "psalm/plugin-phpunit": "^0.6",
+ "slevomat/coding-standard": "^5.0",
+ "squizlabs/php_codesniffer": "3.4.0",
+ "symfony/process": "^4.3"
+ },
+ "suggest": {
+ "ext-igbinary": "^2.0.5"
+ },
+ "bin": [
+ "psalm",
+ "psalter",
+ "psalm-language-server",
+ "psalm-plugin",
+ "psalm-refactor"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.4-dev"
+ "dev-master": "3.x-dev",
+ "dev-2.x": "2.x-dev",
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
"psr-4": {
- "Symfony\\Component\\Stopwatch\\": ""
+ "Psalm\\Plugin\\": "src/Psalm/Plugin",
+ "Psalm\\": "src/Psalm"
},
- "exclude-from-classmap": [
- "/Tests/"
+ "files": [
+ "src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -2991,58 +3282,49 @@
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Matthew Brown"
}
],
- "description": "Symfony Stopwatch Component",
- "homepage": "https://symfony.com",
- "time": "2019-01-16T09:39:14+00:00"
+ "description": "A static analysis tool for finding errors in PHP applications",
+ "keywords": [
+ "code",
+ "inspection",
+ "php"
+ ],
+ "time": "2019-08-09T15:40:46+00:00"
},
{
- "name": "symfony/yaml",
- "version": "v3.4.30",
+ "name": "webmozart/assert",
+ "version": "1.4.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/yaml.git",
- "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b"
+ "url": "https://github.com/webmozart/assert.git",
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/051d045c684148060ebfc9affb7e3f5e0899d40b",
- "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b",
+ "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
"shasum": ""
},
"require": {
- "php": "^5.5.9|>=7.0.8",
- "symfony/polyfill-ctype": "~1.8"
- },
- "conflict": {
- "symfony/console": "<3.4"
+ "php": "^5.3.3 || ^7.0",
+ "symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
- "symfony/console": "~3.4|~4.0"
- },
- "suggest": {
- "symfony/console": "For validating YAML files using the lint command"
+ "phpunit/phpunit": "^4.6",
+ "sebastian/version": "^1.0.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.4-dev"
+ "dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
- "Symfony\\Component\\Yaml\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
+ "Webmozart\\Assert\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -3050,75 +3332,82 @@
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
}
],
- "description": "Symfony Yaml Component",
- "homepage": "https://symfony.com",
- "time": "2019-07-24T13:01:31+00:00"
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "time": "2018-12-25T11:19:39+00:00"
},
{
- "name": "theseer/tokenizer",
- "version": "1.1.3",
+ "name": "webmozart/glob",
+ "version": "4.1.0",
"source": {
"type": "git",
- "url": "https://github.com/theseer/tokenizer.git",
- "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
+ "url": "https://github.com/webmozart/glob.git",
+ "reference": "3cbf63d4973cf9d780b93d2da8eec7e4a9e63bbe"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
- "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+ "url": "https://api.github.com/repos/webmozart/glob/zipball/3cbf63d4973cf9d780b93d2da8eec7e4a9e63bbe",
+ "reference": "3cbf63d4973cf9d780b93d2da8eec7e4a9e63bbe",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-tokenizer": "*",
- "ext-xmlwriter": "*",
- "php": "^7.0"
+ "php": "^5.3.3|^7.0",
+ "webmozart/path-util": "^2.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.6",
+ "sebastian/version": "^1.0.1",
+ "symfony/filesystem": "^2.5"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1-dev"
+ }
+ },
"autoload": {
- "classmap": [
- "src/"
- ]
+ "psr-4": {
+ "Webmozart\\Glob\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Arne Blankerts",
- "email": "arne@blankerts.de",
- "role": "Developer"
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
}
],
- "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
- "time": "2019-06-13T22:48:21+00:00"
+ "description": "A PHP implementation of Ant's glob.",
+ "time": "2015-12-29T11:14:33+00:00"
},
{
- "name": "webmozart/assert",
- "version": "1.4.0",
+ "name": "webmozart/path-util",
+ "version": "2.3.0",
"source": {
"type": "git",
- "url": "https://github.com/webmozart/assert.git",
- "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
+ "url": "https://github.com/webmozart/path-util.git",
+ "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
- "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
+ "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
+ "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0",
- "symfony/polyfill-ctype": "^1.8"
+ "php": ">=5.3.3",
+ "webmozart/assert": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
@@ -3127,12 +3416,12 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.3-dev"
+ "dev-master": "2.3-dev"
}
},
"autoload": {
"psr-4": {
- "Webmozart\\Assert\\": "src/"
+ "Webmozart\\PathUtil\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -3145,13 +3434,8 @@
"email": "bschussek@gmail.com"
}
],
- "description": "Assertions to validate method input/output with nice error messages.",
- "keywords": [
- "assert",
- "check",
- "validate"
- ],
- "time": "2018-12-25T11:19:39+00:00"
+ "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.",
+ "time": "2015-12-17T08:42:14+00:00"
},
{
"name": "yosymfony/resource-watcher",
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 00000000..74da9f7a
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ src
+ tests
+
+
+
+
+
diff --git a/psalm.xml.dist b/psalm.xml.dist
new file mode 100644
index 00000000..42f355b2
--- /dev/null
+++ b/psalm.xml.dist
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Agent.php b/src/Agent.php
index dfd3b26f..73ca09ce 100644
--- a/src/Agent.php
+++ b/src/Agent.php
@@ -1,133 +1,165 @@
config = new Config($this);
- $this->request = new Request($this);
- $this->logger = new NullLogger();
+ $this->config = $configuration;
+ $this->connector = $connector;
+ $this->logger = $logger;
- $this->ignoredEndpoints = new IgnoredEndpoints($this);
- $this->isIgnored = false;
- }
+ $this->request = new Request();
- public function connect()
- {
- $this->connector = new Connector($this);
- if (! $this->connector->connected() && $this->enabled()) {
- $this->logger->info("Scout Core Agent Connection Failed, attempting to start");
- $manager = new CoreAgentManager($this);
- $manager->launch();
-
- $this->connector->connect();
- } else {
- $this->logger->debug("Scout Core Agent Connected");
- }
+ $this->ignoredEndpoints = new IgnoredEndpoints($configuration->get('ignore'));
}
- // Returns true/false on if the agent should attempt to start and collect data.
- public function enabled() : bool
+ private static function createConnectorFromConfig(Config $config) : SocketConnector
{
- return $this->config->get('monitor');
+ return new SocketConnector($config->get('socket_path'));
}
/**
- * Sets the logger for the Agent to use
+ * @deprecated Once getConfig is removed, you cannot overwrite config using this...
*
- * @return void
+ * @todo alternative API to be discussed...
*/
- public function setLogger(\Psr\Log\LoggerInterface $logger)
+ public static function fromDefaults(?LoggerInterface $logger = null, ?Connector $connector = null) : self
{
- $this->logger = $logger;
+ $config = new Config();
+
+ return new self(
+ $config,
+ $connector ?? self::createConnectorFromConfig($config),
+ $logger ?? new NullLogger()
+ );
}
- public function setConfig(Config $config)
+ public static function fromConfig(Config $config, ?LoggerInterface $logger = null, ?Connector $connector = null) : self
{
- $this->config = $config;
+ return new self(
+ $config,
+ $connector ?? self::createConnectorFromConfig($config),
+ $logger ?? new NullLogger()
+ );
}
- /**
- * returns the active logger
- *
- * @return \Psr\Log\LoggerInterface compliant logger
- */
- public function getLogger()
+ public function connect() : void
{
- return $this->logger;
+ if (! $this->connector->connected() && $this->enabled()) {
+ $this->logger->info('Scout Core Agent Connection Failed, attempting to start');
+ $manager = new AutomaticDownloadAndLaunchManager(
+ $this->config,
+ $this->logger,
+ new Downloader(
+ $this->config->get('core_agent_dir'),
+ $this->config->get('core_agent_full_name'),
+ $this->logger,
+ $this->config->get('download_url')
+ )
+ );
+ $manager->launch();
+
+ $this->connector->connect();
+ } else {
+ $this->logger->debug('Scout Core Agent Connected');
+ }
}
/**
- * returns the active configuration
- *
- * @return \Scoutapm\Config
+ * Returns true/false on if the agent should attempt to start and collect data.
*/
- public function getConfig() : Config
+ public function enabled() : bool
{
- return $this->config;
+ return $this->config->get('monitor');
}
/**
- * Starts a new span on the current request.
+ * Starts a fromRequest span on the current request.
*
* NOTE: Do not call stop on the span itself, use the stopSpan() function
* here to ensure the whole system knows its stopped
*
- * @param operation The "name" of the span, something like "Controller/User" or "SQL/Query"
- * @param overrideTimestamp if you need to set the start time to something specific
+ * @param string $operation The "name" of the span, something like "Controller/User" or "SQL/Query"
+ * @param ?float $overrideTimestamp If you need to set the start time to something specific
*
- * @return Span
+ * @throws Exception
*/
- public function startSpan(string $operation, float $overrideTimestamp = null)
+ public function startSpan(string $operation, ?float $overrideTimestamp = null) : Span
{
if ($this->request === null) {
// Must return a Span object to match API. This is a dummy span
// that is not ever used for anything.
- return new Span($this, "Ignored", "ignored-request");
+ return new Span('Ignored', RequestId::new());
}
return $this->request->startSpan($operation, $overrideTimestamp);
}
- public function stopSpan()
+ public function stopSpan() : void
{
if ($this->request === null) {
- return null;
+ return;
}
$this->request->stopSpan();
}
- public function instrument($type, $name, Closure $block)
+ /** @return mixed */
+ public function instrument(string $type, string $name, Closure $block)
{
- $span = $this->startSpan($type . "/" . $name);
+ $span = $this->startSpan($type . '/' . $name);
try {
return $block($span);
@@ -136,31 +168,33 @@ public function instrument($type, $name, Closure $block)
}
}
- public function webTransaction($name, Closure $block)
+ /** @return mixed */
+ public function webTransaction(string $name, Closure $block)
{
- return $this->instrument("Controller", $name, $block);
+ return $this->instrument('Controller', $name, $block);
}
- public function backgroundTransaction($name, Closure $block)
+ /** @return mixed */
+ public function backgroundTransaction(string $name, Closure $block)
{
- return $this->instrument("Job", $name, $block);
+ return $this->instrument('Job', $name, $block);
}
- public function addContext(string $tag, $value)
+ public function addContext(string $tag, string $value) : void
{
- return $this->tagRequest($tag, $value);
+ $this->tagRequest($tag, $value);
}
- public function tagRequest(string $tag, $value)
+ public function tagRequest(string $tag, string $value) : void
{
if ($this->request === null) {
- return null;
+ return;
}
- return $this->request->tag($tag, $value);
+ $this->request->tag($tag, $value);
}
- /*
+ /**
* Check if a given URL was configured as ignored.
* Does not alter the running request. If you wish to abort tracing of this
* request, use ignore()
@@ -170,18 +204,21 @@ public function ignored(string $path) : bool
return $this->ignoredEndpoints->ignored($path);
}
- /*
+ /**
* Mark the running request as ignored. Triggers optimizations in various
* tracing and tagging methods to turn them into NOOPs
*/
- public function ignore()
+ public function ignore() : void
{
- $this->request = null;
+ $this->request = null;
$this->isIgnored = true;
}
- // Returns true only if the request was sent onward to the core agent.
- // False otherwise.
+ /**
+ * Returns true only if the request was sent onward to the core agent. False otherwise.
+ *
+ * @throws Exception
+ */
public function send() : bool
{
// Don't send if the agent is not enabled.
@@ -194,17 +231,35 @@ public function send() : bool
return false;
}
- // Send this request off to the CoreAgent
- $status = $this->connector->sendRequest($this->request);
- return $status;
+ if ($this->request === null) {
+ // @todo throw exception? return false?
+ return false;
+ }
+
+ if (! $this->connector->sendCommand(new RegisterMessage(
+ (string) $this->config->get('name'),
+ (string) $this->config->get('key'),
+ $this->config->get('api_version')
+ ))) {
+ return false;
+ }
+
+ if (! $this->connector->sendCommand(new Metadata(
+ new DateTimeImmutable('now', new DateTimeZone('UTC'))
+ ))) {
+ return false;
+ }
+
+ return $this->connector->sendCommand($this->request);
}
/**
* You probably don't need this, it's useful in testing
*
- * @return Request
+ * @internal
+ * @deprecated
*/
- public function getRequest() : \Scoutapm\Events\Request
+ public function getRequest() : ?Request
{
return $this->request;
}
diff --git a/src/Config.php b/src/Config.php
index 3322be54..e2411d07 100644
--- a/src/Config.php
+++ b/src/Config.php
@@ -1,41 +1,70 @@
*/
private $sources;
+
+ /** @var UserSettingsSource */
private $userSettingsSource;
- private $agent;
+
+ /** @var CoerceType[]|array */
private $coercions;
- public function __construct(\Scoutapm\Agent $agent)
+ public function __construct()
{
- $this->agent = $agent;
- $this->userSettingsSource = new Config\UserSettingsSource();
+ $this->userSettingsSource = new UserSettingsSource();
$this->sources = [
- new Config\EnvSource(),
+ new EnvSource(),
$this->userSettingsSource,
- new Config\DerivedSource($this),
- new Config\DefaultSource(),
- new Config\NullSource(),
+ new DerivedSource($this),
+ new DefaultSource(),
+ new NullSource(),
];
$this->coercions = [
- "monitor" => Config\BoolCoercion::class,
- "ignore" => Config\JSONCoercion::class,
+ 'monitor' => new CoerceBoolean(),
+ 'ignore' => new CoerceJson(),
];
}
+ /** @param mixed[]|array $config */
+ public static function fromArray(array $config = []) : self
+ {
+ $instance = new self();
+
+ foreach ($config as $key => $value) {
+ $instance->set($key, $value);
+ }
+
+ return $instance;
+ }
/**
* Looks through all available sources for the first that can handle this
* key, then returns the value from that source.
+ *
+ * @return mixed
*/
public function get(string $key)
{
@@ -46,21 +75,23 @@ public function get(string $key)
}
}
+ if (! isset($value)) {
+ return null;
+ }
+
if (array_key_exists($key, $this->coercions)) {
- $coercion = new $this->coercions[$key];
- $value = $coercion->coerce($value);
+ $value = $this->coercions[$key]->coerce($value);
}
- return $value;
+ return $value ?? null;
}
-
/**
* Sets a value on the inner UserSettingsSource
*
- * @return void
+ * @param mixed $value
*/
- public function set(string $key, $value)
+ public function set(string $key, $value) : void
{
$this->userSettingsSource->set($key, $value);
}
diff --git a/src/Config/BoolCoercion.php b/src/Config/BoolCoercion.php
deleted file mode 100644
index abca4530..00000000
--- a/src/Config/BoolCoercion.php
+++ /dev/null
@@ -1,23 +0,0 @@
-config = $config;
- $this->handlers = [
- "core_agent_triple",
- "core_agent_full_name",
- "socket_path",
- "testing", // Used for testing. Should be removed and test converted to a real value once we have one.
- ];
- }
-
- /**
- * Returns true iff this config source knows for certain it has an answer for this key
- *
- * @return bool
- */
- public function hasKey(string $key) : bool
- {
- return in_array($key, $this->handlers);
- }
-
-
- /**
- * Returns the value for this configuration key.
- *
- * Only valid if the Source has previously returned "true" to `hasKey`
- *
- * @return The value requested
- */
- public function get(string $key)
- {
- // Whitelisted keys only
- if (! $this->hasKey($key)) {
- return null;
- }
-
- return $this->$key();
- }
-
- ///////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////
- // Derived Keys below this spot.
- //
- public function socket_path()
- {
- $dir = $this->config->get("core_agent_dir");
- $fullName = $this->config->get("core_agent_full_name");
-
- return $dir . "/" . $fullName . "/core-agent.sock";
- }
-
- public function core_agent_full_name()
- {
- $name = "scout_apm_core";
- $version = $this->config->get("core_agent_version");
- $triple = $this->config->get("core_agent_triple");
- return $name . "-" . $version . "-" . $triple;
- }
-
- public function core_agent_triple()
- {
- $arch = 'unknown';
- $unameArch = php_uname("m");
- if ($unameArch == 'i686') {
- $arch = 'i686';
- }
- if ($unameArch == 'x86_64') {
- $arch = 'x86_64';
- }
-
- $platform = "unknown-linux-gnu";
- $unamePlatform = php_uname("s");
- if ($unamePlatform == 'Darwin') {
- $platform = 'apple-darwin';
- }
-
- return $arch . "-" . $platform;
- }
-
- /**
- * Used for testing this class, not a real configuration.
- * We should remove this and adjust the test once we have a real use of this class.
- */
- private function testing()
- {
- $version = $this->config->get("api_version");
- return "derived api version: " . $version;
- }
-}
diff --git a/src/Config/IgnoredEndpoints.php b/src/Config/IgnoredEndpoints.php
new file mode 100644
index 00000000..3500c500
--- /dev/null
+++ b/src/Config/IgnoredEndpoints.php
@@ -0,0 +1,32 @@
+ $ignoredPaths */
+ public function __construct(array $ignoredPaths)
+ {
+ $this->ignoredPaths = $ignoredPaths;
+ }
+
+ public function ignored(string $url) : bool
+ {
+ foreach ($this->ignoredPaths as $ignore) {
+ if (strpos($url, $ignore) === 0) {
+ return true;
+ }
+ }
+
+ // None Matched
+ return false;
+ }
+}
diff --git a/src/Config/DefaultSource.php b/src/Config/Source/DefaultSource.php
similarity index 76%
rename from src/Config/DefaultSource.php
rename to src/Config/Source/DefaultSource.php
index 2d97e9f8..bd5c50b2 100644
--- a/src/Config/DefaultSource.php
+++ b/src/Config/Source/DefaultSource.php
@@ -1,15 +1,21 @@
)> */
private $defaults;
public function __construct()
@@ -19,34 +25,30 @@ public function __construct()
/**
* Returns true iff this config source knows for certain it has an answer for this key
- *
- * @return bool
*/
public function hasKey(string $key) : bool
{
return array_key_exists($key, $this->defaults);
}
-
/**
* Returns the value for this configuration key.
*
* Only valid if the Source has previously returned "true" to `hasKey`
*
- * @return The value requested
+ * @return string|bool|array|null
*/
public function get(string $key)
{
- return ($this->defaults[$key]) ?? null;
+ return $this->defaults[$key] ?? null;
}
-
/**
* Returns the value for this configuration key.
*
* Only valid if the Source has previously returned "true" to `hasKey`
*
- * @return The value requested
+ * @return array)>
*/
private function getDefaultConfig() : array
{
@@ -57,6 +59,8 @@ private function getDefaultConfig() : array
'core_agent_launch' => true,
'core_agent_version' => 'latest',
'download_url' => 'https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release',
+ 'monitor' => false,
+ 'ignore' => [],
];
}
}
diff --git a/src/Config/Source/DerivedSource.php b/src/Config/Source/DerivedSource.php
new file mode 100644
index 00000000..6eb48aff
--- /dev/null
+++ b/src/Config/Source/DerivedSource.php
@@ -0,0 +1,107 @@
+config = $config;
+ }
+
+ /**
+ * Returns true if this config source knows for certain it has an answer for this key
+ */
+ public function hasKey(string $key) : bool
+ {
+ return $this->get($key) !== null;
+ }
+
+ /**
+ * Returns the value for this configuration key.
+ *
+ * Only valid if the Source has previously returned "true" to `hasKey`
+ *
+ * @return mixed
+ */
+ public function get(string $key)
+ {
+ switch ($key) {
+ case 'socket_path':
+ return $this->socketPath();
+ case 'core_agent_full_name':
+ return $this->coreAgentFullName();
+ case 'core_agent_triple':
+ return $this->coreAgentTriple();
+ case 'testing':
+ return $this->testing();
+ }
+
+ return null;
+ }
+
+ private function socketPath() : string
+ {
+ $dir = $this->config->get('core_agent_dir');
+ $fullName = $this->config->get('core_agent_full_name');
+
+ return $dir . '/' . $fullName . '/core-agent.sock';
+ }
+
+ private function coreAgentFullName() : string
+ {
+ $name = 'scout_apm_core';
+ $version = $this->config->get('core_agent_version');
+ $triple = $this->config->get('core_agent_triple');
+
+ return $name . '-' . $version . '-' . $triple;
+ }
+
+ private function coreAgentTriple() : string
+ {
+ $arch = 'unknown';
+ $unameArch = php_uname('m');
+ if ($unameArch === 'i686') {
+ $arch = 'i686';
+ }
+ if ($unameArch === 'x86_64') {
+ $arch = 'x86_64';
+ }
+
+ $platform = 'unknown-linux-gnu';
+ $unamePlatform = php_uname('s');
+ if ($unamePlatform === 'Darwin') {
+ $platform = 'apple-darwin';
+ }
+
+ return $arch . '-' . $platform;
+ }
+
+ /**
+ * Used for testing this class, not a real configuration.
+ * We should remove this and adjust the test once we have a real use of this class.
+ */
+ private function testing() : string
+ {
+ $version = $this->config->get('api_version');
+
+ return 'derived api version: ' . $version;
+ }
+}
diff --git a/src/Config/EnvSource.php b/src/Config/Source/EnvSource.php
similarity index 66%
rename from src/Config/EnvSource.php
rename to src/Config/Source/EnvSource.php
index 3f7be109..0568802d 100644
--- a/src/Config/EnvSource.php
+++ b/src/Config/Source/EnvSource.php
@@ -1,5 +1,7 @@
envVarName($key))) {
- return true;
- } else {
- return false;
- }
+ return getenv($this->envVarName($key)) !== false;
}
-
/**
* Returns the value for this configuration key.
*
* Only valid if the Source has previously returned "true" to `hasKey`
*
- * @return The value requested
+ * @return mixed
*/
public function get(string $key)
{
- $value = getEnv($this->envVarName($key));
+ $value = getenv($this->envVarName($key));
// Make sure this returns null when not found, instead of getEnv's false.
- if ($value == false) {
+ if ($value === false) {
$value = null;
}
return $value;
}
- /**
- * undocumented function
- *
- * @return void
- */
private function envVarName(string $key) : string
{
$upper = strtoupper($key);
- return "SCOUT_" . $upper;
+
+ return 'SCOUT_' . $upper;
}
}
diff --git a/src/Config/NullSource.php b/src/Config/Source/NullSource.php
similarity index 85%
rename from src/Config/NullSource.php
rename to src/Config/Source/NullSource.php
index 29777f9a..5ac4cd7d 100644
--- a/src/Config/NullSource.php
+++ b/src/Config/Source/NullSource.php
@@ -1,13 +1,16 @@
*/
private $config;
public function __construct()
@@ -19,30 +25,27 @@ public function __construct()
}
/**
- * Returns true iff this config source knows for certain it has an answer for this key
- *
- * @return bool
+ * Returns true if this config source knows for certain it has an answer for this key
*/
public function hasKey(string $key) : bool
{
return array_key_exists($key, $this->config);
}
-
/**
* Returns the value for this configuration key.
*
* Only valid if the Source has previously returned "true" to `hasKey`
*
- * @return The value requested
+ * @return mixed
*/
public function get(string $key)
{
- return ($this->config[$key]) ?? null;
+ return $this->config[$key] ?? null;
}
-
- public function set($key, $value)
+ /** @param mixed $value */
+ public function set(string $key, $value) : void
{
$this->config[$key] = $value;
}
diff --git a/src/Config/TypeCoercion/CoerceBoolean.php b/src/Config/TypeCoercion/CoerceBoolean.php
new file mode 100644
index 00000000..6692b7bc
--- /dev/null
+++ b/src/Config/TypeCoercion/CoerceBoolean.php
@@ -0,0 +1,32 @@
+agent = $agent;
- $this->config = $agent->getConfig();
-
- $this->socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
- $this->connect();
- register_shutdown_function([&$this, 'shutdown']);
- }
-
- public function connect() : void
- {
- try {
- $this->connected = socket_connect($this->socket, $this->config->get('socket_path'));
- } catch (\Exception $e) {
- $this->connected = false;
- }
- }
-
- public function connected() : bool
- {
- return $this->connected;
- }
-
- /**
- * @param $message array|\JsonSerializable needs to be a single jsonable command
- */
- private function sendMessage($message) : void
- {
- $serializedJsonString = json_encode($message);
-
- $size = strlen($serializedJsonString);
- socket_send($this->socket, pack('N', $size), 4, 0);
- socket_send($this->socket, $serializedJsonString, $size, 0);
-
- // Read the response back and drop it. Needed for socket liveness
- $responseLength = socket_read($this->socket, 4);
- socket_read($this->socket, unpack('N', $responseLength)[1]);
- }
-
- /** @throws \Exception */
- public function sendRequest(Request $request) : bool
- {
- $this->sendMessage([
- 'Register' => [
- 'app' => $this->config->get('name'),
- 'key' => $this->config->get('key'),
- 'language' => 'php',
- 'api_version' => $this->config->get('api_version'),
- ]
- ]);
-
- $this->sendMessage(new Metadata(
- $this->agent,
- new DateTimeImmutable('now', new DateTimeZone('UTC'))
- ));
-
- // Send the whole Request as a batch command
- $this->sendMessage([
- 'BatchCommand' => [
- 'commands' => $request,
- ]
- ]);
-
- return socket_last_error($this->socket) === 0;
- }
-
- public function shutdown()
- {
- if ($this->connected === true) {
- socket_shutdown($this->socket, 2);
- socket_close($this->socket);
- }
- }
-}
diff --git a/src/Connector/Command.php b/src/Connector/Command.php
new file mode 100644
index 00000000..69343dca
--- /dev/null
+++ b/src/Connector/Command.php
@@ -0,0 +1,11 @@
+getMessage()
+ ));
+ }
+}
diff --git a/src/Connector/Exception/NotConnected.php b/src/Connector/Exception/NotConnected.php
new file mode 100644
index 00000000..485e247f
--- /dev/null
+++ b/src/Connector/Exception/NotConnected.php
@@ -0,0 +1,19 @@
+socketPath = $socketPath;
+
+ $this->socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
+
+ // Pre-emptive attempt to connect, strictly speaking `__construct` should not have side-effects, so if this
+ // fails then swallow it. The `Agent` goes on to call connect() anyway, and handles launching of the core agent.
+ try {
+ $this->connect();
+ } catch (FailedToConnect $failedToConnect) {
+ }
+ }
+
+ public function connect() : void
+ {
+ if ($this->connected()) {
+ return;
+ }
+
+ try {
+ $this->connected = socket_connect($this->socket, $this->socketPath);
+ register_shutdown_function([&$this, 'shutdown']);
+ } catch (Throwable $e) {
+ $this->connected = false;
+ throw FailedToConnect::fromSocketPathAndPrevious($this->socketPath, $e);
+ }
+ }
+
+ public function connected() : bool
+ {
+ return $this->connected;
+ }
+
+ public function sendCommand(Command $message) : bool
+ {
+ if (! $this->connected()) {
+ throw NotConnected::fromSocketPath($this->socketPath);
+ }
+
+ $serializedJsonString = json_encode($message);
+
+ $size = strlen($serializedJsonString);
+ socket_send($this->socket, pack('N', $size), 4, 0);
+ socket_send($this->socket, $serializedJsonString, $size, 0);
+
+ // Read the response back and drop it. Needed for socket liveness
+ $responseLength = socket_read($this->socket, 4);
+ /** @noinspection UnusedFunctionResultInspection */
+ socket_read($this->socket, unpack('N', $responseLength)[1]);
+
+ return socket_last_error($this->socket) === 0;
+ }
+
+ public function shutdown() : void
+ {
+ if (! $this->connected()) {
+ return;
+ }
+
+ socket_shutdown($this->socket, 2);
+ socket_close($this->socket);
+ }
+}
diff --git a/src/CoreAgent/AutomaticDownloadAndLaunchManager.php b/src/CoreAgent/AutomaticDownloadAndLaunchManager.php
new file mode 100644
index 00000000..c59e2839
--- /dev/null
+++ b/src/CoreAgent/AutomaticDownloadAndLaunchManager.php
@@ -0,0 +1,183 @@
+config = $config;
+ $this->logger = $logger;
+ $this->coreAgentDir = $config->get('core_agent_dir') . '/' . $config->get('core_agent_full_name');
+
+ $this->downloader = $downloader;
+ }
+
+ public function launch() : bool
+ {
+ if (! $this->config->get('core_agent_launch')) {
+ $this->logger->debug("Not attempting to launch Core Agent due to 'core_agent_launch' setting.");
+
+ return false;
+ }
+
+ if (! $this->verify()) {
+ if (! $this->config->get('core_agent_download')) {
+ $this->logger->debug(
+ "Not attempting to download Core Agent due to 'core_agent_download' setting."
+ );
+
+ return false;
+ }
+
+ $this->download();
+ }
+
+ if (! $this->verify()) {
+ $this->logger->debug(
+ 'Failed to verify Core Agent. Not launching Core Agent.'
+ );
+
+ return false;
+ }
+
+ return $this->run();
+ }
+
+ /**
+ * Initiate download of the agent
+ */
+ private function download() : void
+ {
+ $this->downloader->download();
+ }
+
+ private function verify() : bool
+ {
+ // Check for a well formed manifest
+ $manifest = new Manifest($this->coreAgentDir . '/manifest.json', $this->logger);
+ if (! $manifest->isValid()) {
+ $this->logger->debug('Core Agent verification failed: Manifest is not valid.');
+ $this->coreAgentBinPath = null;
+
+ return false;
+ }
+
+ // Check that the hash matches
+ $binPath = $this->coreAgentDir . '/' . $manifest->binaryName();
+ if (hash('sha256', file_get_contents($binPath)) === $manifest->hashOfBinary()) {
+ $this->coreAgentBinPath = $binPath;
+
+ return true;
+ }
+
+ $this->logger->debug('Core Agent verification failed: SHA mismatch.');
+ $this->coreAgentBinPath = null;
+
+ return false;
+ }
+
+ private function run() : bool
+ {
+ $this->logger->debug('Core Agent Launch in Progress');
+ try {
+ // @todo ESCAPE THIS !!!
+ $command = $this->agentBinary() . ' ' .
+ $this->daemonizeFlag() . ' ' .
+ $this->logLevel() . ' ' .
+ $this->logFile() . ' ' .
+ $this->configFile() . ' ' .
+ $this->socketPath();
+ $this->logger->debug('Core Agent: ' . $command);
+ exec($command);
+
+ return true;
+ } catch (Throwable $e) {
+ // TODO detect failure of launch properly
+ // logger.error("Error running Core Agent: %r", e);
+ return false;
+ }
+ }
+
+ private function agentBinary() : string
+ {
+ // @todo should this be an exception...?
+ if ($this->coreAgentBinPath === null) {
+ return ' start';
+ }
+
+ return $this->coreAgentBinPath . ' start';
+ }
+
+ private function daemonizeFlag() : string
+ {
+ return '--daemonize true';
+ }
+
+ private function logLevel() : string
+ {
+ $log_level = $this->config->get('logLevel');
+ if ($log_level !== null) {
+ return '--log-level ' . $log_level;
+ }
+
+ return '';
+ }
+
+ /**
+ * Core Agent log file. Does not affect any logging in the PHP side of the agent. Useful only for debugging purposes.
+ */
+ private function logFile() : string
+ {
+ $log_file = $this->config->get('logFile');
+ if ($log_file !== null) {
+ return '--log-file ' . $log_file;
+ }
+
+ return '';
+ }
+
+ /**
+ * Allow a config file to be passed (this is distinct from the php configuration, this is only used for core-agent
+ * specific configs, mostly for debugging, or other niche cases)
+ */
+ private function configFile() : string
+ {
+ $config = $this->config->get('configFile');
+ if ($config !== null) {
+ return '--config-file ' . $config;
+ }
+
+ return '';
+ }
+
+ private function socketPath() : string
+ {
+ return '--socket ' . $this->config->get('socketPath');
+ }
+}
diff --git a/src/CoreAgent/Downloader.php b/src/CoreAgent/Downloader.php
new file mode 100644
index 00000000..d82d4a6f
--- /dev/null
+++ b/src/CoreAgent/Downloader.php
@@ -0,0 +1,165 @@
+logger = $logger;
+
+ $this->coreAgentDir = $coreAgentDir;
+ $this->coreAgentFullName = $coreAgentFullName;
+ $this->stale_download_secs = 120;
+
+ $this->package_location = $coreAgentDir . '/' . $coreAgentFullName . '.tgz';
+ $this->download_lock_path = $coreAgentDir . '/download.lock';
+ $this->downloadUrl = $downloadUrl;
+ }
+
+ public function download() : void
+ {
+ $this->createCoreAgentDir();
+ $this->obtainDownloadLock();
+
+ if ($this->download_lock_fd === null) {
+ return;
+ }
+
+ try {
+ $this->downloadPackage();
+ $this->untar();
+ } catch (Throwable $e) {
+ $this->logger->error('Exception raised while downloading Core Agent: ' . $e);
+ } finally {
+ $this->releaseDownloadLock();
+ }
+ }
+
+ private function createCoreAgentDir() : void
+ {
+ try {
+ $permissions = 0777; // TODO: AgentContext.instance.config.core_agent_permissions()
+ $recursive = true;
+ $destination = $this->coreAgentDir;
+
+ if (! is_dir($destination)) {
+ mkdir($destination, $permissions, $recursive);
+ }
+ } catch (Throwable $e) {
+ $this->logger->error('Failed to create directory: ' . $destination);
+ }
+ }
+
+ private function obtainDownloadLock() : void
+ {
+ $this->cleanStaleDownloadLock();
+
+ try {
+ $this->download_lock_fd = fopen(
+ $this->download_lock_path,
+ 'x+' // This is the same as O_RDWR | O_EXCL | O_CREAT
+ // O_RDWR | O_CREAT | O_EXCL | O_NONBLOCK
+ );
+ } catch (Throwable $e) {
+ $this->logger->debug('Could not obtain download lock on ' . $this->download_lock_path . ': ' . $e);
+ $this->download_lock_fd = null;
+ }
+ }
+
+ private function cleanStaleDownloadLock() : void
+ {
+ try {
+ $delta = time() - filectime($this->download_lock_path);
+ if ($delta > $this->stale_download_secs) {
+ $this->logger->debug('Clearing stale download lock file.');
+ unlink($this->download_lock_path);
+ }
+ } catch (Throwable $e) {
+ // Log this
+ }
+ }
+
+ private function releaseDownloadLock() : void
+ {
+ if ($this->download_lock_fd === null) {
+ return;
+ }
+
+ fclose($this->download_lock_fd);
+ unlink($this->download_lock_path);
+ }
+
+ private function downloadPackage() : void
+ {
+ copy($this->fullUrl(), $this->package_location);
+ }
+
+ private function untar() : void
+ {
+ $destination = $this->coreAgentDir;
+
+ // Uncompress the .tgz
+ $phar = new PharData($this->package_location);
+ $phar->decompress();
+
+ // Extract it to destination
+ $tar_location = dirname($this->package_location) . '/' . basename($this->package_location, '.tgz') . '.tar';
+ $phar = new PharData($tar_location);
+ $phar->extractTo($destination);
+ }
+
+ /**
+ * The URL to download the agent package from
+ */
+ private function fullUrl() : string
+ {
+ return $this->downloadUrl . '/' . $this->coreAgentFullName . '.tgz';
+ }
+}
diff --git a/src/CoreAgent/Manager.php b/src/CoreAgent/Manager.php
new file mode 100644
index 00000000..ac6577c9
--- /dev/null
+++ b/src/CoreAgent/Manager.php
@@ -0,0 +1,10 @@
+manifestPath = $manifestPath;
+ $this->logger = $logger;
+
+ try {
+ $this->parse();
+ } catch (Throwable $e) {
+ $this->valid = false;
+ }
+ }
+
+ private function parse() : void
+ {
+ $this->logger->info('Parsing Core Agent Manifest at ' . $this->manifestPath);
+
+ $raw = file_get_contents($this->manifestPath);
+ $json = json_decode($raw, true); // decode the JSON into an associative array
+
+ // @todo unused, do we need this?
+ //$this->version = $json['version'];
+
+ $this->binVersion = $json['core_agent_version'];
+ $this->binName = $json['core_agent_binary'];
+ $this->sha256 = $json['core_agent_binary_sha256'];
+ $this->valid = true;
+ }
+
+ public function isValid() : bool
+ {
+ return $this->valid;
+ }
+
+ public function hashOfBinary() : string
+ {
+ return $this->sha256;
+ }
+
+ public function binaryName() : string
+ {
+ return $this->binName;
+ }
+
+ public function binaryVersion() : string
+ {
+ return $this->binVersion;
+ }
+}
diff --git a/src/CoreAgentManager.php b/src/CoreAgentManager.php
deleted file mode 100644
index 02fbe796..00000000
--- a/src/CoreAgentManager.php
+++ /dev/null
@@ -1,337 +0,0 @@
-agent = $agent;
- $this->coreAgentDir =
- $agent->getConfig()->get("core_agent_dir") .
- "/" .
- $agent->getConfig()->get("core_agent_full_name");
-
- $this->downloader = new CoreAgentDownloader(
- $this->coreAgentDir,
- $this->agent->getConfig()->get("core_agent_full_name"),
- $agent
- );
- }
-
- /**
- * @return void
- */
- public function launch()
- {
- if (! $this->agent->getConfig()->get("core_agent_launch")) {
- $this->agent->getLogger()->debug(
- "Not attempting to launch Core Agent due to 'core_agent_launch' setting."
- );
- return false;
- }
-
- if (! $this->verify()) {
- if (! $this->agent->getConfig()->get("core_agent_download")) {
- $this->agent->getLogger()->debug(
- "Not attempting to download Core Agent due to 'core_agent_download' setting."
- );
- return false;
- }
-
- $this->download();
- }
-
-
- if (! $this->verify()) {
- $this->agent->getLogger()->debug(
- "Failed to verify Core Agent. Not launching Core Agent."
- );
- return false;
- }
-
- return $this->run();
- }
-
- /**
- * Initiate download of the agent
- *
- * @return void
- */
- public function download()
- {
- $this->downloader->download();
- }
-
- public function verify()
- {
- // Check for a well formed manifest
- $manifest = new CoreAgentManifest($this->coreAgentDir . "/manifest.json", $this->agent);
- if (! $manifest->isValid()) {
- $this->agent->getLogger()->debug("Core Agent verification failed: CoreAgentManifest is not valid.");
- $this->core_agent_bin_path = null;
- $this->core_agent_bin_version = null;
- return false;
- }
-
- // Check that the hash matches
- $binPath = $this->coreAgentDir . "/" . $manifest->binName;
- if (hash("sha256", file_get_contents($binPath)) == $manifest->sha256) {
- $this->core_agent_bin_path = $binPath;
- $this->core_agent_bin_version = $manifest->binVersion;
- return true;
- } else {
- $this->agent->getLogger()->debug("Core Agent verification failed: SHA mismatch.");
- $this->core_agent_bin_path = null;
- $this->core_agent_bin_version = null;
- return false;
- }
- }
-
- /**
- *
- *
- * @return void
- */
- public function run()
- {
- $this->agent->getLogger()->debug("Core Agent Launch in Progress");
- try {
- $command = $this->agent_binary() . " " .
- $this->daemonize_flag() . " " .
- $this->log_level() . " " .
- $this->log_file() . " " .
- $this->config_file() . " " .
- $this->socket_path();
- $this->agent->getLogger()->debug("Core Agent: ".$command);
- exec($command);
- return true;
- } catch (Exception $e) {
- // TODO detect failure of launch properly
- // logger.error("Error running Core Agent: %r", e);
- return false;
- }
- }
-
- public function agent_binary()
- {
- return $this->core_agent_bin_path . " start";
- }
-
- public function daemonize_flag()
- {
- return "--daemonize true";
- }
-
- public function log_level()
- {
- $log_level = $this->agent->getConfig()->get('log_level');
- if (!is_null($log_level)) {
- return "--log-level " . $log_level;
- } else {
- return "";
- }
- }
-
- // Core Agent log file. Does not affect any logging in the PHP side of the
- // agent. Useful only for debugging purposes.
- public function log_file()
- {
- $log_file = $this->agent->getConfig()->get('log_file');
- if (!is_null($log_file)) {
- return "--log-file " . $log_file;
- } else {
- return "";
- }
- }
-
- // Allow a config file to be passed (this is distinct from the
- // php configuration, this is only used for core-agent specific
- // configs, mostly for debugging, or other niche cases)
- public function config_file()
- {
- $config = $this->agent->getConfig()->get('config_file');
- if (!is_null($config)) {
- return "--config-file " . $config;
- } else {
- return "";
- }
- }
-
- public function socket_path()
- {
- return "--socket " . $this->agent->getConfig()->get('socket_path');
- }
-}
-
-/**
- * Class CoreAgentDownloader
- *
- * A helper class for the CoreAgentManager that handles downloading, verifying,
- * and unpacking the CoreAgent.
- */
-class CoreAgentDownloader
-{
- /**
- * @param $coreAgentDir
- * @param $coreAgentFullName
- * @param $agent
- */
- public function __construct($coreAgentDir, $coreAgentFullName, $agent)
- {
- $this->coreAgentDir = $coreAgentDir;
- $this->coreAgentFullName = $coreAgentFullName;
- $this->agent = $agent;
- $this->stale_download_secs = 120;
-
- $this->package_location = $coreAgentDir . "/". $coreAgentFullName . ".tgz";
- $this->download_lock_path = $coreAgentDir . "/download.lock";
- $this->download_lock_fd = null;
- }
-
- public function download()
- {
- $this->create_core_agent_dir();
- $this->obtain_download_lock();
-
- if ($this->download_lock_fd != null) {
- try {
- $this->download_package();
- $this->untar();
- } catch (Exception $e) {
- $this->agent->getLogger()->error("Exception raised while downloading Core Agent: ". $e);
- } finally {
- $this->release_download_lock();
- }
- }
- return null;
- }
-
- public function create_core_agent_dir()
- {
- try {
- $permissions = 0777; // TODO: AgentContext.instance.config.core_agent_permissions()
- $recursive = true;
- $destination = $this->coreAgentDir;
-
- if (!is_dir($destination)) {
- mkdir($destination, $permissions, $recursive);
- }
- } catch (Exception $e) {
- $this->agent->getLogger()->error("Failed to create directory: " . $destination);
- }
- }
-
- public function obtain_download_lock()
- {
- $this->clean_stale_download_lock();
-
- try {
- $this->download_lock_fd = fopen(
- $this->download_lock_path,
- "x+" // This is the same as O_RDWR | O_EXCL | O_CREAT
- // O_RDWR | O_CREAT | O_EXCL | O_NONBLOCK
- );
- } catch (Exception $e) {
- $this->agent->getLogger()->debug("Could not obtain download lock on ".$this->download_lock_path . ": ". $e);
- $this->download_lock_fd = null;
- }
- }
-
- public function clean_stale_download_lock()
- {
- try {
- $delta = time() - filectime($this->download_lock_path);
- if ($delta > $this->stale_download_secs) {
- $this->agent->getLogger()->debug("Clearing stale download lock file.");
- unlink($this->download_lock_path);
- }
- } catch (\Exception $e) {
- // Log this
- }
- }
-
- public function release_download_lock()
- {
- if ($this->download_lock_fd != null) {
- fclose($this->download_lock_fd);
- unlink($this->download_lock_path);
- }
- }
-
- public function download_package()
- {
- file_put_contents(
- $this->package_location,
- file_get_contents($this->full_url())
- );
- }
-
- public function untar()
- {
- $destination = $this->coreAgentDir;
-
- // Uncompress the .tgz
- $phar = new PharData($this->package_location);
- $phar->decompress();
-
- // Extract it to destination
- $tar_location = dirname($this->package_location) . "/" . basename($this->package_location, '.tgz') . '.tar';
- $phar = new PharData($tar_location);
- $phar->extractTo($destination);
- }
-
- // The URL to download the agent package from
- public function full_url()
- {
- $root_url = $this->agent->getConfig()->get("download_url");
- return $root_url . "/" . $this->coreAgentFullName . ".tgz";
- }
-}
-
-class CoreAgentManifest
-{
- public function __construct($path, $agent)
- {
- $this->manifest_path = $path;
- $this->agent = $agent;
-
- try {
- $this->parse();
- } catch (\Exception $e) {
- $this->valid = false;
- }
- }
-
- public function parse()
- {
- $this->agent->getLogger()->info("Parsing Core Agent Manifest at ". $this->manifest_path);
-
- $raw = file_get_contents($this->manifest_path);
- $json = json_decode($raw, true); // decode the JSON into an associative array
-
- $this->version = $json["version"];
- $this->binVersion = $json["core_agent_version"];
- $this->binName = $json["core_agent_binary"];
- $this->sha256 = $json["core_agent_binary_sha256"];
- $this->valid = true;
- }
-
- public function isValid()
- {
- return $this->valid;
- }
-}
diff --git a/src/Events/Event.php b/src/Events/Event.php
deleted file mode 100644
index b32d6273..00000000
--- a/src/Events/Event.php
+++ /dev/null
@@ -1,23 +0,0 @@
-agent = $agent;
- $this->id = Uuid::uuid4()->toString();
- }
-
- public function getId() : string
- {
- return $this->id;
- }
-}
diff --git a/src/Events/Exception/SpanHasNotBeenStarted.php b/src/Events/Exception/SpanHasNotBeenStarted.php
new file mode 100644
index 00000000..49f3938f
--- /dev/null
+++ b/src/Events/Exception/SpanHasNotBeenStarted.php
@@ -0,0 +1,20 @@
+toString()
+ ));
+ }
+}
diff --git a/src/Events/Metadata.php b/src/Events/Metadata.php
index 22ab389d..1174f9b0 100644
--- a/src/Events/Metadata.php
+++ b/src/Events/Metadata.php
@@ -1,30 +1,36 @@
timer = new Timer((float) $now->format('U.u'));
}
/**
- * @return array>
+ * @return array|null)>
*/
- private function data()
+ private function data() : array
{
return [
'language' => 'php',
@@ -47,20 +53,20 @@ private function data()
}
/**
- * @TODO: Return an array of arrays: [["package name", "package version"], ....]
- *
* @return array>
+ *
+ * @TODO: Return an array of arrays: [["package name", "package version"], ....]
*/
- private function getLibraries() : array
- {
- // $composer = require __DIR__ . "/vendor/autoload.php";
- return [];
- }
+// private function getLibraries() : array
+// {
+// $composer = require __DIR__ . "/vendor/autoload.php";
+// return [];
+// }
/**
* Turn this object into a list of commands to send to the CoreAgent
*
- * @return array>
+ * @return array>
*/
public function jsonSerialize() : array
{
diff --git a/src/Events/RegisterMessage.php b/src/Events/RegisterMessage.php
new file mode 100644
index 00000000..2fd11b14
--- /dev/null
+++ b/src/Events/RegisterMessage.php
@@ -0,0 +1,40 @@
+appName = $appName;
+ $this->appKey = $appKey;
+ $this->apiVersion = $apiVersion;
+ }
+
+ /** @return array> */
+ public function jsonSerialize() : array
+ {
+ return [
+ 'Register' => [
+ 'app' => $this->appName,
+ 'key' => $this->appKey,
+ 'language' => 'php',
+ 'api_version' => $this->apiVersion,
+ ],
+ ];
+ }
+}
diff --git a/src/Events/Request.php b/src/Events/Request.php
deleted file mode 100644
index 076c524f..00000000
--- a/src/Events/Request.php
+++ /dev/null
@@ -1,110 +0,0 @@
-timer = new \Scoutapm\Helper\Timer();
- }
-
- public function stop()
- {
- $this->timer->stop();
- }
-
- public function startSpan(string $operation, $overrideTimestamp = null)
- {
- $span = new Span($this->agent, $operation, $this->id, $overrideTimestamp);
-
- // Automatically wire up the parent of this span
- if ($parent = end($this->openSpans)) {
- $span->setParentId($parent->getId());
- }
-
- $this->openSpans[] = $span;
-
- return $span;
- }
-
- // Stop the currently "running" span.
- // You can still tag it if needed up until the request as a whole is finished.
- public function stopSpan($overrideTimestamp = null)
- {
- $span = array_pop($this->openSpans);
-
- if ($span === null) {
- throw new NotStartedException();
- }
- $span->stop($overrideTimestamp);
-
- $threshold = 0.5;
- if ($span->duration() > $threshold) {
- $stack = Backtrace::capture();
- $stack = array_slice($stack, 4);
- $span->tag("stack", $stack);
- }
-
- $this->events[] = $span;
- }
-
- // Add a tag to the request as a whole
- public function tag(string $tag, $value)
- {
- $tag = new TagRequest($this->agent, $tag, $value, $this->id);
- $this->events[] = $tag;
- }
-
- /**
- * turn this object into a list of commands to send to the CoreAgent
- *
- * @return array[core agent commands]
- */
- public function jsonSerialize() : array
- {
- $commands = [];
- $commands[] = ['StartRequest' => [
- 'request_id' => $this->getId(),
- 'timestamp' => $this->timer->getStart(),
- ]];
-
- foreach ($this->events as $event) {
- $array = $event->jsonSerialize();
-
- foreach ($array as $value) {
- $commands[] = $value;
- }
- }
-
- $commands[] = ['FinishRequest' => [
- 'request_id' => $this->getId(),
- 'timestamp' => $this->timer->getStop(),
- ]];
-
- return $commands;
- }
-
- /**
- * You probably don't need this, it's used in testing.
- * Returns all events that have occurred in this Request.
- *
- * @return array[Events]
- */
- public function getEvents() : array
- {
- return $this->events;
- }
-}
diff --git a/src/Events/Request/Request.php b/src/Events/Request/Request.php
new file mode 100644
index 00000000..c6b58591
--- /dev/null
+++ b/src/Events/Request/Request.php
@@ -0,0 +1,140 @@
+ */
+ private $events = [];
+
+ /** @var Span[]|array */
+ private $openSpans = [];
+
+ /** @var RequestId */
+ private $id;
+
+ /** @throws Exception */
+ public function __construct()
+ {
+ $this->id = RequestId::new();
+
+ $this->timer = new Timer();
+ }
+
+ public function stop() : void
+ {
+ $this->timer->stop();
+ }
+
+ /** @throws Exception */
+ public function startSpan(string $operation, ?float $overrideTimestamp = null) : Span
+ {
+ $span = new Span($operation, $this->id, $overrideTimestamp);
+
+ $parent = end($this->openSpans);
+ // Automatically wire up the parent of this span
+ if ($parent instanceof Span) {
+ $span->setParentId($parent->id());
+ }
+
+ $this->openSpans[] = $span;
+
+ return $span;
+ }
+
+ /**
+ * Stop the currently "running" span.
+ * You can still tag it if needed up until the request as a whole is finished.
+ *
+ * @throws SpanHasNotBeenStarted
+ */
+ public function stopSpan(?float $overrideTimestamp = null) : void
+ {
+ /** @var Span|null $span */
+ $span = array_pop($this->openSpans);
+
+ if ($span === null) {
+ throw SpanHasNotBeenStarted::fromRequest($this->id);
+ }
+
+ $span->stop($overrideTimestamp);
+
+ $threshold = 0.5;
+ if ($span->duration() > $threshold) {
+ $stack = Backtrace::capture();
+ $stack = array_slice($stack, 4);
+ $span->tag('stack', $stack);
+ }
+
+ $this->events[] = $span;
+ }
+
+ /**
+ * Add a tag to the request as a whole
+ */
+ public function tag(string $tagName, string $value) : void
+ {
+ $this->events[] = new TagRequest($tagName, $value, $this->id);
+ }
+
+ /**
+ * turn this object into a list of commands to send to the CoreAgent
+ *
+ * @return array>>>
+ */
+ public function jsonSerialize() : array
+ {
+ $commands = [];
+ $commands[] = [
+ 'StartRequest' => [
+ 'request_id' => $this->id->toString(),
+ 'timestamp' => $this->timer->getStart(),
+ ],
+ ];
+
+ foreach ($this->events as $event) {
+ foreach ($event->jsonSerialize() as $value) {
+ $commands[] = $value;
+ }
+ }
+
+ $commands[] = [
+ 'FinishRequest' => [
+ 'request_id' => $this->id->toString(),
+ 'timestamp' => $this->timer->getStop(),
+ ],
+ ];
+
+ return [
+ 'BatchCommand' => ['commands' => $commands],
+ ];
+ }
+
+ /**
+ * You probably don't need this, it's used in testing.
+ * Returns all events that have occurred in this Request.
+ *
+ * @return TagRequest[]|Span[]|array
+ */
+ public function getEvents() : array
+ {
+ return $this->events;
+ }
+}
diff --git a/src/Events/Request/RequestId.php b/src/Events/Request/RequestId.php
new file mode 100644
index 00000000..32ece92a
--- /dev/null
+++ b/src/Events/Request/RequestId.php
@@ -0,0 +1,31 @@
+requestId = $requestId;
+ }
+
+ /** @throws Exception */
+ public static function new() : self
+ {
+ return new self(Uuid::uuid4());
+ }
+
+ public function toString() : string
+ {
+ return $this->requestId->toString();
+ }
+}
diff --git a/src/Events/Span.php b/src/Events/Span.php
deleted file mode 100644
index 051aaa13..00000000
--- a/src/Events/Span.php
+++ /dev/null
@@ -1,109 +0,0 @@
-name = $name;
- $this->requestId = $requestId;
-
- $this->tags = [];
-
- $this->timer = new Timer($override);
- }
-
- // Do not call this directly - use Request#stopSpan() or Agent#stopSpan()
- // to correctly handle bookkeeping
- public function stop($override = null)
- {
- $this->timer->stop($override);
- }
-
-
- // Used if you need to start a span, but don't get a good name for it
- // until later in its execution (or even after it's complete).
- public function updateName($name)
- {
- $this->name = $name;
- }
-
- public function tag($tag, $value)
- {
- $tagSpan = new TagSpan($this->agent, $tag, $value, $this->requestId, $this->id);
- $this->tags[] = $tagSpan;
- }
-
- public function setParentId(string $parentId)
- {
- $this->parentId = $parentId;
- }
-
- public function getName() : string
- {
- return $this->name;
- }
-
- public function getStartTime()
- {
- return $this->timer->getStart();
- }
-
- public function getStopTime()
- {
- return $this->timer->getStop();
- }
-
- public function duration()
- {
- return $this->timer->duration();
- }
-
- public function getTags()
- {
- return $this->tags;
- }
-
- public function jsonSerialize()
- {
- $commands = [];
- $commands[] = ['StartSpan' => [
- 'request_id' => $this->requestId,
- 'span_id' => $this->id,
- 'parent_id' => $this->parentId,
- 'operation' => $this->name,
- 'timestamp' => $this->getStartTime(),
- ]];
-
- foreach ($this->tags as $tag) {
- $array = $tag->jsonSerialize();
-
- foreach ($array as $value) {
- $commands[] = $value;
- }
- }
-
- $commands[] = ['StopSpan' => [
- 'request_id' => $this->requestId,
- 'span_id' => $this->id,
- 'timestamp' => $this->getStopTime(),
- ]];
-
- return $commands;
- }
-}
diff --git a/src/Events/Span/Span.php b/src/Events/Span/Span.php
new file mode 100644
index 00000000..d0c82263
--- /dev/null
+++ b/src/Events/Span/Span.php
@@ -0,0 +1,138 @@
+ */
+ private $tags;
+
+ /** @throws Exception */
+ public function __construct(string $name, RequestId $requestId, ?float $override = null)
+ {
+ $this->id = SpanId::new();
+
+ $this->name = $name;
+ $this->requestId = $requestId;
+
+ $this->tags = [];
+
+ $this->timer = new Timer($override);
+ }
+
+ public function id() : SpanId
+ {
+ return $this->id;
+ }
+
+ /**
+ * Do not call this directly - use Request#stopSpan() or Agent#stopSpan() to correctly handle bookkeeping
+ *
+ * @internal
+ */
+ public function stop(?float $override = null) : void
+ {
+ $this->timer->stop($override);
+ }
+
+ /**
+ * Used if you need to start a span, but don't get a good name for it until later in its execution (or even after
+ * it's complete).
+ */
+ public function updateName(string $name) : void
+ {
+ $this->name = $name;
+ }
+
+ /** @param mixed $value */
+ public function tag(string $tag, $value) : void
+ {
+ $this->tags[] = new TagSpan($tag, $value, $this->requestId, $this->id);
+ }
+
+ public function setParentId(SpanId $parentId) : void
+ {
+ $this->parentId = $parentId;
+ }
+
+ public function getName() : string
+ {
+ return $this->name;
+ }
+
+ public function getStartTime() : ?string
+ {
+ return $this->timer->getStart();
+ }
+
+ public function getStopTime() : ?string
+ {
+ return $this->timer->getStop();
+ }
+
+ public function duration() : ?float
+ {
+ return $this->timer->duration();
+ }
+
+ /** @return TagSpan[]|array */
+ public function getTags() : array
+ {
+ return $this->tags;
+ }
+
+ /** @return array> */
+ public function jsonSerialize() : array
+ {
+ $commands = [];
+ $commands[] = [
+ 'StartSpan' => [
+ 'request_id' => $this->requestId->toString(),
+ 'span_id' => $this->id->toString(),
+ 'parent_id' => $this->parentId ? $this->parentId->toString() : null,
+ 'operation' => $this->name,
+ 'timestamp' => $this->getStartTime(),
+ ],
+ ];
+
+ foreach ($this->tags as $tag) {
+ foreach ($tag->jsonSerialize() as $value) {
+ $commands[] = $value;
+ }
+ }
+
+ $commands[] = [
+ 'StopSpan' => [
+ 'request_id' => $this->requestId->toString(),
+ 'span_id' => $this->id->toString(),
+ 'timestamp' => $this->getStopTime(),
+ ],
+ ];
+
+ return $commands;
+ }
+}
diff --git a/src/Events/Span/SpanId.php b/src/Events/Span/SpanId.php
new file mode 100644
index 00000000..4f84b2db
--- /dev/null
+++ b/src/Events/Span/SpanId.php
@@ -0,0 +1,31 @@
+spanId = $spanId;
+ }
+
+ /** @throws Exception */
+ public static function new() : self
+ {
+ return new self(Uuid::uuid4());
+ }
+
+ public function toString() : string
+ {
+ return $this->spanId->toString();
+ }
+}
diff --git a/src/Events/Tag.php b/src/Events/Tag/Tag.php
similarity index 51%
rename from src/Events/Tag.php
rename to src/Events/Tag/Tag.php
index 0a8f105e..22e01a88 100644
--- a/src/Events/Tag.php
+++ b/src/Events/Tag/Tag.php
@@ -1,50 +1,56 @@
tag = $tag;
- $this->value = $value;
+ $this->tag = $tag;
+ $this->value = $value;
$this->requestId = $requestId;
$this->timestamp = $timestamp;
}
/**
* Get the 'key' portion of this Tag
- *
- * @return string
*/
- public function getTag()
+ public function getTag() : string
{
return $this->tag;
}
-
+
/**
* Get the 'value' portion of this Tag
- *
- * @return mixed
*/
- public function getValue()
+ public function getValue() : string
{
return $this->value;
}
diff --git a/src/Events/Tag/TagRequest.php b/src/Events/Tag/TagRequest.php
new file mode 100644
index 00000000..5339b3bf
--- /dev/null
+++ b/src/Events/Tag/TagRequest.php
@@ -0,0 +1,39 @@
+> */
+ public function jsonSerialize() : array
+ {
+ // Format the timestamp
+ $timestamp = DateTime::createFromFormat('U.u', sprintf('%.6F', $this->timestamp));
+ $timestamp->setTimeZone(new DateTimeZone('UTC'));
+ $timestamp = $timestamp->format('Y-m-d\TH:i:s.u\Z');
+
+ return [
+ [
+ 'TagRequest' => [
+ 'request_id' => $this->requestId->toString(),
+ 'tag' => $this->tag,
+ 'value' => $this->value,
+ 'timestamp' => $timestamp,
+ ],
+ ],
+ ];
+ }
+}
diff --git a/src/Events/Tag/TagSpan.php b/src/Events/Tag/TagSpan.php
new file mode 100644
index 00000000..f84a7f92
--- /dev/null
+++ b/src/Events/Tag/TagSpan.php
@@ -0,0 +1,54 @@
+spanId = $spanId;
+ }
+
+ /**
+ * @return array>
+ */
+ public function jsonSerialize() : array
+ {
+ // Format the timestamp
+ $timestamp = DateTime::createFromFormat('U.u', sprintf('%.6F', $this->timestamp));
+ $timestamp->setTimeZone(new DateTimeZone('UTC'));
+ $timestamp = $timestamp->format('Y-m-d\TH:i:s.u\Z');
+
+ return [
+ [
+ 'TagSpan' => [
+ 'request_id' => $this->requestId->toString(),
+ 'span_id' => $this->spanId->toString(),
+ 'tag' => $this->tag,
+ 'value' => $this->value,
+ 'timestamp' => $timestamp,
+ ],
+ ],
+ ];
+ }
+}
diff --git a/src/Events/TagRequest.php b/src/Events/TagRequest.php
deleted file mode 100644
index 1a1ee756..00000000
--- a/src/Events/TagRequest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-timestamp));
- $timestamp->setTimeZone(new \DateTimeZone('UTC'));
- $timestamp = $timestamp->format('Y-m-d\TH:i:s.u\Z');
-
- return [
- ['TagRequest' => [
- 'request_id' => $this->requestId,
- 'tag' => $this->tag,
- 'value' => $this->value,
- 'timestamp' => $timestamp,
- ]]
- ];
- }
-}
diff --git a/src/Events/TagSpan.php b/src/Events/TagSpan.php
deleted file mode 100644
index 7a4d0469..00000000
--- a/src/Events/TagSpan.php
+++ /dev/null
@@ -1,32 +0,0 @@
-spanId = $spanId;
- }
-
- public function jsonSerialize()
- {
- // Format the timestamp
- $timestamp = \DateTime::createFromFormat('U.u', sprintf('%.6F', $this->timestamp));
- $timestamp->setTimeZone(new \DateTimeZone('UTC'));
- $timestamp = $timestamp->format('Y-m-d\TH:i:s.u\Z');
-
- return [
- ['TagSpan' => [
- 'request_id' => $this->requestId,
- 'span_id' => $this->spanId,
- 'tag' => $this->tag,
- 'value' => $this->value,
- 'timestamp' => $timestamp,
- ]]
- ];
- }
-}
diff --git a/src/Exception/MissingAppNameException.php b/src/Exception/MissingAppNameException.php
deleted file mode 100644
index cac0de8a..00000000
--- a/src/Exception/MissingAppNameException.php
+++ /dev/null
@@ -1,11 +0,0 @@
-> */
+ public static function capture() : array
{
$stack = debug_backtrace();
-
+
$formatted_stack = [];
foreach ($stack as $frame) {
- if (isset($frame["file"]) && isset($frame["line"]) && isset($frame["function"])) {
- array_push($formatted_stack, ["file" => $frame["file"], "line" => $frame["line"], "function" => $frame["function"]]);
+ if (! isset($frame['file']) || ! isset($frame['line']) || ! isset($frame['function'])) {
+ continue;
}
+
+ array_push($formatted_stack, ['file' => $frame['file'], 'line' => $frame['line'], 'function' => $frame['function']]);
}
return $formatted_stack;
diff --git a/src/Helper/Timer.php b/src/Helper/Timer.php
index 0f648feb..81bfc04b 100644
--- a/src/Helper/Timer.php
+++ b/src/Helper/Timer.php
@@ -1,4 +1,5 @@
start($override);
}
- public function start(float $override = null) : void
+ public function start(?float $override = null) : void
{
$this->start = $override ?? microtime(true);
}
- public function stop(float $override = null) : void
+ public function stop(?float $override = null) : void
{
$this->stop = $override ?? microtime(true);
}
@@ -54,6 +56,7 @@ public function getStop() : ?string
sprintf(self::FORMAT_FLOAT_TO_6_DECIMAL_PLACES, $this->stop),
new DateTimeZone('UTC')
);
+
return $timestamp->format(self::FORMAT_FOR_CORE_AGENT);
}
@@ -67,6 +70,7 @@ public function getStart() : ?string
sprintf(self::FORMAT_FLOAT_TO_6_DECIMAL_PLACES, $this->start),
new DateTimeZone('UTC')
);
+
return $timestamp->format(self::FORMAT_FOR_CORE_AGENT);
}
diff --git a/src/IgnoredEndpoints.php b/src/IgnoredEndpoints.php
deleted file mode 100644
index 1308d5ea..00000000
--- a/src/IgnoredEndpoints.php
+++ /dev/null
@@ -1,35 +0,0 @@
-agent = $agent;
- $this->config = $agent->getConfig();
- }
-
- public function ignored(string $url) : bool
- {
- $ignored = $this->config->get("ignore");
- if ($ignored == null) {
- return false;
- }
-
- foreach ($ignored as $ignore) {
- if (substr($url, 0, strlen($ignore)) === $ignore) {
- return true;
- }
- }
-
- // None Matched
- return false;
- }
-}
diff --git a/src/Loggers/Logger.php b/src/Loggers/Logger.php
deleted file mode 100644
index 448daafa..00000000
--- a/src/Loggers/Logger.php
+++ /dev/null
@@ -1,77 +0,0 @@
-name = $name;
- $this->logPath = $logPath;
- }
-
- public function getName() : string
- {
- return $this->name;
- }
-
- public function emergency($message, array $context = [])
- {
- $this->log('EMERGENCY', $message, $context);
- }
-
- public function alert($message, array $context = [])
- {
- $this->log('ALERT', $message, $context);
- }
-
- public function critical($message, array $context = [])
- {
- $this->log('CRITICAL', $message, $context);
- }
-
- public function error($message, array $context = [])
- {
- $this->log('ERROR', $message, $context);
- }
-
- public function warning($message, array $context = [])
- {
- $this->log('WARNING', $message, $context);
- }
-
- public function notice($message, array $context = [])
- {
- $this->log('NOTICE', $message, $context);
- }
-
- public function info($message, array $context = [])
- {
- $this->log('INFO', $message, $context);
- }
-
- public function debug($message, array $context = [])
- {
- $this->log('DEBUG', $message, $context);
- }
-
- public function log($level, $message, array $context = [])
- {
- $handle = fopen($this->logPath, 'a');
- fwrite($handle, "$level: $message");
- fwrite($handle, print_r($context, true));
- fclose($handle);
- }
-}
diff --git a/src/Loggers/LoggerSelector.php b/src/Loggers/LoggerSelector.php
deleted file mode 100644
index 60619158..00000000
--- a/src/Loggers/LoggerSelector.php
+++ /dev/null
@@ -1,17 +0,0 @@
-getConfig();
- $config->set("ignore", [
- "/health",
- "/status",
- ]);
- $ignoredEndpoints = new IgnoredEndpoints($agent);
-
- // Exact Match
- $this->assertEquals(true, $ignoredEndpoints->ignored("/health"));
- $this->assertEquals(true, $ignoredEndpoints->ignored("/status"));
-
- // Prefix Match
- $this->assertEquals(true, $ignoredEndpoints->ignored("/health/database"));
- $this->assertEquals(true, $ignoredEndpoints->ignored("/status/time"));
-
- // No Match
- $this->assertEquals(false, $ignoredEndpoints->ignored("/signup"));
-
- // Not-prefix doesn't Match
- $this->assertEquals(false, $ignoredEndpoints->ignored("/hero/1/health"));
- }
-
- public function testWorksWithNullIgnoreSetting()
- {
- $agent = new Agent();
- $config = $agent->getConfig();
- $ignoredEndpoints = new IgnoredEndpoints($agent);
-
- // No Match
- $this->assertEquals(false, $ignoredEndpoints->ignored("/signup"));
- }
-}
diff --git a/tests/Integration/AgentTest.php b/tests/Integration/AgentTest.php
index 78acb6a0..1877890f 100644
--- a/tests/Integration/AgentTest.php
+++ b/tests/Integration/AgentTest.php
@@ -6,42 +6,100 @@
use PHPUnit\Framework\TestCase;
use Scoutapm\Agent;
+use Scoutapm\Config;
+use Scoutapm\Connector\SocketConnector;
+use function getenv;
+use function json_decode;
+use function json_encode;
use function sleep;
+/** @coversNothing */
final class AgentTest extends TestCase
{
public function testLoggingIsSent() : void
{
- $scoutApmKey = \getenv('SCOUT_APM_KEY');
+ $scoutApmKey = getenv('SCOUT_APM_KEY');
if ($scoutApmKey === false) {
self::markTestSkipped('Set the environment variable SCOUT_APM_KEY to enable this test.');
+
return;
}
- $agent = new Agent();
+ $config = Config::fromArray([
+ 'name' => 'Agent Integration Test',
+ 'key' => $scoutApmKey,
+ 'monitor' => true,
+ ]);
- $config = $agent->getConfig();
+ $connector = new MessageCapturingConnectorDelegator(new SocketConnector($config->get('socket_path')));
- $config->set('name', 'Agent integration test');
- $config->set('key', $scoutApmKey);
- $config->set('monitor', true);
+ $agent = Agent::fromConfig($config, null, $connector);
+ // @todo connection is not happening, seems to be a mismatch with path expectations currently...
+ self::markTestIncomplete(__METHOD__);
$agent->connect();
- // @todo currently have wait for agent to become available, not ideal, fix this...)
+ // @todo seems that we need to wait a moment before the core agent starts :/ find a better way to do this
sleep(1);
- $agent->webTransaction('Yay', function () use ($agent) {
- $agent->instrument('test', 'foo', function () {
+ $agent->webTransaction('Yay', static function () use ($agent) : void {
+ $agent->instrument('test', 'foo', static function () : void {
});
- $agent->instrument('test', 'foo2', function () {
+ $agent->instrument('test', 'foo2', static function () : void {
});
$agent->tagRequest('testtag', '1.23');
});
self::assertTrue($agent->send());
+ // @todo check the format of this matches up with expectations
+ self::markTestIncomplete(__METHOD__);
+ self::assertEquals(
+ [
+ [
+ 'Register' => [],
+ ],
+ [
+ 'ApplicationEvent' => [],
+ ],
+ [
+ 'BatchCommand' => [
+ 'commands' => [
+ [
+ 'StartRequest' => [],
+ ],
+ [
+ 'StartSpan' => [],
+ ],
+ [
+ 'StopSpan' => [],
+ ],
+ [
+ 'StartSpan' => [],
+ ],
+ [
+ 'StopSpan' => [],
+ ],
+ [
+ 'TagRequest' => [],
+ ],
+ [
+ 'StartSpan' => [],
+ ],
+ [
+ 'StopSpan' => [],
+ ],
+ [
+ 'FinishRequest' => [],
+ ],
+ ],
+ ],
+ ],
+ ],
+ json_decode(json_encode($connector->sentMessages), true)
+ );
+
// @todo perform more assertions - did we actually successfully send payload in the right format, etc.?
}
}
diff --git a/tests/Integration/MessageCapturingConnectorDelegator.php b/tests/Integration/MessageCapturingConnectorDelegator.php
new file mode 100644
index 00000000..18068e20
--- /dev/null
+++ b/tests/Integration/MessageCapturingConnectorDelegator.php
@@ -0,0 +1,44 @@
+delegate = $delegate;
+ }
+
+ public function connect() : void
+ {
+ $this->delegate->connect();
+ }
+
+ public function connected() : bool
+ {
+ return $this->delegate->connected();
+ }
+
+ public function sendCommand(Command $message) : bool
+ {
+ $this->sentMessages[] = $message;
+
+ return $this->delegate->sendCommand($message);
+ }
+
+ public function shutdown() : void
+ {
+ $this->delegate->shutdown();
+ }
+}
diff --git a/tests/Unit/AgentTest.php b/tests/Unit/AgentTest.php
index 2a467ddb..e6ca52e9 100644
--- a/tests/Unit/AgentTest.php
+++ b/tests/Unit/AgentTest.php
@@ -1,30 +1,34 @@
startSpan("Controller/Test");
+ $agent->startSpan('Controller/Test');
// Tag Whole Request
- $agent->tagRequest("uri", "example.com/foo/bar.php");
+ $agent->tagRequest('uri', 'example.com/foo/bar.php');
// Start a Child Span
- $span = $agent->startSpan("SQL/Query");
+ $span = $agent->startSpan('SQL/Query');
// Tag the span
- $span->tag("sql.query", "select * from foo");
+ $span->tag('sql.query', 'select * from foo');
// Finish Child Span
$agent->stopSpan();
@@ -32,150 +36,131 @@ public function testFullAgentSequence()
// Stop Controller Span
$agent->stopSpan();
- $this->assertNotNull($agent);
+ self::assertNotNull($agent);
}
- public function testInstrument()
+ public function testInstrument() : void
{
- $agent = new Agent();
- $retval = $agent->instrument("Custom", "Test", function ($span) {
- $span->tag("OMG", "Thingy");
+ $agent = Agent::fromDefaults();
+ $retval = $agent->instrument('Custom', 'Test', static function (Span $span) {
+ $span->tag('OMG', 'Thingy');
+
+ self::assertSame($span->getName(), 'Custom/Test');
- $this->assertEquals($span->getName(), "Custom/Test");
- return "arbitrary return value";
+ return 'arbitrary return value';
});
// Check that the instrument helper propagates the return value
- $this->assertEquals($retval, "arbitrary return value");
+ self::assertSame($retval, 'arbitrary return value');
// Check that the span was stopped and tagged
- $request = $agent->getRequest();
- $events = $request->getEvents();
+ $events = $agent->getRequest()->getEvents();
$foundSpan = end($events);
- $this->assertInstanceOf(\Scoutapm\Events\Span::class, $foundSpan);
- $this->assertNotNull($foundSpan->getStopTime());
- $this->assertEquals($foundSpan->getTags()[0]->getTag(), "OMG");
- $this->assertEquals($foundSpan->getTags()[0]->getValue(), "Thingy");
+ self::assertInstanceOf(Span::class, $foundSpan);
+ self::assertNotNull($foundSpan->getStopTime());
+ self::assertSame($foundSpan->getTags()[0]->getTag(), 'OMG');
+ self::assertSame($foundSpan->getTags()[0]->getValue(), 'Thingy');
}
- public function testWebTransaction()
+ public function testWebTransaction() : void
{
- $agent = new Agent();
- $retval = $agent->webTransaction("Test", function ($span) {
+ $retval = Agent::fromDefaults()->webTransaction('Test', static function (Span $span) {
// Check span name is prefixed with "Controller"
- $this->assertEquals($span->getName(), "Controller/Test");
+ self::assertSame($span->getName(), 'Controller/Test');
- return "arbitrary return value";
+ return 'arbitrary return value';
});
// Check that the instrument helper propagates the return value
- $this->assertEquals($retval, "arbitrary return value");
+ self::assertSame($retval, 'arbitrary return value');
}
- public function testBackgroundTransaction()
+ public function testBackgroundTransaction() : void
{
- $agent = new Agent();
- $retval = $agent->backgroundTransaction("Test", function ($span) {
+ $retval = Agent::fromDefaults()->backgroundTransaction('Test', static function (Span $span) {
// Check span name is prefixed with "Job"
- $this->assertEquals($span->getName(), "Job/Test");
+ self::assertSame($span->getName(), 'Job/Test');
- return "arbitrary return value";
+ return 'arbitrary return value';
});
// Check that the instrument helper propagates the return value
- $this->assertEquals($retval, "arbitrary return value");
- }
-
- public function testCanSetLogger()
- {
- $agent = new Agent();
- $logger = new NullLogger();
-
- $agent->setLogger($logger);
-
- $this->assertEquals($agent->getLogger(), $logger);
- }
-
- public function testCanGetConfig()
- {
- $agent = new Agent();
- $config = $agent->getConfig();
- $this->assertInstanceOf(\Scoutapm\Config::class, $config);
+ self::assertSame($retval, 'arbitrary return value');
}
- public function testStartSpan()
+ public function testStartSpan() : void
{
- $agent = new Agent();
- $span = $agent->startSpan("foo/bar");
- $this->assertEquals("foo/bar", $span->getName());
- $this->assertInstanceOf(\Scoutapm\Events\Span::class, $span);
+ $span = Agent::fromDefaults()->startSpan('foo/bar');
+ self::assertSame('foo/bar', $span->getName());
}
- public function testStopSpan()
+ public function testStopSpan() : void
{
- $agent = new Agent();
- $span = $agent->startSpan("foo/bar");
- $this->assertNull($span->getStopTime());
+ $agent = Agent::fromDefaults();
+ $span = $agent->startSpan('foo/bar');
+ self::assertNull($span->getStopTime());
$agent->stopSpan();
- $this->assertNotNull($span->getStopTime());
+ self::assertNotNull($span->getStopTime());
}
- public function testTagRequest()
+ public function testTagRequest() : void
{
- $agent = new Agent();
- $agent->tagRequest("foo", "bar");
+ $agent = Agent::fromDefaults();
+ $agent->tagRequest('foo', 'bar');
- $request = $agent->getRequest();
- $events = $request->getEvents();
+ $events = $agent->getRequest()->getEvents();
$tag = end($events);
- $this->assertInstanceOf(\Scoutapm\Events\TagRequest::class, $tag);
- $this->assertEquals("foo", $tag->getTag());
- $this->assertEquals("bar", $tag->getValue());
+ self::assertInstanceOf(TagRequest::class, $tag);
+ self::assertSame('foo', $tag->getTag());
+ self::assertSame('bar', $tag->getValue());
}
- public function testEnabled()
+ public function testEnabled() : void
{
// without affirmatively enabling, it's not enabled.
- $agent = new Agent();
- $this->assertEquals(false, $agent->enabled());
+ $agentWithoutEnabling = Agent::fromDefaults();
+ self::assertFalse($agentWithoutEnabling->enabled());
// but a config that has monitor = true, it is set
- $config = new \Scoutapm\Config($agent);
- $config->set("monitor", "true");
- $agent->setConfig($config);
+ $config = new Config();
+ $config->set('monitor', 'true');
- $this->assertEquals(true, $agent->enabled());
+ $enabledAgent = Agent::fromConfig($config);
+ self::assertTrue($enabledAgent->enabled());
}
- public function testIgnoredEndpoints()
+ public function testIgnoredEndpoints() : void
{
- $agent = new Agent();
- $agent->getConfig()->set("ignore", ["/foo"]);
+ $config = new Config();
+ $config->set('ignore', ['/foo']);
+
+ $agent = Agent::fromConfig($config);
- $this->assertEquals(true, $agent->ignored("/foo"));
- $this->assertEquals(false, $agent->ignored("/bar"));
+ self::assertTrue($agent->ignored('/foo'));
+ self::assertFalse($agent->ignored('/bar'));
}
- // Many instrumentation calls are NOOPs when ignore is called. Make sure
- // the sequence works as expected
- public function testIgnoredAgentSequence()
+ /**
+ * Many instrumentation calls are NOOPs when ignore is called. Make sure the sequence works as expected
+ */
+ public function testIgnoredAgentSequence() : void
{
- $agent = new Agent();
+ $agent = Agent::fromDefaults();
$agent->ignore();
// Start a Parent Controller Span
- $span = $agent->startSpan("Controller/Test");
+ $agent->startSpan('Controller/Test');
// Tag Whole Request
- $agent->tagRequest("uri", "example.com/foo/bar.php");
+ $agent->tagRequest('uri', 'example.com/foo/bar.php');
// Start a Child Span
- $span = $agent->startSpan("SQL/Query");
+ $span = $agent->startSpan('SQL/Query');
// Tag the span
- $span->tag("sql.query", "select * from foo");
+ $span->tag('sql.query', 'select * from foo');
// Finish Child Span
$agent->stopSpan();
@@ -185,6 +170,6 @@ public function testIgnoredAgentSequence()
$agent->send();
- $this->assertNotNull($agent);
+ self::assertNotNull($agent);
}
}
diff --git a/tests/Unit/Config/BoolCoercionTest.php b/tests/Unit/Config/BoolCoercionTest.php
deleted file mode 100644
index d7db6363..00000000
--- a/tests/Unit/Config/BoolCoercionTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-assertEquals(true, $c->coerce('t'));
- $this->assertEquals(true, $c->coerce('true'));
- $this->assertEquals(true, $c->coerce('1'));
- $this->assertEquals(true, $c->coerce('yes'));
- $this->assertEquals(true, $c->coerce('YES'));
- $this->assertEquals(true, $c->coerce('T'));
- $this->assertEquals(true, $c->coerce('TRUE'));
-
- // Falses
- $this->assertEquals(false, $c->coerce('f'));
- $this->assertEquals(false, $c->coerce('false'));
- $this->assertEquals(false, $c->coerce('no'));
- $this->assertEquals(false, $c->coerce('0'));
- }
-
- public function testIgnoresBooleans()
- {
- $c = new BoolCoercion();
-
- $this->assertEquals(true, $c->coerce(true));
- $this->assertEquals(false, $c->coerce(false));
- }
-
- public function testNullIsFalse()
- {
- $c = new BoolCoercion();
-
- $this->assertEquals(false, $c->coerce(null));
- }
-
- public function testAnythingElseIsFalse()
- {
- $c = new BoolCoercion();
-
- // $c is "any object"
- $this->assertEquals(false, $c->coerce($c));
- }
-}
diff --git a/tests/Unit/Config/DefaultSourceTest.php b/tests/Unit/Config/DefaultSourceTest.php
deleted file mode 100644
index 56d358c7..00000000
--- a/tests/Unit/Config/DefaultSourceTest.php
+++ /dev/null
@@ -1,26 +0,0 @@
-hasKey("api_version"));
- self::assertFalse($defaults->hasKey("notAValue"));
- }
-
- public function testGet()
- {
- $defaults = new DefaultSource();
- self::assertEquals("1.0", $defaults->get("api_version"));
- }
-}
diff --git a/tests/Unit/Config/DerivedSourceTest.php b/tests/Unit/Config/DerivedSourceTest.php
deleted file mode 100644
index a566d2eb..00000000
--- a/tests/Unit/Config/DerivedSourceTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-assertTrue($derived->hasKey("testing"));
- $this->assertFalse($derived->hasKey("is_array"));
- }
-
- public function testGet()
- {
- $config = new Config(new Agent());
- $derived = new DerivedSource($config);
-
- $this->assertEquals("derived api version: 1.0", $derived->get("testing"));
- }
-}
diff --git a/tests/Unit/Config/EnvSourceTest.php b/tests/Unit/Config/EnvSourceTest.php
deleted file mode 100644
index ad638655..00000000
--- a/tests/Unit/Config/EnvSourceTest.php
+++ /dev/null
@@ -1,34 +0,0 @@
-assertFalse($config->hasKey("test_case_foo"));
-
- putenv("SCOUT_TEST_CASE_FOO=thevalue");
-
- $this->assertTrue($config->hasKey("test_case_foo"));
-
- // Clean up the var
- putenv('SCOUT_TEST_CASE_FOO');
- }
-
- public function testGet()
- {
- $config = new EnvSource();
- $this->assertNull($config->get("test_case_bar"));
-
- putenv("SCOUT_TEST_CASE_BAR=thevalue");
-
- $this->assertEquals("thevalue", $config->get("test_case_bar"));
-
- // Clean up the var
- putenv('SCOUT_TEST_CASE_BAR');
- }
-}
diff --git a/tests/Unit/Config/IgnoredEndpointsTest.php b/tests/Unit/Config/IgnoredEndpointsTest.php
new file mode 100644
index 00000000..2a6ed372
--- /dev/null
+++ b/tests/Unit/Config/IgnoredEndpointsTest.php
@@ -0,0 +1,40 @@
+ignored('/health'));
+ self::assertTrue($ignoredEndpoints->ignored('/status'));
+
+ // Prefix Match
+ self::assertTrue($ignoredEndpoints->ignored('/health/database'));
+ self::assertTrue($ignoredEndpoints->ignored('/status/time'));
+
+ // No Match
+ self::assertFalse($ignoredEndpoints->ignored('/signup'));
+
+ // Not-prefix doesn't Match
+ self::assertFalse($ignoredEndpoints->ignored('/hero/1/health'));
+ }
+
+ public function testWorksWithNullIgnoreSetting() : void
+ {
+ // No Match
+ self::assertFalse((new IgnoredEndpoints([]))->ignored('/signup'));
+ }
+}
diff --git a/tests/Unit/Config/JSONCoercionTest.php b/tests/Unit/Config/JSONCoercionTest.php
deleted file mode 100644
index 0471819d..00000000
--- a/tests/Unit/Config/JSONCoercionTest.php
+++ /dev/null
@@ -1,32 +0,0 @@
-assertEquals([
- "foo" => 1
- ], $c->coerce('{"foo": 1}'));
- }
-
- // Return null for any invalid JSON
- public function testInvalidJSON()
- {
- $c = new JSONCoercion();
- $this->assertEquals(null, $c->coerce('foo: 1}'));
- }
-
- public function testIgnoresNonString()
- {
- $c = new JSONCoercion();
- $this->assertEquals(10, $c->coerce(10));
- }
-}
diff --git a/tests/Unit/Config/NullSourceTest.php b/tests/Unit/Config/NullSourceTest.php
deleted file mode 100644
index 65fb163e..00000000
--- a/tests/Unit/Config/NullSourceTest.php
+++ /dev/null
@@ -1,22 +0,0 @@
-assertTrue($defaults->hasKey("apiVersion"));
- $this->assertTrue($defaults->hasKey("notAValue"));
- }
-
- public function testGet()
- {
- $defaults = new NullSource();
- $this->assertEquals(null, $defaults->get("apiVersion"));
- $this->assertEquals(null, $defaults->get("weirdThing"));
- }
-}
diff --git a/tests/Unit/Config/Source/DefaultSourceTest.php b/tests/Unit/Config/Source/DefaultSourceTest.php
new file mode 100644
index 00000000..5829dea7
--- /dev/null
+++ b/tests/Unit/Config/Source/DefaultSourceTest.php
@@ -0,0 +1,25 @@
+hasKey('api_version'));
+ self::assertFalse($defaults->hasKey('notAValue'));
+ }
+
+ public function testGet() : void
+ {
+ $defaults = new DefaultSource();
+ self::assertSame('1.0', $defaults->get('api_version'));
+ }
+}
diff --git a/tests/Unit/Config/Source/DerivedSourceTest.php b/tests/Unit/Config/Source/DerivedSourceTest.php
new file mode 100644
index 00000000..4911a08a
--- /dev/null
+++ b/tests/Unit/Config/Source/DerivedSourceTest.php
@@ -0,0 +1,28 @@
+hasKey('testing'));
+ self::assertFalse($derived->hasKey('is_array'));
+ }
+
+ public function testGet() : void
+ {
+ $derived = new DerivedSource(new Config());
+
+ self::assertSame('derived api version: 1.0', $derived->get('testing'));
+ }
+}
diff --git a/tests/Unit/Config/Source/EnvSourceTest.php b/tests/Unit/Config/Source/EnvSourceTest.php
new file mode 100644
index 00000000..db63a931
--- /dev/null
+++ b/tests/Unit/Config/Source/EnvSourceTest.php
@@ -0,0 +1,39 @@
+hasKey('test_case_foo'));
+
+ putenv('SCOUT_TEST_CASE_FOO=thevalue');
+
+ self::assertTrue($config->hasKey('test_case_foo'));
+
+ // Clean up the var
+ putenv('SCOUT_TEST_CASE_FOO');
+ }
+
+ public function testGet() : void
+ {
+ $config = new EnvSource();
+ self::assertNull($config->get('test_case_bar'));
+
+ putenv('SCOUT_TEST_CASE_BAR=thevalue');
+
+ self::assertSame('thevalue', $config->get('test_case_bar'));
+
+ // Clean up the var
+ putenv('SCOUT_TEST_CASE_BAR');
+ }
+}
diff --git a/tests/Unit/Config/Source/NullSourceTest.php b/tests/Unit/Config/Source/NullSourceTest.php
new file mode 100644
index 00000000..bfbac2ff
--- /dev/null
+++ b/tests/Unit/Config/Source/NullSourceTest.php
@@ -0,0 +1,26 @@
+hasKey('apiVersion'));
+ self::assertTrue($defaults->hasKey('notAValue'));
+ }
+
+ public function testGet() : void
+ {
+ $defaults = new NullSource();
+ self::assertNull($defaults->get('apiVersion'));
+ self::assertNull($defaults->get('weirdThing'));
+ }
+}
diff --git a/tests/Unit/Config/Source/UserSettingsSourceTest.php b/tests/Unit/Config/Source/UserSettingsSourceTest.php
new file mode 100644
index 00000000..98b57547
--- /dev/null
+++ b/tests/Unit/Config/Source/UserSettingsSourceTest.php
@@ -0,0 +1,32 @@
+hasKey('foo'));
+
+ $config->set('foo', 'bar');
+
+ self::assertTrue($config->hasKey('foo'));
+ }
+
+ public function testGet() : void
+ {
+ $config = new UserSettingsSource();
+ self::assertNull($config->get('foo'));
+
+ $config->set('foo', 'bar');
+
+ self::assertSame('bar', $config->get('foo'));
+ }
+}
diff --git a/tests/Unit/Config/TypeCoercion/CoerceBooleanTest.php b/tests/Unit/Config/TypeCoercion/CoerceBooleanTest.php
new file mode 100644
index 00000000..653f28e4
--- /dev/null
+++ b/tests/Unit/Config/TypeCoercion/CoerceBooleanTest.php
@@ -0,0 +1,55 @@
+coerce('t'));
+ self::assertTrue($c->coerce('true'));
+ self::assertTrue($c->coerce('1'));
+ self::assertTrue($c->coerce('yes'));
+ self::assertTrue($c->coerce('YES'));
+ self::assertTrue($c->coerce('T'));
+ self::assertTrue($c->coerce('TRUE'));
+
+ // Falses
+ self::assertFalse($c->coerce('f'));
+ self::assertFalse($c->coerce('false'));
+ self::assertFalse($c->coerce('no'));
+ self::assertFalse($c->coerce('0'));
+ }
+
+ public function testIgnoresBooleans() : void
+ {
+ $c = new CoerceBoolean();
+
+ self::assertTrue($c->coerce(true));
+ self::assertFalse($c->coerce(false));
+ }
+
+ public function testNullIsFalse() : void
+ {
+ $c = new CoerceBoolean();
+
+ self::assertFalse($c->coerce(null));
+ }
+
+ public function testAnythingElseIsFalse() : void
+ {
+ $c = new CoerceBoolean();
+
+ // $c is "any object"
+ self::assertFalse($c->coerce($c));
+ }
+}
diff --git a/tests/Unit/Config/TypeCoercion/CoerceJsonTest.php b/tests/Unit/Config/TypeCoercion/CoerceJsonTest.php
new file mode 100644
index 00000000..071c2c31
--- /dev/null
+++ b/tests/Unit/Config/TypeCoercion/CoerceJsonTest.php
@@ -0,0 +1,37 @@
+ 1],
+ $c->coerce('{"foo": 1}')
+ );
+ }
+
+ /**
+ * Return null for any invalid JSON
+ */
+ public function testInvalidJSON() : void
+ {
+ $c = new CoerceJson();
+ // @todo add a data provider for more invalid json strings
+ self::assertNull($c->coerce('foo: 1}'));
+ }
+
+ public function testIgnoresNonString() : void
+ {
+ $c = new CoerceJson();
+ self::assertSame(10, $c->coerce(10));
+ }
+}
diff --git a/tests/Unit/Config/UserSettingsSourceTest.php b/tests/Unit/Config/UserSettingsSourceTest.php
deleted file mode 100644
index 675fd0f9..00000000
--- a/tests/Unit/Config/UserSettingsSourceTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-assertFalse($config->hasKey("foo"));
-
- $config->set("foo", "bar");
-
- $this->assertTrue($config->hasKey("foo"));
- }
-
- public function testGet()
- {
- $config = new UserSettingsSource();
- $this->assertNull($config->get("foo"));
-
- $config->set("foo", "bar");
-
- $this->assertEquals("bar", $config->get("foo"));
- }
-}
diff --git a/tests/Unit/ConfigTest.php b/tests/Unit/ConfigTest.php
index f5f6e07e..16315498 100644
--- a/tests/Unit/ConfigTest.php
+++ b/tests/Unit/ConfigTest.php
@@ -1,59 +1,65 @@
assertEquals('1.0', $config->get("api_version"));
+ self::assertSame('1.0', $config->get('api_version'));
}
- public function testUserSettingsOverridesDefaults()
+ public function testUserSettingsOverridesDefaults() : void
{
- $config = new Config(new Agent());
- $config->set("api_version", "viauserconf");
+ $config = new Config();
+ $config->set('api_version', 'viauserconf');
- $this->assertEquals("viauserconf", $config->get("api_version"));
+ self::assertSame('viauserconf', $config->get('api_version'));
}
- public function testEnvOverridesAll()
+ public function testEnvOverridesAll() : void
{
- $config = new Config(new Agent());
+ $config = new Config();
// Set a user config. This won't be looked up
- $config->set("api_version", "viauserconf");
+ $config->set('api_version', 'viauserconf');
// And set the env var
- putEnv("SCOUT_API_VERSION=viaenvvar");
+ putenv('SCOUT_API_VERSION=viaenvvar');
- $this->assertEquals("viaenvvar", $config->get("api_version"));
+ self::assertSame('viaenvvar', $config->get('api_version'));
}
- public function testBooleanCoercionOfMonitor()
+ public function testBooleanCoercionOfMonitor() : void
{
- $config = new Config(new Agent());
+ $config = new Config();
// Set a user config. This won't be looked up
- $config->set("monitor", "true");
- $this->assertSame(true, $config->get("monitor"));
+ $config->set('monitor', 'true');
+ self::assertTrue($config->get('monitor'));
}
- public function testJSONCoercionOfIgnore()
+ public function testJSONCoercionOfIgnore() : void
{
- $config = new Config(new Agent());
+ $config = new Config();
// Set a user config. This won't be looked up
- $config->set("ignore", '["/foo", "/bar"]');
- $this->assertSame(["/foo", "/bar"], $config->get("ignore"));
+ $config->set('ignore', '["/foo", "/bar"]');
+ self::assertSame(['/foo', '/bar'], $config->get('ignore'));
+ }
+
+ public function testIgnoreDefaultsToEmptyArray() : void
+ {
+ self::assertSame([], (new Config())->get('ignore'));
}
}
diff --git a/tests/Unit/CoreAgent/CoreAgentManagerTest.php b/tests/Unit/CoreAgent/CoreAgentManagerTest.php
new file mode 100644
index 00000000..488855ca
--- /dev/null
+++ b/tests/Unit/CoreAgent/CoreAgentManagerTest.php
@@ -0,0 +1,27 @@
+createMock(LoggerInterface::class),
+ $this->createMock(Downloader::class)
+ );
+
+ // Provided by the DefaultConfig
+ self::assertNotNull($cam);
+ }
+}
diff --git a/tests/Unit/CoreAgentManagerTest.php b/tests/Unit/CoreAgentManagerTest.php
deleted file mode 100644
index e6f02616..00000000
--- a/tests/Unit/CoreAgentManagerTest.php
+++ /dev/null
@@ -1,21 +0,0 @@
-assertNotNull($cam);
- }
-}
diff --git a/tests/Unit/Events/MetadataTest.php b/tests/Unit/Events/MetadataTest.php
index 68a31e51..5329b183 100644
--- a/tests/Unit/Events/MetadataTest.php
+++ b/tests/Unit/Events/MetadataTest.php
@@ -7,13 +7,13 @@
use DateTimeImmutable;
use DateTimeZone;
use Exception;
-use function gethostname;
-use function json_decode;
-use function json_encode;
use PHPUnit\Framework\TestCase;
-use Scoutapm\Agent;
use Scoutapm\Events\Metadata;
use Scoutapm\Helper\Timer;
+use const PHP_VERSION;
+use function gethostname;
+use function json_decode;
+use function json_encode;
/** @covers \Scoutapm\Events\Metadata */
final class MetadataTest extends TestCase
@@ -21,10 +21,9 @@ final class MetadataTest extends TestCase
/** @throws Exception */
public function testMetadataSerializesToJson() : void
{
- $agent = $this->createMock(Agent::class);
$time = new DateTimeImmutable('now', new DateTimeZone('UTC'));
- $serialized = json_encode(new Metadata($agent, $time));
+ $serialized = json_encode(new Metadata($time));
self::assertNotEmpty($serialized);
diff --git a/tests/Unit/Events/Request/RequestTest.php b/tests/Unit/Events/Request/RequestTest.php
new file mode 100644
index 00000000..897c9938
--- /dev/null
+++ b/tests/Unit/Events/Request/RequestTest.php
@@ -0,0 +1,54 @@
+stop();
+ self::assertNotNull($request);
+ }
+
+ public function testJsonSerializes() : void
+ {
+ // Make a request with some interesting content.
+ $request = new Request();
+ $request->tag('t', 'v');
+ $span = $request->startSpan('foo');
+ $span->tag('spantag', 'spanvalue');
+ $request->stopSpan();
+ $request->stop();
+
+ $serialized = $request->jsonSerialize();
+ self::assertIsArray($serialized);
+
+ self::assertArrayHasKey('BatchCommand', $serialized);
+ self::assertArrayHasKey('commands', $serialized['BatchCommand']);
+ $commands = $serialized['BatchCommand']['commands'];
+
+ self::assertArrayHasKey('StartRequest', reset($commands));
+ self::assertArrayHasKey('TagRequest', next($commands));
+
+ self::assertArrayHasKey('StartSpan', next($commands));
+ self::assertArrayHasKey('TagSpan', next($commands));
+ self::assertArrayHasKey('StopSpan', next($commands));
+
+ self::assertArrayHasKey('FinishRequest', next($commands));
+ }
+}
diff --git a/tests/Unit/Events/RequestTest.php b/tests/Unit/Events/RequestTest.php
deleted file mode 100644
index 6772d2ee..00000000
--- a/tests/Unit/Events/RequestTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-assertNotNull($request);
- }
-
- public function testCanBeStopped()
- {
- $request = new Request(new Agent(), '');
- $request->stop();
- $this->assertNotNull($request);
- }
-
- public function testJsonSerializes()
- {
- // Make a request with some interesting content.
- $request = new Request(new Agent(), '');
- $request->tag('t', 'v');
- $span = $request->startSpan("foo");
- $span->tag("spantag", "spanvalue");
- $request->stopSpan();
- $request->stop();
-
- $serialized = $request->jsonSerialize();
- $this->assertIsArray($serialized);
- $this->assertArrayHasKey("StartRequest", reset($serialized));
- $this->assertArrayHasKey("TagRequest", next($serialized));
-
- $this->assertArrayHasKey("StartSpan", next($serialized));
- $this->assertArrayHasKey("TagSpan", next($serialized));
- $this->assertArrayHasKey("StopSpan", next($serialized));
-
- $this->assertArrayHasKey("FinishRequest", next($serialized));
- }
-}
diff --git a/tests/Unit/Events/Span/SpanTest.php b/tests/Unit/Events/Span/SpanTest.php
new file mode 100644
index 00000000..ff9793a0
--- /dev/null
+++ b/tests/Unit/Events/Span/SpanTest.php
@@ -0,0 +1,54 @@
+stop();
+ self::assertNotNull($span);
+ }
+
+ /** @throws Exception */
+ public function testJsonSerializes() : void
+ {
+ $span = new Span('', RequestId::new());
+ $span->tag('Foo', 'Bar');
+ $span->stop();
+
+ $serialized = $span->jsonSerialize();
+
+ self::assertIsArray($serialized);
+ self::assertArrayHasKey('StartSpan', $serialized[0]);
+ self::assertArrayHasKey('TagSpan', $serialized[1]);
+ self::assertArrayHasKey('StopSpan', $serialized[2]);
+ }
+
+ /** @throws Exception */
+ public function testSpanNameOverride() : void
+ {
+ $span = new Span('original', RequestId::new());
+ self::assertSame('original', $span->getName());
+
+ $span->updateName('fromRequest');
+ self::assertSame('fromRequest', $span->getName());
+ }
+}
diff --git a/tests/Unit/Events/SpanTest.php b/tests/Unit/Events/SpanTest.php
deleted file mode 100644
index 37ab4dbe..00000000
--- a/tests/Unit/Events/SpanTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-assertNotNull($span);
- }
-
- public function testCanBeStopped()
- {
- $span = new Span(new Agent(), '', 'reqid');
- $span->stop();
- $this->assertNotNull($span);
- }
-
- public function testJsonSerializes()
- {
- $span = new Span(new Agent(), '', 'reqid');
- $span->tag("Foo", "Bar");
- $span->stop();
-
- $serialized = $span->jsonSerialize();
-
- $this->assertIsArray($serialized);
- $this->assertArrayHasKey("StartSpan", $serialized[0]);
- $this->assertArrayHasKey("TagSpan", $serialized[1]);
- $this->assertArrayHasKey("StopSpan", $serialized[2]);
- }
-
- public function testSpanNameOverride()
- {
- $span = new Span(new Agent(), 'original', 'reqid');
- $this->assertEquals('original', $span->getName());
-
- $span->updateName("new");
- $this->assertEquals('new', $span->getName());
- }
-}
diff --git a/tests/Unit/Events/Tag/TagRequestTest.php b/tests/Unit/Events/Tag/TagRequestTest.php
new file mode 100644
index 00000000..34cb2a43
--- /dev/null
+++ b/tests/Unit/Events/Tag/TagRequestTest.php
@@ -0,0 +1,36 @@
+jsonSerialize();
+
+ self::assertIsArray($serialized);
+ self::assertArrayHasKey('TagRequest', $serialized[0]);
+
+ $data = $serialized[0]['TagRequest'];
+ self::assertSame('t', $data['tag']);
+ self::assertSame('v', $data['value']);
+ self::assertSame($requestId->toString(), $data['request_id']);
+ }
+}
diff --git a/tests/Unit/Events/Tag/TagSpanTest.php b/tests/Unit/Events/Tag/TagSpanTest.php
new file mode 100644
index 00000000..905b8edd
--- /dev/null
+++ b/tests/Unit/Events/Tag/TagSpanTest.php
@@ -0,0 +1,40 @@
+jsonSerialize();
+
+ self::assertIsArray($serialized);
+ self::assertArrayHasKey('TagSpan', $serialized[0]);
+
+ $data = $serialized[0]['TagSpan'];
+ self::assertSame('t', $data['tag']);
+ self::assertSame('v', $data['value']);
+ self::assertSame($requestId->toString(), $data['request_id']);
+ self::assertSame($spanId->toString(), $data['span_id']);
+ }
+}
diff --git a/tests/Unit/Events/TagRequestTest.php b/tests/Unit/Events/TagRequestTest.php
deleted file mode 100644
index 90af2ced..00000000
--- a/tests/Unit/Events/TagRequestTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-assertNotNull($tag);
- }
-
- public function testJsonSerializes()
- {
- $tag = new TagRequest(new Agent(), 't', 'v', 'reqid');
-
- $serialized = $tag->jsonSerialize();
-
- $this->assertIsArray($serialized);
- $this->assertArrayHasKey("TagRequest", $serialized[0]);
-
- $data = $serialized[0]["TagRequest"];
- $this->assertEquals("t", $data["tag"]);
- $this->assertEquals("v", $data["value"]);
- $this->assertEquals("reqid", $data["request_id"]);
- }
-}
diff --git a/tests/Unit/Events/TagSpanTest.php b/tests/Unit/Events/TagSpanTest.php
deleted file mode 100644
index 8369400d..00000000
--- a/tests/Unit/Events/TagSpanTest.php
+++ /dev/null
@@ -1,34 +0,0 @@
-assertNotNull($tag);
- }
-
- public function testJsonSerializes()
- {
- $tag = new TagSpan(new Agent(), 't', 'v', 'reqid', 'spanid');
-
- $serialized = $tag->jsonSerialize();
-
- $this->assertIsArray($serialized);
- $this->assertArrayHasKey("TagSpan", $serialized[0]);
-
- $data = $serialized[0]["TagSpan"];
- $this->assertEquals("t", $data["tag"]);
- $this->assertEquals("v", $data["value"]);
- $this->assertEquals("reqid", $data["request_id"]);
- $this->assertEquals("spanid", $data["span_id"]);
- }
-}
diff --git a/tests/Unit/Helper/BacktraceTest.php b/tests/Unit/Helper/BacktraceTest.php
index 0b9c0ca8..413db218 100644
--- a/tests/Unit/Helper/BacktraceTest.php
+++ b/tests/Unit/Helper/BacktraceTest.php
@@ -1,22 +1,23 @@
assertNotNull($stack);
+ self::assertNotNull($stack);
foreach ($stack as $frame) {
- $this->assertArrayHasKey('file', $frame);
- $this->assertArrayHasKey('line', $frame);
- $this->assertArrayHasKey('function', $frame);
+ self::assertArrayHasKey('file', $frame);
+ self::assertArrayHasKey('line', $frame);
+ self::assertArrayHasKey('function', $frame);
}
}
}
diff --git a/tests/Unit/Helper/TimerTest.php b/tests/Unit/Helper/TimerTest.php
index ea252e8b..c9ea8e4a 100644
--- a/tests/Unit/Helper/TimerTest.php
+++ b/tests/Unit/Helper/TimerTest.php
@@ -1,19 +1,19 @@