Full Code of Happyr/LinkedIn-API-client for AI

master 58bb3bca346e cached
40 files
99.5 KB
26.0k tokens
199 symbols
1 requests
Download .txt
Repository: Happyr/LinkedIn-API-client
Branch: master
Commit: 58bb3bca346e
Files: 40
Total size: 99.5 KB

Directory structure:
gitextract__ze4uh7e/

├── .github/
│   ├── ISSUE_TEMPLATE.md
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .scrutinizer.yml
├── .styleci.yml
├── .travis.yml
├── LICENSE
├── Readme.md
├── Upgrade.md
├── composer.json
├── phpunit.xml.dist
├── src/
│   ├── AccessToken.php
│   ├── Authenticator.php
│   ├── AuthenticatorInterface.php
│   ├── Exception/
│   │   ├── InvalidArgumentException.php
│   │   ├── LinkedInException.php
│   │   ├── LinkedInTransferException.php
│   │   └── LoginError.php
│   ├── Http/
│   │   ├── CurrentUrlGeneratorInterface.php
│   │   ├── GlobalVariableGetter.php
│   │   ├── LinkedInUrlGeneratorInterface.php
│   │   ├── RequestManager.php
│   │   ├── RequestManagerInterface.php
│   │   ├── ResponseConverter.php
│   │   ├── UrlGenerator.php
│   │   └── UrlGeneratorInterface.php
│   ├── LinkedIn.php
│   ├── LinkedInInterface.php
│   └── Storage/
│       ├── BaseDataStorage.php
│       ├── DataStorageInterface.php
│       ├── IlluminateSessionStorage.php
│       └── SessionStorage.php
└── tests/
    ├── AccessTokenTest.php
    ├── AuthenticatorTest.php
    ├── Exceptions/
    │   └── LoginErrorTest.php
    ├── Http/
    │   ├── ResponseConverterTest.php
    │   └── UrlGeneratorTest.php
    ├── LinkedInTest.php
    └── Storage/
        ├── IlluminateSessionStorageTest.php
        └── SessionStorageTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
| Q                       | A
| ----------------------- | ---
| Bug?                    | no|yes
| New Feature?            | no|yes
| Are you using composer? | no|yes
| Version                 | Specific version or SHA of a commit


#### Actual Behavior

What is the actual behavior?


#### Expected Behavior

What is the behavior you expect?


#### Steps to Reproduce

What are the steps to reproduce this bug? Please add code examples,
screenshots or links to GitHub repositories that reproduce the problem.


#### Possible Solutions

If you have already ideas how to solve the issue, add them here.
(remove this section if not needed)


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
| Q               | A
| --------------- | ---
| Bug fix?        | no|yes
| New feature?    | no|yes
| BC breaks?      | no|yes
| Deprecations?   | no|yes
| Related tickets | fixes #X, partially #Y, mentioned in #Z
| License         | MIT


#### What's in this PR?

Explain what the changes in this PR do.


#### Why?

Which problem does the PR fix? (remove this section if you linked an issue above)


#### Example Usage

``` php
// If you added new features, show examples of how to use them here
// (remove this section if not a new feature)

$foo = new Foo();

// Now we can do
$foo->doSomething();
```


#### Checklist

- [ ] Updated CHANGELOG.md to describe BC breaks / deprecations | new feature | bugfix
- [ ] Documentation pull request created (if not simply a bugfix)


#### To Do

- [ ] If the PR is not complete but you want to discuss the approach, list what remains to be done here


================================================
FILE: .gitignore
================================================
/build/
composer.lock
index.php
phpunit.xml
/vendor/


================================================
FILE: .scrutinizer.yml
================================================
filter:
    paths: [src/*]
checks:
    php:
        code_rating: true
        duplication: true
tools:
    external_code_coverage: true


================================================
FILE: .styleci.yml
================================================
preset: symfony

finder:
    path:
        - "src"
        - "tests"

enabled:
    - short_array_syntax

disabled:
    - phpdoc_annotation_without_dot


================================================
FILE: .travis.yml
================================================
language: php
sudo: false

cache:
    directories:
        - $HOME/.composer/cache/files

php:
  - 5.5
  - 5.6
  - 7.0
  - 7.1
  - hhvm

env:
    global:
        - TEST_COMMAND="composer test"

matrix:
    fast_finish: true
    include:
        - php: 5.5
          env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci"

before_install:
  - composer self-update

install:
    - travis_retry composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction

script:
    - $TEST_COMMAND

after_success:
    - if [[ $COVERAGE = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
    - if [[ $COVERAGE = true ]]; then php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml; fi


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Tobias Nyholm

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Readme.md
================================================
# LinkedIn API client in PHP

[![Latest Version](https://img.shields.io/github/release/Happyr/LinkedIn-API-client.svg?style=flat-square)](https://github.com/Happyr/LinkedIn-API-client/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Build Status](https://img.shields.io/travis/Happyr/LinkedIn-API-client/master.svg?style=flat-square)](https://travis-ci.org/Happyr/LinkedIn-API-client)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/44c425af-90f6-4c25-b789-4ece28b01a2b/mini.png)](https://insight.sensiolabs.com/projects/44c425af-90f6-4c25-b789-4ece28b01a2b)
[![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)
[![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)
[![Total Downloads](https://img.shields.io/packagist/dt/happyr/linkedin-api-client.svg?style=flat-square)](https://packagist.org/packages/happyr/linkedin-api-client)


A PHP library to handle authentication and communication with LinkedIn API. The library/SDK helps you to get an access
token and when authenticated it helps you to send API requests. You will not get *everything* for free though... You
have to read the [LinkedIn documentation][api-doc-core] to understand how you should query the API. 

To get an overview what this library actually is doing for you. Take a look at the authentication page from
the [API docs][api-doc-authentication].

## Features

Here is a list of features that might convince you to choose this LinkedIn client over some of our competitors'.

* Flexible and easy to extend
* Developed with modern PHP standards
* Not developed for a specific framework. 
* Handles the authentication process
* Respects the CSRF protection

## Installation

**TL;DR**
```bash
composer require php-http/curl-client guzzlehttp/psr7 php-http/message happyr/linkedin-api-client
```

This library does not have a dependency on Guzzle or any other library that sends HTTP requests. We use the awesome 
HTTPlug to achieve the decoupling. We want you to choose what library to use for sending HTTP requests. Consult this list 
of packages that support [php-http/client-implementation](https://packagist.org/providers/php-http/client-implementation) 
find clients to use. For more information about virtual packages please refer to 
[HTTPlug](http://docs.php-http.org/en/latest/httplug/users.html). Example:

```bash
composer require php-http/guzzle6-adapter
```

You do also need to install a PSR-7 implementation and a factory to create PSR-7 messages (PSR-17 whenever that is 
released). You could use Guzzles PSR-7 implementation and factories from php-http:

```bash
composer require guzzlehttp/psr7 php-http/message 
```

Now you may install the library by running the following:

```bash
composer require happyr/linkedin-api-client
```

If you are updating form a previous version make sure to read [the upgrade documentation](Upgrade.md).

### Finding the HTTP client (optional) 

The LinkedIn client need to know what library you are using to send HTTP messages. You could provide an instance of 
HttpClient and MessageFactory or you could fallback on auto discovery. Below is an example on where you provide a Guzzle6 
instance.

```php
$linkedIn=new Happyr\LinkedIn\LinkedIn('app_id', 'app_secret');
$linkedIn->setHttpClient(new \Http\Adapter\Guzzle6\Client());
$linkedIn->setHttpMessageFactory(new Http\Message\MessageFactory\GuzzleMessageFactory());

```

## Usage

In order to use this API client (or any other LinkedIn clients) you have to [register your application][register-app]
with LinkedIn to receive an API key. Once you've registered your LinkedIn app, you will be provided with
an *API Key* and *Secret Key*.

### LinkedIn login

This example below is showing how to login with LinkedIn.

```php 
<?php

/**
 * This demonstrates how to authenticate with LinkedIn and send api requests
 */

/*
 * First you need to make sure you've used composers auto load. You have is probably 
 * already done this before. You usually don't bother..
 */
//require_once "vendor/autoload.php";

$linkedIn=new Happyr\LinkedIn\LinkedIn('client_id', 'client_secret');

if ($linkedIn->isAuthenticated()) {
    //we know that the user is authenticated now. Start query the API
    $user=$linkedIn->get('v1/people/~:(firstName,lastName)');
    echo "Welcome ".$user['firstName'];

    exit();
} elseif ($linkedIn->hasError()) {
    echo "User canceled the login.";
    exit();
}

//if not authenticated
$url = $linkedIn->getLoginUrl();
echo "<a href='$url'>Login with LinkedIn</a>";

```

### How to post on LinkedIn wall

The example below shows how you can post on a users wall. The access token is fetched from the database. 

```php
$linkedIn=new Happyr\LinkedIn\LinkedIn('app_id', 'app_secret');
$linkedIn->setAccessToken('access_token_from_db');

$options = array('json'=>
    array(
        'comment' => 'Im testing Happyr LinkedIn client! https://github.com/Happyr/LinkedIn-API-client',
        'visibility' => array(
            'code' => 'anyone'
        )
    )
);

$result = $linkedIn->post('v1/people/~/shares', $options);

var_dump($result);

// Prints: 
// array (size=2)
//   'updateKey' => string 'UPDATE-01234567-0123456789012345678' (length=35)
//   'updateUrl' => string 'https://www.linkedin.com/updates?discuss=&scope=01234567&stype=M&topic=0123456789012345678&type=U&a=mVKU' (length=104)

```

You may of course do the same in xml. Use the following options array.
```php
$options = array(
'format' => 'xml',
'body' => '<share>
 <comment>Im testing Happyr LinkedIn client! https://github.com/Happyr/LinkedIn-API-client</comment>
 <visibility>
   <code>anyone</code>
 </visibility>
</share>');
```

## Configuration

### The api options

The third parameter of `LinkedIn::api` is an array with options. Below is a table of array keys that you may use. 

| Option name | Description
| ----------- | -----------
| body | The body of a HTTP request. Put your xml string here. 
| format | Set this to 'json', 'xml' or 'simple_xml' to override the default value.
| headers | This is HTTP headers to the request
| 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. 
| response_data_type | To override the response format for one request 
| query | This is an array with query parameters



### Changing request format

The default format when communicating with LinkedIn API is json. You can let the API do `json_encode` for you. 
The following code shows you how. 

```php
$body = array(
    'comment' => 'Im testing Happyr LinkedIn client! https://github.com/Happyr/LinkedIn-API-client',
    'visibility' => array('code' => 'anyone')
);

$linkedIn->post('v1/people/~/shares', array('json'=>$body));
$linkedIn->post('v1/people/~/shares', array('body'=>json_encode($body)));
```

When using `array('json'=>$body)` as option the format will always be `json`. You can change the request format in three ways.

```php
// By constructor argument
$linkedIn=new Happyr\LinkedIn\LinkedIn('app_id', 'app_secret', 'xml');

// By setter
$linkedIn->setFormat('xml');

// Set format for just one request
$linkedIn->post('v1/people/~/shares', array('format'=>'xml', 'body'=>$body));
```


### Understanding response data type

The data type returned from `LinkedIn::api` can be configured. You may use the forth construtor argument, the
`LinkedIn::setResponseDataType` or as an option for `LinkedIn::api`

```php
// By constructor argument
$linkedIn=new Happyr\LinkedIn\LinkedIn('app_id', 'app_secret', 'json', 'array');

// By setter
$linkedIn->setResponseDataType('simple_xml');

// Set format for just one request
$linkedIn->get('v1/people/~:(firstName,lastName)', array('response_data_type'=>'psr7'));

```

Below is a table that specifies what the possible return data types are when you call `LinkedIn::api`.

| Type | Description
| ------ | ------------
| array | An assosiative array. This can only be used with the `json` format.
| simple_xml | A SimpleXMLElement. See [PHP manual](http://php.net/manual/en/class.simplexmlelement.php). This can only be used with the `xml` format.
| psr7 | A PSR7 response.
| stream | A file stream.
| string | A plain old string.


### Use different Session classes

You might want to use an other storage than the default `SessionStorage`. If you are using Laravel
you are more likely to inject the `IlluminateSessionStorage`.  
```php
$linkedIn=new Happyr\LinkedIn\LinkedIn('app_id', 'app_secret');
$linkedIn->setStorage(new IlluminateSessionStorage());
```

You can inject any class implementing `DataStorageInterface`. You can also inject different `UrlGenerator` classes.

### Using different scopes

If you want to define special scopes when you authenticate the user you should specify them when you are generating the 
login url. If you don't specify scopes LinkedIn will use the default scopes that you have configured for the app.  

```php
$scope = 'r_fullprofile,r_emailaddress,w_share';
//or 
$scope = array('rw_groups', 'r_contactinfo', 'r_fullprofile', 'w_messages');

$url = $linkedIn->getLoginUrl(array('scope'=>$scope));
echo "<a href='$url'>Login with LinkedIn</a>";
```

## Framework integration

If you want an easier integration with a framwork you may want to check out these repositories: 

* [HappyrLinkedInBundle](https://github.com/Happyr/LinkedInBundle) for Symfony
* [Laravel-Linkedin by mauri870](https://github.com/artesaos/laravel-linkedin) for Laravel 5


[register-app]: https://www.linkedin.com/secure/developer
[linkedin-code-samples]: https://developer.linkedin.com/documents/code-samples
[api-doc-authentication]: https://developer.linkedin.com/documents/authentication
[api-doc-core]: https://developer.linkedin.com/core-concepts


================================================
FILE: Upgrade.md
================================================
# Upgrade

This document explains how you upgrade from one version to another. 

## Upgrade from 0.7.2 to 1.0

### Changes

* We do not longer require `php-http/message`. You have to make sure to put that in your own composer.json.

## Upgrade from 0.7.1 to 0.7.2

### Changes

* Using `php-http/discovery:1.0`
* Code style changes.

## Upgrade from 0.7.0 to 0.7.1

### Changes

* Using `php-http/discovery:0.9` which makes Puli optional
* Using new URL's to LinkedIn API so users are provided with the new authentication UX. (Thanks to @mbarwick83)

## Upgrade from 0.6 to 0.7

### Changes

* Introduced PHP-HTTP and PSR-7 messages
* Added constructor argument for responseDataType
* Added setResponseDataType()
* Moved authentication functions to `Authenticator` class  

To make sure you can upgrade you need to install a HTTP adapter.

```bash
php composer.phar require php-http/guzzle6-adapter
```

### BC breaks

* Removed `LinkedIn::setRequest` in favor of `LinkedIn::setHttpAdapter`
* Removed `LinkedIn::getAppSecret` and `LinkedIn::getAppId` 
* Removed `LinkedIn::getUser`
* Removed `LinkedInApiException` in favor of `LinkedInException`, `InvalidArgumentException` and `LinkedInTransferException` 
* Removed `LinkedIn::getLastHeaders` in favor of `LinkedIn::getLastResponse`
* Made the public functions `LinkedIn::getResponseDataType` and `LinkedIn::getFormat` protected

## Upgrade from 0.5 to 0.6

### Changes

* When exchanging the code for an access token we are now using the post body instead of query parameters
* Better error handling when exchange from code to access token fails

### BC breaks

There are a few minor BC breaks. We removed the functions below: 

* `LinkedIn::getUserId`, use `LinkedIn::getUser` instead
* `AccessToken::constructFromJson`, Use the constructor instead. 

## Upgrade from 0.4 to 0.5

### Changed signature of `LinkedIn::api`

The signature of `LinkedIn::api` has changed to be more easy to work with. 
```php
// Version 0.4
public function api($resource, array $urlParams=array(), $method='GET', $postParams=array())

// Version 0.5
public function api($method, $resource, array $options=array())
```

This means that you have to modify your calls to: 
```php
// Version 0.5
$options = array('query'=>$urlParams, 'body'=>$postParams);
$linkedIn->api('POST', $resource, $options)
```
See the Readme about more options to the API function. 

### Must inject IlluminateSessionStorage

We have removed the protected `LinkedIn::init` function. That means if you were using `IlluminateSessionStorage` you have
to make a minor adjustment to your code. 

```php
// Version 0.4
$linkedIn=new Happyr\LinkedIn\LinkedIn('app_id', 'app_secret');

// Version 0.5
$linkedIn=new Happyr\LinkedIn\LinkedIn('app_id', 'app_secret');
$linkedIn->setStorage(new IlluminateSessionStorage());
```

If you don't know about `IlluminateSessionStorage` you are probably good ignoring this. 

### Default format 

The default format when communicating with LinkedIn API is changed to  json. 

### Updated RequestInterface

The `RequestInterface::send` was updated with a new signature. We did also introduce `RequestInterface::getHeadersFromLastResponse`. 


================================================
FILE: composer.json
================================================
{
    "name": "happyr/linkedin-api-client",
    "type": "library",
    "description": "LinkedIn API client. Handles OAuth, CSRF protection. Easy to implement and extend. This is a standalone library for any composer project.",
    "keywords": ["LinkedIn", "OAuth", "API", "Client", "SDK"],
    "homepage": "http://developer.happyr.com/libraries/linkedin-php-client",
    "license": "MIT",
    "authors": [
        {
            "name": "Tobias Nyholm",
            "email": "tobias@happyr.com"
        }
    ],
    "require": {
        "php": "^5.5 || ^7.0",
        "php-http/client-implementation": "^1.0",
        "php-http/httplug": "^1.0",
        "php-http/message-factory": "^1.0",
        "php-http/discovery": "^1.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.5 || ^5.0",
        "php-http/guzzle5-adapter": "^1.0",
        "guzzlehttp/psr7": "^1.2",
        "mockery/mockery": "^0.9",
        "illuminate/support": "^5.0"
    },
    "autoload": {
        "psr-4": { "Happyr\\LinkedIn\\": "src/" }
    },
    "scripts": {
        "test": "vendor/bin/phpunit",
        "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.1.x-dev"
        }
    }
}


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         colors="false"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="false"
         codecoverage= "true"
         bootstrap="./vendor/autoload.php"
        >

    <formatter type="clover" usefile="false" />

    <testsuites>
        <testsuite name="Happyr Api Client Test Suite">
            <directory>./tests</directory>
        </testsuite>
    </testsuites>

    <listeners>
        <listener class="\Mockery\Adapter\Phpunit\TestListener" file="vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php">
        </listener>
    </listeners>

    <filter>
        <whitelist>
            <directory suffix=".php">src</directory>
            <exclude>
                <directory>vendor</directory>
                <directory>tests</directory>
            </exclude>
        </whitelist>
    </filter>

    <logging>
        <log type="coverage-clover" target="build/logs/phpunit.coverage.xml"/>
        <log type="junit" target="build/logs/phpunit.xml" logIncompleteSkipped="false"/>
    </logging>

</phpunit>


================================================
FILE: src/AccessToken.php
================================================
<?php

namespace Happyr\LinkedIn;

/**
 * @author Tobias Nyholm
 */
class AccessToken
{
    /**
     * @var null|string token
     */
    private $token;

    /**
     * @var \DateTime expiresAt
     */
    private $expiresAt;

    /**
     * @param string        $token
     * @param \DateTime|int $expiresIn
     */
    public function __construct($token = null, $expiresIn = null)
    {
        $this->token = $token;

        if ($expiresIn !== null) {
            if ($expiresIn instanceof \DateTime) {
                $this->expiresAt = $expiresIn;
            } else {
                $this->expiresAt = new \DateTime(sprintf('+%dseconds', $expiresIn));
            }
        }
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->token ?: '';
    }

    /**
     * Does a token string exist?
     *
     * @return bool
     */
    public function hasToken()
    {
        return !empty($this->token);
    }

    /**
     * @param \DateTime $expiresAt
     *
     * @return $this
     */
    public function setExpiresAt(\DateTime $expiresAt = null)
    {
        $this->expiresAt = $expiresAt;

        return $this;
    }

    /**
     * @return \DateTime
     */
    public function getExpiresAt()
    {
        return $this->expiresAt;
    }

    /**
     * @param null|string $token
     *
     * @return $this
     */
    public function setToken($token)
    {
        $this->token = $token;

        return $this;
    }

    /**
     * @return null|string
     */
    public function getToken()
    {
        return $this->token;
    }
}


================================================
FILE: src/Authenticator.php
================================================
<?php

namespace Happyr\LinkedIn;

use Happyr\LinkedIn\Exception\LinkedInTransferException;
use Happyr\LinkedIn\Exception\LinkedInException;
use Happyr\LinkedIn\Http\GlobalVariableGetter;
use Happyr\LinkedIn\Http\LinkedInUrlGeneratorInterface;
use Happyr\LinkedIn\Http\RequestManager;
use Happyr\LinkedIn\Http\RequestManagerInterface;
use Happyr\LinkedIn\Http\ResponseConverter;
use Happyr\LinkedIn\Storage\DataStorageInterface;
use Happyr\LinkedIn\Storage\SessionStorage;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class Authenticator implements AuthenticatorInterface
{
    /**
     * The application ID.
     *
     * @var string
     */
    protected $appId;

    /**
     * The application secret.
     *
     * @var string
     */
    protected $appSecret;

    /**
     * A storage to use to store data between requests.
     *
     * @var DataStorageInterface storage
     */
    private $storage;

    /**
     * @var RequestManagerInterface
     */
    private $requestManager;

    /**
     * @param RequestManagerInterface $requestManager
     * @param string                  $appId
     * @param string                  $appSecret
     */
    public function __construct(RequestManagerInterface $requestManager, $appId, $appSecret)
    {
        $this->appId = $appId;
        $this->appSecret = $appSecret;
        $this->requestManager = $requestManager;
    }

    /**
     * {@inheritdoc}
     */
    public function fetchNewAccessToken(LinkedInUrlGeneratorInterface $urlGenerator)
    {
        $storage = $this->getStorage();
        $code = $this->getCode();

        if ($code === null) {
            /*
             * As a fallback, just return whatever is in the persistent
             * store, knowing nothing explicit (signed request, authorization
             *  code, etc.) was present to shadow it.
             */
            return $storage->get('access_token');
        }

        try {
            $accessToken = $this->getAccessTokenFromCode($urlGenerator, $code);
        } catch (LinkedInException $e) {
            // code was bogus, so everything based on it should be invalidated.
            $storage->clearAll();
            throw $e;
        }

        $storage->set('code', $code);
        $storage->set('access_token', $accessToken);

        return $accessToken;
    }

    /**
     * Retrieves an access token for the given authorization code
     * (previously generated from www.linkedin.com on behalf of
     * a specific user). The authorization code is sent to www.linkedin.com
     * and a legitimate access token is generated provided the access token
     * and the user for which it was generated all match, and the user is
     * either logged in to LinkedIn or has granted an offline access permission.
     *
     * @param LinkedInUrlGeneratorInterface $urlGenerator
     * @param string                        $code         An authorization code.
     *
     * @return AccessToken An access token exchanged for the authorization code.
     *
     * @throws LinkedInException
     */
    protected function getAccessTokenFromCode(LinkedInUrlGeneratorInterface $urlGenerator, $code)
    {
        if (empty($code)) {
            throw new LinkedInException('Could not get access token: The code was empty.');
        }

        $redirectUri = $this->getStorage()->get('redirect_uri');
        try {
            $url = $urlGenerator->getUrl('www', 'oauth/v2/accessToken');
            $headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
            $body = http_build_query(
                [
                    'grant_type' => 'authorization_code',
                    'code' => $code,
                    'redirect_uri' => $redirectUri,
                    'client_id' => $this->appId,
                    'client_secret' => $this->appSecret,
                ]
            );

            $response = ResponseConverter::convertToArray($this->getRequestManager()->sendRequest('POST', $url, $headers, $body));
        } catch (LinkedInTransferException $e) {
            // most likely that user very recently revoked authorization.
            // In any event, we don't have an access token, so throw an exception.
            throw new LinkedInException('Could not get access token: The user may have revoked the authorization response from LinkedIn.com was empty.', $e->getCode(), $e);
        }

        if (empty($response)) {
            throw new LinkedInException('Could not get access token: The response from LinkedIn.com was empty.');
        }

        $tokenData = array_merge(['access_token' => null, 'expires_in' => null], $response);
        $token = new AccessToken($tokenData['access_token'], $tokenData['expires_in']);

        if (!$token->hasToken()) {
            throw new LinkedInException('Could not get access token: The response from LinkedIn.com did not contain a token.');
        }

        return $token;
    }

    /**
     * {@inheritdoc}
     */
    public function getLoginUrl(LinkedInUrlGeneratorInterface $urlGenerator, $options = [])
    {
        // Generate a state
        $this->establishCSRFTokenState();

        // Build request params
        $requestParams = array_merge([
            'response_type' => 'code',
            'client_id' => $this->appId,
            'state' => $this->getStorage()->get('state'),
            'redirect_uri' => null,
        ], $options);

        // Save the redirect url for later
        $this->getStorage()->set('redirect_uri', $requestParams['redirect_uri']);

        // if 'scope' is passed as an array, convert to space separated list
        $scopeParams = isset($options['scope']) ? $options['scope'] : null;
        if ($scopeParams) {
            //if scope is an array
            if (is_array($scopeParams)) {
                $requestParams['scope'] = implode(' ', $scopeParams);
            } elseif (is_string($scopeParams)) {
                //if scope is a string with ',' => make it to an array
                $requestParams['scope'] = str_replace(',', ' ', $scopeParams);
            }
        }

        return $urlGenerator->getUrl('www', 'oauth/v2/authorization', $requestParams);
    }

    /**
     * Get the authorization code from the query parameters, if it exists,
     * and otherwise return null to signal no authorization code was
     * discovered.
     *
     * @return string|null The authorization code, or null if the authorization code not exists.
     *
     * @throws LinkedInException on invalid CSRF tokens
     */
    protected function getCode()
    {
        $storage = $this->getStorage();

        if (!GlobalVariableGetter::has('code')) {
            return;
        }

        if ($storage->get('code') === GlobalVariableGetter::get('code')) {
            //we have already validated this code
            return;
        }

        // if stored state does not exists
        if (null === $state = $storage->get('state')) {
            throw new LinkedInException('Could not find a stored CSRF state token.');
        }

        // if state not exists in the request
        if (!GlobalVariableGetter::has('state')) {
            throw new LinkedInException('Could not find a CSRF state token in the request.');
        }

        // if state exists in session and in request and if they are not equal
        if ($state !== GlobalVariableGetter::get('state')) {
            throw new LinkedInException('The CSRF state token from the request does not match the stored token.');
        }

        // CSRF state has done its job, so clear it
        $storage->clear('state');

        return GlobalVariableGetter::get('code');
    }

    /**
     * Lays down a CSRF state token for this process.
     */
    protected function establishCSRFTokenState()
    {
        $storage = $this->getStorage();
        if ($storage->get('state') === null) {
            $storage->set('state', md5(uniqid(mt_rand(), true)));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function clearStorage()
    {
        $this->getStorage()->clearAll();

        return $this;
    }

    /**
     * @return DataStorageInterface
     */
    protected function getStorage()
    {
        if ($this->storage === null) {
            $this->storage = new SessionStorage();
        }

        return $this->storage;
    }

    /**
     * {@inheritdoc}
     */
    public function setStorage(DataStorageInterface $storage)
    {
        $this->storage = $storage;

        return $this;
    }

    /**
     * @return RequestManager
     */
    protected function getRequestManager()
    {
        return $this->requestManager;
    }
}


================================================
FILE: src/AuthenticatorInterface.php
================================================
<?php

namespace Happyr\LinkedIn;

use Happyr\LinkedIn\Exception\LinkedInException;
use Happyr\LinkedIn\Http\LinkedInUrlGeneratorInterface;
use Happyr\LinkedIn\Storage\DataStorageInterface;

/**
 * This interface is responsible for the authentication process with LinkedIn.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
interface AuthenticatorInterface
{
    /**
     * Tries to get a new access token from data storage or code. If it fails, it will return null.
     *
     * @param LinkedInUrlGeneratorInterface $urlGenerator
     *
     * @return AccessToken|null A valid user access token, or null if one could not be fetched.
     *
     * @throws LinkedInException
     */
    public function fetchNewAccessToken(LinkedInUrlGeneratorInterface $urlGenerator);

    /**
     * Generate a login url.
     *
     * @param LinkedInUrlGeneratorInterface $urlGenerator
     * @param array                         $options
     *
     * @return string
     */
    public function getLoginUrl(LinkedInUrlGeneratorInterface $urlGenerator, $options = []);

    /**
     * Clear the storage.
     *
     * @return $this
     */
    public function clearStorage();

    /**
     * @param DataStorageInterface $storage
     *
     * @return $this
     */
    public function setStorage(DataStorageInterface $storage);
}


================================================
FILE: src/Exception/InvalidArgumentException.php
================================================
<?php

namespace Happyr\LinkedIn\Exception;

class InvalidArgumentException extends LinkedInException
{
    /**
     * Treat this constructor as sprintf().
     */
    public function __construct()
    {
        parent::__construct(call_user_func_array('sprintf', func_get_args()));
    }
}


================================================
FILE: src/Exception/LinkedInException.php
================================================
<?php

namespace Happyr\LinkedIn\Exception;

/**
 * This is the base exception of this library.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class LinkedInException extends \Exception
{
}


================================================
FILE: src/Exception/LinkedInTransferException.php
================================================
<?php

namespace Happyr\LinkedIn\Exception;

/**
 * An exception with thrown by unexpected result form the remote API.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class LinkedInTransferException extends LinkedInException
{
}


================================================
FILE: src/Exception/LoginError.php
================================================
<?php

namespace Happyr\LinkedIn\Exception;

/**
 * Class LoginError.
 *
 * @author Tobias Nyholm
 */
class LoginError
{
    /**
     * @var string name
     */
    protected $name;

    /**
     * @var string description
     */
    protected $description;

    /**
     * @param string $name
     * @param string $description
     */
    public function __construct($name, $description)
    {
        $this->name = $name;
        $this->description = $description;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return sprintf('Name: %s, Description: %s', $this->getName(), $this->getDescription());
    }
}


================================================
FILE: src/Http/CurrentUrlGeneratorInterface.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

/**
 * An interface to get the current URL.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
interface CurrentUrlGeneratorInterface
{
    /**
     * Returns the current URL.
     *
     * @return string The current URL
     */
    public function getCurrentUrl();

    /**
     * Should we trust forwarded headers?
     *
     * @param bool $trustForwarded
     *
     * @return $this
     */
    public function setTrustForwarded($trustForwarded);
}


================================================
FILE: src/Http/GlobalVariableGetter.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

/**
 * Look in $_REQUEST and $_GET if there is a variable we want.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class GlobalVariableGetter
{
    /**
     * Returns true iff the $_REQUEST or $_GET variables has a key with $name.
     *
     * @param string $name
     *
     * @return bool
     */
    public static function has($name)
    {
        if (isset($_REQUEST[$name])) {
            return true;
        }

        return isset($_GET[$name]);
    }

    /**
     * Returns the value in $_REQUEST[$name] or $_GET[$name] if the former was empty. If no value found, return null.
     *
     * @param string $name
     *
     * @return mixed|null
     */
    public static function get($name)
    {
        if (isset($_REQUEST[$name])) {
            return $_REQUEST[$name];
        }

        if (isset($_GET[$name])) {
            return $_GET[$name];
        }

        return;
    }
}


================================================
FILE: src/Http/LinkedInUrlGeneratorInterface.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

/**
 * An interface to generate LinkedIn specific urls.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
interface LinkedInUrlGeneratorInterface
{
    /**
     * Build the URL for given domain alias, path and parameters.
     *
     * @param $name string The name of the domain, 'www' or 'api'
     * @param $path string without a leading slash
     * @param $params array query parameters
     *
     * @return string The URL for the given parameters. The URL query  MUST be build with PHP_QUERY_RFC3986
     */
    public function getUrl($name, $path = '', $params = []);
}


================================================
FILE: src/Http/RequestManager.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

use Happyr\LinkedIn\Exception\LinkedInTransferException;
use Http\Client\Exception\TransferException;
use Http\Client\HttpClient;
use Http\Discovery\HttpClientDiscovery;
use Http\Discovery\MessageFactoryDiscovery;
use Http\Message\MessageFactory;

/**
 * A class to create HTTP requests and to send them.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class RequestManager implements RequestManagerInterface
{
    /**
     * @var \Http\Client\HttpClient
     */
    private $httpClient;

    /**
     * @var \Http\Message\MessageFactory
     */
    private $messageFactory;

    /**
     * {@inheritdoc}
     */
    public function sendRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1')
    {
        $request = $this->getMessageFactory()->createRequest($method, $uri, $headers, $body, $protocolVersion);

        try {
            return $this->getHttpClient()->sendRequest($request);
        } catch (TransferException $e) {
            throw new LinkedInTransferException('Error while requesting data from LinkedIn.com: '.$e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function setHttpClient(HttpClient $httpClient)
    {
        $this->httpClient = $httpClient;

        return $this;
    }

    /**
     * @return HttpClient
     */
    protected function getHttpClient()
    {
        if ($this->httpClient === null) {
            $this->httpClient = HttpClientDiscovery::find();
        }

        return $this->httpClient;
    }

    /**
     * @param MessageFactory $messageFactory
     *
     * @return RequestManager
     */
    public function setMessageFactory(MessageFactory $messageFactory)
    {
        $this->messageFactory = $messageFactory;

        return $this;
    }

    /**
     * @return \Http\Message\MessageFactory
     */
    private function getMessageFactory()
    {
        if ($this->messageFactory === null) {
            $this->messageFactory = MessageFactoryDiscovery::find();
        }

        return $this->messageFactory;
    }
}


================================================
FILE: src/Http/RequestManagerInterface.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

use Happyr\LinkedIn\Exception\LinkedInTransferException;
use Http\Client\HttpClient;

/**
 * A request manager builds a request.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
interface RequestManagerInterface
{
    /**
     * Send a request.
     *
     * @param string $method
     * @param string $uri
     * @param array  $headers
     * @param string $body
     * @param string $protocolVersion
     *
     * @return \Psr\Http\Message\ResponseInterface
     *
     * @throws LinkedInTransferException
     */
    public function sendRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1');

    /**
     * @param \Http\Client\HttpClient $httpClient
     *
     * @return RequestManager
     */
    public function setHttpClient(HttpClient $httpClient);
}


================================================
FILE: src/Http/ResponseConverter.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

use Happyr\LinkedIn\Exception\InvalidArgumentException;
use Happyr\LinkedIn\Exception\LinkedInTransferException;
use Psr\Http\Message\ResponseInterface;

class ResponseConverter
{
    /**
     * Convert a PSR-7 response to a data type you want to work with.
     *
     * @param ResponseInterface $response
     * @param string            $requestFormat
     * @param string            $dataType
     *
     * @return ResponseInterface|\Psr\Http\Message\StreamInterface|\SimpleXMLElement|string
     *
     * @throws InvalidArgumentException
     * @throws LinkedInTransferException
     */
    public static function convert(ResponseInterface $response, $requestFormat, $dataType)
    {
        if (($requestFormat === 'json' && $dataType === 'simple_xml') ||
            ($requestFormat === 'xml' && $dataType === 'array')) {
            throw new InvalidArgumentException('Can not use reponse data format "%s" with the request format "%s".', $dataType, $requestFormat);
        }

        switch ($dataType) {
            case 'array':
                return self::convertToArray($response);
            case 'string':
                return $response->getBody()->__toString();
            case 'simple_xml':
                return self::convertToSimpleXml($response);
            case 'stream':
                return $response->getBody();
            case 'psr7':
                return $response;
            default:
                throw new InvalidArgumentException('Format "%s" is not supported', $dataType);
        }
    }

    /**
     * @param ResponseInterface $response
     *
     * @return string
     */
    public static function convertToArray(ResponseInterface $response)
    {
        return json_decode($response->getBody(), true);
    }

    /**
     * @param ResponseInterface $response
     *
     * @return \SimpleXMLElement
     *
     * @throws LinkedInTransferException
     */
    public static function convertToSimpleXml(ResponseInterface $response)
    {
        $body = $response->getBody();
        try {
            return new \SimpleXMLElement((string) $body ?: '<root />');
        } catch (\Exception $e) {
            throw new LinkedInTransferException('Unable to parse response body into XML.');
        }
    }
}


================================================
FILE: src/Http/UrlGenerator.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class UrlGenerator implements UrlGeneratorInterface
{
    /**
     * @var array knownLinkedInParams
     *
     * A list of params that might be in the query string
     */
    public static $knownLinkedInParams = ['state', 'code', 'access_token', 'user'];

    /**
     * @var array domainMap
     *
     * Maps aliases to LinkedIn domains.
     */
    public static $domainMap = [
        'api' => 'https://api.linkedin.com/',
        'www' => 'https://www.linkedin.com/',
    ];

    /**
     * @var bool
     *
     * Indicates if we trust HTTP_X_FORWARDED_* headers.
     */
    protected $trustForwarded = false;

    /**
     * {@inheritdoc}
     */
    public function getUrl($name, $path = '', $params = [])
    {
        $url = self::$domainMap[$name];
        if ($path) {
            if ($path[0] === '/') {
                $path = substr($path, 1);
            }
            $url .= $path;
        }

        if (!empty($params)) {
            // does it exist a query string?
            $queryString = parse_url($url, PHP_URL_QUERY);
            if (empty($queryString)) {
                $url .= '?';
            } else {
                $url .= '&';
            }

            // it needs to be PHP_QUERY_RFC3986. We want to have %20 between scopes
            $url .= http_build_query($params, null, '&', PHP_QUERY_RFC3986);
        }

        return $url;
    }

    /**
     * {@inheritdoc}
     */
    public function getCurrentUrl()
    {
        $protocol = $this->getHttpProtocol().'://';
        $host = $this->getHttpHost();
        $currentUrl = $protocol.$host.$_SERVER['REQUEST_URI'];
        $parts = parse_url($currentUrl);

        $query = '';
        if (!empty($parts['query'])) {
            // drop known linkedin params
            $query = $this->dropLinkedInParams($parts['query']);
        }

        // use port if non default
        $port =
            isset($parts['port']) &&
            (($protocol === 'http://' && $parts['port'] !== 80) ||
                ($protocol === 'https://' && $parts['port'] !== 443))
                ? ':'.$parts['port'] : '';

        // rebuild
        return $protocol.$parts['host'].$port.$parts['path'].$query;
    }

    /**
     * Drop known LinkedIn params. Ie those in self::$knownLinkeInParams.
     *
     * @param string $query
     *
     * @return string query without LinkedIn params. This string is prepended with a question mark '?'
     */
    protected function dropLinkedInParams($query)
    {
        if ($query == '') {
            return '';
        }

        $params = explode('&', $query);
        foreach ($params as $i => $param) {
            /*
             * A key or key/value pair might me 'foo=bar', 'foo=', or 'foo'.
             */
            //get the first value of the array you will get when you explode()
            list($key) = explode('=', $param, 2);
            if (in_array($key, self::$knownLinkedInParams)) {
                unset($params[$i]);
            }
        }

        //assert: params is an array. It might be empty
        if (!empty($params)) {
            return '?'.implode($params, '&');
        }

        return '';
    }

    /**
     * Get the host.
     *
     *
     * @return mixed
     */
    protected function getHttpHost()
    {
        if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
            return $_SERVER['HTTP_X_FORWARDED_HOST'];
        }

        return $_SERVER['HTTP_HOST'];
    }

    /**
     * Get the protocol.
     *
     *
     * @return string
     */
    protected function getHttpProtocol()
    {
        if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
            if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
                return 'https';
            }

            return 'http';
        }

        /*apache + variants specific way of checking for https*/
        if (isset($_SERVER['HTTPS']) &&
            ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1)) {
            return 'https';
        }

        /*nginx way of checking for https*/
        if (isset($_SERVER['SERVER_PORT']) &&
            ($_SERVER['SERVER_PORT'] === '443')) {
            return 'https';
        }

        return 'http';
    }

    /**
     * {@inheritdoc}
     */
    public function setTrustForwarded($trustForwarded)
    {
        $this->trustForwarded = $trustForwarded;

        return $this;
    }
}


================================================
FILE: src/Http/UrlGeneratorInterface.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
interface UrlGeneratorInterface extends LinkedInUrlGeneratorInterface, CurrentUrlGeneratorInterface
{
}


================================================
FILE: src/LinkedIn.php
================================================
<?php

namespace Happyr\LinkedIn;

use Happyr\LinkedIn\Exception\LoginError;
use Happyr\LinkedIn\Http\GlobalVariableGetter;
use Happyr\LinkedIn\Http\RequestManager;
use Happyr\LinkedIn\Http\ResponseConverter;
use Happyr\LinkedIn\Http\UrlGenerator;
use Happyr\LinkedIn\Http\UrlGeneratorInterface;
use Happyr\LinkedIn\Storage\DataStorageInterface;
use Http\Client\HttpClient;
use Http\Message\MessageFactory;
use Psr\Http\Message\ResponseInterface;

/**
 * Class LinkedIn lets you talk to LinkedIn api.
 *
 * When a new user arrives and want to authenticate here is whats happens:
 * 1. You redirect him to whatever url getLoginUrl() returns.
 * 2. The user logs in on www.linkedin.com and authorize your application.
 * 3. The user returns to your site with a *code* in the the $_REQUEST.
 * 4. You call isAuthenticated() or getAccessToken()
 * 5. If we don't have an access token (only a *code*), getAccessToken() will call fetchNewAccessToken()
 * 6. fetchNewAccessToken() gets the *code* from the $_REQUEST and calls getAccessTokenFromCode()
 * 7. getAccessTokenFromCode() makes a request to www.linkedin.com and exchanges the *code* for an access token
 * 8. When you have the access token you should store it in a database and/or query the API.
 * 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
 *    authentication steps again.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class LinkedIn implements LinkedInInterface
{
    /**
     * The OAuth access token received in exchange for a valid authorization
     * code.  null means the access token has yet to be determined.
     *
     * @var AccessToken
     */
    protected $accessToken = null;

    /**
     * @var string format
     */
    private $format;

    /**
     * @var string responseFormat
     */
    private $responseDataType;

    /**
     * @var ResponseInterface
     */
    private $lastResponse;

    /**
     * @var RequestManager
     */
    private $requestManager;

    /**
     * @var Authenticator
     */
    private $authenticator;

    /**
     * @var UrlGeneratorInterface
     */
    private $urlGenerator;

    /**
     * Constructor.
     *
     * @param string $appId
     * @param string $appSecret
     * @param string $format           'json', 'xml'
     * @param string $responseDataType 'array', 'string', 'simple_xml' 'psr7', 'stream'
     */
    public function __construct($appId, $appSecret, $format = 'json', $responseDataType = 'array')
    {
        $this->format = $format;
        $this->responseDataType = $responseDataType;

        $this->requestManager = new RequestManager();
        $this->authenticator = new Authenticator($this->requestManager, $appId, $appSecret);
    }

    /**
     * {@inheritdoc}
     */
    public function isAuthenticated()
    {
        $accessToken = $this->getAccessToken();
        if ($accessToken === null) {
            return false;
        }

        $user = $this->api('GET', '/v1/people/~:(id,firstName,lastName)', ['format' => 'json', 'response_data_type' => 'array']);

        return !empty($user['id']);
    }

    /**
     * {@inheritdoc}
     */
    public function api($method, $resource, array $options = [])
    {
        // Add access token to the headers
        $options['headers']['Authorization'] = sprintf('Bearer %s', (string) $this->getAccessToken());

        // Do logic and adjustments to the options
        $requestFormat = $this->filterRequestOption($options);

        // Generate an url
        $url = $this->getUrlGenerator()->getUrl(
            'api',
            $resource,
            isset($options['query']) ? $options['query'] : []
        );

        $body = isset($options['body']) ? $options['body'] : null;
        $this->lastResponse = $this->getRequestManager()->sendRequest($method, $url, $options['headers'], $body);

        //Get the response data format
        if (isset($options['response_data_type'])) {
            $responseDataType = $options['response_data_type'];
        } else {
            $responseDataType = $this->getResponseDataType();
        }

        return ResponseConverter::convert($this->lastResponse, $requestFormat, $responseDataType);
    }

    /**
     * Modify and filter the request options. Make sure we use the correct query parameters and headers.
     *
     * @param array &$options
     *
     * @return string the request format to use
     */
    protected function filterRequestOption(array &$options)
    {
        if (isset($options['json'])) {
            $options['format'] = 'json';
            $options['body'] = json_encode($options['json']);
        } elseif (!isset($options['format'])) {
            // Make sure we always have a format
            $options['format'] = $this->getFormat();
        }

        // Set correct headers for this format
        switch ($options['format']) {
            case 'xml':
                $options['headers']['Content-Type'] = 'text/xml';
                break;
            case 'json':
                $options['headers']['Content-Type'] = 'application/json';
                $options['headers']['x-li-format'] = 'json';
                $options['query']['format'] = 'json';
                break;
            default:
                // Do nothing
        }

        return $options['format'];
    }

    /**
     * {@inheritdoc}
     */
    public function getLoginUrl($options = [])
    {
        $urlGenerator = $this->getUrlGenerator();

        // Set redirect_uri to current URL if not defined
        if (!isset($options['redirect_uri'])) {
            $options['redirect_uri'] = $urlGenerator->getCurrentUrl();
        }

        return $this->getAuthenticator()->getLoginUrl($urlGenerator, $options);
    }

    /**
     * See docs for LinkedIn::api().
     *
     * @param string $resource
     * @param array  $options
     *
     * @return mixed
     */
    public function get($resource, array $options = [])
    {
        return $this->api('GET', $resource, $options);
    }

    /**
     * See docs for LinkedIn::api().
     *
     * @param string $resource
     * @param array  $options
     *
     * @return mixed
     */
    public function post($resource, array $options = [])
    {
        return $this->api('POST', $resource, $options);
    }

    /**
     * {@inheritdoc}
     */
    public function clearStorage()
    {
        $this->getAuthenticator()->clearStorage();

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function hasError()
    {
        return GlobalVariableGetter::has('error');
    }

    /**
     * {@inheritdoc}
     */
    public function getError()
    {
        if ($this->hasError()) {
            return new LoginError(GlobalVariableGetter::get('error'), GlobalVariableGetter::get('error_description'));
        }
    }

    /**
     * Get the default format to use when sending requests.
     *
     * @return string
     */
    protected function getFormat()
    {
        return $this->format;
    }

    /**
     * {@inheritdoc}
     */
    public function setFormat($format)
    {
        $this->format = $format;

        return $this;
    }

    /**
     * Get the default data type to be returned as a response.
     *
     * @return string
     */
    protected function getResponseDataType()
    {
        return $this->responseDataType;
    }

    /**
     * {@inheritdoc}
     */
    public function setResponseDataType($responseDataType)
    {
        $this->responseDataType = $responseDataType;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getLastResponse()
    {
        return $this->lastResponse;
    }

    /**
     * {@inheritdoc}
     */
    public function getAccessToken()
    {
        if ($this->accessToken === null) {
            if (null !== $newAccessToken = $this->getAuthenticator()->fetchNewAccessToken($this->getUrlGenerator())) {
                $this->setAccessToken($newAccessToken);
            }
        }

        // return the new access token or null if none found
        return $this->accessToken;
    }

    /**
     * {@inheritdoc}
     */
    public function setAccessToken($accessToken)
    {
        if (!($accessToken instanceof AccessToken)) {
            $accessToken = new AccessToken($accessToken);
        }

        $this->accessToken = $accessToken;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setUrlGenerator(UrlGeneratorInterface $urlGenerator)
    {
        $this->urlGenerator = $urlGenerator;

        return $this;
    }

    /**
     * @return UrlGeneratorInterface
     */
    protected function getUrlGenerator()
    {
        if ($this->urlGenerator === null) {
            $this->urlGenerator = new UrlGenerator();
        }

        return $this->urlGenerator;
    }

    /**
     * {@inheritdoc}
     */
    public function setStorage(DataStorageInterface $storage)
    {
        $this->getAuthenticator()->setStorage($storage);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setHttpClient(HttpClient $client)
    {
        $this->getRequestManager()->setHttpClient($client);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setHttpMessageFactory(MessageFactory $factory)
    {
        $this->getRequestManager()->setMessageFactory($factory);

        return $this;
    }

    /**
     * @return RequestManager
     */
    protected function getRequestManager()
    {
        return $this->requestManager;
    }

    /**
     * @return Authenticator
     */
    protected function getAuthenticator()
    {
        return $this->authenticator;
    }
}


================================================
FILE: src/LinkedInInterface.php
================================================
<?php

namespace Happyr\LinkedIn;

use Happyr\LinkedIn\Exception\LoginError;
use Happyr\LinkedIn\Http\UrlGeneratorInterface;
use Happyr\LinkedIn\Storage\DataStorageInterface;
use Http\Client\HttpClient;
use Http\Message\MessageFactory;
use Psr\Http\Message\ResponseInterface;

/**
 * Interface that lets you talk to LinkedIn api.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
interface LinkedInInterface
{
    /**
     * Is the current user authenticated?
     *
     * @return bool
     */
    public function isAuthenticated();

    /**
     * Make an API call. Read about what calls that are possible here: https://developer.linkedin.com/docs/rest-api.
     *
     * Example:
     * $linkedIn->api('GET', '/v1/people/~:(id,firstName,lastName,headline)');
     *
     * The options:
     * - body: the body of the request
     * - format: the format you are using to send the request
     * - headers: array with headers to use
     * - response_data_type: the data type to get back
     * - query: query parameters to the request
     *
     * @param string $method   This is the HTTP verb
     * @param string $resource everything after the domain in the URL.
     * @param array  $options  See the readme for option description.
     *
     * @return mixed this depends on the response_data_type parameter.
     */
    public function api($method, $resource, array $options = []);

    /**
     * Get a login URL where the user can put his/hers LinkedIn credentials and authorize the application.
     *
     * The options:
     * - redirect_uri: the url to go to after a successful login
     * - scope: comma (or space) separated list of requested extended permissions
     *
     * @param array $options Provide custom parameters
     *
     * @return string The URL for the login flow
     */
    public function getLoginUrl($options = []);

    /**
     * See docs for LinkedIn::api().
     *
     * @param string $resource
     * @param array  $options
     *
     * @return mixed
     */
    public function get($resource, array $options = []);

    /**
     * See docs for LinkedIn::api().
     *
     * @param string $resource
     * @param array  $options
     *
     * @return mixed
     */
    public function post($resource, array $options = []);

    /**
     * Clear the data storage. This will forget everything about the user and authentication process.
     *
     * @return $this
     */
    public function clearStorage();

    /**
     * If the user has canceled the login we will return with an error.
     *
     * @return bool
     */
    public function hasError();

    /**
     * Returns a LoginError or null.
     *
     * @return LoginError|null
     */
    public function getError();

    /**
     * Set the default format to use when sending requests.
     *
     * @param string $format
     *
     * @return $this
     */
    public function setFormat($format);

    /**
     * Set the default data type to be returned as a response.
     *
     * @param string $responseDataType
     *
     * @return $this
     */
    public function setResponseDataType($responseDataType);

    /**
     * Get the last response. This will always return a PSR-7 response no matter of the data type used.
     *
     * @return ResponseInterface|null
     */
    public function getLastResponse();

    /**
     * Returns an access token. If we do not have one in memory, try to fetch one from a *code* in the $_REQUEST.
     *
     * @return AccessToken|null The access token of null if the access token is not found
     */
    public function getAccessToken();

    /**
     * If you have stored a previous access token in a storage (database) you could set it here. After setting an
     * access token you have to make sure to verify it is still valid by running LinkedIn::isAuthenticated.
     *
     * @param string|AccessToken $accessToken
     *
     * @return $this
     */
    public function setAccessToken($accessToken);

    /**
     * Set a URL generator.
     *
     * @param UrlGeneratorInterface $urlGenerator
     *
     * @return $this
     */
    public function setUrlGenerator(UrlGeneratorInterface $urlGenerator);

    /**
     * Set a data storage.
     *
     * @param DataStorageInterface $storage
     *
     * @return $this
     */
    public function setStorage(DataStorageInterface $storage);

    /**
     * Set a http client.
     *
     * @param HttpClient $client
     *
     * @return $this
     */
    public function setHttpClient(HttpClient $client);

    /**
     * Set a http message factory.
     *
     * @param MessageFactory $factory
     *
     * @return $this
     */
    public function setHttpMessageFactory(MessageFactory $factory);
}


================================================
FILE: src/Storage/BaseDataStorage.php
================================================
<?php

namespace Happyr\LinkedIn\Storage;

use Happyr\LinkedIn\Exception\InvalidArgumentException;

/**
 * @author Tobias Nyholm
 */
abstract class BaseDataStorage implements DataStorageInterface
{
    public static $validKeys = ['state', 'code', 'access_token', 'redirect_uri'];

    /**
     * {@inheritdoc}
     */
    public function clearAll()
    {
        foreach (self::$validKeys as $key) {
            $this->clear($key);
        }
    }

    /**
     * Validate key. Throws an exception if key is not valid.
     *
     * @param string $key
     *
     * @throws InvalidArgumentException
     */
    protected function validateKey($key)
    {
        if (!in_array($key, self::$validKeys)) {
            throw new InvalidArgumentException('Unsupported key "%s" passed to LinkedIn data storage. Valid keys are: %s', $key, implode(', ', self::$validKeys));
        }
    }

    /**
     * Generate an ID to use with the data storage.
     *
     * @param $key
     *
     * @return string
     */
    protected function getStorageKeyId($key)
    {
        return 'linkedIn_'.$key;
    }
}


================================================
FILE: src/Storage/DataStorageInterface.php
================================================
<?php

namespace Happyr\LinkedIn\Storage;

/**
 * We need to store data somewhere. It might be in a apc cache, filesystem cache, database or in the session.
 * We need it to protect us from CSRF attacks and to reduce the requests to the API.
 *
 * @author Tobias Nyholm
 */
interface DataStorageInterface
{
    /**
     * Stores the given ($key, $value) pair, so that future calls to
     * getPersistentData($key) return $value. This call may be in another request.
     *
     * @param string $key
     * @param mixed  $value
     */
    public function set($key, $value);

    /**
     * Get the data for $key, persisted by BaseFacebook::setPersistentData().
     *
     * @param string $key The key of the data to retrieve
     *
     * @return mixed
     */
    public function get($key);

    /**
     * Clear the data with $key from the persistent storage.
     *
     * @param string $key
     */
    public function clear($key);

    /**
     * Clear all data from the persistent storage.
     */
    public function clearAll();
}


================================================
FILE: src/Storage/IlluminateSessionStorage.php
================================================
<?php

namespace Happyr\LinkedIn\Storage;

use Illuminate\Support\Facades\Session;

/**
 * Store data in a IlluminateSession.
 *
 * @author Andreas Creten
 */
class IlluminateSessionStorage extends BaseDataStorage
{
    /**
     * {@inheritdoc}
     */
    public function set($key, $value)
    {
        $this->validateKey($key);
        $name = $this->getStorageKeyId($key);

        return Session::put($name, $value);
    }

    /**
     * {@inheritdoc}
     */
    public function get($key)
    {
        $this->validateKey($key);
        $name = $this->getStorageKeyId($key);

        return Session::get($name);
    }

    /**
     * {@inheritdoc}
     */
    public function clear($key)
    {
        $this->validateKey($key);
        $name = $this->getStorageKeyId($key);

        return Session::forget($name);
    }
}


================================================
FILE: src/Storage/SessionStorage.php
================================================
<?php

namespace Happyr\LinkedIn\Storage;

/**
 * Store data in the global session.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class SessionStorage extends BaseDataStorage
{
    public function __construct()
    {
        //start the session if it not already been started
        if (php_sapi_name() !== 'cli') {
            if (session_id() === '') {
                session_start();
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function set($key, $value)
    {
        $this->validateKey($key);

        $name = $this->getStorageKeyId($key);
        $_SESSION[$name] = $value;
    }

    /**
     * {@inheritdoc}
     */
    public function get($key)
    {
        $this->validateKey($key);
        $name = $this->getStorageKeyId($key);

        return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
    }

    /**
     * {@inheritdoc}
     */
    public function clear($key)
    {
        $this->validateKey($key);

        $name = $this->getStorageKeyId($key);
        if (isset($_SESSION[$name])) {
            unset($_SESSION[$name]);
        }
    }
}


================================================
FILE: tests/AccessTokenTest.php
================================================
<?php

namespace Happyr\LinkedIn;

/**
 * @author Tobias Nyholm
 */
class AccessTokenTest extends \PHPUnit_Framework_TestCase
{
    public function testToString()
    {
        $token = new AccessToken();
        $this->assertEquals('', $token);

        $token->setToken('foobar');
        $this->assertEquals('foobar', $token);
    }

    public function testConstructor()
    {
        $token = new AccessToken('foobar', 10);
        $this->assertInstanceOf('\DateTime', $token->getExpiresAt());
        $this->assertEquals('foobar', $token->getToken());

        $token = new AccessToken();
        $this->assertNull($token->getExpiresAt());
        $this->assertEmpty($token->getToken());

        $token = new AccessToken(null, new \DateTime('+2minutes'));
        $this->assertInstanceOf('\DateTime', $token->getExpiresAt());
    }

    public function testSetExpiresAt()
    {
        $token = new AccessToken();
        $token->setExpiresAt(new \DateTime('+2minutes'));
        $this->assertInstanceOf('\DateTime', $token->getExpiresAt());
    }
}


================================================
FILE: tests/AuthenticatorTest.php
================================================
<?php

namespace Happyr\LinkedIn;

use GuzzleHttp\Psr7\Response;
use Happyr\LinkedIn\Exception\LinkedInException;
use Mockery as m;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class AuthenticatorTest extends \PHPUnit_Framework_TestCase
{
    const APP_ID = '123456789';
    const APP_SECRET = '987654321';

    private function getRequestManagerMock()
    {
        return m::mock('Happyr\LinkedIn\Http\RequestManager');
    }

    public function testGetLoginUrl()
    {
        $expected = 'loginUrl';
        $state = 'random';
        $params = [
            'response_type' => 'code',
            'client_id' => self::APP_ID,
            'redirect_uri' => null,
            'state' => $state,
        ];

        $storage = $this->getMock('Happyr\LinkedIn\Storage\DataStorageInterface');
        $storage->method('get')->with('state')->willReturn($state);

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['establishCSRFTokenState', 'getStorage'], [$this->getRequestManagerMock(), self::APP_ID, self::APP_SECRET]);
        $auth->expects($this->exactly(2))->method('establishCSRFTokenState')->willReturn(null);
        $auth->method('getStorage')->will($this->returnValue($storage));

        $generator = m::mock('Happyr\LinkedIn\Http\LinkedInUrlGeneratorInterface')
            ->shouldReceive('getUrl')->once()->with('www', 'oauth/v2/authorization', $params)->andReturn($expected)
            ->getMock();

        $this->assertEquals($expected, $auth->getLoginUrl($generator));

        /*
         * Test with a url in the param
         */
        $otherUrl = 'otherUrl';
        $scope = ['foo', 'bar', 'baz'];
        $params = [
            'response_type' => 'code',
            'client_id' => self::APP_ID,
            'redirect_uri' => $otherUrl,
            'state' => $state,
            'scope' => 'foo bar baz',
        ];

        $generator = m::mock('Happyr\LinkedIn\Http\LinkedInUrlGeneratorInterface')
            ->shouldReceive('getUrl')->once()->with('www', 'oauth/v2/authorization', $params)->andReturn($expected)
            ->getMock();

        $this->assertEquals($expected, $auth->getLoginUrl($generator, ['redirect_uri' => $otherUrl, 'scope' => $scope]));
    }

    public function testFetchNewAccessToken()
    {
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator');
        $code = 'newCode';
        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('set')->once()->with('code', $code)
            ->shouldReceive('set')->once()->with('access_token', 'at')
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getCode', 'getStorage', 'getAccessTokenFromCode'], [], '', false);
        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));
        $auth->expects($this->once())->method('getAccessTokenFromCode')->with($generator, $code)->will($this->returnValue('at'));
        $auth->expects($this->once())->method('getCode')->will($this->returnValue($code));

        $this->assertEquals('at', $auth->fetchNewAccessToken($generator));
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInException
     */
    public function testFetchNewAccessTokenFail()
    {
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator');
        $code = 'newCode';
        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('clearAll')->once()
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getCode', 'getStorage', 'getAccessTokenFromCode'], [], '', false);
        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));
        $auth->expects($this->once())->method('getAccessTokenFromCode')->with($generator, $code)->willThrowException(new LinkedInException());
        $auth->expects($this->once())->method('getCode')->will($this->returnValue($code));

        $auth->fetchNewAccessToken($generator);
    }

    public function testFetchNewAccessTokenNoCode()
    {
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator');
        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('get')->with('code')->andReturn('foobar')
            ->shouldReceive('get')->once()->with('access_token')->andReturn('baz')
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getCode', 'getStorage'], [], '', false);
        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));
        $auth->expects($this->once())->method('getCode');

        $this->assertEquals('baz', $auth->fetchNewAccessToken($generator));
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInException
     */
    public function testGetAccessTokenFromCodeEmptyString()
    {
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator');

        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getAccessTokenFromCode');
        $method->setAccessible(true);
        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', [], [], '', false);

        $method->invoke($auth, $generator, '');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInException
     */
    public function testGetAccessTokenFromCodeNull()
    {
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator');

        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getAccessTokenFromCode');
        $method->setAccessible(true);
        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', [], [], '', false);

        $method->invoke($auth, $generator, null);
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInException
     */
    public function testGetAccessTokenFromCodeFalse()
    {
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator');

        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getAccessTokenFromCode');
        $method->setAccessible(true);
        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', [], [], '', false);

        $method->invoke($auth, $generator, false);
    }

    public function testGetAccessTokenFromCode()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getAccessTokenFromCode');
        $method->setAccessible(true);

        $code = 'code';
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator')
            ->shouldReceive('getUrl')->with(
                'www',
                'oauth/v2/accessToken'
            )->andReturn('url')
            ->getMock();

        $response = ['access_token' => 'foobar', 'expires_in' => 10];
        $auth = $this->prepareGetAccessTokenFromCode($code, $response);
        $token = $method->invoke($auth, $generator, $code);
        $this->assertEquals('foobar', $token, 'Standard get access token form code');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInException
     */
    public function testGetAccessTokenFromCodeNoTokenInResponse()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getAccessTokenFromCode');
        $method->setAccessible(true);

        $code = 'code';
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator')
            ->shouldReceive('getUrl')->with(
                'www',
                'oauth/v2/accessToken'
            )->andReturn('url')
            ->getMock();

        $response = ['foo' => 'bar'];
        $auth = $this->prepareGetAccessTokenFromCode($code, $response);
        $this->assertNull($method->invoke($auth, $generator, $code), 'Found array but no access token');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInException
     */
    public function testGetAccessTokenFromCodeEmptyResponse()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getAccessTokenFromCode');
        $method->setAccessible(true);

        $code = 'code';
        $generator = m::mock('Happyr\LinkedIn\Http\UrlGenerator')
            ->shouldReceive('getUrl')->with(
                'www',
                'oauth/v2/accessToken'
            )->andReturn('url')
            ->getMock();

        $response = '';
        $auth = $this->prepareGetAccessTokenFromCode($code, $response);
        $this->assertNull($method->invoke($auth, $generator, $code), 'Empty result');
    }

    /**
     * Default stuff for GetAccessTokenFromCode.
     *
     * @param $response
     *
     * @return array
     */
    protected function prepareGetAccessTokenFromCode($code, $responseData)
    {
        $response = new Response(200, [], json_encode($responseData));
        $currentUrl = 'foobar';

        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('get')->with('redirect_uri')->andReturn($currentUrl)
            ->getMock();

        $requestManager = m::mock('Happyr\LinkedIn\Http\RequestManager')
            ->shouldReceive('sendRequest')->once()->with('POST', 'url', [
                'Content-Type' => 'application/x-www-form-urlencoded',
            ], http_build_query([
                'grant_type' => 'authorization_code',
                'code' => $code,
                'redirect_uri' => $currentUrl,
                'client_id' => self::APP_ID,
                'client_secret' => self::APP_SECRET,
            ]))->andReturn($response)
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getStorage'], [$requestManager, self::APP_ID, self::APP_SECRET]);
        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));

        return $auth;
    }

    public function testEstablishCSRFTokenState()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'establishCSRFTokenState');
        $method->setAccessible(true);

        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('get')->with('state')->andReturn(null, 'state')
            ->shouldReceive('set')->once()->with('state', \Mockery::on(function (&$param) {
                return !empty($param);
            }))
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getStorage'], [], '', false);
        $auth->expects($this->any())->method('getStorage')->will($this->returnValue($storage));

        // Make sure we only set the state once
        $method->invoke($auth);
        $method->invoke($auth);
    }

    public function testGetCodeEmpty()
    {
        unset($_REQUEST['code']);
        unset($_GET['code']);

        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getCode');
        $method->setAccessible(true);
        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', [], [], '', false);

        $this->assertNull($method->invoke($auth));
    }

    public function testGetCode()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getCode');
        $method->setAccessible(true);
        $state = 'bazbar';

        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('clear')->once()->with('state')
            ->shouldReceive('get')->once()->with('code')->andReturn(null)
            ->shouldReceive('get')->once()->with('state')->andReturn($state)
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getStorage'], [], '', false);
        $auth->expects($this->once())->method('getStorage')->will($this->returnValue($storage));

        $_REQUEST['code'] = 'foobar';
        $_REQUEST['state'] = $state;

        $this->assertEquals('foobar', $method->invoke($auth));
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInException
     */
    public function testGetCodeInvalidCode()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getCode');
        $method->setAccessible(true);

        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('get')->once()->with('code')->andReturn(null)
            ->shouldReceive('get')->once()->with('state')->andReturn('bazbar')
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getStorage'], [], '', false);
        $auth->expects($this->once())->method('getStorage')->will($this->returnValue($storage));

        $_REQUEST['code'] = 'foobar';
        $_REQUEST['state'] = 'invalid';

        $this->assertEquals('foobar', $method->invoke($auth));
    }

    public function testGetCodeUsedCode()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getCode');
        $method->setAccessible(true);

        $storage = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface')
            ->shouldReceive('get')->once()->with('code')->andReturn('foobar')
            ->getMock();

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getStorage'], [], '', false);
        $auth->expects($this->once())->method('getStorage')->will($this->returnValue($storage));

        $_REQUEST['code'] = 'foobar';

        $this->assertEquals(null, $method->invoke($auth));
    }

    public function testStorageAccessors()
    {
        $method = new \ReflectionMethod('Happyr\LinkedIn\Authenticator', 'getStorage');
        $method->setAccessible(true);
        $requestManager = $this->getRequestManagerMock();
        $auth = new Authenticator($requestManager, self::APP_ID, self::APP_SECRET);

        // test default
        $this->assertInstanceOf('Happyr\LinkedIn\Storage\SessionStorage', $method->invoke($auth));

        $object = m::mock('Happyr\LinkedIn\Storage\DataStorageInterface');
        $auth->setStorage($object);
        $this->assertEquals($object, $method->invoke($auth));
    }
}


================================================
FILE: tests/Exceptions/LoginErrorTest.php
================================================
<?php

namespace Happyr\LinkedIn\Exception;

/**
 * Class LoginErrorTest.
 *
 * @author Tobias Nyholm
 */
class LoginErrorTest extends \PHPUnit_Framework_TestCase
{
    public function testGetters()
    {
        $error = new LoginError('foo', 'bar');

        $this->assertEquals('foo', $error->getName());
        $this->assertEquals('bar', $error->getDescription());
    }
}


================================================
FILE: tests/Http/ResponseConverterTest.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

use GuzzleHttp\Psr7\Response;

class ResponseConverterTest extends \PHPUnit_Framework_TestCase
{
    public function testConvert()
    {
        $body = '{"foo":"bar"}';
        $response = new Response(200, [], $body);

        $result = ResponseConverter::convert($response, 'json', 'psr7');
        $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $result);

        $result = ResponseConverter::convert($response, 'json', 'stream');
        $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $result);

        $result = ResponseConverter::convert($response, 'json', 'string');
        $this->assertTrue(is_string($result));
        $this->assertEquals($body, $result);

        $result = ResponseConverter::convert($response, 'json', 'array');
        $this->assertTrue(is_array($result));

        $body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
  <firstname>foo</firstname>
  <lastname>bar</lastname>
</person>
';
        $response = new Response(200, [], $body);
        $result = ResponseConverter::convert($response, 'xml', 'simple_xml');
        $this->assertInstanceOf('\SimpleXMLElement', $result);
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\InvalidArgumentException
     */
    public function testConvertJsonToSimpleXml()
    {
        $body = '{"foo":"bar"}';
        $response = new Response(200, [], $body);

        ResponseConverter::convert($response, 'json', 'simple_xml');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\InvalidArgumentException
     */
    public function testConvertXmlToArray()
    {
        $body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
  <firstname>foo</firstname>
  <lastname>bar</lastname>
</person>
';
        $response = new Response(200, [], $body);

        ResponseConverter::convert($response, 'xml', 'array');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\InvalidArgumentException
     */
    public function testConvertJsonToFoobar()
    {
        $body = '{"foo":"bar"}';
        $response = new Response(200, [], $body);

        ResponseConverter::convert($response, 'json', 'foobar');
    }

    public function testConvertToSimpleXml()
    {
        $body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
  <firstname>foo</firstname>
  <lastname>bar</lastname>
</person>
';

        $response = new Response(200, [], $body);
        $result = ResponseConverter::convertToSimpleXml($response);

        $this->assertInstanceOf('\SimpleXMLElement', $result);
        $this->assertEquals('foo', $result->firstname);
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\LinkedInTransferException
     */
    public function testConvertToSimpleXmlError()
    {
        $body = '{Foo: bar}';

        $response = new Response(200, [], $body);
        $result = ResponseConverter::convertToSimpleXml($response);

        $this->assertInstanceOf('\SimpleXMLElement', $result);
        $this->assertEquals('foo', $result->firstname);
    }
}


================================================
FILE: tests/Http/UrlGeneratorTest.php
================================================
<?php

namespace Happyr\LinkedIn\Http;

/**
 * Class UrlGeneratorTest.
 *
 * @author Tobias Nyholm
 */
class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
{
    public function testDropLinkedInParams()
    {
        $gen = new DummyUrlGenerator();

        $test = 'foo=bar&code=foobar&baz=foo';
        $expected = '?foo=bar&baz=foo';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        $test = 'code=foobar&baz=foo';
        $expected = '?baz=foo';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        $test = 'foo=bar&code=foobar';
        $expected = '?foo=bar';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        $test = 'code=foobar';
        $expected = '';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        $test = '';
        $expected = '';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        /* ----------------- */

        $test = 'foo=bar&code=';
        $expected = '?foo=bar';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        $test = 'code=';
        $expected = '';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        $test = 'foo=bar&code';
        $expected = '?foo=bar';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));

        $test = 'code';
        $expected = '';
        $this->assertEquals($expected, $gen->dropLinkedInParams($test));
    }

    public function testGetUrl()
    {
        $gen = new DummyUrlGenerator();

        $expected = 'https://api.linkedin.com/?bar=baz';
        $this->assertEquals($expected, $gen->getUrl('api', '', ['bar' => 'baz']), 'No path');

        $expected = 'https://api.linkedin.com/foobar';
        $this->assertEquals($expected, $gen->getUrl('api', 'foobar'), 'Path does not begin with forward slash');
        $this->assertEquals($expected, $gen->getUrl('api', '/foobar'), 'Path begins with forward slash');

        $expected = 'https://api.linkedin.com/foobar?bar=baz';
        $this->assertEquals($expected, $gen->getUrl('api', 'foobar', ['bar' => 'baz']), 'One parameter');

        $expected = 'https://api.linkedin.com/foobar?bar=baz&a=b&c=d';
        $this->assertEquals($expected, $gen->getUrl('api', 'foobar', ['bar' => 'baz', 'a' => 'b', 'c' => 'd']), 'Many parameters');

        $expected = 'https://api.linkedin.com/foobar?bar=baz%20a%20b';
        $notExpected = 'https://api.linkedin.com/foobar?bar=baz+a+b';
        $this->assertEquals($expected, $gen->getUrl('api', 'foobar', ['bar' => 'baz a b']), 'Use of PHP_QUERY_RFC3986');
        $this->assertNotEquals($notExpected, $gen->getUrl('api', 'foobar', ['bar' => 'baz a b']), 'Dont use PHP_QUERY_RFC1738');
    }

    public function testGetUrlWithParams()
    {
        $gen = new UrlGenerator();

        $expected = 'https://api.linkedin.com/endpoint?bar=baz&format=json';
        $this->assertEquals($expected, $gen->getUrl('api', 'endpoint?bar=baz', ['format' => 'json']));

        $expected = 'https://api.linkedin.com/endpoint?bar=baz&bar=baz';
        $this->assertEquals($expected, $gen->getUrl('api', 'endpoint?bar=baz', ['bar' => 'baz']));
    }

    public function testGetCurrentURL()
    {
        $gen = $this->getMock('Happyr\LinkedIn\Http\UrlGenerator', ['getHttpProtocol', 'getHttpHost', 'dropLinkedInParams'], []);
        $gen->expects($this->any())->method('getHttpProtocol')->will($this->returnValue('http'));
        $gen->expects($this->any())->method('getHttpHost')->will($this->returnValue('www.test.com'));
        $gen->expects($this->any())->method('dropLinkedInParams')->will($this->returnCallback(function ($arg) {
            return empty($arg) ? '' : '?'.$arg;
        }));

        // fake the HPHP $_SERVER globals
        $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=one&two=two&three=three';
        $this->assertEquals(
            'http://www.test.com/unit-tests.php?one=one&two=two&three=three',
            $gen->getCurrentUrl(),
            'getCurrentUrl function is changing the current URL');

        // ensure structure of valueless GET params is retained (sometimes
        // an = sign was present, and sometimes it was not)
        // first test when equal signs are present
        $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=&two=&three=';
        $this->assertEquals(
            'http://www.test.com/unit-tests.php?one=&two=&three=',
            $gen->getCurrentUrl(),
            'getCurrentUrl function is changing the current URL');

        // now confirm that
        $_SERVER['REQUEST_URI'] = '/unit-tests.php?one&two&three';
        $this->assertEquals(
            'http://www.test.com/unit-tests.php?one&two&three',
            $gen->getCurrentUrl(),
            'getCurrentUrl function is changing the current URL'
        );
    }

    public function testGetCurrentURLPort80()
    {
        $gen = $this->getMock('Happyr\LinkedIn\Http\UrlGenerator', ['getHttpProtocol', 'getHttpHost', 'dropLinkedInParams'], []);
        $gen->expects($this->any())->method('getHttpProtocol')->will($this->returnValue('http'));
        $gen->expects($this->any())->method('getHttpHost')->will($this->returnValue('www.test.com:80'));
        $gen->expects($this->any())->method('dropLinkedInParams')->will($this->returnCallback(function ($arg) {
            return empty($arg) ? '' : '?'.$arg;
        }));

        //test port 80
        $_SERVER['REQUEST_URI'] = '/foobar.php';
        $this->assertEquals(
            'http://www.test.com/foobar.php',
            $gen->getCurrentUrl(),
            'port 80 should not be shown'
        );
    }

    public function testGetCurrentURLPort8080()
    {
        $gen = $this->getMock('Happyr\LinkedIn\Http\UrlGenerator', ['getHttpProtocol', 'getHttpHost', 'dropLinkedInParams'], []);
        $gen->expects($this->any())->method('getHttpProtocol')->will($this->returnValue('http'));
        $gen->expects($this->any())->method('getHttpHost')->will($this->returnValue('www.test.com:8080'));
        $gen->expects($this->any())->method('dropLinkedInParams')->will($this->returnCallback(function ($arg) {
            return empty($arg) ? '' : '?'.$arg;
        }));

        //test non default port 8080
        $_SERVER['REQUEST_URI'] = '/foobar.php';
        $this->assertEquals(
            'http://www.test.com:8080/foobar.php',
            $gen->getCurrentUrl(),
            'port 80 should not be shown'
        );
    }

    public function testHttpHost()
    {
        $real = 'foo.com';
        $_SERVER['HTTP_HOST'] = $real;
        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'evil.com';
        $gen = new DummyUrlGenerator();
        $this->assertEquals($real, $gen->GetHttpHost());
    }

    public function testHttpProtocolApache()
    {
        $_SERVER['HTTPS'] = 'on';
        $gen = new DummyUrlGenerator();
        $this->assertEquals('https', $gen->GetHttpProtocol());
    }

    public function testHttpProtocolNginx()
    {
        $_SERVER['SERVER_PORT'] = '443';
        $gen = new DummyUrlGenerator();
        $this->assertEquals('https', $gen->GetHttpProtocol());
    }

    public function testHttpHostForwarded()
    {
        $real = 'foo.com';
        $_SERVER['HTTP_HOST'] = 'localhost';
        $_SERVER['HTTP_X_FORWARDED_HOST'] = $real;
        $gen = new DummyUrlGenerator();
        $gen->setTrustForwarded(true);
        $this->assertEquals($real, $gen->GetHttpHost());
    }

    public function testHttpProtocolForwarded()
    {
        $_SERVER['HTTPS'] = 'on';
        $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
        $gen = new DummyUrlGenerator();
        $gen->setTrustForwarded(true);
        $this->assertEquals('http', $gen->GetHttpProtocol());
    }

    public function testHttpProtocolForwardedSecure()
    {
        $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
        $gen = new DummyUrlGenerator();
        $this->assertEquals('http', $gen->GetHttpProtocol());

        $gen->setTrustForwarded(true);
        $this->assertEquals('https', $gen->GetHttpProtocol());
    }

    protected function tearDown()
    {
        unset($_SERVER['HTTPS']);
        unset($_SERVER['HTTP_X_FORWARDED_PROTO']);
        $_SERVER['HTTP_HOST'] = 'localhost';
        unset($_SERVER['HTTP_X_FORWARDED_HOST']);
        $_SERVER['SERVER_PORT'] = '80';
        $_SERVER['REQUEST_URI'] = '';
    }
}

class DummyUrlGenerator extends UrlGenerator
{
    public function getHttpHost()
    {
        return parent::getHttpHost();
    }

    public function getHttpProtocol()
    {
        return parent::getHttpProtocol();
    }

    public function dropLinkedInParams($query)
    {
        return parent::dropLinkedInParams($query);
    }
}


================================================
FILE: tests/LinkedInTest.php
================================================
<?php

namespace Happyr\LinkedIn;

use GuzzleHttp\Psr7\Response;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class LinkedInTest extends \PHPUnit_Framework_TestCase
{
    const APP_ID = '123456789';
    const APP_SECRET = '987654321';

    public function testApi()
    {
        $resource = 'resource';
        $token = 'token';
        $urlParams = ['url' => 'foo'];
        $postParams = ['post' => 'bar'];
        $method = 'GET';
        $expected = ['foobar' => 'test'];
        $response = new Response(200, [], json_encode($expected));
        $url = 'http://example.com/test';

        $headers = ['Authorization' => 'Bearer '.$token, 'Content-Type' => 'application/json', 'x-li-format' => 'json'];

        $generator = $this->getMock('Happyr\LinkedIn\Http\UrlGenerator', ['getUrl']);
        $generator->expects($this->once())->method('getUrl')->with(
            $this->equalTo('api'),
            $this->equalTo($resource),
            $this->equalTo([
                'url' => 'foo',
                'format' => 'json',
            ]))
            ->willReturn($url);

        $requestManager = $this->getMock('Happyr\LinkedIn\Http\RequestManager', ['sendRequest']);
        $requestManager->expects($this->once())->method('sendRequest')->with(
                $this->equalTo($method),
                $this->equalTo($url),
                $this->equalTo($headers),
                $this->equalTo(json_encode($postParams)))
            ->willReturn($response);

        $linkedIn = $this->getMock('Happyr\LinkedIn\LinkedIn', ['getAccessToken', 'getUrlGenerator', 'getRequestManager'], [self::APP_ID, self::APP_SECRET]);

        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn($token);
        $linkedIn->expects($this->once())->method('getUrlGenerator')->willReturn($generator);
        $linkedIn->expects($this->once())->method('getRequestManager')->willReturn($requestManager);

        $result = $linkedIn->api($method, $resource, ['query' => $urlParams, 'json' => $postParams]);
        $this->assertEquals($expected, $result);
    }

    public function testIsAuthenticated()
    {
        $linkedIn = $this->getMock('Happyr\LinkedIn\LinkedIn', ['getAccessToken'], [self::APP_ID, self::APP_SECRET]);
        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn(null);
        $this->assertFalse($linkedIn->isAuthenticated());

        $linkedIn = $this->getMock('Happyr\LinkedIn\LinkedIn', ['api', 'getAccessToken'], [self::APP_ID, self::APP_SECRET]);
        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn('token');
        $linkedIn->expects($this->once())->method('api')->willReturn(['id' => 4711]);
        $this->assertTrue($linkedIn->isAuthenticated());

        $linkedIn = $this->getMock('Happyr\LinkedIn\LinkedIn', ['api', 'getAccessToken'], [self::APP_ID, self::APP_SECRET]);
        $linkedIn->expects($this->once())->method('getAccessToken')->willReturn('token');
        $linkedIn->expects($this->once())->method('api')->willReturn(['foobar' => 4711]);
        $this->assertFalse($linkedIn->isAuthenticated());
    }

    /**
     * Test a call to getAccessToken when there is no token.
     */
    public function testAccessTokenAccessors()
    {
        $token = 'token';

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['fetchNewAccessToken'], [], '', false);
        $auth->expects($this->once())->method('fetchNewAccessToken')->will($this->returnValue($token));

        $linkedIn = $this->getMock('Happyr\LinkedIn\LinkedIn', ['getAuthenticator'], [], '', false);
        $linkedIn->expects($this->once())->method('getAuthenticator')->willReturn($auth);

        // Make sure we go to the authenticator only once
        $this->assertEquals($token, $linkedIn->getAccessToken());
        $this->assertEquals($token, $linkedIn->getAccessToken());
    }

    public function testGeneratorAccessors()
    {
        $get = new \ReflectionMethod('Happyr\LinkedIn\LinkedIn', 'getUrlGenerator');
        $get->setAccessible(true);
        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);

        // test default
        $this->assertInstanceOf('Happyr\LinkedIn\Http\UrlGenerator', $get->invoke($linkedIn));

        $object = $this->getMock('Happyr\LinkedIn\Http\UrlGenerator');
        $linkedIn->setUrlGenerator($object);
        $this->assertEquals($object, $get->invoke($linkedIn));
    }

    public function testHasError()
    {
        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);

        unset($_GET['error']);
        $this->assertFalse($linkedIn->hasError());

        $_GET['error'] = 'foobar';
        $this->assertTrue($linkedIn->hasError());
    }

    public function testGetError()
    {
        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);

        unset($_GET['error']);
        unset($_GET['error_description']);

        $this->assertNull($linkedIn->getError());

        $_GET['error'] = 'foo';
        $_GET['error_description'] = 'bar';

        $this->assertEquals('foo', $linkedIn->getError()->getName());
        $this->assertEquals('bar', $linkedIn->getError()->getDescription());
    }

    public function testGetErrorWithMissingDescription()
    {
        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);

        unset($_GET['error']);
        unset($_GET['error_description']);

        $_GET['error'] = 'foo';

        $this->assertEquals('foo', $linkedIn->getError()->getName());
        $this->assertNull($linkedIn->getError()->getDescription());
    }

    public function testFormatAccessors()
    {
        $get = new \ReflectionMethod('Happyr\LinkedIn\LinkedIn', 'getFormat');
        $get->setAccessible(true);
        $linkedIn = new LinkedIn(self::APP_ID, self::APP_SECRET);

        //test default
        $this->assertEquals('json', $get->invoke($linkedIn));

        $format = 'foo';
        $linkedIn->setFormat($format);
        $this->assertEquals($format, $get->invoke($linkedIn));
    }

    public function testLoginUrl()
    {
        $currentUrl = 'currentUrl';
        $loginUrl = 'result';

        $generator = $this->getMock('Happyr\LinkedIn\Http\UrlGenerator', ['getCurrentUrl']);
        $generator->expects($this->once())->method('getCurrentUrl')->willReturn($currentUrl);

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getLoginUrl'], [], '', false);
        $auth->expects($this->once())->method('getLoginUrl')
            ->with($generator, ['redirect_uri' => $currentUrl])
            ->will($this->returnValue($loginUrl));

        $linkedIn = $this->getMock('Happyr\LinkedIn\LinkedIn', ['getAuthenticator', 'getUrlGenerator'], [], '', false);
        $linkedIn->expects($this->once())->method('getAuthenticator')->willReturn($auth);
        $linkedIn->expects($this->once())->method('getUrlGenerator')->willReturn($generator);

        $linkedIn->getLoginUrl();
    }

    public function testLoginUrlWithParameter()
    {
        $loginUrl = 'result';
        $otherUrl = 'otherUrl';

        $generator = $this->getMock('Happyr\LinkedIn\Http\UrlGenerator');

        $auth = $this->getMock('Happyr\LinkedIn\Authenticator', ['getLoginUrl'], [], '', false);
        $auth->expects($this->once())->method('getLoginUrl')
            ->with($generator, ['redirect_uri' => $otherUrl])
            ->will($this->returnValue($loginUrl));

        $linkedIn = $this->getMock('Happyr\LinkedIn\LinkedIn', ['getAuthenticator', 'getUrlGenerator'], [], '', false);
        $linkedIn->expects($this->once())->method('getAuthenticator')->willReturn($auth);
        $linkedIn->expects($this->once())->method('getUrlGenerator')->willReturn($generator);

        $linkedIn->getLoginUrl(['redirect_uri' => $otherUrl]);
    }
}


================================================
FILE: tests/Storage/IlluminateSessionStorageTest.php
================================================
<?php

namespace Happyr\LinkedIn\Storage;

use Illuminate\Support\Facades\Session;

/**
 * Class SessionStorageTest.
 *
 * @author Andreas Creten
 */
class IlluminateSessionStorageTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var \Happyr\LinkedIn\Storage\SessionStorage storage
     */
    protected $storage;

    protected $prefix = 'linkedIn_';

    public function setUp()
    {
        $this->storage = new IlluminateSessionStorage();
    }

    public function testSet()
    {
        Session::shouldReceive('put')->once()->with($this->prefix.'code', 'foobar');

        $this->storage->set('code', 'foobar');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\InvalidArgumentException
     */
    public function testSetFail()
    {
        $this->storage->set('foobar', 'baz');
    }

    public function testGet()
    {
        $expected = 'foobar';
        Session::shouldReceive('get')->once()->with($this->prefix.'code')->andReturn($expected);
        $result = $this->storage->get('code');
        $this->assertEquals($expected, $result);

        Session::shouldReceive('get')->once()->with($this->prefix.'state')->andReturn(null);
        $result = $this->storage->get('state');
        $this->assertNull($result);
    }

    public function testClear()
    {
        Session::shouldReceive('forget')->once()->with($this->prefix.'code')->andReturn(true);
        $this->storage->clear('code');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\InvalidArgumentException
     */
    public function testClearFail()
    {
        $this->storage->clear('foobar');
    }
}


================================================
FILE: tests/Storage/SessionStorageTest.php
================================================
<?php

namespace Happyr\LinkedIn\Storage;

use Mockery as m;

/**
 * Class SessionStorageTest.
 *
 * @author Tobias Nyholm
 */
class SessionStorageTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var \Happyr\LinkedIn\Storage\SessionStorage storage
     */
    protected $storage;

    protected $prefix = 'linkedIn_';

    public function setUp()
    {
        $this->storage = new SessionStorage();
    }

    public function testSet()
    {
        $this->storage->set('code', 'foobar');
        $this->assertEquals($_SESSION[$this->prefix.'code'], 'foobar');
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\InvalidArgumentException
     */
    public function testSetFail()
    {
        $this->storage->set('foobar', 'baz');
    }

    public function testGet()
    {
        unset($_SESSION[$this->prefix.'state']);
        $result = $this->storage->get('state');
        $this->assertNull($result);

        $expected = 'foobar';
        $_SESSION[$this->prefix.'code'] = $expected;
        $result = $this->storage->get('code');
        $this->assertEquals($expected, $result);
    }

    public function testClear()
    {
        $_SESSION[$this->prefix.'code'] = 'foobar';
        $this->storage->clear('code');
        $this->assertFalse(isset($_SESSION[$this->prefix.'code']));
    }

    /**
     * @expectedException \Happyr\LinkedIn\Exception\InvalidArgumentException
     */
    public function testClearFail()
    {
        $this->storage->clear('foobar');
    }

    public function testClearAll()
    {
        $validKeys = SessionStorage::$validKeys;

        $storage = m::mock('Happyr\LinkedIn\Storage\SessionStorage[clear]')
            ->shouldReceive('clear')->times(count($validKeys))
            ->with(m::on(function ($arg) use ($validKeys) {
                return in_array($arg, $validKeys);
            }))
            ->getMock();

        $storage->clearAll();
    }
}
Download .txt
gitextract__ze4uh7e/

├── .github/
│   ├── ISSUE_TEMPLATE.md
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .scrutinizer.yml
├── .styleci.yml
├── .travis.yml
├── LICENSE
├── Readme.md
├── Upgrade.md
├── composer.json
├── phpunit.xml.dist
├── src/
│   ├── AccessToken.php
│   ├── Authenticator.php
│   ├── AuthenticatorInterface.php
│   ├── Exception/
│   │   ├── InvalidArgumentException.php
│   │   ├── LinkedInException.php
│   │   ├── LinkedInTransferException.php
│   │   └── LoginError.php
│   ├── Http/
│   │   ├── CurrentUrlGeneratorInterface.php
│   │   ├── GlobalVariableGetter.php
│   │   ├── LinkedInUrlGeneratorInterface.php
│   │   ├── RequestManager.php
│   │   ├── RequestManagerInterface.php
│   │   ├── ResponseConverter.php
│   │   ├── UrlGenerator.php
│   │   └── UrlGeneratorInterface.php
│   ├── LinkedIn.php
│   ├── LinkedInInterface.php
│   └── Storage/
│       ├── BaseDataStorage.php
│       ├── DataStorageInterface.php
│       ├── IlluminateSessionStorage.php
│       └── SessionStorage.php
└── tests/
    ├── AccessTokenTest.php
    ├── AuthenticatorTest.php
    ├── Exceptions/
    │   └── LoginErrorTest.php
    ├── Http/
    │   ├── ResponseConverterTest.php
    │   └── UrlGeneratorTest.php
    ├── LinkedInTest.php
    └── Storage/
        ├── IlluminateSessionStorageTest.php
        └── SessionStorageTest.php
Download .txt
SYMBOL INDEX (199 symbols across 29 files)

FILE: src/AccessToken.php
  class AccessToken (line 8) | class AccessToken
    method __construct (line 24) | public function __construct($token = null, $expiresIn = null)
    method __toString (line 40) | public function __toString()
    method hasToken (line 50) | public function hasToken()
    method setExpiresAt (line 60) | public function setExpiresAt(\DateTime $expiresAt = null)
    method getExpiresAt (line 70) | public function getExpiresAt()
    method setToken (line 80) | public function setToken($token)
    method getToken (line 90) | public function getToken()

FILE: src/Authenticator.php
  class Authenticator (line 18) | class Authenticator implements AuthenticatorInterface
    method __construct (line 51) | public function __construct(RequestManagerInterface $requestManager, $...
    method fetchNewAccessToken (line 61) | public function fetchNewAccessToken(LinkedInUrlGeneratorInterface $url...
    method getAccessTokenFromCode (line 104) | protected function getAccessTokenFromCode(LinkedInUrlGeneratorInterfac...
    method getLoginUrl (line 148) | public function getLoginUrl(LinkedInUrlGeneratorInterface $urlGenerato...
    method getCode (line 188) | protected function getCode()
    method establishCSRFTokenState (line 225) | protected function establishCSRFTokenState()
    method clearStorage (line 236) | public function clearStorage()
    method getStorage (line 246) | protected function getStorage()
    method setStorage (line 258) | public function setStorage(DataStorageInterface $storage)
    method getRequestManager (line 268) | protected function getRequestManager()

FILE: src/AuthenticatorInterface.php
  type AuthenticatorInterface (line 14) | interface AuthenticatorInterface
    method fetchNewAccessToken (line 25) | public function fetchNewAccessToken(LinkedInUrlGeneratorInterface $url...
    method getLoginUrl (line 35) | public function getLoginUrl(LinkedInUrlGeneratorInterface $urlGenerato...
    method clearStorage (line 42) | public function clearStorage();
    method setStorage (line 49) | public function setStorage(DataStorageInterface $storage);

FILE: src/Exception/InvalidArgumentException.php
  class InvalidArgumentException (line 5) | class InvalidArgumentException extends LinkedInException
    method __construct (line 10) | public function __construct()

FILE: src/Exception/LinkedInException.php
  class LinkedInException (line 10) | class LinkedInException extends \Exception

FILE: src/Exception/LinkedInTransferException.php
  class LinkedInTransferException (line 10) | class LinkedInTransferException extends LinkedInException

FILE: src/Exception/LoginError.php
  class LoginError (line 10) | class LoginError
    method __construct (line 26) | public function __construct($name, $description)
    method getName (line 35) | public function getName()
    method getDescription (line 43) | public function getDescription()
    method __toString (line 51) | public function __toString()

FILE: src/Http/CurrentUrlGeneratorInterface.php
  type CurrentUrlGeneratorInterface (line 10) | interface CurrentUrlGeneratorInterface
    method getCurrentUrl (line 17) | public function getCurrentUrl();
    method setTrustForwarded (line 26) | public function setTrustForwarded($trustForwarded);

FILE: src/Http/GlobalVariableGetter.php
  class GlobalVariableGetter (line 10) | class GlobalVariableGetter
    method has (line 19) | public static function has($name)
    method get (line 35) | public static function get($name)

FILE: src/Http/LinkedInUrlGeneratorInterface.php
  type LinkedInUrlGeneratorInterface (line 10) | interface LinkedInUrlGeneratorInterface
    method getUrl (line 21) | public function getUrl($name, $path = '', $params = []);

FILE: src/Http/RequestManager.php
  class RequestManager (line 17) | class RequestManager implements RequestManagerInterface
    method sendRequest (line 32) | public function sendRequest($method, $uri, array $headers = [], $body ...
    method setHttpClient (line 46) | public function setHttpClient(HttpClient $httpClient)
    method getHttpClient (line 56) | protected function getHttpClient()
    method setMessageFactory (line 70) | public function setMessageFactory(MessageFactory $messageFactory)
    method getMessageFactory (line 80) | private function getMessageFactory()

FILE: src/Http/RequestManagerInterface.php
  type RequestManagerInterface (line 13) | interface RequestManagerInterface
    method sendRequest (line 28) | public function sendRequest($method, $uri, array $headers = [], $body ...
    method setHttpClient (line 35) | public function setHttpClient(HttpClient $httpClient);

FILE: src/Http/ResponseConverter.php
  class ResponseConverter (line 9) | class ResponseConverter
    method convert (line 23) | public static function convert(ResponseInterface $response, $requestFo...
    method convertToArray (line 51) | public static function convertToArray(ResponseInterface $response)
    method convertToSimpleXml (line 63) | public static function convertToSimpleXml(ResponseInterface $response)

FILE: src/Http/UrlGenerator.php
  class UrlGenerator (line 8) | class UrlGenerator implements UrlGeneratorInterface
    method getUrl (line 37) | public function getUrl($name, $path = '', $params = [])
    method getCurrentUrl (line 66) | public function getCurrentUrl()
    method dropLinkedInParams (line 97) | protected function dropLinkedInParams($query)
    method getHttpHost (line 129) | protected function getHttpHost()
    method getHttpProtocol (line 144) | protected function getHttpProtocol()
    method setTrustForwarded (line 172) | public function setTrustForwarded($trustForwarded)

FILE: src/Http/UrlGeneratorInterface.php
  type UrlGeneratorInterface (line 8) | interface UrlGeneratorInterface extends LinkedInUrlGeneratorInterface, C...

FILE: src/LinkedIn.php
  class LinkedIn (line 33) | class LinkedIn implements LinkedInInterface
    method __construct (line 81) | public function __construct($appId, $appSecret, $format = 'json', $res...
    method isAuthenticated (line 93) | public function isAuthenticated()
    method api (line 108) | public function api($method, $resource, array $options = [])
    method filterRequestOption (line 143) | protected function filterRequestOption(array &$options)
    method getLoginUrl (line 173) | public function getLoginUrl($options = [])
    method get (line 193) | public function get($resource, array $options = [])
    method post (line 206) | public function post($resource, array $options = [])
    method clearStorage (line 214) | public function clearStorage()
    method hasError (line 224) | public function hasError()
    method getError (line 232) | public function getError()
    method getFormat (line 244) | protected function getFormat()
    method setFormat (line 252) | public function setFormat($format)
    method getResponseDataType (line 264) | protected function getResponseDataType()
    method setResponseDataType (line 272) | public function setResponseDataType($responseDataType)
    method getLastResponse (line 282) | public function getLastResponse()
    method getAccessToken (line 290) | public function getAccessToken()
    method setAccessToken (line 305) | public function setAccessToken($accessToken)
    method setUrlGenerator (line 319) | public function setUrlGenerator(UrlGeneratorInterface $urlGenerator)
    method getUrlGenerator (line 329) | protected function getUrlGenerator()
    method setStorage (line 341) | public function setStorage(DataStorageInterface $storage)
    method setHttpClient (line 351) | public function setHttpClient(HttpClient $client)
    method setHttpMessageFactory (line 361) | public function setHttpMessageFactory(MessageFactory $factory)
    method getRequestManager (line 371) | protected function getRequestManager()
    method getAuthenticator (line 379) | protected function getAuthenticator()

FILE: src/LinkedInInterface.php
  type LinkedInInterface (line 17) | interface LinkedInInterface
    method isAuthenticated (line 24) | public function isAuthenticated();
    method api (line 45) | public function api($method, $resource, array $options = []);
    method getLoginUrl (line 58) | public function getLoginUrl($options = []);
    method get (line 68) | public function get($resource, array $options = []);
    method post (line 78) | public function post($resource, array $options = []);
    method clearStorage (line 85) | public function clearStorage();
    method hasError (line 92) | public function hasError();
    method getError (line 99) | public function getError();
    method setFormat (line 108) | public function setFormat($format);
    method setResponseDataType (line 117) | public function setResponseDataType($responseDataType);
    method getLastResponse (line 124) | public function getLastResponse();
    method getAccessToken (line 131) | public function getAccessToken();
    method setAccessToken (line 141) | public function setAccessToken($accessToken);
    method setUrlGenerator (line 150) | public function setUrlGenerator(UrlGeneratorInterface $urlGenerator);
    method setStorage (line 159) | public function setStorage(DataStorageInterface $storage);
    method setHttpClient (line 168) | public function setHttpClient(HttpClient $client);
    method setHttpMessageFactory (line 177) | public function setHttpMessageFactory(MessageFactory $factory);

FILE: src/Storage/BaseDataStorage.php
  class BaseDataStorage (line 10) | abstract class BaseDataStorage implements DataStorageInterface
    method clearAll (line 17) | public function clearAll()
    method validateKey (line 31) | protected function validateKey($key)
    method getStorageKeyId (line 45) | protected function getStorageKeyId($key)

FILE: src/Storage/DataStorageInterface.php
  type DataStorageInterface (line 11) | interface DataStorageInterface
    method set (line 20) | public function set($key, $value);
    method get (line 29) | public function get($key);
    method clear (line 36) | public function clear($key);
    method clearAll (line 41) | public function clearAll();

FILE: src/Storage/IlluminateSessionStorage.php
  class IlluminateSessionStorage (line 12) | class IlluminateSessionStorage extends BaseDataStorage
    method set (line 17) | public function set($key, $value)
    method get (line 28) | public function get($key)
    method clear (line 39) | public function clear($key)

FILE: src/Storage/SessionStorage.php
  class SessionStorage (line 10) | class SessionStorage extends BaseDataStorage
    method __construct (line 12) | public function __construct()
    method set (line 25) | public function set($key, $value)
    method get (line 36) | public function get($key)
    method clear (line 47) | public function clear($key)

FILE: tests/AccessTokenTest.php
  class AccessTokenTest (line 8) | class AccessTokenTest extends \PHPUnit_Framework_TestCase
    method testToString (line 10) | public function testToString()
    method testConstructor (line 19) | public function testConstructor()
    method testSetExpiresAt (line 33) | public function testSetExpiresAt()

FILE: tests/AuthenticatorTest.php
  class AuthenticatorTest (line 12) | class AuthenticatorTest extends \PHPUnit_Framework_TestCase
    method getRequestManagerMock (line 17) | private function getRequestManagerMock()
    method testGetLoginUrl (line 22) | public function testGetLoginUrl()
    method testFetchNewAccessToken (line 66) | public function testFetchNewAccessToken()
    method testFetchNewAccessTokenFail (line 86) | public function testFetchNewAccessTokenFail()
    method testFetchNewAccessTokenNoCode (line 102) | public function testFetchNewAccessTokenNoCode()
    method testGetAccessTokenFromCodeEmptyString (line 120) | public function testGetAccessTokenFromCodeEmptyString()
    method testGetAccessTokenFromCodeNull (line 134) | public function testGetAccessTokenFromCodeNull()
    method testGetAccessTokenFromCodeFalse (line 148) | public function testGetAccessTokenFromCodeFalse()
    method testGetAccessTokenFromCode (line 159) | public function testGetAccessTokenFromCode()
    method testGetAccessTokenFromCodeNoTokenInResponse (line 181) | public function testGetAccessTokenFromCodeNoTokenInResponse()
    method testGetAccessTokenFromCodeEmptyResponse (line 202) | public function testGetAccessTokenFromCodeEmptyResponse()
    method prepareGetAccessTokenFromCode (line 227) | protected function prepareGetAccessTokenFromCode($code, $responseData)
    method testEstablishCSRFTokenState (line 254) | public function testEstablishCSRFTokenState()
    method testGetCodeEmpty (line 274) | public function testGetCodeEmpty()
    method testGetCode (line 286) | public function testGetCode()
    method testGetCodeInvalidCode (line 310) | public function testGetCodeInvalidCode()
    method testGetCodeUsedCode (line 329) | public function testGetCodeUsedCode()
    method testStorageAccessors (line 346) | public function testStorageAccessors()

FILE: tests/Exceptions/LoginErrorTest.php
  class LoginErrorTest (line 10) | class LoginErrorTest extends \PHPUnit_Framework_TestCase
    method testGetters (line 12) | public function testGetters()

FILE: tests/Http/ResponseConverterTest.php
  class ResponseConverterTest (line 7) | class ResponseConverterTest extends \PHPUnit_Framework_TestCase
    method testConvert (line 9) | public function testConvert()
    method testConvertJsonToSimpleXml (line 41) | public function testConvertJsonToSimpleXml()
    method testConvertXmlToArray (line 52) | public function testConvertXmlToArray()
    method testConvertJsonToFoobar (line 68) | public function testConvertJsonToFoobar()
    method testConvertToSimpleXml (line 76) | public function testConvertToSimpleXml()
    method testConvertToSimpleXmlError (line 95) | public function testConvertToSimpleXmlError()

FILE: tests/Http/UrlGeneratorTest.php
  class UrlGeneratorTest (line 10) | class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
    method testDropLinkedInParams (line 12) | public function testDropLinkedInParams()
    method testGetUrl (line 55) | public function testGetUrl()
    method testGetUrlWithParams (line 78) | public function testGetUrlWithParams()
    method testGetCurrentURL (line 89) | public function testGetCurrentURL()
    method testGetCurrentURLPort80 (line 123) | public function testGetCurrentURLPort80()
    method testGetCurrentURLPort8080 (line 141) | public function testGetCurrentURLPort8080()
    method testHttpHost (line 159) | public function testHttpHost()
    method testHttpProtocolApache (line 168) | public function testHttpProtocolApache()
    method testHttpProtocolNginx (line 175) | public function testHttpProtocolNginx()
    method testHttpHostForwarded (line 182) | public function testHttpHostForwarded()
    method testHttpProtocolForwarded (line 192) | public function testHttpProtocolForwarded()
    method testHttpProtocolForwardedSecure (line 201) | public function testHttpProtocolForwardedSecure()
    method tearDown (line 211) | protected function tearDown()
  class DummyUrlGenerator (line 222) | class DummyUrlGenerator extends UrlGenerator
    method getHttpHost (line 224) | public function getHttpHost()
    method getHttpProtocol (line 229) | public function getHttpProtocol()
    method dropLinkedInParams (line 234) | public function dropLinkedInParams($query)

FILE: tests/LinkedInTest.php
  class LinkedInTest (line 10) | class LinkedInTest extends \PHPUnit_Framework_TestCase
    method testApi (line 15) | public function testApi()
    method testIsAuthenticated (line 56) | public function testIsAuthenticated()
    method testAccessTokenAccessors (line 76) | public function testAccessTokenAccessors()
    method testGeneratorAccessors (line 91) | public function testGeneratorAccessors()
    method testHasError (line 105) | public function testHasError()
    method testGetError (line 116) | public function testGetError()
    method testGetErrorWithMissingDescription (line 132) | public function testGetErrorWithMissingDescription()
    method testFormatAccessors (line 145) | public function testFormatAccessors()
    method testLoginUrl (line 159) | public function testLoginUrl()
    method testLoginUrlWithParameter (line 179) | public function testLoginUrlWithParameter()

FILE: tests/Storage/IlluminateSessionStorageTest.php
  class IlluminateSessionStorageTest (line 12) | class IlluminateSessionStorageTest extends \PHPUnit_Framework_TestCase
    method setUp (line 21) | public function setUp()
    method testSet (line 26) | public function testSet()
    method testSetFail (line 36) | public function testSetFail()
    method testGet (line 41) | public function testGet()
    method testClear (line 53) | public function testClear()
    method testClearFail (line 62) | public function testClearFail()

FILE: tests/Storage/SessionStorageTest.php
  class SessionStorageTest (line 12) | class SessionStorageTest extends \PHPUnit_Framework_TestCase
    method setUp (line 21) | public function setUp()
    method testSet (line 26) | public function testSet()
    method testSetFail (line 35) | public function testSetFail()
    method testGet (line 40) | public function testGet()
    method testClear (line 52) | public function testClear()
    method testClearFail (line 62) | public function testClearFail()
    method testClearAll (line 67) | public function testClearAll()
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (108K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 638,
    "preview": "| Q                       | A\n| ----------------------- | ---\n| Bug?                    | no|yes\n| New Feature?         "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 895,
    "preview": "| Q               | A\n| --------------- | ---\n| Bug fix?        | no|yes\n| New feature?    | no|yes\n| BC breaks?      | "
  },
  {
    "path": ".gitignore",
    "chars": 53,
    "preview": "/build/\ncomposer.lock\nindex.php\nphpunit.xml\n/vendor/\n"
  },
  {
    "path": ".scrutinizer.yml",
    "chars": 136,
    "preview": "filter:\n    paths: [src/*]\nchecks:\n    php:\n        code_rating: true\n        duplication: true\ntools:\n    external_code"
  },
  {
    "path": ".styleci.yml",
    "chars": 151,
    "preview": "preset: symfony\n\nfinder:\n    path:\n        - \"src\"\n        - \"tests\"\n\nenabled:\n    - short_array_syntax\n\ndisabled:\n    -"
  },
  {
    "path": ".travis.yml",
    "chars": 751,
    "preview": "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 "
  },
  {
    "path": "LICENSE",
    "chars": 1080,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Tobias Nyholm\n\nPermission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "Readme.md",
    "chars": 9985,
    "preview": "# LinkedIn API client in PHP\n\n[![Latest Version](https://img.shields.io/github/release/Happyr/LinkedIn-API-client.svg?st"
  },
  {
    "path": "Upgrade.md",
    "chars": 3177,
    "preview": "# 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### Chang"
  },
  {
    "path": "composer.json",
    "chars": 1282,
    "preview": "{\n    \"name\": \"happyr/linkedin-api-client\",\n    \"type\": \"library\",\n    \"description\": \"LinkedIn API client. Handles OAut"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 1312,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit backupGlobals=\"false\"\n         backupStaticAttributes=\"false\"\n         "
  },
  {
    "path": "src/AccessToken.php",
    "chars": 1605,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\n/**\n * @author Tobias Nyholm\n */\nclass AccessToken\n{\n    /**\n     * @var null|string "
  },
  {
    "path": "src/Authenticator.php",
    "chars": 8640,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInTransferException;\nuse Happyr\\LinkedIn\\Exceptio"
  },
  {
    "path": "src/AuthenticatorInterface.php",
    "chars": 1324,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInException;\nuse Happyr\\LinkedIn\\Http\\LinkedInUrl"
  },
  {
    "path": "src/Exception/InvalidArgumentException.php",
    "chars": 291,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\nclass InvalidArgumentException extends LinkedInException\n{\n    /**\n     * T"
  },
  {
    "path": "src/Exception/LinkedInException.php",
    "chars": 201,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * This is the base exception of this library.\n *\n * @author Tobias Nyh"
  },
  {
    "path": "src/Exception/LinkedInTransferException.php",
    "chars": 239,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * An exception with thrown by unexpected result form the remote API.\n "
  },
  {
    "path": "src/Exception/LoginError.php",
    "chars": 887,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * Class LoginError.\n *\n * @author Tobias Nyholm\n */\nclass LoginError\n{"
  },
  {
    "path": "src/Http/CurrentUrlGeneratorInterface.php",
    "chars": 500,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * An interface to get the current URL.\n *\n * @author Tobias Nyholm <tobias."
  },
  {
    "path": "src/Http/GlobalVariableGetter.php",
    "chars": 947,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * Look in $_REQUEST and $_GET if there is a variable we want.\n *\n * @author"
  },
  {
    "path": "src/Http/LinkedInUrlGeneratorInterface.php",
    "chars": 625,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * An interface to generate LinkedIn specific urls.\n *\n * @author Tobias Nyh"
  },
  {
    "path": "src/Http/RequestManager.php",
    "chars": 2110,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInTransferException;\nuse Http\\Client\\Excepti"
  },
  {
    "path": "src/Http/RequestManagerInterface.php",
    "chars": 839,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse Happyr\\LinkedIn\\Exception\\LinkedInTransferException;\nuse Http\\Client\\HttpCli"
  },
  {
    "path": "src/Http/ResponseConverter.php",
    "chars": 2298,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse Happyr\\LinkedIn\\Exception\\InvalidArgumentException;\nuse Happyr\\LinkedIn\\Exce"
  },
  {
    "path": "src/Http/UrlGenerator.php",
    "chars": 4529,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\nclass UrlGenerator im"
  },
  {
    "path": "src/Http/UrlGeneratorInterface.php",
    "chars": 203,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>\n */\ninterface UrlGenerato"
  },
  {
    "path": "src/LinkedIn.php",
    "chars": 9718,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LoginError;\nuse Happyr\\LinkedIn\\Http\\GlobalVariableGett"
  },
  {
    "path": "src/LinkedInInterface.php",
    "chars": 4719,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse Happyr\\LinkedIn\\Exception\\LoginError;\nuse Happyr\\LinkedIn\\Http\\UrlGeneratorInterf"
  },
  {
    "path": "src/Storage/BaseDataStorage.php",
    "chars": 1098,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Happyr\\LinkedIn\\Exception\\InvalidArgumentException;\n\n/**\n * @author Tobia"
  },
  {
    "path": "src/Storage/DataStorageInterface.php",
    "chars": 1040,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\n/**\n * We need to store data somewhere. It might be in a apc cache, filesyste"
  },
  {
    "path": "src/Storage/IlluminateSessionStorage.php",
    "chars": 829,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Illuminate\\Support\\Facades\\Session;\n\n/**\n * Store data in a IlluminateSes"
  },
  {
    "path": "src/Storage/SessionStorage.php",
    "chars": 1113,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\n/**\n * Store data in the global session.\n *\n * @author Tobias Nyholm <tobias."
  },
  {
    "path": "tests/AccessTokenTest.php",
    "chars": 1057,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\n/**\n * @author Tobias Nyholm\n */\nclass AccessTokenTest extends \\PHPUnit_Framework_Tes"
  },
  {
    "path": "tests/AuthenticatorTest.php",
    "chars": 14001,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse GuzzleHttp\\Psr7\\Response;\nuse Happyr\\LinkedIn\\Exception\\LinkedInException;\nuse Mo"
  },
  {
    "path": "tests/Exceptions/LoginErrorTest.php",
    "chars": 378,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Exception;\n\n/**\n * Class LoginErrorTest.\n *\n * @author Tobias Nyholm\n */\nclass LoginErr"
  },
  {
    "path": "tests/Http/ResponseConverterTest.php",
    "chars": 3110,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\nuse GuzzleHttp\\Psr7\\Response;\n\nclass ResponseConverterTest extends \\PHPUnit_Fram"
  },
  {
    "path": "tests/Http/UrlGeneratorTest.php",
    "chars": 8746,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Http;\n\n/**\n * Class UrlGeneratorTest.\n *\n * @author Tobias Nyholm\n */\nclass UrlGenerato"
  },
  {
    "path": "tests/LinkedInTest.php",
    "chars": 7770,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn;\n\nuse GuzzleHttp\\Psr7\\Response;\n\n/**\n * @author Tobias Nyholm <tobias.nyholm@gmail.com>"
  },
  {
    "path": "tests/Storage/IlluminateSessionStorageTest.php",
    "chars": 1631,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Illuminate\\Support\\Facades\\Session;\n\n/**\n * Class SessionStorageTest.\n *\n"
  },
  {
    "path": "tests/Storage/SessionStorageTest.php",
    "chars": 1931,
    "preview": "<?php\n\nnamespace Happyr\\LinkedIn\\Storage;\n\nuse Mockery as m;\n\n/**\n * Class SessionStorageTest.\n *\n * @author Tobias Nyho"
  }
]

About this extraction

This page contains the full source code of the Happyr/LinkedIn-API-client GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (99.5 KB), approximately 26.0k tokens, and a symbol index with 199 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!