[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "| Q                       | A\n| ----------------------- | ---\n| Bug?                    | no|yes\n| New Feature?            | no|yes\n| Are you using composer? | no|yes\n| Version                 | Specific version or SHA of a commit\n\n\n#### Actual Behavior\n\nWhat is the actual behavior?\n\n\n#### Expected Behavior\n\nWhat is the behavior you expect?\n\n\n#### Steps to Reproduce\n\nWhat are the steps to reproduce this bug? Please add code examples,\nscreenshots or links to GitHub repositories that reproduce the problem.\n\n\n#### Possible Solutions\n\nIf you have already ideas how to solve the issue, add them here.\n(remove this section if not needed)\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "| Q               | A\n| --------------- | ---\n| Bug fix?        | no|yes\n| New feature?    | no|yes\n| BC breaks?      | no|yes\n| Deprecations?   | no|yes\n| Related tickets | fixes #X, partially #Y, mentioned in #Z\n| License         | MIT\n\n\n#### What's in this PR?\n\nExplain what the changes in this PR do.\n\n\n#### Why?\n\nWhich problem does the PR fix? (remove this section if you linked an issue above)\n\n\n#### Example Usage\n\n``` php\n// If you added new features, show examples of how to use them here\n// (remove this section if not a new feature)\n\n$foo = new Foo();\n\n// Now we can do\n$foo->doSomething();\n```\n\n\n#### Checklist\n\n- [ ] Updated CHANGELOG.md to describe BC breaks / deprecations | new feature | bugfix\n- [ ] Documentation pull request created (if not simply a bugfix)\n\n\n#### To Do\n\n- [ ] If the PR is not complete but you want to discuss the approach, list what remains to be done here\n"
  },
  {
    "path": ".gitignore",
    "content": "/build/\ncomposer.lock\nindex.php\nphpunit.xml\n/vendor/\n"
  },
  {
    "path": ".scrutinizer.yml",
    "content": "filter:\n    paths: [src/*]\nchecks:\n    php:\n        code_rating: true\n        duplication: true\ntools:\n    external_code_coverage: true\n"
  },
  {
    "path": ".styleci.yml",
    "content": "preset: symfony\n\nfinder:\n    path:\n        - \"src\"\n        - \"tests\"\n\nenabled:\n    - short_array_syntax\n\ndisabled:\n    - phpdoc_annotation_without_dot\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: php\nsudo: false\n\ncache:\n    directories:\n        - $HOME/.composer/cache/files\n\nphp:\n  - 5.5\n  - 5.6\n  - 7.0\n  - 7.1\n  - hhvm\n\nenv:\n    global:\n        - TEST_COMMAND=\"composer test\"\n\nmatrix:\n    fast_finish: true\n    include:\n        - php: 5.5\n          env: COMPOSER_FLAGS=\"--prefer-stable --prefer-lowest\" COVERAGE=true TEST_COMMAND=\"composer test-ci\"\n\nbefore_install:\n  - composer self-update\n\ninstall:\n    - travis_retry composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction\n\nscript:\n    - $TEST_COMMAND\n\nafter_success:\n    - if [[ $COVERAGE = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi\n    - if [[ $COVERAGE = true ]]; then php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml; fi\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Tobias Nyholm\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Readme.md",
    "content": "# LinkedIn API client in PHP\n\n[![Latest Version](https://img.shields.io/github/release/Happyr/LinkedIn-API-client.svg?style=flat-square)](https://github.com/Happyr/LinkedIn-API-client/releases)\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)\n[![Build Status](https://img.shields.io/travis/Happyr/LinkedIn-API-client/master.svg?style=flat-square)](https://travis-ci.org/Happyr/LinkedIn-API-client)\n[![SensioLabsInsight](https://insight.sensiolabs.com/projects/44c425af-90f6-4c25-b789-4ece28b01a2b/mini.png)](https://insight.sensiolabs.com/projects/44c425af-90f6-4c25-b789-4ece28b01a2b)\n[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/Happyr/LinkedIn-API-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/Happyr/LinkedIn-API-client)\n[![Quality Score](https://img.shields.io/scrutinizer/g/Happyr/LinkedIn-API-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/Happyr/LinkedIn-API-client)\n[![Total Downloads](https://img.shields.io/packagist/dt/happyr/linkedin-api-client.svg?style=flat-square)](https://packagist.org/packages/happyr/linkedin-api-client)\n\n\nA PHP library to handle authentication and communication with LinkedIn API. The library/SDK helps you to get an access\ntoken and when authenticated it helps you to send API requests. You will not get *everything* for free though... You\nhave to read the [LinkedIn documentation][api-doc-core] to understand how you should query the API. \n\nTo get an overview what this library actually is doing for you. Take a look at the authentication page from\nthe [API docs][api-doc-authentication].\n\n## Features\n\nHere is a list of features that might convince you to choose this LinkedIn client over some of our competitors'.\n\n* Flexible and easy to extend\n* Developed with modern PHP standards\n* Not developed for a specific framework. \n* Handles the authentication process\n* Respects the CSRF protection\n\n## Installation\n\n**TL;DR**\n```bash\ncomposer require php-http/curl-client guzzlehttp/psr7 php-http/message happyr/linkedin-api-client\n```\n\nThis library does not have a dependency on Guzzle or any other library that sends HTTP requests. We use the awesome \nHTTPlug to achieve the decoupling. We want you to choose what library to use for sending HTTP requests. Consult this list \nof packages that support [php-http/client-implementation](https://packagist.org/providers/php-http/client-implementation) \nfind clients to use. For more information about virtual packages please refer to \n[HTTPlug](http://docs.php-http.org/en/latest/httplug/users.html). Example:\n\n```bash\ncomposer require php-http/guzzle6-adapter\n```\n\nYou do also need to install a PSR-7 implementation and a factory to create PSR-7 messages (PSR-17 whenever that is \nreleased). You could use Guzzles PSR-7 implementation and factories from php-http:\n\n```bash\ncomposer require guzzlehttp/psr7 php-http/message \n```\n\nNow you may install the library by running the following:\n\n```bash\ncomposer require happyr/linkedin-api-client\n```\n\nIf you are updating form a previous version make sure to read [the upgrade documentation](Upgrade.md).\n\n### Finding the HTTP client (optional) \n\nThe LinkedIn client need to know what library you are using to send HTTP messages. You could provide an instance of \nHttpClient and MessageFactory or you could fallback on auto discovery. Below is an example on where you provide a Guzzle6 \ninstance.\n\n```php\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('app_id', 'app_secret');\n$linkedIn->setHttpClient(new \\Http\\Adapter\\Guzzle6\\Client());\n$linkedIn->setHttpMessageFactory(new Http\\Message\\MessageFactory\\GuzzleMessageFactory());\n\n```\n\n## Usage\n\nIn order to use this API client (or any other LinkedIn clients) you have to [register your application][register-app]\nwith LinkedIn to receive an API key. Once you've registered your LinkedIn app, you will be provided with\nan *API Key* and *Secret Key*.\n\n### LinkedIn login\n\nThis example below is showing how to login with LinkedIn.\n\n```php \n<?php\n\n/**\n * This demonstrates how to authenticate with LinkedIn and send api requests\n */\n\n/*\n * First you need to make sure you've used composers auto load. You have is probably \n * already done this before. You usually don't bother..\n */\n//require_once \"vendor/autoload.php\";\n\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('client_id', 'client_secret');\n\nif ($linkedIn->isAuthenticated()) {\n    //we know that the user is authenticated now. Start query the API\n    $user=$linkedIn->get('v1/people/~:(firstName,lastName)');\n    echo \"Welcome \".$user['firstName'];\n\n    exit();\n} elseif ($linkedIn->hasError()) {\n    echo \"User canceled the login.\";\n    exit();\n}\n\n//if not authenticated\n$url = $linkedIn->getLoginUrl();\necho \"<a href='$url'>Login with LinkedIn</a>\";\n\n```\n\n### How to post on LinkedIn wall\n\nThe example below shows how you can post on a users wall. The access token is fetched from the database. \n\n```php\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('app_id', 'app_secret');\n$linkedIn->setAccessToken('access_token_from_db');\n\n$options = array('json'=>\n    array(\n        'comment' => 'Im testing Happyr LinkedIn client! https://github.com/Happyr/LinkedIn-API-client',\n        'visibility' => array(\n            'code' => 'anyone'\n        )\n    )\n);\n\n$result = $linkedIn->post('v1/people/~/shares', $options);\n\nvar_dump($result);\n\n// Prints: \n// array (size=2)\n//   'updateKey' => string 'UPDATE-01234567-0123456789012345678' (length=35)\n//   'updateUrl' => string 'https://www.linkedin.com/updates?discuss=&scope=01234567&stype=M&topic=0123456789012345678&type=U&a=mVKU' (length=104)\n\n```\n\nYou may of course do the same in xml. Use the following options array.\n```php\n$options = array(\n'format' => 'xml',\n'body' => '<share>\n <comment>Im testing Happyr LinkedIn client! https://github.com/Happyr/LinkedIn-API-client</comment>\n <visibility>\n   <code>anyone</code>\n </visibility>\n</share>');\n```\n\n## Configuration\n\n### The api options\n\nThe third parameter of `LinkedIn::api` is an array with options. Below is a table of array keys that you may use. \n\n| Option name | Description\n| ----------- | -----------\n| body | The body of a HTTP request. Put your xml string here. \n| format | Set this to 'json', 'xml' or 'simple_xml' to override the default value.\n| headers | This is HTTP headers to the request\n| json | This is an array with json data that will be encoded to a json string. Using this option you do need to specify a format. \n| response_data_type | To override the response format for one request \n| query | This is an array with query parameters\n\n\n\n### Changing request format\n\nThe default format when communicating with LinkedIn API is json. You can let the API do `json_encode` for you. \nThe following code shows you how. \n\n```php\n$body = array(\n    'comment' => 'Im testing Happyr LinkedIn client! https://github.com/Happyr/LinkedIn-API-client',\n    'visibility' => array('code' => 'anyone')\n);\n\n$linkedIn->post('v1/people/~/shares', array('json'=>$body));\n$linkedIn->post('v1/people/~/shares', array('body'=>json_encode($body)));\n```\n\nWhen using `array('json'=>$body)` as option the format will always be `json`. You can change the request format in three ways.\n\n```php\n// By constructor argument\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('app_id', 'app_secret', 'xml');\n\n// By setter\n$linkedIn->setFormat('xml');\n\n// Set format for just one request\n$linkedIn->post('v1/people/~/shares', array('format'=>'xml', 'body'=>$body));\n```\n\n\n### Understanding response data type\n\nThe data type returned from `LinkedIn::api` can be configured. You may use the forth construtor argument, the\n`LinkedIn::setResponseDataType` or as an option for `LinkedIn::api`\n\n```php\n// By constructor argument\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('app_id', 'app_secret', 'json', 'array');\n\n// By setter\n$linkedIn->setResponseDataType('simple_xml');\n\n// Set format for just one request\n$linkedIn->get('v1/people/~:(firstName,lastName)', array('response_data_type'=>'psr7'));\n\n```\n\nBelow is a table that specifies what the possible return data types are when you call `LinkedIn::api`.\n\n| Type | Description\n| ------ | ------------\n| array | An assosiative array. This can only be used with the `json` format.\n| simple_xml | A SimpleXMLElement. See [PHP manual](http://php.net/manual/en/class.simplexmlelement.php). This can only be used with the `xml` format.\n| psr7 | A PSR7 response.\n| stream | A file stream.\n| string | A plain old string.\n\n\n### Use different Session classes\n\nYou might want to use an other storage than the default `SessionStorage`. If you are using Laravel\nyou are more likely to inject the `IlluminateSessionStorage`.  \n```php\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('app_id', 'app_secret');\n$linkedIn->setStorage(new IlluminateSessionStorage());\n```\n\nYou can inject any class implementing `DataStorageInterface`. You can also inject different `UrlGenerator` classes.\n\n### Using different scopes\n\nIf you want to define special scopes when you authenticate the user you should specify them when you are generating the \nlogin url. If you don't specify scopes LinkedIn will use the default scopes that you have configured for the app.  \n\n```php\n$scope = 'r_fullprofile,r_emailaddress,w_share';\n//or \n$scope = array('rw_groups', 'r_contactinfo', 'r_fullprofile', 'w_messages');\n\n$url = $linkedIn->getLoginUrl(array('scope'=>$scope));\necho \"<a href='$url'>Login with LinkedIn</a>\";\n```\n\n## Framework integration\n\nIf you want an easier integration with a framwork you may want to check out these repositories: \n\n* [HappyrLinkedInBundle](https://github.com/Happyr/LinkedInBundle) for Symfony\n* [Laravel-Linkedin by mauri870](https://github.com/artesaos/laravel-linkedin) for Laravel 5\n\n\n[register-app]: https://www.linkedin.com/secure/developer\n[linkedin-code-samples]: https://developer.linkedin.com/documents/code-samples\n[api-doc-authentication]: https://developer.linkedin.com/documents/authentication\n[api-doc-core]: https://developer.linkedin.com/core-concepts\n"
  },
  {
    "path": "Upgrade.md",
    "content": "# Upgrade\n\nThis document explains how you upgrade from one version to another. \n\n## Upgrade from 0.7.2 to 1.0\n\n### Changes\n\n* We do not longer require `php-http/message`. You have to make sure to put that in your own composer.json.\n\n## Upgrade from 0.7.1 to 0.7.2\n\n### Changes\n\n* Using `php-http/discovery:1.0`\n* Code style changes.\n\n## Upgrade from 0.7.0 to 0.7.1\n\n### Changes\n\n* Using `php-http/discovery:0.9` which makes Puli optional\n* Using new URL's to LinkedIn API so users are provided with the new authentication UX. (Thanks to @mbarwick83)\n\n## Upgrade from 0.6 to 0.7\n\n### Changes\n\n* Introduced PHP-HTTP and PSR-7 messages\n* Added constructor argument for responseDataType\n* Added setResponseDataType()\n* Moved authentication functions to `Authenticator` class  \n\nTo make sure you can upgrade you need to install a HTTP adapter.\n\n```bash\nphp composer.phar require php-http/guzzle6-adapter\n```\n\n### BC breaks\n\n* Removed `LinkedIn::setRequest` in favor of `LinkedIn::setHttpAdapter`\n* Removed `LinkedIn::getAppSecret` and `LinkedIn::getAppId` \n* Removed `LinkedIn::getUser`\n* Removed `LinkedInApiException` in favor of `LinkedInException`, `InvalidArgumentException` and `LinkedInTransferException` \n* Removed `LinkedIn::getLastHeaders` in favor of `LinkedIn::getLastResponse`\n* Made the public functions `LinkedIn::getResponseDataType` and `LinkedIn::getFormat` protected\n\n## Upgrade from 0.5 to 0.6\n\n### Changes\n\n* When exchanging the code for an access token we are now using the post body instead of query parameters\n* Better error handling when exchange from code to access token fails\n\n### BC breaks\n\nThere are a few minor BC breaks. We removed the functions below: \n\n* `LinkedIn::getUserId`, use `LinkedIn::getUser` instead\n* `AccessToken::constructFromJson`, Use the constructor instead. \n\n## Upgrade from 0.4 to 0.5\n\n### Changed signature of `LinkedIn::api`\n\nThe signature of `LinkedIn::api` has changed to be more easy to work with. \n```php\n// Version 0.4\npublic function api($resource, array $urlParams=array(), $method='GET', $postParams=array())\n\n// Version 0.5\npublic function api($method, $resource, array $options=array())\n```\n\nThis means that you have to modify your calls to: \n```php\n// Version 0.5\n$options = array('query'=>$urlParams, 'body'=>$postParams);\n$linkedIn->api('POST', $resource, $options)\n```\nSee the Readme about more options to the API function. \n\n### Must inject IlluminateSessionStorage\n\nWe have removed the protected `LinkedIn::init` function. That means if you were using `IlluminateSessionStorage` you have\nto make a minor adjustment to your code. \n\n```php\n// Version 0.4\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('app_id', 'app_secret');\n\n// Version 0.5\n$linkedIn=new Happyr\\LinkedIn\\LinkedIn('app_id', 'app_secret');\n$linkedIn->setStorage(new IlluminateSessionStorage());\n```\n\nIf you don't know about `IlluminateSessionStorage` you are probably good ignoring this. \n\n### Default format \n\nThe default format when communicating with LinkedIn API is changed to  json. \n\n### Updated RequestInterface\n\nThe `RequestInterface::send` was updated with a new signature. We did also introduce `RequestInterface::getHeadersFromLastResponse`. \n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"happyr/linkedin-api-client\",\n    \"type\": \"library\",\n    \"description\": \"LinkedIn API client. Handles OAuth, CSRF protection. Easy to implement and extend. This is a standalone library for any composer project.\",\n    \"keywords\": [\"LinkedIn\", \"OAuth\", \"API\", \"Client\", \"SDK\"],\n    \"homepage\": \"http://developer.happyr.com/libraries/linkedin-php-client\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Tobias Nyholm\",\n            \"email\": \"tobias@happyr.com\"\n        }\n    ],\n    \"require\": {\n        \"php\": \"^5.5 || ^7.0\",\n        \"php-http/client-implementation\": \"^1.0\",\n        \"php-http/httplug\": \"^1.0\",\n        \"php-http/message-factory\": \"^1.0\",\n        \"php-http/discovery\": \"^1.0\"\n    },\n    \"require-dev\": {\n        \"phpunit/phpunit\": \"^4.5 || ^5.0\",\n        \"php-http/guzzle5-adapter\": \"^1.0\",\n        \"guzzlehttp/psr7\": \"^1.2\",\n        \"mockery/mockery\": \"^0.9\",\n        \"illuminate/support\": \"^5.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": { \"Happyr\\\\LinkedIn\\\\\": \"src/\" }\n    },\n    \"scripts\": {\n        \"test\": \"vendor/bin/phpunit\",\n        \"test-ci\": \"vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml\"\n    },\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-master\": \"1.1.x-dev\"\n        }\n    }\n}\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit backupGlobals=\"false\"\n         backupStaticAttributes=\"false\"\n         colors=\"false\"\n         convertErrorsToExceptions=\"true\"\n         convertNoticesToExceptions=\"true\"\n         convertWarningsToExceptions=\"true\"\n         processIsolation=\"false\"\n         stopOnFailure=\"false\"\n         syntaxCheck=\"false\"\n         codecoverage= \"true\"\n         bootstrap=\"./vendor/autoload.php\"\n        >\n\n    <formatter type=\"clover\" usefile=\"false\" />\n\n    <testsuites>\n        <testsuite name=\"Happyr Api Client Test Suite\">\n            <directory>./tests</directory>\n        </testsuite>\n    </testsuites>\n\n    <listeners>\n        <listener class=\"\\Mockery\\Adapter\\Phpunit\\TestListener\" file=\"vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php\">\n        </listener>\n    </listeners>\n\n    <filter>\n        <whitelist>\n            <directory suffix=\".php\">src</directory>\n            <exclude>\n                <directory>vendor</directory>\n                <directory>tests</directory>\n            </exclude>\n        </whitelist>\n    </filter>\n\n    <logging>\n        <log type=\"coverage-clover\" target=\"build/logs/phpunit.coverage.xml\"/>\n        <log type=\"junit\" target=\"build/logs/phpunit.xml\" logIncompleteSkipped=\"false\"/>\n    </logging>\n\n</phpunit>\n"
  },
  {
    "path": "src/AccessToken.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\n/**\n * @author Tobias Nyholm\n */\nclass AccessToken\n{\n    /**\n     * @var null|string token\n     */\n    private $token;\n\n    /**\n     * @var \\DateTime expiresAt\n     */\n    private $expiresAt;\n\n    /**\n     * @param string        $token\n     * @param \\DateTime|int $expiresIn\n     */\n    public function __construct($token = null, $expiresIn = null)\n    {\n        $this->token = $token;\n\n        if ($expiresIn !== null) {\n            if ($expiresIn instanceof \\DateTime) {\n                $this->expiresAt = $expiresIn;\n            } else {\n                $this->expiresAt = new \\DateTime(sprintf('+%dseconds', $expiresIn));\n            }\n        }\n    }\n\n    /**\n     * @return string\n     */\n    public function __toString()\n    {\n        return $this->token ?: '';\n    }\n\n    /**\n     * Does a token string exist?\n     *\n     * @return bool\n     */\n    public function hasToken()\n    {\n        return !empty($this->token);\n    }\n\n    /**\n     * @param \\DateTime $expiresAt\n     *\n     * @return $this\n     */\n    public function setExpiresAt(\\DateTime $expiresAt = null)\n    {\n        $this->expiresAt = $expiresAt;\n\n        return $this;\n    }\n\n    /**\n     * @return \\DateTime\n     */\n    public function getExpiresAt()\n    {\n        return $this->expiresAt;\n    }\n\n    /**\n     * @param null|string $token\n     *\n     * @return $this\n     */\n    public function setToken($token)\n    {\n        $this->token = $token;\n\n        return $this;\n    }\n\n    /**\n     * @return null|string\n     */\n    public function getToken()\n    {\n        return $this->token;\n    }\n}\n"
  },
  {
    "path": "src/Authenticator.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInTransferException;\nuse Happyr\\LinkedIn\\Exception\\LinkedInException;\nuse Happyr\\LinkedIn\\Http\\GlobalVariableGetter;\nuse Happyr\\LinkedIn\\Http\\LinkedInUrlGeneratorInterface;\nuse Happyr\\LinkedIn\\Http\\RequestManager;\nuse Happyr\\LinkedIn\\Http\\RequestManagerInterface;\nuse Happyr\\LinkedIn\\Http\\ResponseConverter;\nuse Happyr\\LinkedIn\\Storage\\DataStorageInterface;\nuse Happyr\\LinkedIn\\Storage\\SessionStorage;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass Authenticator implements AuthenticatorInterface\n{\n    /**\n     * The application ID.\n     *\n     * @var string\n     */\n    protected $appId;\n\n    /**\n     * The application secret.\n     *\n     * @var string\n     */\n    protected $appSecret;\n\n    /**\n     * A storage to use to store data between requests.\n     *\n     * @var DataStorageInterface storage\n     */\n    private $storage;\n\n    /**\n     * @var RequestManagerInterface\n     */\n    private $requestManager;\n\n    /**\n     * @param RequestManagerInterface $requestManager\n     * @param string                  $appId\n     * @param string                  $appSecret\n     */\n    public function __construct(RequestManagerInterface $requestManager, $appId, $appSecret)\n    {\n        $this->appId = $appId;\n        $this->appSecret = $appSecret;\n        $this->requestManager = $requestManager;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function fetchNewAccessToken(LinkedInUrlGeneratorInterface $urlGenerator)\n    {\n        $storage = $this->getStorage();\n        $code = $this->getCode();\n\n        if ($code === null) {\n            /*\n             * As a fallback, just return whatever is in the persistent\n             * store, knowing nothing explicit (signed request, authorization\n             *  code, etc.) was present to shadow it.\n             */\n            return $storage->get('access_token');\n        }\n\n        try {\n            $accessToken = $this->getAccessTokenFromCode($urlGenerator, $code);\n        } catch (LinkedInException $e) {\n            // code was bogus, so everything based on it should be invalidated.\n            $storage->clearAll();\n            throw $e;\n        }\n\n        $storage->set('code', $code);\n        $storage->set('access_token', $accessToken);\n\n        return $accessToken;\n    }\n\n    /**\n     * Retrieves an access token for the given authorization code\n     * (previously generated from www.linkedin.com on behalf of\n     * a specific user). The authorization code is sent to www.linkedin.com\n     * and a legitimate access token is generated provided the access token\n     * and the user for which it was generated all match, and the user is\n     * either logged in to LinkedIn or has granted an offline access permission.\n     *\n     * @param LinkedInUrlGeneratorInterface $urlGenerator\n     * @param string                        $code         An authorization code.\n     *\n     * @return AccessToken An access token exchanged for the authorization code.\n     *\n     * @throws LinkedInException\n     */\n    protected function getAccessTokenFromCode(LinkedInUrlGeneratorInterface $urlGenerator, $code)\n    {\n        if (empty($code)) {\n            throw new LinkedInException('Could not get access token: The code was empty.');\n        }\n\n        $redirectUri = $this->getStorage()->get('redirect_uri');\n        try {\n            $url = $urlGenerator->getUrl('www', 'oauth/v2/accessToken');\n            $headers = ['Content-Type' => 'application/x-www-form-urlencoded'];\n            $body = http_build_query(\n                [\n                    'grant_type' => 'authorization_code',\n                    'code' => $code,\n                    'redirect_uri' => $redirectUri,\n                    'client_id' => $this->appId,\n                    'client_secret' => $this->appSecret,\n                ]\n            );\n\n            $response = ResponseConverter::convertToArray($this->getRequestManager()->sendRequest('POST', $url, $headers, $body));\n        } catch (LinkedInTransferException $e) {\n            // most likely that user very recently revoked authorization.\n            // In any event, we don't have an access token, so throw an exception.\n            throw new LinkedInException('Could not get access token: The user may have revoked the authorization response from LinkedIn.com was empty.', $e->getCode(), $e);\n        }\n\n        if (empty($response)) {\n            throw new LinkedInException('Could not get access token: The response from LinkedIn.com was empty.');\n        }\n\n        $tokenData = array_merge(['access_token' => null, 'expires_in' => null], $response);\n        $token = new AccessToken($tokenData['access_token'], $tokenData['expires_in']);\n\n        if (!$token->hasToken()) {\n            throw new LinkedInException('Could not get access token: The response from LinkedIn.com did not contain a token.');\n        }\n\n        return $token;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getLoginUrl(LinkedInUrlGeneratorInterface $urlGenerator, $options = [])\n    {\n        // Generate a state\n        $this->establishCSRFTokenState();\n\n        // Build request params\n        $requestParams = array_merge([\n            'response_type' => 'code',\n            'client_id' => $this->appId,\n            'state' => $this->getStorage()->get('state'),\n            'redirect_uri' => null,\n        ], $options);\n\n        // Save the redirect url for later\n        $this->getStorage()->set('redirect_uri', $requestParams['redirect_uri']);\n\n        // if 'scope' is passed as an array, convert to space separated list\n        $scopeParams = isset($options['scope']) ? $options['scope'] : null;\n        if ($scopeParams) {\n            //if scope is an array\n            if (is_array($scopeParams)) {\n                $requestParams['scope'] = implode(' ', $scopeParams);\n            } elseif (is_string($scopeParams)) {\n                //if scope is a string with ',' => make it to an array\n                $requestParams['scope'] = str_replace(',', ' ', $scopeParams);\n            }\n        }\n\n        return $urlGenerator->getUrl('www', 'oauth/v2/authorization', $requestParams);\n    }\n\n    /**\n     * Get the authorization code from the query parameters, if it exists,\n     * and otherwise return null to signal no authorization code was\n     * discovered.\n     *\n     * @return string|null The authorization code, or null if the authorization code not exists.\n     *\n     * @throws LinkedInException on invalid CSRF tokens\n     */\n    protected function getCode()\n    {\n        $storage = $this->getStorage();\n\n        if (!GlobalVariableGetter::has('code')) {\n            return;\n        }\n\n        if ($storage->get('code') === GlobalVariableGetter::get('code')) {\n            //we have already validated this code\n            return;\n        }\n\n        // if stored state does not exists\n        if (null === $state = $storage->get('state')) {\n            throw new LinkedInException('Could not find a stored CSRF state token.');\n        }\n\n        // if state not exists in the request\n        if (!GlobalVariableGetter::has('state')) {\n            throw new LinkedInException('Could not find a CSRF state token in the request.');\n        }\n\n        // if state exists in session and in request and if they are not equal\n        if ($state !== GlobalVariableGetter::get('state')) {\n            throw new LinkedInException('The CSRF state token from the request does not match the stored token.');\n        }\n\n        // CSRF state has done its job, so clear it\n        $storage->clear('state');\n\n        return GlobalVariableGetter::get('code');\n    }\n\n    /**\n     * Lays down a CSRF state token for this process.\n     */\n    protected function establishCSRFTokenState()\n    {\n        $storage = $this->getStorage();\n        if ($storage->get('state') === null) {\n            $storage->set('state', md5(uniqid(mt_rand(), true)));\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clearStorage()\n    {\n        $this->getStorage()->clearAll();\n\n        return $this;\n    }\n\n    /**\n     * @return DataStorageInterface\n     */\n    protected function getStorage()\n    {\n        if ($this->storage === null) {\n            $this->storage = new SessionStorage();\n        }\n\n        return $this->storage;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setStorage(DataStorageInterface $storage)\n    {\n        $this->storage = $storage;\n\n        return $this;\n    }\n\n    /**\n     * @return RequestManager\n     */\n    protected function getRequestManager()\n    {\n        return $this->requestManager;\n    }\n}\n"
  },
  {
    "path": "src/AuthenticatorInterface.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInException;\nuse Happyr\\LinkedIn\\Http\\LinkedInUrlGeneratorInterface;\nuse Happyr\\LinkedIn\\Storage\\DataStorageInterface;\n\n/**\n * This interface is responsible for the authentication process with LinkedIn.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\ninterface AuthenticatorInterface\n{\n    /**\n     * Tries to get a new access token from data storage or code. If it fails, it will return null.\n     *\n     * @param LinkedInUrlGeneratorInterface $urlGenerator\n     *\n     * @return AccessToken|null A valid user access token, or null if one could not be fetched.\n     *\n     * @throws LinkedInException\n     */\n    public function fetchNewAccessToken(LinkedInUrlGeneratorInterface $urlGenerator);\n\n    /**\n     * Generate a login url.\n     *\n     * @param LinkedInUrlGeneratorInterface $urlGenerator\n     * @param array                         $options\n     *\n     * @return string\n     */\n    public function getLoginUrl(LinkedInUrlGeneratorInterface $urlGenerator, $options = []);\n\n    /**\n     * Clear the storage.\n     *\n     * @return $this\n     */\n    public function clearStorage();\n\n    /**\n     * @param DataStorageInterface $storage\n     *\n     * @return $this\n     */\n    public function setStorage(DataStorageInterface $storage);\n}\n"
  },
  {
    "path": "src/Exception/InvalidArgumentException.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\nclass InvalidArgumentException extends LinkedInException\n{\n    /**\n     * Treat this constructor as sprintf().\n     */\n    public function __construct()\n    {\n        parent::__construct(call_user_func_array('sprintf', func_get_args()));\n    }\n}\n"
  },
  {
    "path": "src/Exception/LinkedInException.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * This is the base exception of this library.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass LinkedInException extends \\Exception\n{\n}\n"
  },
  {
    "path": "src/Exception/LinkedInTransferException.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * An exception with thrown by unexpected result form the remote API.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass LinkedInTransferException extends LinkedInException\n{\n}\n"
  },
  {
    "path": "src/Exception/LoginError.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * Class LoginError.\n *\n * @author Tobias Nyholm\n */\nclass LoginError\n{\n    /**\n     * @var string name\n     */\n    protected $name;\n\n    /**\n     * @var string description\n     */\n    protected $description;\n\n    /**\n     * @param string $name\n     * @param string $description\n     */\n    public function __construct($name, $description)\n    {\n        $this->name = $name;\n        $this->description = $description;\n    }\n\n    /**\n     * @return string\n     */\n    public function getName()\n    {\n        return $this->name;\n    }\n\n    /**\n     * @return string\n     */\n    public function getDescription()\n    {\n        return $this->description;\n    }\n\n    /**\n     * @return string\n     */\n    public function __toString()\n    {\n        return sprintf('Name: %s, Description: %s', $this->getName(), $this->getDescription());\n    }\n}\n"
  },
  {
    "path": "src/Http/CurrentUrlGeneratorInterface.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * An interface to get the current URL.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\ninterface CurrentUrlGeneratorInterface\n{\n    /**\n     * Returns the current URL.\n     *\n     * @return string The current URL\n     */\n    public function getCurrentUrl();\n\n    /**\n     * Should we trust forwarded headers?\n     *\n     * @param bool $trustForwarded\n     *\n     * @return $this\n     */\n    public function setTrustForwarded($trustForwarded);\n}\n"
  },
  {
    "path": "src/Http/GlobalVariableGetter.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * Look in $_REQUEST and $_GET if there is a variable we want.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass GlobalVariableGetter\n{\n    /**\n     * Returns true iff the $_REQUEST or $_GET variables has a key with $name.\n     *\n     * @param string $name\n     *\n     * @return bool\n     */\n    public static function has($name)\n    {\n        if (isset($_REQUEST[$name])) {\n            return true;\n        }\n\n        return isset($_GET[$name]);\n    }\n\n    /**\n     * Returns the value in $_REQUEST[$name] or $_GET[$name] if the former was empty. If no value found, return null.\n     *\n     * @param string $name\n     *\n     * @return mixed|null\n     */\n    public static function get($name)\n    {\n        if (isset($_REQUEST[$name])) {\n            return $_REQUEST[$name];\n        }\n\n        if (isset($_GET[$name])) {\n            return $_GET[$name];\n        }\n\n        return;\n    }\n}\n"
  },
  {
    "path": "src/Http/LinkedInUrlGeneratorInterface.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * An interface to generate LinkedIn specific urls.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\ninterface LinkedInUrlGeneratorInterface\n{\n    /**\n     * Build the URL for given domain alias, path and parameters.\n     *\n     * @param $name string The name of the domain, 'www' or 'api'\n     * @param $path string without a leading slash\n     * @param $params array query parameters\n     *\n     * @return string The URL for the given parameters. The URL query  MUST be build with PHP_QUERY_RFC3986\n     */\n    public function getUrl($name, $path = '', $params = []);\n}\n"
  },
  {
    "path": "src/Http/RequestManager.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInTransferException;\nuse Http\\Client\\Exception\\TransferException;\nuse Http\\Client\\HttpClient;\nuse Http\\Discovery\\HttpClientDiscovery;\nuse Http\\Discovery\\MessageFactoryDiscovery;\nuse Http\\Message\\MessageFactory;\n\n/**\n * A class to create HTTP requests and to send them.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass RequestManager implements RequestManagerInterface\n{\n    /**\n     * @var \\Http\\Client\\HttpClient\n     */\n    private $httpClient;\n\n    /**\n     * @var \\Http\\Message\\MessageFactory\n     */\n    private $messageFactory;\n\n    /**\n     * {@inheritdoc}\n     */\n    public function sendRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1')\n    {\n        $request = $this->getMessageFactory()->createRequest($method, $uri, $headers, $body, $protocolVersion);\n\n        try {\n            return $this->getHttpClient()->sendRequest($request);\n        } catch (TransferException $e) {\n            throw new LinkedInTransferException('Error while requesting data from LinkedIn.com: '.$e->getMessage(), $e->getCode(), $e);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setHttpClient(HttpClient $httpClient)\n    {\n        $this->httpClient = $httpClient;\n\n        return $this;\n    }\n\n    /**\n     * @return HttpClient\n     */\n    protected function getHttpClient()\n    {\n        if ($this->httpClient === null) {\n            $this->httpClient = HttpClientDiscovery::find();\n        }\n\n        return $this->httpClient;\n    }\n\n    /**\n     * @param MessageFactory $messageFactory\n     *\n     * @return RequestManager\n     */\n    public function setMessageFactory(MessageFactory $messageFactory)\n    {\n        $this->messageFactory = $messageFactory;\n\n        return $this;\n    }\n\n    /**\n     * @return \\Http\\Message\\MessageFactory\n     */\n    private function getMessageFactory()\n    {\n        if ($this->messageFactory === null) {\n            $this->messageFactory = MessageFactoryDiscovery::find();\n        }\n\n        return $this->messageFactory;\n    }\n}\n"
  },
  {
    "path": "src/Http/RequestManagerInterface.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInTransferException;\nuse Http\\Client\\HttpClient;\n\n/**\n * A request manager builds a request.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\ninterface RequestManagerInterface\n{\n    /**\n     * Send a request.\n     *\n     * @param string $method\n     * @param string $uri\n     * @param array  $headers\n     * @param string $body\n     * @param string $protocolVersion\n     *\n     * @return \\Psr\\Http\\Message\\ResponseInterface\n     *\n     * @throws LinkedInTransferException\n     */\n    public function sendRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1');\n\n    /**\n     * @param \\Http\\Client\\HttpClient $httpClient\n     *\n     * @return RequestManager\n     */\n    public function setHttpClient(HttpClient $httpClient);\n}\n"
  },
  {
    "path": "src/Http/ResponseConverter.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse Happyr\\LinkedIn\\Exception\\InvalidArgumentException;\nuse Happyr\\LinkedIn\\Exception\\LinkedInTransferException;\nuse Psr\\Http\\Message\\ResponseInterface;\n\nclass ResponseConverter\n{\n    /**\n     * Convert a PSR-7 response to a data type you want to work with.\n     *\n     * @param ResponseInterface $response\n     * @param string            $requestFormat\n     * @param string            $dataType\n     *\n     * @return ResponseInterface|\\Psr\\Http\\Message\\StreamInterface|\\SimpleXMLElement|string\n     *\n     * @throws InvalidArgumentException\n     * @throws LinkedInTransferException\n     */\n    public static function convert(ResponseInterface $response, $requestFormat, $dataType)\n    {\n        if (($requestFormat === 'json' && $dataType === 'simple_xml') ||\n            ($requestFormat === 'xml' && $dataType === 'array')) {\n            throw new InvalidArgumentException('Can not use reponse data format \"%s\" with the request format \"%s\".', $dataType, $requestFormat);\n        }\n\n        switch ($dataType) {\n            case 'array':\n                return self::convertToArray($response);\n            case 'string':\n                return $response->getBody()->__toString();\n            case 'simple_xml':\n                return self::convertToSimpleXml($response);\n            case 'stream':\n                return $response->getBody();\n            case 'psr7':\n                return $response;\n            default:\n                throw new InvalidArgumentException('Format \"%s\" is not supported', $dataType);\n        }\n    }\n\n    /**\n     * @param ResponseInterface $response\n     *\n     * @return string\n     */\n    public static function convertToArray(ResponseInterface $response)\n    {\n        return json_decode($response->getBody(), true);\n    }\n\n    /**\n     * @param ResponseInterface $response\n     *\n     * @return \\SimpleXMLElement\n     *\n     * @throws LinkedInTransferException\n     */\n    public static function convertToSimpleXml(ResponseInterface $response)\n    {\n        $body = $response->getBody();\n        try {\n            return new \\SimpleXMLElement((string) $body ?: '<root />');\n        } catch (\\Exception $e) {\n            throw new LinkedInTransferException('Unable to parse response body into XML.');\n        }\n    }\n}\n"
  },
  {
    "path": "src/Http/UrlGenerator.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass UrlGenerator implements UrlGeneratorInterface\n{\n    /**\n     * @var array knownLinkedInParams\n     *\n     * A list of params that might be in the query string\n     */\n    public static $knownLinkedInParams = ['state', 'code', 'access_token', 'user'];\n\n    /**\n     * @var array domainMap\n     *\n     * Maps aliases to LinkedIn domains.\n     */\n    public static $domainMap = [\n        'api' => 'https://api.linkedin.com/',\n        'www' => 'https://www.linkedin.com/',\n    ];\n\n    /**\n     * @var bool\n     *\n     * Indicates if we trust HTTP_X_FORWARDED_* headers.\n     */\n    protected $trustForwarded = false;\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getUrl($name, $path = '', $params = [])\n    {\n        $url = self::$domainMap[$name];\n        if ($path) {\n            if ($path[0] === '/') {\n                $path = substr($path, 1);\n            }\n            $url .= $path;\n        }\n\n        if (!empty($params)) {\n            // does it exist a query string?\n            $queryString = parse_url($url, PHP_URL_QUERY);\n            if (empty($queryString)) {\n                $url .= '?';\n            } else {\n                $url .= '&';\n            }\n\n            // it needs to be PHP_QUERY_RFC3986. We want to have %20 between scopes\n            $url .= http_build_query($params, null, '&', PHP_QUERY_RFC3986);\n        }\n\n        return $url;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getCurrentUrl()\n    {\n        $protocol = $this->getHttpProtocol().'://';\n        $host = $this->getHttpHost();\n        $currentUrl = $protocol.$host.$_SERVER['REQUEST_URI'];\n        $parts = parse_url($currentUrl);\n\n        $query = '';\n        if (!empty($parts['query'])) {\n            // drop known linkedin params\n            $query = $this->dropLinkedInParams($parts['query']);\n        }\n\n        // use port if non default\n        $port =\n            isset($parts['port']) &&\n            (($protocol === 'http://' && $parts['port'] !== 80) ||\n                ($protocol === 'https://' && $parts['port'] !== 443))\n                ? ':'.$parts['port'] : '';\n\n        // rebuild\n        return $protocol.$parts['host'].$port.$parts['path'].$query;\n    }\n\n    /**\n     * Drop known LinkedIn params. Ie those in self::$knownLinkeInParams.\n     *\n     * @param string $query\n     *\n     * @return string query without LinkedIn params. This string is prepended with a question mark '?'\n     */\n    protected function dropLinkedInParams($query)\n    {\n        if ($query == '') {\n            return '';\n        }\n\n        $params = explode('&', $query);\n        foreach ($params as $i => $param) {\n            /*\n             * A key or key/value pair might me 'foo=bar', 'foo=', or 'foo'.\n             */\n            //get the first value of the array you will get when you explode()\n            list($key) = explode('=', $param, 2);\n            if (in_array($key, self::$knownLinkedInParams)) {\n                unset($params[$i]);\n            }\n        }\n\n        //assert: params is an array. It might be empty\n        if (!empty($params)) {\n            return '?'.implode($params, '&');\n        }\n\n        return '';\n    }\n\n    /**\n     * Get the host.\n     *\n     *\n     * @return mixed\n     */\n    protected function getHttpHost()\n    {\n        if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {\n            return $_SERVER['HTTP_X_FORWARDED_HOST'];\n        }\n\n        return $_SERVER['HTTP_HOST'];\n    }\n\n    /**\n     * Get the protocol.\n     *\n     *\n     * @return string\n     */\n    protected function getHttpProtocol()\n    {\n        if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {\n            if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {\n                return 'https';\n            }\n\n            return 'http';\n        }\n\n        /*apache + variants specific way of checking for https*/\n        if (isset($_SERVER['HTTPS']) &&\n            ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1)) {\n            return 'https';\n        }\n\n        /*nginx way of checking for https*/\n        if (isset($_SERVER['SERVER_PORT']) &&\n            ($_SERVER['SERVER_PORT'] === '443')) {\n            return 'https';\n        }\n\n        return 'http';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setTrustForwarded($trustForwarded)\n    {\n        $this->trustForwarded = $trustForwarded;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "src/Http/UrlGeneratorInterface.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\ninterface UrlGeneratorInterface extends LinkedInUrlGeneratorInterface, CurrentUrlGeneratorInterface\n{\n}\n"
  },
  {
    "path": "src/LinkedIn.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LoginError;\nuse Happyr\\LinkedIn\\Http\\GlobalVariableGetter;\nuse Happyr\\LinkedIn\\Http\\RequestManager;\nuse Happyr\\LinkedIn\\Http\\ResponseConverter;\nuse Happyr\\LinkedIn\\Http\\UrlGenerator;\nuse Happyr\\LinkedIn\\Http\\UrlGeneratorInterface;\nuse Happyr\\LinkedIn\\Storage\\DataStorageInterface;\nuse Http\\Client\\HttpClient;\nuse Http\\Message\\MessageFactory;\nuse Psr\\Http\\Message\\ResponseInterface;\n\n/**\n * Class LinkedIn lets you talk to LinkedIn api.\n *\n * When a new user arrives and want to authenticate here is whats happens:\n * 1. You redirect him to whatever url getLoginUrl() returns.\n * 2. The user logs in on www.linkedin.com and authorize your application.\n * 3. The user returns to your site with a *code* in the the $_REQUEST.\n * 4. You call isAuthenticated() or getAccessToken()\n * 5. If we don't have an access token (only a *code*), getAccessToken() will call fetchNewAccessToken()\n * 6. fetchNewAccessToken() gets the *code* from the $_REQUEST and calls getAccessTokenFromCode()\n * 7. getAccessTokenFromCode() makes a request to www.linkedin.com and exchanges the *code* for an access token\n * 8. When you have the access token you should store it in a database and/or query the API.\n * 9. When you make a second request to the API we have the access token in memory, so we don't go through all these\n *    authentication steps again.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass LinkedIn implements LinkedInInterface\n{\n    /**\n     * The OAuth access token received in exchange for a valid authorization\n     * code.  null means the access token has yet to be determined.\n     *\n     * @var AccessToken\n     */\n    protected $accessToken = null;\n\n    /**\n     * @var string format\n     */\n    private $format;\n\n    /**\n     * @var string responseFormat\n     */\n    private $responseDataType;\n\n    /**\n     * @var ResponseInterface\n     */\n    private $lastResponse;\n\n    /**\n     * @var RequestManager\n     */\n    private $requestManager;\n\n    /**\n     * @var Authenticator\n     */\n    private $authenticator;\n\n    /**\n     * @var UrlGeneratorInterface\n     */\n    private $urlGenerator;\n\n    /**\n     * Constructor.\n     *\n     * @param string $appId\n     * @param string $appSecret\n     * @param string $format           'json', 'xml'\n     * @param string $responseDataType 'array', 'string', 'simple_xml' 'psr7', 'stream'\n     */\n    public function __construct($appId, $appSecret, $format = 'json', $responseDataType = 'array')\n    {\n        $this->format = $format;\n        $this->responseDataType = $responseDataType;\n\n        $this->requestManager = new RequestManager();\n        $this->authenticator = new Authenticator($this->requestManager, $appId, $appSecret);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function isAuthenticated()\n    {\n        $accessToken = $this->getAccessToken();\n        if ($accessToken === null) {\n            return false;\n        }\n\n        $user = $this->api('GET', '/v1/people/~:(id,firstName,lastName)', ['format' => 'json', 'response_data_type' => 'array']);\n\n        return !empty($user['id']);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function api($method, $resource, array $options = [])\n    {\n        // Add access token to the headers\n        $options['headers']['Authorization'] = sprintf('Bearer %s', (string) $this->getAccessToken());\n\n        // Do logic and adjustments to the options\n        $requestFormat = $this->filterRequestOption($options);\n\n        // Generate an url\n        $url = $this->getUrlGenerator()->getUrl(\n            'api',\n            $resource,\n            isset($options['query']) ? $options['query'] : []\n        );\n\n        $body = isset($options['body']) ? $options['body'] : null;\n        $this->lastResponse = $this->getRequestManager()->sendRequest($method, $url, $options['headers'], $body);\n\n        //Get the response data format\n        if (isset($options['response_data_type'])) {\n            $responseDataType = $options['response_data_type'];\n        } else {\n            $responseDataType = $this->getResponseDataType();\n        }\n\n        return ResponseConverter::convert($this->lastResponse, $requestFormat, $responseDataType);\n    }\n\n    /**\n     * Modify and filter the request options. Make sure we use the correct query parameters and headers.\n     *\n     * @param array &$options\n     *\n     * @return string the request format to use\n     */\n    protected function filterRequestOption(array &$options)\n    {\n        if (isset($options['json'])) {\n            $options['format'] = 'json';\n            $options['body'] = json_encode($options['json']);\n        } elseif (!isset($options['format'])) {\n            // Make sure we always have a format\n            $options['format'] = $this->getFormat();\n        }\n\n        // Set correct headers for this format\n        switch ($options['format']) {\n            case 'xml':\n                $options['headers']['Content-Type'] = 'text/xml';\n                break;\n            case 'json':\n                $options['headers']['Content-Type'] = 'application/json';\n                $options['headers']['x-li-format'] = 'json';\n                $options['query']['format'] = 'json';\n                break;\n            default:\n                // Do nothing\n        }\n\n        return $options['format'];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getLoginUrl($options = [])\n    {\n        $urlGenerator = $this->getUrlGenerator();\n\n        // Set redirect_uri to current URL if not defined\n        if (!isset($options['redirect_uri'])) {\n            $options['redirect_uri'] = $urlGenerator->getCurrentUrl();\n        }\n\n        return $this->getAuthenticator()->getLoginUrl($urlGenerator, $options);\n    }\n\n    /**\n     * See docs for LinkedIn::api().\n     *\n     * @param string $resource\n     * @param array  $options\n     *\n     * @return mixed\n     */\n    public function get($resource, array $options = [])\n    {\n        return $this->api('GET', $resource, $options);\n    }\n\n    /**\n     * See docs for LinkedIn::api().\n     *\n     * @param string $resource\n     * @param array  $options\n     *\n     * @return mixed\n     */\n    public function post($resource, array $options = [])\n    {\n        return $this->api('POST', $resource, $options);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clearStorage()\n    {\n        $this->getAuthenticator()->clearStorage();\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function hasError()\n    {\n        return GlobalVariableGetter::has('error');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getError()\n    {\n        if ($this->hasError()) {\n            return new LoginError(GlobalVariableGetter::get('error'), GlobalVariableGetter::get('error_description'));\n        }\n    }\n\n    /**\n     * Get the default format to use when sending requests.\n     *\n     * @return string\n     */\n    protected function getFormat()\n    {\n        return $this->format;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setFormat($format)\n    {\n        $this->format = $format;\n\n        return $this;\n    }\n\n    /**\n     * Get the default data type to be returned as a response.\n     *\n     * @return string\n     */\n    protected function getResponseDataType()\n    {\n        return $this->responseDataType;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setResponseDataType($responseDataType)\n    {\n        $this->responseDataType = $responseDataType;\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getLastResponse()\n    {\n        return $this->lastResponse;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getAccessToken()\n    {\n        if ($this->accessToken === null) {\n            if (null !== $newAccessToken = $this->getAuthenticator()->fetchNewAccessToken($this->getUrlGenerator())) {\n                $this->setAccessToken($newAccessToken);\n            }\n        }\n\n        // return the new access token or null if none found\n        return $this->accessToken;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setAccessToken($accessToken)\n    {\n        if (!($accessToken instanceof AccessToken)) {\n            $accessToken = new AccessToken($accessToken);\n        }\n\n        $this->accessToken = $accessToken;\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setUrlGenerator(UrlGeneratorInterface $urlGenerator)\n    {\n        $this->urlGenerator = $urlGenerator;\n\n        return $this;\n    }\n\n    /**\n     * @return UrlGeneratorInterface\n     */\n    protected function getUrlGenerator()\n    {\n        if ($this->urlGenerator === null) {\n            $this->urlGenerator = new UrlGenerator();\n        }\n\n        return $this->urlGenerator;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setStorage(DataStorageInterface $storage)\n    {\n        $this->getAuthenticator()->setStorage($storage);\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setHttpClient(HttpClient $client)\n    {\n        $this->getRequestManager()->setHttpClient($client);\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setHttpMessageFactory(MessageFactory $factory)\n    {\n        $this->getRequestManager()->setMessageFactory($factory);\n\n        return $this;\n    }\n\n    /**\n     * @return RequestManager\n     */\n    protected function getRequestManager()\n    {\n        return $this->requestManager;\n    }\n\n    /**\n     * @return Authenticator\n     */\n    protected function getAuthenticator()\n    {\n        return $this->authenticator;\n    }\n}\n"
  },
  {
    "path": "src/LinkedInInterface.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LoginError;\nuse Happyr\\LinkedIn\\Http\\UrlGeneratorInterface;\nuse Happyr\\LinkedIn\\Storage\\DataStorageInterface;\nuse Http\\Client\\HttpClient;\nuse Http\\Message\\MessageFactory;\nuse Psr\\Http\\Message\\ResponseInterface;\n\n/**\n * Interface that lets you talk to LinkedIn api.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\ninterface LinkedInInterface\n{\n    /**\n     * Is the current user authenticated?\n     *\n     * @return bool\n     */\n    public function isAuthenticated();\n\n    /**\n     * Make an API call. Read about what calls that are possible here: https://developer.linkedin.com/docs/rest-api.\n     *\n     * Example:\n     * $linkedIn->api('GET', '/v1/people/~:(id,firstName,lastName,headline)');\n     *\n     * The options:\n     * - body: the body of the request\n     * - format: the format you are using to send the request\n     * - headers: array with headers to use\n     * - response_data_type: the data type to get back\n     * - query: query parameters to the request\n     *\n     * @param string $method   This is the HTTP verb\n     * @param string $resource everything after the domain in the URL.\n     * @param array  $options  See the readme for option description.\n     *\n     * @return mixed this depends on the response_data_type parameter.\n     */\n    public function api($method, $resource, array $options = []);\n\n    /**\n     * Get a login URL where the user can put his/hers LinkedIn credentials and authorize the application.\n     *\n     * The options:\n     * - redirect_uri: the url to go to after a successful login\n     * - scope: comma (or space) separated list of requested extended permissions\n     *\n     * @param array $options Provide custom parameters\n     *\n     * @return string The URL for the login flow\n     */\n    public function getLoginUrl($options = []);\n\n    /**\n     * See docs for LinkedIn::api().\n     *\n     * @param string $resource\n     * @param array  $options\n     *\n     * @return mixed\n     */\n    public function get($resource, array $options = []);\n\n    /**\n     * See docs for LinkedIn::api().\n     *\n     * @param string $resource\n     * @param array  $options\n     *\n     * @return mixed\n     */\n    public function post($resource, array $options = []);\n\n    /**\n     * Clear the data storage. This will forget everything about the user and authentication process.\n     *\n     * @return $this\n     */\n    public function clearStorage();\n\n    /**\n     * If the user has canceled the login we will return with an error.\n     *\n     * @return bool\n     */\n    public function hasError();\n\n    /**\n     * Returns a LoginError or null.\n     *\n     * @return LoginError|null\n     */\n    public function getError();\n\n    /**\n     * Set the default format to use when sending requests.\n     *\n     * @param string $format\n     *\n     * @return $this\n     */\n    public function setFormat($format);\n\n    /**\n     * Set the default data type to be returned as a response.\n     *\n     * @param string $responseDataType\n     *\n     * @return $this\n     */\n    public function setResponseDataType($responseDataType);\n\n    /**\n     * Get the last response. This will always return a PSR-7 response no matter of the data type used.\n     *\n     * @return ResponseInterface|null\n     */\n    public function getLastResponse();\n\n    /**\n     * Returns an access token. If we do not have one in memory, try to fetch one from a *code* in the $_REQUEST.\n     *\n     * @return AccessToken|null The access token of null if the access token is not found\n     */\n    public function getAccessToken();\n\n    /**\n     * If you have stored a previous access token in a storage (database) you could set it here. After setting an\n     * access token you have to make sure to verify it is still valid by running LinkedIn::isAuthenticated.\n     *\n     * @param string|AccessToken $accessToken\n     *\n     * @return $this\n     */\n    public function setAccessToken($accessToken);\n\n    /**\n     * Set a URL generator.\n     *\n     * @param UrlGeneratorInterface $urlGenerator\n     *\n     * @return $this\n     */\n    public function setUrlGenerator(UrlGeneratorInterface $urlGenerator);\n\n    /**\n     * Set a data storage.\n     *\n     * @param DataStorageInterface $storage\n     *\n     * @return $this\n     */\n    public function setStorage(DataStorageInterface $storage);\n\n    /**\n     * Set a http client.\n     *\n     * @param HttpClient $client\n     *\n     * @return $this\n     */\n    public function setHttpClient(HttpClient $client);\n\n    /**\n     * Set a http message factory.\n     *\n     * @param MessageFactory $factory\n     *\n     * @return $this\n     */\n    public function setHttpMessageFactory(MessageFactory $factory);\n}\n"
  },
  {
    "path": "src/Storage/BaseDataStorage.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Happyr\\LinkedIn\\Exception\\InvalidArgumentException;\n\n/**\n * @author Tobias Nyholm\n */\nabstract class BaseDataStorage implements DataStorageInterface\n{\n    public static $validKeys = ['state', 'code', 'access_token', 'redirect_uri'];\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clearAll()\n    {\n        foreach (self::$validKeys as $key) {\n            $this->clear($key);\n        }\n    }\n\n    /**\n     * Validate key. Throws an exception if key is not valid.\n     *\n     * @param string $key\n     *\n     * @throws InvalidArgumentException\n     */\n    protected function validateKey($key)\n    {\n        if (!in_array($key, self::$validKeys)) {\n            throw new InvalidArgumentException('Unsupported key \"%s\" passed to LinkedIn data storage. Valid keys are: %s', $key, implode(', ', self::$validKeys));\n        }\n    }\n\n    /**\n     * Generate an ID to use with the data storage.\n     *\n     * @param $key\n     *\n     * @return string\n     */\n    protected function getStorageKeyId($key)\n    {\n        return 'linkedIn_'.$key;\n    }\n}\n"
  },
  {
    "path": "src/Storage/DataStorageInterface.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\n/**\n * We need to store data somewhere. It might be in a apc cache, filesystem cache, database or in the session.\n * We need it to protect us from CSRF attacks and to reduce the requests to the API.\n *\n * @author Tobias Nyholm\n */\ninterface DataStorageInterface\n{\n    /**\n     * Stores the given ($key, $value) pair, so that future calls to\n     * getPersistentData($key) return $value. This call may be in another request.\n     *\n     * @param string $key\n     * @param mixed  $value\n     */\n    public function set($key, $value);\n\n    /**\n     * Get the data for $key, persisted by BaseFacebook::setPersistentData().\n     *\n     * @param string $key The key of the data to retrieve\n     *\n     * @return mixed\n     */\n    public function get($key);\n\n    /**\n     * Clear the data with $key from the persistent storage.\n     *\n     * @param string $key\n     */\n    public function clear($key);\n\n    /**\n     * Clear all data from the persistent storage.\n     */\n    public function clearAll();\n}\n"
  },
  {
    "path": "src/Storage/IlluminateSessionStorage.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Illuminate\\Support\\Facades\\Session;\n\n/**\n * Store data in a IlluminateSession.\n *\n * @author Andreas Creten\n */\nclass IlluminateSessionStorage extends BaseDataStorage\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function set($key, $value)\n    {\n        $this->validateKey($key);\n        $name = $this->getStorageKeyId($key);\n\n        return Session::put($name, $value);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function get($key)\n    {\n        $this->validateKey($key);\n        $name = $this->getStorageKeyId($key);\n\n        return Session::get($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clear($key)\n    {\n        $this->validateKey($key);\n        $name = $this->getStorageKeyId($key);\n\n        return Session::forget($name);\n    }\n}\n"
  },
  {
    "path": "src/Storage/SessionStorage.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\n/**\n * Store data in the global session.\n *\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass SessionStorage extends BaseDataStorage\n{\n    public function __construct()\n    {\n        //start the session if it not already been started\n        if (php_sapi_name() !== 'cli') {\n            if (session_id() === '') {\n                session_start();\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function set($key, $value)\n    {\n        $this->validateKey($key);\n\n        $name = $this->getStorageKeyId($key);\n        $_SESSION[$name] = $value;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function get($key)\n    {\n        $this->validateKey($key);\n        $name = $this->getStorageKeyId($key);\n\n        return isset($_SESSION[$name]) ? $_SESSION[$name] : null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clear($key)\n    {\n        $this->validateKey($key);\n\n        $name = $this->getStorageKeyId($key);\n        if (isset($_SESSION[$name])) {\n            unset($_SESSION[$name]);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/AccessTokenTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\n/**\n * @author Tobias Nyholm\n */\nclass AccessTokenTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testToString()\n    {\n        $token = new AccessToken();\n        $this->assertEquals('', $token);\n\n        $token->setToken('foobar');\n        $this->assertEquals('foobar', $token);\n    }\n\n    public function testConstructor()\n    {\n        $token = new AccessToken('foobar', 10);\n        $this->assertInstanceOf('\\DateTime', $token->getExpiresAt());\n        $this->assertEquals('foobar', $token->getToken());\n\n        $token = new AccessToken();\n        $this->assertNull($token->getExpiresAt());\n        $this->assertEmpty($token->getToken());\n\n        $token = new AccessToken(null, new \\DateTime('+2minutes'));\n        $this->assertInstanceOf('\\DateTime', $token->getExpiresAt());\n    }\n\n    public function testSetExpiresAt()\n    {\n        $token = new AccessToken();\n        $token->setExpiresAt(new \\DateTime('+2minutes'));\n        $this->assertInstanceOf('\\DateTime', $token->getExpiresAt());\n    }\n}\n"
  },
  {
    "path": "tests/AuthenticatorTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse GuzzleHttp\\Psr7\\Response;\nuse Happyr\\LinkedIn\\Exception\\LinkedInException;\nuse Mockery as m;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass AuthenticatorTest extends \\PHPUnit_Framework_TestCase\n{\n    const APP_ID = '123456789';\n    const APP_SECRET = '987654321';\n\n    private function getRequestManagerMock()\n    {\n        return m::mock('Happyr\\LinkedIn\\Http\\RequestManager');\n    }\n\n    public function testGetLoginUrl()\n    {\n        $expected = 'loginUrl';\n        $state = 'random';\n        $params = [\n            'response_type' => 'code',\n            'client_id' => self::APP_ID,\n            'redirect_uri' => null,\n            'state' => $state,\n        ];\n\n        $storage = $this->getMock('Happyr\\LinkedIn\\Storage\\DataStorageInterface');\n        $storage->method('get')->with('state')->willReturn($state);\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['establishCSRFTokenState', 'getStorage'], [$this->getRequestManagerMock(), self::APP_ID, self::APP_SECRET]);\n        $auth->expects($this->exactly(2))->method('establishCSRFTokenState')->willReturn(null);\n        $auth->method('getStorage')->will($this->returnValue($storage));\n\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\LinkedInUrlGeneratorInterface')\n            ->shouldReceive('getUrl')->once()->with('www', 'oauth/v2/authorization', $params)->andReturn($expected)\n            ->getMock();\n\n        $this->assertEquals($expected, $auth->getLoginUrl($generator));\n\n        /*\n         * Test with a url in the param\n         */\n        $otherUrl = 'otherUrl';\n        $scope = ['foo', 'bar', 'baz'];\n        $params = [\n            'response_type' => 'code',\n            'client_id' => self::APP_ID,\n            'redirect_uri' => $otherUrl,\n            'state' => $state,\n            'scope' => 'foo bar baz',\n        ];\n\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\LinkedInUrlGeneratorInterface')\n            ->shouldReceive('getUrl')->once()->with('www', 'oauth/v2/authorization', $params)->andReturn($expected)\n            ->getMock();\n\n        $this->assertEquals($expected, $auth->getLoginUrl($generator, ['redirect_uri' => $otherUrl, 'scope' => $scope]));\n    }\n\n    public function testFetchNewAccessToken()\n    {\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n        $code = 'newCode';\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('set')->once()->with('code', $code)\n            ->shouldReceive('set')->once()->with('access_token', 'at')\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getCode', 'getStorage', 'getAccessTokenFromCode'], [], '', false);\n        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));\n        $auth->expects($this->once())->method('getAccessTokenFromCode')->with($generator, $code)->will($this->returnValue('at'));\n        $auth->expects($this->once())->method('getCode')->will($this->returnValue($code));\n\n        $this->assertEquals('at', $auth->fetchNewAccessToken($generator));\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInException\n     */\n    public function testFetchNewAccessTokenFail()\n    {\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n        $code = 'newCode';\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('clearAll')->once()\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getCode', 'getStorage', 'getAccessTokenFromCode'], [], '', false);\n        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));\n        $auth->expects($this->once())->method('getAccessTokenFromCode')->with($generator, $code)->willThrowException(new LinkedInException());\n        $auth->expects($this->once())->method('getCode')->will($this->returnValue($code));\n\n        $auth->fetchNewAccessToken($generator);\n    }\n\n    public function testFetchNewAccessTokenNoCode()\n    {\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('get')->with('code')->andReturn('foobar')\n            ->shouldReceive('get')->once()->with('access_token')->andReturn('baz')\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getCode', 'getStorage'], [], '', false);\n        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));\n        $auth->expects($this->once())->method('getCode');\n\n        $this->assertEquals('baz', $auth->fetchNewAccessToken($generator));\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInException\n     */\n    public function testGetAccessTokenFromCodeEmptyString()\n    {\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getAccessTokenFromCode');\n        $method->setAccessible(true);\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', [], [], '', false);\n\n        $method->invoke($auth, $generator, '');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInException\n     */\n    public function testGetAccessTokenFromCodeNull()\n    {\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getAccessTokenFromCode');\n        $method->setAccessible(true);\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', [], [], '', false);\n\n        $method->invoke($auth, $generator, null);\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInException\n     */\n    public function testGetAccessTokenFromCodeFalse()\n    {\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getAccessTokenFromCode');\n        $method->setAccessible(true);\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', [], [], '', false);\n\n        $method->invoke($auth, $generator, false);\n    }\n\n    public function testGetAccessTokenFromCode()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getAccessTokenFromCode');\n        $method->setAccessible(true);\n\n        $code = 'code';\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator')\n            ->shouldReceive('getUrl')->with(\n                'www',\n                'oauth/v2/accessToken'\n            )->andReturn('url')\n            ->getMock();\n\n        $response = ['access_token' => 'foobar', 'expires_in' => 10];\n        $auth = $this->prepareGetAccessTokenFromCode($code, $response);\n        $token = $method->invoke($auth, $generator, $code);\n        $this->assertEquals('foobar', $token, 'Standard get access token form code');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInException\n     */\n    public function testGetAccessTokenFromCodeNoTokenInResponse()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getAccessTokenFromCode');\n        $method->setAccessible(true);\n\n        $code = 'code';\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator')\n            ->shouldReceive('getUrl')->with(\n                'www',\n                'oauth/v2/accessToken'\n            )->andReturn('url')\n            ->getMock();\n\n        $response = ['foo' => 'bar'];\n        $auth = $this->prepareGetAccessTokenFromCode($code, $response);\n        $this->assertNull($method->invoke($auth, $generator, $code), 'Found array but no access token');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInException\n     */\n    public function testGetAccessTokenFromCodeEmptyResponse()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getAccessTokenFromCode');\n        $method->setAccessible(true);\n\n        $code = 'code';\n        $generator = m::mock('Happyr\\LinkedIn\\Http\\UrlGenerator')\n            ->shouldReceive('getUrl')->with(\n                'www',\n                'oauth/v2/accessToken'\n            )->andReturn('url')\n            ->getMock();\n\n        $response = '';\n        $auth = $this->prepareGetAccessTokenFromCode($code, $response);\n        $this->assertNull($method->invoke($auth, $generator, $code), 'Empty result');\n    }\n\n    /**\n     * Default stuff for GetAccessTokenFromCode.\n     *\n     * @param $response\n     *\n     * @return array\n     */\n    protected function prepareGetAccessTokenFromCode($code, $responseData)\n    {\n        $response = new Response(200, [], json_encode($responseData));\n        $currentUrl = 'foobar';\n\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('get')->with('redirect_uri')->andReturn($currentUrl)\n            ->getMock();\n\n        $requestManager = m::mock('Happyr\\LinkedIn\\Http\\RequestManager')\n            ->shouldReceive('sendRequest')->once()->with('POST', 'url', [\n                'Content-Type' => 'application/x-www-form-urlencoded',\n            ], http_build_query([\n                'grant_type' => 'authorization_code',\n                'code' => $code,\n                'redirect_uri' => $currentUrl,\n                'client_id' => self::APP_ID,\n                'client_secret' => self::APP_SECRET,\n            ]))->andReturn($response)\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getStorage'], [$requestManager, self::APP_ID, self::APP_SECRET]);\n        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));\n\n        return $auth;\n    }\n\n    public function testEstablishCSRFTokenState()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'establishCSRFTokenState');\n        $method->setAccessible(true);\n\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('get')->with('state')->andReturn(null, 'state')\n            ->shouldReceive('set')->once()->with('state', \\Mockery::on(function (&$param) {\n                return !empty($param);\n            }))\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getStorage'], [], '', false);\n        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));\n\n        // Make sure we only set the state once\n        $method->invoke($auth);\n        $method->invoke($auth);\n    }\n\n    public function testGetCodeEmpty()\n    {\n        unset($_REQUEST['code']);\n        unset($_GET['code']);\n\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getCode');\n        $method->setAccessible(true);\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', [], [], '', false);\n\n        $this->assertNull($method->invoke($auth));\n    }\n\n    public function testGetCode()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getCode');\n        $method->setAccessible(true);\n        $state = 'bazbar';\n\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('clear')->once()->with('state')\n            ->shouldReceive('get')->once()->with('code')->andReturn(null)\n            ->shouldReceive('get')->once()->with('state')->andReturn($state)\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getStorage'], [], '', false);\n        $auth->expects($this->once())->method('getStorage')->will($this->returnValue($storage));\n\n        $_REQUEST['code'] = 'foobar';\n        $_REQUEST['state'] = $state;\n\n        $this->assertEquals('foobar', $method->invoke($auth));\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInException\n     */\n    public function testGetCodeInvalidCode()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getCode');\n        $method->setAccessible(true);\n\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('get')->once()->with('code')->andReturn(null)\n            ->shouldReceive('get')->once()->with('state')->andReturn('bazbar')\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getStorage'], [], '', false);\n        $auth->expects($this->once())->method('getStorage')->will($this->returnValue($storage));\n\n        $_REQUEST['code'] = 'foobar';\n        $_REQUEST['state'] = 'invalid';\n\n        $this->assertEquals('foobar', $method->invoke($auth));\n    }\n\n    public function testGetCodeUsedCode()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getCode');\n        $method->setAccessible(true);\n\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface')\n            ->shouldReceive('get')->once()->with('code')->andReturn('foobar')\n            ->getMock();\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getStorage'], [], '', false);\n        $auth->expects($this->once())->method('getStorage')->will($this->returnValue($storage));\n\n        $_REQUEST['code'] = 'foobar';\n\n        $this->assertEquals(null, $method->invoke($auth));\n    }\n\n    public function testStorageAccessors()\n    {\n        $method = new \\ReflectionMethod('Happyr\\LinkedIn\\Authenticator', 'getStorage');\n        $method->setAccessible(true);\n        $requestManager = $this->getRequestManagerMock();\n        $auth = new Authenticator($requestManager, self::APP_ID, self::APP_SECRET);\n\n        // test default\n        $this->assertInstanceOf('Happyr\\LinkedIn\\Storage\\SessionStorage', $method->invoke($auth));\n\n        $object = m::mock('Happyr\\LinkedIn\\Storage\\DataStorageInterface');\n        $auth->setStorage($object);\n        $this->assertEquals($object, $method->invoke($auth));\n    }\n}\n"
  },
  {
    "path": "tests/Exceptions/LoginErrorTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * Class LoginErrorTest.\n *\n * @author Tobias Nyholm\n */\nclass LoginErrorTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testGetters()\n    {\n        $error = new LoginError('foo', 'bar');\n\n        $this->assertEquals('foo', $error->getName());\n        $this->assertEquals('bar', $error->getDescription());\n    }\n}\n"
  },
  {
    "path": "tests/Http/ResponseConverterTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse GuzzleHttp\\Psr7\\Response;\n\nclass ResponseConverterTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testConvert()\n    {\n        $body = '{\"foo\":\"bar\"}';\n        $response = new Response(200, [], $body);\n\n        $result = ResponseConverter::convert($response, 'json', 'psr7');\n        $this->assertInstanceOf('Psr\\Http\\Message\\ResponseInterface', $result);\n\n        $result = ResponseConverter::convert($response, 'json', 'stream');\n        $this->assertInstanceOf('Psr\\Http\\Message\\StreamInterface', $result);\n\n        $result = ResponseConverter::convert($response, 'json', 'string');\n        $this->assertTrue(is_string($result));\n        $this->assertEquals($body, $result);\n\n        $result = ResponseConverter::convert($response, 'json', 'array');\n        $this->assertTrue(is_array($result));\n\n        $body = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<person>\n  <firstname>foo</firstname>\n  <lastname>bar</lastname>\n</person>\n';\n        $response = new Response(200, [], $body);\n        $result = ResponseConverter::convert($response, 'xml', 'simple_xml');\n        $this->assertInstanceOf('\\SimpleXMLElement', $result);\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\InvalidArgumentException\n     */\n    public function testConvertJsonToSimpleXml()\n    {\n        $body = '{\"foo\":\"bar\"}';\n        $response = new Response(200, [], $body);\n\n        ResponseConverter::convert($response, 'json', 'simple_xml');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\InvalidArgumentException\n     */\n    public function testConvertXmlToArray()\n    {\n        $body = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<person>\n  <firstname>foo</firstname>\n  <lastname>bar</lastname>\n</person>\n';\n        $response = new Response(200, [], $body);\n\n        ResponseConverter::convert($response, 'xml', 'array');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\InvalidArgumentException\n     */\n    public function testConvertJsonToFoobar()\n    {\n        $body = '{\"foo\":\"bar\"}';\n        $response = new Response(200, [], $body);\n\n        ResponseConverter::convert($response, 'json', 'foobar');\n    }\n\n    public function testConvertToSimpleXml()\n    {\n        $body = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<person>\n  <firstname>foo</firstname>\n  <lastname>bar</lastname>\n</person>\n';\n\n        $response = new Response(200, [], $body);\n        $result = ResponseConverter::convertToSimpleXml($response);\n\n        $this->assertInstanceOf('\\SimpleXMLElement', $result);\n        $this->assertEquals('foo', $result->firstname);\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\LinkedInTransferException\n     */\n    public function testConvertToSimpleXmlError()\n    {\n        $body = '{Foo: bar}';\n\n        $response = new Response(200, [], $body);\n        $result = ResponseConverter::convertToSimpleXml($response);\n\n        $this->assertInstanceOf('\\SimpleXMLElement', $result);\n        $this->assertEquals('foo', $result->firstname);\n    }\n}\n"
  },
  {
    "path": "tests/Http/UrlGeneratorTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * Class UrlGeneratorTest.\n *\n * @author Tobias Nyholm\n */\nclass UrlGeneratorTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testDropLinkedInParams()\n    {\n        $gen = new DummyUrlGenerator();\n\n        $test = 'foo=bar&code=foobar&baz=foo';\n        $expected = '?foo=bar&baz=foo';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        $test = 'code=foobar&baz=foo';\n        $expected = '?baz=foo';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        $test = 'foo=bar&code=foobar';\n        $expected = '?foo=bar';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        $test = 'code=foobar';\n        $expected = '';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        $test = '';\n        $expected = '';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        /* ----------------- */\n\n        $test = 'foo=bar&code=';\n        $expected = '?foo=bar';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        $test = 'code=';\n        $expected = '';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        $test = 'foo=bar&code';\n        $expected = '?foo=bar';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n\n        $test = 'code';\n        $expected = '';\n        $this->assertEquals($expected, $gen->dropLinkedInParams($test));\n    }\n\n    public function testGetUrl()\n    {\n        $gen = new DummyUrlGenerator();\n\n        $expected = 'https://api.linkedin.com/?bar=baz';\n        $this->assertEquals($expected, $gen->getUrl('api', '', ['bar' => 'baz']), 'No path');\n\n        $expected = 'https://api.linkedin.com/foobar';\n        $this->assertEquals($expected, $gen->getUrl('api', 'foobar'), 'Path does not begin with forward slash');\n        $this->assertEquals($expected, $gen->getUrl('api', '/foobar'), 'Path begins with forward slash');\n\n        $expected = 'https://api.linkedin.com/foobar?bar=baz';\n        $this->assertEquals($expected, $gen->getUrl('api', 'foobar', ['bar' => 'baz']), 'One parameter');\n\n        $expected = 'https://api.linkedin.com/foobar?bar=baz&a=b&c=d';\n        $this->assertEquals($expected, $gen->getUrl('api', 'foobar', ['bar' => 'baz', 'a' => 'b', 'c' => 'd']), 'Many parameters');\n\n        $expected = 'https://api.linkedin.com/foobar?bar=baz%20a%20b';\n        $notExpected = 'https://api.linkedin.com/foobar?bar=baz+a+b';\n        $this->assertEquals($expected, $gen->getUrl('api', 'foobar', ['bar' => 'baz a b']), 'Use of PHP_QUERY_RFC3986');\n        $this->assertNotEquals($notExpected, $gen->getUrl('api', 'foobar', ['bar' => 'baz a b']), 'Dont use PHP_QUERY_RFC1738');\n    }\n\n    public function testGetUrlWithParams()\n    {\n        $gen = new UrlGenerator();\n\n        $expected = 'https://api.linkedin.com/endpoint?bar=baz&format=json';\n        $this->assertEquals($expected, $gen->getUrl('api', 'endpoint?bar=baz', ['format' => 'json']));\n\n        $expected = 'https://api.linkedin.com/endpoint?bar=baz&bar=baz';\n        $this->assertEquals($expected, $gen->getUrl('api', 'endpoint?bar=baz', ['bar' => 'baz']));\n    }\n\n    public function testGetCurrentURL()\n    {\n        $gen = $this->getMock('Happyr\\LinkedIn\\Http\\UrlGenerator', ['getHttpProtocol', 'getHttpHost', 'dropLinkedInParams'], []);\n        $gen->expects($this->any())->method('getHttpProtocol')->will($this->returnValue('http'));\n        $gen->expects($this->any())->method('getHttpHost')->will($this->returnValue('www.test.com'));\n        $gen->expects($this->any())->method('dropLinkedInParams')->will($this->returnCallback(function ($arg) {\n            return empty($arg) ? '' : '?'.$arg;\n        }));\n\n        // fake the HPHP $_SERVER globals\n        $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=one&two=two&three=three';\n        $this->assertEquals(\n            'http://www.test.com/unit-tests.php?one=one&two=two&three=three',\n            $gen->getCurrentUrl(),\n            'getCurrentUrl function is changing the current URL');\n\n        // ensure structure of valueless GET params is retained (sometimes\n        // an = sign was present, and sometimes it was not)\n        // first test when equal signs are present\n        $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=&two=&three=';\n        $this->assertEquals(\n            'http://www.test.com/unit-tests.php?one=&two=&three=',\n            $gen->getCurrentUrl(),\n            'getCurrentUrl function is changing the current URL');\n\n        // now confirm that\n        $_SERVER['REQUEST_URI'] = '/unit-tests.php?one&two&three';\n        $this->assertEquals(\n            'http://www.test.com/unit-tests.php?one&two&three',\n            $gen->getCurrentUrl(),\n            'getCurrentUrl function is changing the current URL'\n        );\n    }\n\n    public function testGetCurrentURLPort80()\n    {\n        $gen = $this->getMock('Happyr\\LinkedIn\\Http\\UrlGenerator', ['getHttpProtocol', 'getHttpHost', 'dropLinkedInParams'], []);\n        $gen->expects($this->any())->method('getHttpProtocol')->will($this->returnValue('http'));\n        $gen->expects($this->any())->method('getHttpHost')->will($this->returnValue('www.test.com:80'));\n        $gen->expects($this->any())->method('dropLinkedInParams')->will($this->returnCallback(function ($arg) {\n            return empty($arg) ? '' : '?'.$arg;\n        }));\n\n        //test port 80\n        $_SERVER['REQUEST_URI'] = '/foobar.php';\n        $this->assertEquals(\n            'http://www.test.com/foobar.php',\n            $gen->getCurrentUrl(),\n            'port 80 should not be shown'\n        );\n    }\n\n    public function testGetCurrentURLPort8080()\n    {\n        $gen = $this->getMock('Happyr\\LinkedIn\\Http\\UrlGenerator', ['getHttpProtocol', 'getHttpHost', 'dropLinkedInParams'], []);\n        $gen->expects($this->any())->method('getHttpProtocol')->will($this->returnValue('http'));\n        $gen->expects($this->any())->method('getHttpHost')->will($this->returnValue('www.test.com:8080'));\n        $gen->expects($this->any())->method('dropLinkedInParams')->will($this->returnCallback(function ($arg) {\n            return empty($arg) ? '' : '?'.$arg;\n        }));\n\n        //test non default port 8080\n        $_SERVER['REQUEST_URI'] = '/foobar.php';\n        $this->assertEquals(\n            'http://www.test.com:8080/foobar.php',\n            $gen->getCurrentUrl(),\n            'port 80 should not be shown'\n        );\n    }\n\n    public function testHttpHost()\n    {\n        $real = 'foo.com';\n        $_SERVER['HTTP_HOST'] = $real;\n        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'evil.com';\n        $gen = new DummyUrlGenerator();\n        $this->assertEquals($real, $gen->GetHttpHost());\n    }\n\n    public function testHttpProtocolApache()\n    {\n        $_SERVER['HTTPS'] = 'on';\n        $gen = new DummyUrlGenerator();\n        $this->assertEquals('https', $gen->GetHttpProtocol());\n    }\n\n    public function testHttpProtocolNginx()\n    {\n        $_SERVER['SERVER_PORT'] = '443';\n        $gen = new DummyUrlGenerator();\n        $this->assertEquals('https', $gen->GetHttpProtocol());\n    }\n\n    public function testHttpHostForwarded()\n    {\n        $real = 'foo.com';\n        $_SERVER['HTTP_HOST'] = 'localhost';\n        $_SERVER['HTTP_X_FORWARDED_HOST'] = $real;\n        $gen = new DummyUrlGenerator();\n        $gen->setTrustForwarded(true);\n        $this->assertEquals($real, $gen->GetHttpHost());\n    }\n\n    public function testHttpProtocolForwarded()\n    {\n        $_SERVER['HTTPS'] = 'on';\n        $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';\n        $gen = new DummyUrlGenerator();\n        $gen->setTrustForwarded(true);\n        $this->assertEquals('http', $gen->GetHttpProtocol());\n    }\n\n    public function testHttpProtocolForwardedSecure()\n    {\n        $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';\n        $gen = new DummyUrlGenerator();\n        $this->assertEquals('http', $gen->GetHttpProtocol());\n\n        $gen->setTrustForwarded(true);\n        $this->assertEquals('https', $gen->GetHttpProtocol());\n    }\n\n    protected function tearDown()\n    {\n        unset($_SERVER['HTTPS']);\n        unset($_SERVER['HTTP_X_FORWARDED_PROTO']);\n        $_SERVER['HTTP_HOST'] = 'localhost';\n        unset($_SERVER['HTTP_X_FORWARDED_HOST']);\n        $_SERVER['SERVER_PORT'] = '80';\n        $_SERVER['REQUEST_URI'] = '';\n    }\n}\n\nclass DummyUrlGenerator extends UrlGenerator\n{\n    public function getHttpHost()\n    {\n        return parent::getHttpHost();\n    }\n\n    public function getHttpProtocol()\n    {\n        return parent::getHttpProtocol();\n    }\n\n    public function dropLinkedInParams($query)\n    {\n        return parent::dropLinkedInParams($query);\n    }\n}\n"
  },
  {
    "path": "tests/LinkedInTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse GuzzleHttp\\Psr7\\Response;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass LinkedInTest extends \\PHPUnit_Framework_TestCase\n{\n    const APP_ID = '123456789';\n    const APP_SECRET = '987654321';\n\n    public function testApi()\n    {\n        $resource = 'resource';\n        $token = 'token';\n        $urlParams = ['url' => 'foo'];\n        $postParams = ['post' => 'bar'];\n        $method = 'GET';\n        $expected = ['foobar' => 'test'];\n        $response = new Response(200, [], json_encode($expected));\n        $url = 'http://example.com/test';\n\n        $headers = ['Authorization' => 'Bearer '.$token, 'Content-Type' => 'application/json', 'x-li-format' => 'json'];\n\n        $generator = $this->getMock('Happyr\\LinkedIn\\Http\\UrlGenerator', ['getUrl']);\n        $generator->expects($this->once())->method('getUrl')->with(\n            $this->equalTo('api'),\n            $this->equalTo($resource),\n            $this->equalTo([\n                'url' => 'foo',\n                'format' => 'json',\n            ]))\n            ->willReturn($url);\n\n        $requestManager = $this->getMock('Happyr\\LinkedIn\\Http\\RequestManager', ['sendRequest']);\n        $requestManager->expects($this->once())->method('sendRequest')->with(\n                $this->equalTo($method),\n                $this->equalTo($url),\n                $this->equalTo($headers),\n                $this->equalTo(json_encode($postParams)))\n            ->willReturn($response);\n\n        $linkedIn = $this->getMock('Happyr\\LinkedIn\\LinkedIn', ['getAccessToken', 'getUrlGenerator', 'getRequestManager'], [self::APP_ID, self::APP_SECRET]);\n\n        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn($token);\n        $linkedIn->expects($this->once())->method('getUrlGenerator')->willReturn($generator);\n        $linkedIn->expects($this->once())->method('getRequestManager')->willReturn($requestManager);\n\n        $result = $linkedIn->api($method, $resource, ['query' => $urlParams, 'json' => $postParams]);\n        $this->assertEquals($expected, $result);\n    }\n\n    public function testIsAuthenticated()\n    {\n        $linkedIn = $this->getMock('Happyr\\LinkedIn\\LinkedIn', ['getAccessToken'], [self::APP_ID, self::APP_SECRET]);\n        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn(null);\n        $this->assertFalse($linkedIn->isAuthenticated());\n\n        $linkedIn = $this->getMock('Happyr\\LinkedIn\\LinkedIn', ['api', 'getAccessToken'], [self::APP_ID, self::APP_SECRET]);\n        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn('token');\n        $linkedIn->expects($this->once())->method('api')->willReturn(['id' => 4711]);\n        $this->assertTrue($linkedIn->isAuthenticated());\n\n        $linkedIn = $this->getMock('Happyr\\LinkedIn\\LinkedIn', ['api', 'getAccessToken'], [self::APP_ID, self::APP_SECRET]);\n        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn('token');\n        $linkedIn->expects($this->once())->method('api')->willReturn(['foobar' => 4711]);\n        $this->assertFalse($linkedIn->isAuthenticated());\n    }\n\n    /**\n     * Test a call to getAccessToken when there is no token.\n     */\n    public function testAccessTokenAccessors()\n    {\n        $token = 'token';\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['fetchNewAccessToken'], [], '', false);\n        $auth->expects($this->once())->method('fetchNewAccessToken')->will($this->returnValue($token));\n\n        $linkedIn = $this->getMock('Happyr\\LinkedIn\\LinkedIn', ['getAuthenticator'], [], '', false);\n        $linkedIn->expects($this->once())->method('getAuthenticator')->willReturn($auth);\n\n        // Make sure we go to the authenticator only once\n        $this->assertEquals($token, $linkedIn->getAccessToken());\n        $this->assertEquals($token, $linkedIn->getAccessToken());\n    }\n\n    public function testGeneratorAccessors()\n    {\n        $get = new \\ReflectionMethod('Happyr\\LinkedIn\\LinkedIn', 'getUrlGenerator');\n        $get->setAccessible(true);\n        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);\n\n        // test default\n        $this->assertInstanceOf('Happyr\\LinkedIn\\Http\\UrlGenerator', $get->invoke($linkedIn));\n\n        $object = $this->getMock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n        $linkedIn->setUrlGenerator($object);\n        $this->assertEquals($object, $get->invoke($linkedIn));\n    }\n\n    public function testHasError()\n    {\n        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);\n\n        unset($_GET['error']);\n        $this->assertFalse($linkedIn->hasError());\n\n        $_GET['error'] = 'foobar';\n        $this->assertTrue($linkedIn->hasError());\n    }\n\n    public function testGetError()\n    {\n        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);\n\n        unset($_GET['error']);\n        unset($_GET['error_description']);\n\n        $this->assertNull($linkedIn->getError());\n\n        $_GET['error'] = 'foo';\n        $_GET['error_description'] = 'bar';\n\n        $this->assertEquals('foo', $linkedIn->getError()->getName());\n        $this->assertEquals('bar', $linkedIn->getError()->getDescription());\n    }\n\n    public function testGetErrorWithMissingDescription()\n    {\n        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);\n\n        unset($_GET['error']);\n        unset($_GET['error_description']);\n\n        $_GET['error'] = 'foo';\n\n        $this->assertEquals('foo', $linkedIn->getError()->getName());\n        $this->assertNull($linkedIn->getError()->getDescription());\n    }\n\n    public function testFormatAccessors()\n    {\n        $get = new \\ReflectionMethod('Happyr\\LinkedIn\\LinkedIn', 'getFormat');\n        $get->setAccessible(true);\n        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);\n\n        //test default\n        $this->assertEquals('json', $get->invoke($linkedIn));\n\n        $format = 'foo';\n        $linkedIn->setFormat($format);\n        $this->assertEquals($format, $get->invoke($linkedIn));\n    }\n\n    public function testLoginUrl()\n    {\n        $currentUrl = 'currentUrl';\n        $loginUrl = 'result';\n\n        $generator = $this->getMock('Happyr\\LinkedIn\\Http\\UrlGenerator', ['getCurrentUrl']);\n        $generator->expects($this->once())->method('getCurrentUrl')->willReturn($currentUrl);\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getLoginUrl'], [], '', false);\n        $auth->expects($this->once())->method('getLoginUrl')\n            ->with($generator, ['redirect_uri' => $currentUrl])\n            ->will($this->returnValue($loginUrl));\n\n        $linkedIn = $this->getMock('Happyr\\LinkedIn\\LinkedIn', ['getAuthenticator', 'getUrlGenerator'], [], '', false);\n        $linkedIn->expects($this->once())->method('getAuthenticator')->willReturn($auth);\n        $linkedIn->expects($this->once())->method('getUrlGenerator')->willReturn($generator);\n\n        $linkedIn->getLoginUrl();\n    }\n\n    public function testLoginUrlWithParameter()\n    {\n        $loginUrl = 'result';\n        $otherUrl = 'otherUrl';\n\n        $generator = $this->getMock('Happyr\\LinkedIn\\Http\\UrlGenerator');\n\n        $auth = $this->getMock('Happyr\\LinkedIn\\Authenticator', ['getLoginUrl'], [], '', false);\n        $auth->expects($this->once())->method('getLoginUrl')\n            ->with($generator, ['redirect_uri' => $otherUrl])\n            ->will($this->returnValue($loginUrl));\n\n        $linkedIn = $this->getMock('Happyr\\LinkedIn\\LinkedIn', ['getAuthenticator', 'getUrlGenerator'], [], '', false);\n        $linkedIn->expects($this->once())->method('getAuthenticator')->willReturn($auth);\n        $linkedIn->expects($this->once())->method('getUrlGenerator')->willReturn($generator);\n\n        $linkedIn->getLoginUrl(['redirect_uri' => $otherUrl]);\n    }\n}\n"
  },
  {
    "path": "tests/Storage/IlluminateSessionStorageTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Illuminate\\Support\\Facades\\Session;\n\n/**\n * Class SessionStorageTest.\n *\n * @author Andreas Creten\n */\nclass IlluminateSessionStorageTest extends \\PHPUnit_Framework_TestCase\n{\n    /**\n     * @var \\Happyr\\LinkedIn\\Storage\\SessionStorage storage\n     */\n    protected $storage;\n\n    protected $prefix = 'linkedIn_';\n\n    public function setUp()\n    {\n        $this->storage = new IlluminateSessionStorage();\n    }\n\n    public function testSet()\n    {\n        Session::shouldReceive('put')->once()->with($this->prefix.'code', 'foobar');\n\n        $this->storage->set('code', 'foobar');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\InvalidArgumentException\n     */\n    public function testSetFail()\n    {\n        $this->storage->set('foobar', 'baz');\n    }\n\n    public function testGet()\n    {\n        $expected = 'foobar';\n        Session::shouldReceive('get')->once()->with($this->prefix.'code')->andReturn($expected);\n        $result = $this->storage->get('code');\n        $this->assertEquals($expected, $result);\n\n        Session::shouldReceive('get')->once()->with($this->prefix.'state')->andReturn(null);\n        $result = $this->storage->get('state');\n        $this->assertNull($result);\n    }\n\n    public function testClear()\n    {\n        Session::shouldReceive('forget')->once()->with($this->prefix.'code')->andReturn(true);\n        $this->storage->clear('code');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\InvalidArgumentException\n     */\n    public function testClearFail()\n    {\n        $this->storage->clear('foobar');\n    }\n}\n"
  },
  {
    "path": "tests/Storage/SessionStorageTest.php",
    "content": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Mockery as m;\n\n/**\n * Class SessionStorageTest.\n *\n * @author Tobias Nyholm\n */\nclass SessionStorageTest extends \\PHPUnit_Framework_TestCase\n{\n    /**\n     * @var \\Happyr\\LinkedIn\\Storage\\SessionStorage storage\n     */\n    protected $storage;\n\n    protected $prefix = 'linkedIn_';\n\n    public function setUp()\n    {\n        $this->storage = new SessionStorage();\n    }\n\n    public function testSet()\n    {\n        $this->storage->set('code', 'foobar');\n        $this->assertEquals($_SESSION[$this->prefix.'code'], 'foobar');\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\InvalidArgumentException\n     */\n    public function testSetFail()\n    {\n        $this->storage->set('foobar', 'baz');\n    }\n\n    public function testGet()\n    {\n        unset($_SESSION[$this->prefix.'state']);\n        $result = $this->storage->get('state');\n        $this->assertNull($result);\n\n        $expected = 'foobar';\n        $_SESSION[$this->prefix.'code'] = $expected;\n        $result = $this->storage->get('code');\n        $this->assertEquals($expected, $result);\n    }\n\n    public function testClear()\n    {\n        $_SESSION[$this->prefix.'code'] = 'foobar';\n        $this->storage->clear('code');\n        $this->assertFalse(isset($_SESSION[$this->prefix.'code']));\n    }\n\n    /**\n     * @expectedException \\Happyr\\LinkedIn\\Exception\\InvalidArgumentException\n     */\n    public function testClearFail()\n    {\n        $this->storage->clear('foobar');\n    }\n\n    public function testClearAll()\n    {\n        $validKeys = SessionStorage::$validKeys;\n\n        $storage = m::mock('Happyr\\LinkedIn\\Storage\\SessionStorage[clear]')\n            ->shouldReceive('clear')->times(count($validKeys))\n            ->with(m::on(function ($arg) use ($validKeys) {\n                return in_array($arg, $validKeys);\n            }))\n            ->getMock();\n\n        $storage->clearAll();\n    }\n}\n"
  }
]