├── var └── cache │ └── .gitignore ├── .gitignore ├── .env ├── Readme.md ├── .php_cs ├── src ├── Controller │ ├── CandidateReturn.php │ ├── AuthenticationStep1.php │ ├── GetFindTypes.php │ ├── GetTestTypes.php │ ├── DownloadRoleCategory.php │ ├── CreateFind.php │ ├── AuthenticationStep2.php │ ├── CreateMatch.php │ ├── AuthenticationRefresh.php │ ├── CreateTest.php │ ├── Dashboard.php │ └── CreateRole.php └── Service │ ├── AccessTokenManager.php │ └── Database.php ├── composer.json ├── public └── index.php └── composer.lock /var/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .php_cs.cache 3 | .env.local -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | MATCH_BASE_URL=https://api-happyrmatch.demo.happyr-dev.com 2 | MATCH_CLIENT_IDENTIFIER=foo 3 | MATCH_CLIENT_SECRET=bar 4 | 5 | # The redirect_uri must match the URI configured in the API dashboard. 6 | MATCH_AUTH_REDIRECT_URI=http://127.0.0.1:8000/step-2 -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Demo implementation of Happyr Match 2 | 3 | 4 | ## Try it out 5 | 6 | Clone the project 7 | 8 | ``` 9 | composer install 10 | 11 | # Use Symfony binary (it is super nice) 12 | symfony serve 13 | 14 | # Or built in php server 15 | php -S 127.0.0.1:8000 -t public 16 | ``` -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | in(__DIR__.'/src') 5 | ->in(__DIR__.'/public') 6 | ; 7 | 8 | return PhpCsFixer\Config::create() 9 | ->setRiskyAllowed(true) 10 | ->setRules([ 11 | '@Symfony' => true, 12 | 'declare_strict_types' => true, 13 | 'date_time_immutable' => true, 14 | ]) 15 | ->setFinder($finder) 16 | ; -------------------------------------------------------------------------------- /src/Controller/CandidateReturn.php: -------------------------------------------------------------------------------- 1 | Back to Dashboard'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "happyr/match-demo", 3 | "type": "project", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Nyholm", 8 | "email": "tobias.nyholm@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=7.2", 13 | "symfony/http-client": "^4.3", 14 | "symfony/dotenv": "^4.3", 15 | "nyholm/psr7": "^1.3" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "App\\": "src/" 20 | } 21 | }, 22 | "config": { 23 | "platform": { 24 | "php": "7.4.12" 25 | }, 26 | "preferred-install": { 27 | "*": "dist" 28 | }, 29 | "sort-packages": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Service/AccessTokenManager.php: -------------------------------------------------------------------------------- 1 | Match demo 18 |

The HTML is very limited and the application is poor. We are using minimal code just to demo stuff.

19 | HTML; 20 | 21 | $authUrl = getenv('MATCH_BASE_URL').'/oauth/authorize?'.http_build_query([ 22 | 'response_type' => 'code', 23 | 'client_id' => getenv('MATCH_CLIENT_IDENTIFIER'), 24 | 'redirect_uri' => getenv('MATCH_AUTH_REDIRECT_URI'), 25 | 'scope' => 'find add_candidate test match learn', 26 | ]); 27 | 28 | echo 'Authenticate

'; 29 | 30 | // TODO if we got an access token already, we can skip authentication 31 | if (AccessTokenManager::hasToken()) { 32 | echo '

You already have a token stored. It might still be valid, do you want to try?

'; 33 | echo 'Continue with existing token'; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Controller/GetFindTypes.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 16 | 'auth_bearer' => AccessTokenManager::fetch()['access_token'] ?? '', 17 | 'headers' => [ 18 | 'Accept' => 'application/vnd.api+json', 19 | 'Content-Type' => 'application/vnd.api+json', 20 | ], 21 | ]); 22 | 23 | $response = $httpClient->request('GET', '/api/find-types'); 24 | 25 | if (200 !== $response->getStatusCode()) { 26 | echo 'Error when getting find types:'; 27 | echo '

'.$response->getContent(false).'

'; 28 | 29 | echo 'Back to Startpage'; 30 | 31 | return; 32 | } 33 | 34 | echo '

Got find types

'; 35 | 36 | $data = $response->toArray()['data']; 37 | echo '
'.json_encode($data, JSON_PRETTY_PRINT).'
'; 38 | 39 | echo '
Back to Dashboard'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Controller/GetTestTypes.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 16 | 'auth_bearer' => AccessTokenManager::fetch()['access_token'] ?? '', 17 | 'headers' => [ 18 | 'Accept' => 'application/vnd.api+json', 19 | 'Content-Type' => 'application/vnd.api+json', 20 | ], 21 | ]); 22 | 23 | $response = $httpClient->request('GET', '/api/test-types'); 24 | 25 | if (200 !== $response->getStatusCode()) { 26 | echo 'Error when getting test types:'; 27 | echo '

'.$response->getContent(false).'

'; 28 | 29 | echo 'Back to Startpage'; 30 | 31 | return; 32 | } 33 | 34 | echo '

Got test types

'; 35 | 36 | $data = $response->toArray()['data']; 37 | echo '
'.json_encode($data, JSON_PRETTY_PRINT).'
'; 38 | 39 | echo '
Back to Dashboard'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Controller/DownloadRoleCategory.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 16 | 'headers' => [ 17 | 'Accept' => 'application/vnd.api+json', 18 | ], 19 | ]); 20 | 21 | $response = $httpClient->request('GET', '/api/roles/all-categories', [ 22 | 'auth_bearer' => AccessTokenManager::fetch()['access_token'] ?? '', 23 | 'headers' => [ 24 | 'Content-Type' => 'application/vnd.api+json', 25 | ], 26 | ]); 27 | 28 | if (200 !== $response->getStatusCode()) { 29 | echo 'Error when creating role:'; 30 | echo '

'.$response->getContent(false).'

'; 31 | 32 | echo 'Back to Startpage'; 33 | 34 | return; 35 | } 36 | 37 | echo 'Back to Dashboard
'; 38 | echo 'Here they are:

'; 39 | 40 | $data = $response->toArray()['data']; 41 | echo '
'.json_encode($data, JSON_PRETTY_PRINT).'
'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Controller/CreateFind.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 17 | 'auth_bearer' => AccessTokenManager::fetch()['access_token'] ?? '', 18 | 'headers' => [ 19 | 'Accept' => 'application/vnd.api+json', 20 | 'Content-Type' => 'application/vnd.api+json', 21 | ], 22 | ]); 23 | 24 | $response = $httpClient->request('POST', '/api/find', [ 25 | 'json' => [ 26 | 'role' => Database::findRole(), 27 | 'type' => '585e6742-c187-4ee3-adea-947cae9e0acf', 28 | 'callback_url' => 'https://example.com/where-to-send-candidates', 29 | ], 30 | ]); 31 | 32 | if (202 !== $response->getStatusCode()) { 33 | echo 'Error when creating Find:'; 34 | echo '

'.$response->getContent(false).'

'; 35 | 36 | echo 'Back to Startpage'; 37 | 38 | return; 39 | } 40 | 41 | echo 'Find request was accepted
'; 42 | echo 'Back to Dashboard'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Controller/AuthenticationStep2.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 21 | ]); 22 | 23 | $response = $httpClient->request('POST', '/oauth/token', [ 24 | 'body' => http_build_query([ 25 | 'code' => $query['code'], 26 | 'grant_type' => 'authorization_code', 27 | 'client_id' => getenv('MATCH_CLIENT_IDENTIFIER'), 28 | 'client_secret' => getenv('MATCH_CLIENT_SECRET'), 29 | 'redirect_uri' => getenv('MATCH_AUTH_REDIRECT_URI'), 30 | ]), 31 | ]); 32 | 33 | if (200 !== $response->getStatusCode()) { 34 | echo 'Error when getting access code:'; 35 | echo '

'.$response->getContent(false).'

'; 36 | 37 | echo 'Back to Startpage'; 38 | 39 | return; 40 | } 41 | 42 | AccessTokenManager::store($response->toArray()); 43 | 44 | echo 'We saved the access token to disk. Go to next step

'; 45 | echo 'Continue'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Controller/CreateMatch.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 17 | 'auth_bearer' => AccessTokenManager::fetch()['access_token'] ?? '', 18 | 'headers' => [ 19 | 'Accept' => 'application/vnd.api+json', 20 | 'Content-Type' => 'application/vnd.api+json', 21 | ], 22 | ]); 23 | 24 | $candidateId = Database::findCandidate(); 25 | $role = Database::findRole(); 26 | 27 | if (empty($candidateId)) { 28 | echo 'Cannot get match without a candidate'; 29 | 30 | return; 31 | } 32 | 33 | if (empty($role)) { 34 | echo 'Cannot get match without a role'; 35 | 36 | return; 37 | } 38 | 39 | $response = $httpClient->request('GET', '/api/candidates/'.$candidateId.'/match?type=medium&role='.$role); 40 | 41 | if (200 !== $response->getStatusCode()) { 42 | echo 'Error when getting match:'; 43 | echo '

'.$response->getContent(false).'

'; 44 | 45 | echo 'Back to Startpage'; 46 | 47 | return; 48 | } 49 | 50 | Database::storeMatch($response->toArray()); 51 | 52 | echo 'We got a match
'; 53 | echo 'Back to Dashboard'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Controller/AuthenticationRefresh.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 21 | 'auth_bearer' => $accessToken['access_token'] ?? '', 22 | 'headers' => [ 23 | 'Content-Type' => 'application/x-www-form-urlencoded', 24 | ], 25 | ]); 26 | 27 | $response = $httpClient->request('POST', '/oauth/token', [ 28 | 'body' => http_build_query([ 29 | 'refresh_token' => $accessToken['refresh_token'] ?? '', 30 | 'grant_type' => 'refresh_token', 31 | 'client_id' => getenv('MATCH_CLIENT_IDENTIFIER'), 32 | 'client_secret' => getenv('MATCH_CLIENT_SECRET'), 33 | ]), 34 | ]); 35 | 36 | if (200 !== $response->getStatusCode()) { 37 | echo 'Error when the new getting access code:'; 38 | echo '

'.$response->getContent(false).'

'; 39 | 40 | echo 'Back to Startpage'; 41 | 42 | return; 43 | } 44 | 45 | AccessTokenManager::store($response->toArray()); 46 | 47 | echo 'We saved the new access token to disk. Go to next step

'; 48 | echo 'Continue'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | loadEnv(dirname(__DIR__).'/.env'); 10 | 11 | $url = $_SERVER['REQUEST_URI']; 12 | $urlPath = parse_url($url, PHP_URL_PATH); 13 | 14 | // A very simple router 15 | switch ($urlPath) { 16 | case '/': 17 | case '/step-1': 18 | (new \App\Controller\AuthenticationStep1())->run($url); 19 | break; 20 | case '/step-2': 21 | (new \App\Controller\AuthenticationStep2())->run($url); 22 | break; 23 | case '/refresh': 24 | (new \App\Controller\AuthenticationRefresh())->run($url); 25 | break; 26 | case '/dashboard': 27 | (new \App\Controller\Dashboard())->run($url); 28 | break; 29 | case '/candidate-return': 30 | (new \App\Controller\CandidateReturn())->run($url); 31 | break; 32 | case '/create-role': 33 | (new \App\Controller\CreateRole())->run($url); 34 | break; 35 | case '/download-role-category': 36 | (new \App\Controller\DownloadRoleCategory())->run($url); 37 | break; 38 | case '/create-test': 39 | (new \App\Controller\CreateTest())->run($url); 40 | break; 41 | case '/get-match': 42 | (new \App\Controller\CreateMatch())->run($url); 43 | break; 44 | case '/find': 45 | (new \App\Controller\CreateFind())->run($url); 46 | break; 47 | case '/find-types': 48 | (new \App\Controller\GetFindTypes())->run($url); 49 | break; 50 | case '/test-types': 51 | (new \App\Controller\GetTestTypes())->run($url); 52 | break; 53 | default: 54 | echo 'Page not found'; 55 | } 56 | -------------------------------------------------------------------------------- /src/Controller/CreateTest.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 17 | 'auth_bearer' => AccessTokenManager::fetch()['access_token'] ?? '', 18 | 'headers' => [ 19 | 'Accept' => 'application/vnd.api+json', 20 | 'Content-Type' => 'application/vnd.api+json', 21 | ], 22 | ]); 23 | 24 | $candidateRedirectUri = 'http://'.$_SERVER['HTTP_HOST'].'/candidate-return'; 25 | $response = $httpClient->request('POST', '/api/tests', [ 26 | 'json' => [ 27 | 'role' => Database::findRole(), 28 | 'types' => ['ca2cfc8b-f2f9-4d5b-a293-925622f63ebb'], 29 | 'redirect_uri' => $candidateRedirectUri, 30 | ], 31 | ]); 32 | 33 | if (201 !== $response->getStatusCode()) { 34 | echo 'Error when creating test:'; 35 | echo '

'.$response->getContent(false).'

'; 36 | 37 | echo 'Back to Startpage'; 38 | 39 | return; 40 | } 41 | 42 | $data = $response->toArray(); 43 | 44 | Database::storeTest([ 45 | 'id' => $data['data']['id'], 46 | 'url' => $data['data']['attributes']['url'].'?redirect-uri='.urlencode($candidateRedirectUri), 47 | ]); 48 | 49 | echo 'Test is created
'; 50 | echo 'Back to Dashboard'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Controller/Dashboard.php: -------------------------------------------------------------------------------- 1 | Dashboard 24 |

We assume that you have a valid access token. If you don't have that yet you will notice soon =)

25 | 26 |

Role

27 |

Current role id: {$role}

28 | Create new role 29 | 30 |

31 | Get all role categories 32 | 33 |

Test

34 |

Current test id: {$test['id']}

35 | Create new test 36 | HTML; 37 | if (!empty($test['url'])) { 38 | echo '

Send a candidate to this URL to ask them to "do the test":

'.$test['url']; 39 | } 40 | 41 | echo <<Candidate 43 |

Current candidate id: {$candidate}

44 |

Here is the match of that candidate:

45 | HTML; 46 | 47 | if (empty($match)) { 48 | echo 'Calculate match'; 49 | } else { 50 | echo '
'.json_encode($match, JSON_PRETTY_PRINT).'
'; 51 | } 52 | 53 | echo <<Find request 55 | Send new Find request by clicking here. 56 | 57 |

Get all find types

58 | See all type of Find requests by clicking here. 59 | 60 |

Get all test types

61 | See all type of tests by clicking here. 62 | 63 | 64 |

Refresh AccessToken

65 | If the token have expired, you may refresh it. 66 | 67 | 68 |

Startpage

69 | Back to startpage 70 | 71 | HTML; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Service/Database.php: -------------------------------------------------------------------------------- 1 | null]; 38 | } 39 | 40 | public static function storeTest(array $test): void 41 | { 42 | self::store('test', $test); 43 | } 44 | 45 | public static function findMatch(?string $candidateId): array 46 | { 47 | if (null === $candidateId) { 48 | return []; 49 | } 50 | 51 | return self::fetch()['match'] ?? []; 52 | } 53 | 54 | public static function storeMatch(array $match): void 55 | { 56 | self::store('match', $match); 57 | } 58 | 59 | private static function store(string $key, $value): void 60 | { 61 | $data = self::fetch(); 62 | $data[$key] = $value; 63 | $file = self::getStoragePath(); 64 | file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT)); 65 | } 66 | 67 | private static function fetch(): array 68 | { 69 | $file = self::getStoragePath(); 70 | if (!file_exists($file)) { 71 | return []; 72 | } 73 | 74 | $fileContent = file_get_contents($file); 75 | 76 | return json_decode($fileContent, true); 77 | } 78 | 79 | private static function getStoragePath() 80 | { 81 | return dirname(__DIR__).'/..'.self::STORAGE; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Controller/CreateRole.php: -------------------------------------------------------------------------------- 1 | getenv('MATCH_BASE_URL'), 17 | 'headers' => [ 18 | 'Accept' => 'application/vnd.api+json', 19 | ], 20 | ]); 21 | 22 | // Get the role category 23 | $response = $httpClient->request('GET', 'https://api.happyrmatch.com/api/role-categories/search?language=sv&name=receptionist'); 24 | $roleCategory = $response->toArray()['data'][0]['id']; 25 | 26 | $response = $httpClient->request('POST', '/api/roles', [ 27 | 'auth_bearer' => AccessTokenManager::fetch()['access_token'] ?? '', 28 | 'headers' => [ 29 | 'Content-Type' => 'application/vnd.api+json', 30 | ], 31 | 'json' => [ 32 | 'advert_title' => 'Funny receptionist', 33 | 'advert_body_text' => 'Long text', 34 | 'advert_body_html' => 'Long text<\/b>', 35 | 'advert_link' => 'https://my-app.com/advert/123', 36 | 'description' => 'Short text', 37 | 'employment_duration' => 1, 38 | 'role_category' => $roleCategory, 39 | 'work_hours' => 1, 40 | 'location' => [ 41 | 'country' => 'SE', 42 | 'region' => 'Stockholms län', 43 | 'city' => 'Stockholm', 44 | 'address' => 'Drottninggatan 7', 45 | ], 46 | ], 47 | ]); 48 | 49 | if (201 !== $response->getStatusCode()) { 50 | echo 'Error when creating role:'; 51 | echo '

'.$response->getContent(false).'

'; 52 | 53 | echo 'Back to Startpage'; 54 | 55 | return; 56 | } 57 | 58 | Database::storeRole($response->toArray()['data']['id']); 59 | 60 | echo 'Role is created
'; 61 | echo 'Back to Dashboard'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "84996011575ad0f3df25773cf6ebba3e", 8 | "packages": [ 9 | { 10 | "name": "nyholm/psr7", 11 | "version": "1.4.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/Nyholm/psr7.git", 15 | "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/Nyholm/psr7/zipball/2212385b47153ea71b1c1b1374f8cb5e4f7892ec", 20 | "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=7.1", 25 | "php-http/message-factory": "^1.0", 26 | "psr/http-factory": "^1.0", 27 | "psr/http-message": "^1.0" 28 | }, 29 | "provide": { 30 | "psr/http-factory-implementation": "1.0", 31 | "psr/http-message-implementation": "1.0" 32 | }, 33 | "require-dev": { 34 | "http-interop/http-factory-tests": "^0.9", 35 | "php-http/psr7-integration-tests": "^1.0", 36 | "phpunit/phpunit": "^7.5 || 8.5 || 9.4", 37 | "symfony/error-handler": "^4.4" 38 | }, 39 | "type": "library", 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "1.4-dev" 43 | } 44 | }, 45 | "autoload": { 46 | "psr-4": { 47 | "Nyholm\\Psr7\\": "src/" 48 | } 49 | }, 50 | "notification-url": "https://packagist.org/downloads/", 51 | "license": [ 52 | "MIT" 53 | ], 54 | "authors": [ 55 | { 56 | "name": "Tobias Nyholm", 57 | "email": "tobias.nyholm@gmail.com" 58 | }, 59 | { 60 | "name": "Martijn van der Ven", 61 | "email": "martijn@vanderven.se" 62 | } 63 | ], 64 | "description": "A fast PHP7 implementation of PSR-7", 65 | "homepage": "https://tnyholm.se", 66 | "keywords": [ 67 | "psr-17", 68 | "psr-7" 69 | ], 70 | "support": { 71 | "issues": "https://github.com/Nyholm/psr7/issues", 72 | "source": "https://github.com/Nyholm/psr7/tree/1.4.1" 73 | }, 74 | "funding": [ 75 | { 76 | "url": "https://github.com/Zegnat", 77 | "type": "github" 78 | }, 79 | { 80 | "url": "https://github.com/nyholm", 81 | "type": "github" 82 | } 83 | ], 84 | "time": "2021-07-02T08:32:20+00:00" 85 | }, 86 | { 87 | "name": "php-http/message-factory", 88 | "version": "v1.0.2", 89 | "source": { 90 | "type": "git", 91 | "url": "https://github.com/php-http/message-factory.git", 92 | "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" 93 | }, 94 | "dist": { 95 | "type": "zip", 96 | "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", 97 | "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", 98 | "shasum": "" 99 | }, 100 | "require": { 101 | "php": ">=5.4", 102 | "psr/http-message": "^1.0" 103 | }, 104 | "type": "library", 105 | "extra": { 106 | "branch-alias": { 107 | "dev-master": "1.0-dev" 108 | } 109 | }, 110 | "autoload": { 111 | "psr-4": { 112 | "Http\\Message\\": "src/" 113 | } 114 | }, 115 | "notification-url": "https://packagist.org/downloads/", 116 | "license": [ 117 | "MIT" 118 | ], 119 | "authors": [ 120 | { 121 | "name": "Márk Sági-Kazár", 122 | "email": "mark.sagikazar@gmail.com" 123 | } 124 | ], 125 | "description": "Factory interfaces for PSR-7 HTTP Message", 126 | "homepage": "http://php-http.org", 127 | "keywords": [ 128 | "factory", 129 | "http", 130 | "message", 131 | "stream", 132 | "uri" 133 | ], 134 | "support": { 135 | "issues": "https://github.com/php-http/message-factory/issues", 136 | "source": "https://github.com/php-http/message-factory/tree/master" 137 | }, 138 | "time": "2015-12-19T14:08:53+00:00" 139 | }, 140 | { 141 | "name": "psr/container", 142 | "version": "1.1.1", 143 | "source": { 144 | "type": "git", 145 | "url": "https://github.com/php-fig/container.git", 146 | "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" 147 | }, 148 | "dist": { 149 | "type": "zip", 150 | "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", 151 | "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", 152 | "shasum": "" 153 | }, 154 | "require": { 155 | "php": ">=7.2.0" 156 | }, 157 | "type": "library", 158 | "autoload": { 159 | "psr-4": { 160 | "Psr\\Container\\": "src/" 161 | } 162 | }, 163 | "notification-url": "https://packagist.org/downloads/", 164 | "license": [ 165 | "MIT" 166 | ], 167 | "authors": [ 168 | { 169 | "name": "PHP-FIG", 170 | "homepage": "https://www.php-fig.org/" 171 | } 172 | ], 173 | "description": "Common Container Interface (PHP FIG PSR-11)", 174 | "homepage": "https://github.com/php-fig/container", 175 | "keywords": [ 176 | "PSR-11", 177 | "container", 178 | "container-interface", 179 | "container-interop", 180 | "psr" 181 | ], 182 | "support": { 183 | "issues": "https://github.com/php-fig/container/issues", 184 | "source": "https://github.com/php-fig/container/tree/1.1.1" 185 | }, 186 | "time": "2021-03-05T17:36:06+00:00" 187 | }, 188 | { 189 | "name": "psr/http-factory", 190 | "version": "1.0.1", 191 | "source": { 192 | "type": "git", 193 | "url": "https://github.com/php-fig/http-factory.git", 194 | "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" 195 | }, 196 | "dist": { 197 | "type": "zip", 198 | "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", 199 | "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", 200 | "shasum": "" 201 | }, 202 | "require": { 203 | "php": ">=7.0.0", 204 | "psr/http-message": "^1.0" 205 | }, 206 | "type": "library", 207 | "extra": { 208 | "branch-alias": { 209 | "dev-master": "1.0.x-dev" 210 | } 211 | }, 212 | "autoload": { 213 | "psr-4": { 214 | "Psr\\Http\\Message\\": "src/" 215 | } 216 | }, 217 | "notification-url": "https://packagist.org/downloads/", 218 | "license": [ 219 | "MIT" 220 | ], 221 | "authors": [ 222 | { 223 | "name": "PHP-FIG", 224 | "homepage": "http://www.php-fig.org/" 225 | } 226 | ], 227 | "description": "Common interfaces for PSR-7 HTTP message factories", 228 | "keywords": [ 229 | "factory", 230 | "http", 231 | "message", 232 | "psr", 233 | "psr-17", 234 | "psr-7", 235 | "request", 236 | "response" 237 | ], 238 | "support": { 239 | "source": "https://github.com/php-fig/http-factory/tree/master" 240 | }, 241 | "time": "2019-04-30T12:38:16+00:00" 242 | }, 243 | { 244 | "name": "psr/http-message", 245 | "version": "1.0.1", 246 | "source": { 247 | "type": "git", 248 | "url": "https://github.com/php-fig/http-message.git", 249 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 250 | }, 251 | "dist": { 252 | "type": "zip", 253 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 254 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 255 | "shasum": "" 256 | }, 257 | "require": { 258 | "php": ">=5.3.0" 259 | }, 260 | "type": "library", 261 | "extra": { 262 | "branch-alias": { 263 | "dev-master": "1.0.x-dev" 264 | } 265 | }, 266 | "autoload": { 267 | "psr-4": { 268 | "Psr\\Http\\Message\\": "src/" 269 | } 270 | }, 271 | "notification-url": "https://packagist.org/downloads/", 272 | "license": [ 273 | "MIT" 274 | ], 275 | "authors": [ 276 | { 277 | "name": "PHP-FIG", 278 | "homepage": "http://www.php-fig.org/" 279 | } 280 | ], 281 | "description": "Common interface for HTTP messages", 282 | "homepage": "https://github.com/php-fig/http-message", 283 | "keywords": [ 284 | "http", 285 | "http-message", 286 | "psr", 287 | "psr-7", 288 | "request", 289 | "response" 290 | ], 291 | "support": { 292 | "source": "https://github.com/php-fig/http-message/tree/master" 293 | }, 294 | "time": "2016-08-06T14:39:51+00:00" 295 | }, 296 | { 297 | "name": "psr/log", 298 | "version": "1.1.4", 299 | "source": { 300 | "type": "git", 301 | "url": "https://github.com/php-fig/log.git", 302 | "reference": "d49695b909c3b7628b6289db5479a1c204601f11" 303 | }, 304 | "dist": { 305 | "type": "zip", 306 | "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", 307 | "reference": "d49695b909c3b7628b6289db5479a1c204601f11", 308 | "shasum": "" 309 | }, 310 | "require": { 311 | "php": ">=5.3.0" 312 | }, 313 | "type": "library", 314 | "extra": { 315 | "branch-alias": { 316 | "dev-master": "1.1.x-dev" 317 | } 318 | }, 319 | "autoload": { 320 | "psr-4": { 321 | "Psr\\Log\\": "Psr/Log/" 322 | } 323 | }, 324 | "notification-url": "https://packagist.org/downloads/", 325 | "license": [ 326 | "MIT" 327 | ], 328 | "authors": [ 329 | { 330 | "name": "PHP-FIG", 331 | "homepage": "https://www.php-fig.org/" 332 | } 333 | ], 334 | "description": "Common interface for logging libraries", 335 | "homepage": "https://github.com/php-fig/log", 336 | "keywords": [ 337 | "log", 338 | "psr", 339 | "psr-3" 340 | ], 341 | "support": { 342 | "source": "https://github.com/php-fig/log/tree/1.1.4" 343 | }, 344 | "time": "2021-05-03T11:20:27+00:00" 345 | }, 346 | { 347 | "name": "symfony/dotenv", 348 | "version": "v4.4.29", 349 | "source": { 350 | "type": "git", 351 | "url": "https://github.com/symfony/dotenv.git", 352 | "reference": "2b078eff6268875f4639cf16e48e321982c671bb" 353 | }, 354 | "dist": { 355 | "type": "zip", 356 | "url": "https://api.github.com/repos/symfony/dotenv/zipball/2b078eff6268875f4639cf16e48e321982c671bb", 357 | "reference": "2b078eff6268875f4639cf16e48e321982c671bb", 358 | "shasum": "" 359 | }, 360 | "require": { 361 | "php": ">=7.1.3" 362 | }, 363 | "require-dev": { 364 | "symfony/process": "^3.4.2|^4.0|^5.0" 365 | }, 366 | "type": "library", 367 | "autoload": { 368 | "psr-4": { 369 | "Symfony\\Component\\Dotenv\\": "" 370 | }, 371 | "exclude-from-classmap": [ 372 | "/Tests/" 373 | ] 374 | }, 375 | "notification-url": "https://packagist.org/downloads/", 376 | "license": [ 377 | "MIT" 378 | ], 379 | "authors": [ 380 | { 381 | "name": "Fabien Potencier", 382 | "email": "fabien@symfony.com" 383 | }, 384 | { 385 | "name": "Symfony Community", 386 | "homepage": "https://symfony.com/contributors" 387 | } 388 | ], 389 | "description": "Registers environment variables from a .env file", 390 | "homepage": "https://symfony.com", 391 | "keywords": [ 392 | "dotenv", 393 | "env", 394 | "environment" 395 | ], 396 | "support": { 397 | "source": "https://github.com/symfony/dotenv/tree/v4.4.29" 398 | }, 399 | "funding": [ 400 | { 401 | "url": "https://symfony.com/sponsor", 402 | "type": "custom" 403 | }, 404 | { 405 | "url": "https://github.com/fabpot", 406 | "type": "github" 407 | }, 408 | { 409 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 410 | "type": "tidelift" 411 | } 412 | ], 413 | "time": "2021-07-27T16:19:30+00:00" 414 | }, 415 | { 416 | "name": "symfony/http-client", 417 | "version": "v4.4.31", 418 | "source": { 419 | "type": "git", 420 | "url": "https://github.com/symfony/http-client.git", 421 | "reference": "6b900ffa399e25203f30f79f6f4a56b89eee14c2" 422 | }, 423 | "dist": { 424 | "type": "zip", 425 | "url": "https://api.github.com/repos/symfony/http-client/zipball/6b900ffa399e25203f30f79f6f4a56b89eee14c2", 426 | "reference": "6b900ffa399e25203f30f79f6f4a56b89eee14c2", 427 | "shasum": "" 428 | }, 429 | "require": { 430 | "php": ">=7.1.3", 431 | "psr/log": "^1|^2|^3", 432 | "symfony/http-client-contracts": "^1.1.10|^2", 433 | "symfony/polyfill-php73": "^1.11", 434 | "symfony/polyfill-php80": "^1.16", 435 | "symfony/service-contracts": "^1.0|^2" 436 | }, 437 | "provide": { 438 | "php-http/async-client-implementation": "*", 439 | "php-http/client-implementation": "*", 440 | "psr/http-client-implementation": "1.0", 441 | "symfony/http-client-implementation": "1.1|2.0" 442 | }, 443 | "require-dev": { 444 | "guzzlehttp/promises": "^1.4", 445 | "nyholm/psr7": "^1.0", 446 | "php-http/httplug": "^1.0|^2.0", 447 | "psr/http-client": "^1.0", 448 | "symfony/dependency-injection": "^4.3|^5.0", 449 | "symfony/http-kernel": "^4.4.13", 450 | "symfony/process": "^4.2|^5.0" 451 | }, 452 | "type": "library", 453 | "autoload": { 454 | "psr-4": { 455 | "Symfony\\Component\\HttpClient\\": "" 456 | }, 457 | "exclude-from-classmap": [ 458 | "/Tests/" 459 | ] 460 | }, 461 | "notification-url": "https://packagist.org/downloads/", 462 | "license": [ 463 | "MIT" 464 | ], 465 | "authors": [ 466 | { 467 | "name": "Nicolas Grekas", 468 | "email": "p@tchwork.com" 469 | }, 470 | { 471 | "name": "Symfony Community", 472 | "homepage": "https://symfony.com/contributors" 473 | } 474 | ], 475 | "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", 476 | "homepage": "https://symfony.com", 477 | "support": { 478 | "source": "https://github.com/symfony/http-client/tree/v4.4.31" 479 | }, 480 | "funding": [ 481 | { 482 | "url": "https://symfony.com/sponsor", 483 | "type": "custom" 484 | }, 485 | { 486 | "url": "https://github.com/fabpot", 487 | "type": "github" 488 | }, 489 | { 490 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 491 | "type": "tidelift" 492 | } 493 | ], 494 | "time": "2021-09-06T10:00:00+00:00" 495 | }, 496 | { 497 | "name": "symfony/http-client-contracts", 498 | "version": "v2.4.0", 499 | "source": { 500 | "type": "git", 501 | "url": "https://github.com/symfony/http-client-contracts.git", 502 | "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4" 503 | }, 504 | "dist": { 505 | "type": "zip", 506 | "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4", 507 | "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4", 508 | "shasum": "" 509 | }, 510 | "require": { 511 | "php": ">=7.2.5" 512 | }, 513 | "suggest": { 514 | "symfony/http-client-implementation": "" 515 | }, 516 | "type": "library", 517 | "extra": { 518 | "branch-alias": { 519 | "dev-main": "2.4-dev" 520 | }, 521 | "thanks": { 522 | "name": "symfony/contracts", 523 | "url": "https://github.com/symfony/contracts" 524 | } 525 | }, 526 | "autoload": { 527 | "psr-4": { 528 | "Symfony\\Contracts\\HttpClient\\": "" 529 | } 530 | }, 531 | "notification-url": "https://packagist.org/downloads/", 532 | "license": [ 533 | "MIT" 534 | ], 535 | "authors": [ 536 | { 537 | "name": "Nicolas Grekas", 538 | "email": "p@tchwork.com" 539 | }, 540 | { 541 | "name": "Symfony Community", 542 | "homepage": "https://symfony.com/contributors" 543 | } 544 | ], 545 | "description": "Generic abstractions related to HTTP clients", 546 | "homepage": "https://symfony.com", 547 | "keywords": [ 548 | "abstractions", 549 | "contracts", 550 | "decoupling", 551 | "interfaces", 552 | "interoperability", 553 | "standards" 554 | ], 555 | "support": { 556 | "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0" 557 | }, 558 | "funding": [ 559 | { 560 | "url": "https://symfony.com/sponsor", 561 | "type": "custom" 562 | }, 563 | { 564 | "url": "https://github.com/fabpot", 565 | "type": "github" 566 | }, 567 | { 568 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 569 | "type": "tidelift" 570 | } 571 | ], 572 | "time": "2021-04-11T23:07:08+00:00" 573 | }, 574 | { 575 | "name": "symfony/polyfill-php73", 576 | "version": "v1.23.0", 577 | "source": { 578 | "type": "git", 579 | "url": "https://github.com/symfony/polyfill-php73.git", 580 | "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" 581 | }, 582 | "dist": { 583 | "type": "zip", 584 | "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", 585 | "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", 586 | "shasum": "" 587 | }, 588 | "require": { 589 | "php": ">=7.1" 590 | }, 591 | "type": "library", 592 | "extra": { 593 | "branch-alias": { 594 | "dev-main": "1.23-dev" 595 | }, 596 | "thanks": { 597 | "name": "symfony/polyfill", 598 | "url": "https://github.com/symfony/polyfill" 599 | } 600 | }, 601 | "autoload": { 602 | "psr-4": { 603 | "Symfony\\Polyfill\\Php73\\": "" 604 | }, 605 | "files": [ 606 | "bootstrap.php" 607 | ], 608 | "classmap": [ 609 | "Resources/stubs" 610 | ] 611 | }, 612 | "notification-url": "https://packagist.org/downloads/", 613 | "license": [ 614 | "MIT" 615 | ], 616 | "authors": [ 617 | { 618 | "name": "Nicolas Grekas", 619 | "email": "p@tchwork.com" 620 | }, 621 | { 622 | "name": "Symfony Community", 623 | "homepage": "https://symfony.com/contributors" 624 | } 625 | ], 626 | "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", 627 | "homepage": "https://symfony.com", 628 | "keywords": [ 629 | "compatibility", 630 | "polyfill", 631 | "portable", 632 | "shim" 633 | ], 634 | "support": { 635 | "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" 636 | }, 637 | "funding": [ 638 | { 639 | "url": "https://symfony.com/sponsor", 640 | "type": "custom" 641 | }, 642 | { 643 | "url": "https://github.com/fabpot", 644 | "type": "github" 645 | }, 646 | { 647 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 648 | "type": "tidelift" 649 | } 650 | ], 651 | "time": "2021-02-19T12:13:01+00:00" 652 | }, 653 | { 654 | "name": "symfony/polyfill-php80", 655 | "version": "v1.23.1", 656 | "source": { 657 | "type": "git", 658 | "url": "https://github.com/symfony/polyfill-php80.git", 659 | "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" 660 | }, 661 | "dist": { 662 | "type": "zip", 663 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", 664 | "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", 665 | "shasum": "" 666 | }, 667 | "require": { 668 | "php": ">=7.1" 669 | }, 670 | "type": "library", 671 | "extra": { 672 | "branch-alias": { 673 | "dev-main": "1.23-dev" 674 | }, 675 | "thanks": { 676 | "name": "symfony/polyfill", 677 | "url": "https://github.com/symfony/polyfill" 678 | } 679 | }, 680 | "autoload": { 681 | "psr-4": { 682 | "Symfony\\Polyfill\\Php80\\": "" 683 | }, 684 | "files": [ 685 | "bootstrap.php" 686 | ], 687 | "classmap": [ 688 | "Resources/stubs" 689 | ] 690 | }, 691 | "notification-url": "https://packagist.org/downloads/", 692 | "license": [ 693 | "MIT" 694 | ], 695 | "authors": [ 696 | { 697 | "name": "Ion Bazan", 698 | "email": "ion.bazan@gmail.com" 699 | }, 700 | { 701 | "name": "Nicolas Grekas", 702 | "email": "p@tchwork.com" 703 | }, 704 | { 705 | "name": "Symfony Community", 706 | "homepage": "https://symfony.com/contributors" 707 | } 708 | ], 709 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 710 | "homepage": "https://symfony.com", 711 | "keywords": [ 712 | "compatibility", 713 | "polyfill", 714 | "portable", 715 | "shim" 716 | ], 717 | "support": { 718 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" 719 | }, 720 | "funding": [ 721 | { 722 | "url": "https://symfony.com/sponsor", 723 | "type": "custom" 724 | }, 725 | { 726 | "url": "https://github.com/fabpot", 727 | "type": "github" 728 | }, 729 | { 730 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 731 | "type": "tidelift" 732 | } 733 | ], 734 | "time": "2021-07-28T13:41:28+00:00" 735 | }, 736 | { 737 | "name": "symfony/service-contracts", 738 | "version": "v2.4.0", 739 | "source": { 740 | "type": "git", 741 | "url": "https://github.com/symfony/service-contracts.git", 742 | "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" 743 | }, 744 | "dist": { 745 | "type": "zip", 746 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", 747 | "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", 748 | "shasum": "" 749 | }, 750 | "require": { 751 | "php": ">=7.2.5", 752 | "psr/container": "^1.1" 753 | }, 754 | "suggest": { 755 | "symfony/service-implementation": "" 756 | }, 757 | "type": "library", 758 | "extra": { 759 | "branch-alias": { 760 | "dev-main": "2.4-dev" 761 | }, 762 | "thanks": { 763 | "name": "symfony/contracts", 764 | "url": "https://github.com/symfony/contracts" 765 | } 766 | }, 767 | "autoload": { 768 | "psr-4": { 769 | "Symfony\\Contracts\\Service\\": "" 770 | } 771 | }, 772 | "notification-url": "https://packagist.org/downloads/", 773 | "license": [ 774 | "MIT" 775 | ], 776 | "authors": [ 777 | { 778 | "name": "Nicolas Grekas", 779 | "email": "p@tchwork.com" 780 | }, 781 | { 782 | "name": "Symfony Community", 783 | "homepage": "https://symfony.com/contributors" 784 | } 785 | ], 786 | "description": "Generic abstractions related to writing services", 787 | "homepage": "https://symfony.com", 788 | "keywords": [ 789 | "abstractions", 790 | "contracts", 791 | "decoupling", 792 | "interfaces", 793 | "interoperability", 794 | "standards" 795 | ], 796 | "support": { 797 | "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" 798 | }, 799 | "funding": [ 800 | { 801 | "url": "https://symfony.com/sponsor", 802 | "type": "custom" 803 | }, 804 | { 805 | "url": "https://github.com/fabpot", 806 | "type": "github" 807 | }, 808 | { 809 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 810 | "type": "tidelift" 811 | } 812 | ], 813 | "time": "2021-04-01T10:43:52+00:00" 814 | } 815 | ], 816 | "packages-dev": [], 817 | "aliases": [], 818 | "minimum-stability": "stable", 819 | "stability-flags": [], 820 | "prefer-stable": false, 821 | "prefer-lowest": false, 822 | "platform": { 823 | "php": ">=7.2" 824 | }, 825 | "platform-dev": [], 826 | "platform-overrides": { 827 | "php": "7.4.12" 828 | }, 829 | "plugin-api-version": "2.0.0" 830 | } 831 | --------------------------------------------------------------------------------