DEV Community

Cover image for Smart Guide On Creating a Complete PHP Package Using Composer
Ahmed Khan
Ahmed Khan

Posted on

Smart Guide On Creating a Complete PHP Package Using Composer

Composer is a dependency manager for PHP created by Nils Adermann, Jordi Boggiano and other community contributors. With Composer, developers could develop code that include components from several libraries and packages. Composer installs all required libraries and packages automatically, and thus the code does not break because of the absence of a required code component.

Another important use of Composer is the creation of a self-contained package that contains all the required code components in one place. Thus, instead of pulling code from several locations and resources, the developer has to include a single package and focus solely on implementation of the logic rather than worrying about external code resources.

The most common usage of the Composer is the following single command that installs a particular package:

$ composer install 'packagename:version'

Package Creation Using Composer

Composer is also used to create custom packages from open source packages and/or private packages. These custom packages could then be installed using the above mentioned command. In this article, I will demonstrate the package creation capability of Composer by creating a package for Twitter search API. I will then register it and then install it on my server.

Before starting, make sure that Composer is installed on the local system.

Git Repo Creation

The first step is to create a public Git repo. Go to the GitHub account and create a new repository with .gitignore file. Next, add the following folder and file to .gitignore file. ( in some instances, these items are auto-generated)

composer.phar
/vendor/

Once the repo has been created, clone it to the local system. The next step is the creation of a TwitterSearch class, which will be based on Twitter API.

The TwitterSearch Class

I will base the Twitter PHP-SDK on PSR-4 namespacing. Before getting started with the class creation, run the following command in the local Git repository folder to install Guzzle HTTP Client for handling Twitter request and response.

$composer require "guzzlehttp/guzzle:^6.2";

In the local Git folder create a new folder and name it Twitter. Next, in this folder, create a new file and name it Base.php. Open this file in your code editor and add the following code to it:

namespace Twitter;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;

/**
*
*/
class Base
{

    const API_URL = "https://api.twitter.com/1.1";

    protected $client;
    protected $token;
    protected $tokensecret;
    protected $accesstoken;
    public function __construct()
    {
        $this->client = new \GuzzleHttp\Client();
    }

    public function setToken($token,$secret)
    {
        $this->token = $token;
        $this->tokensecret = $secret;
    }

    protected function prepareAccessToken()
    {
        try{
            $url = "https://api.twitter.com/oauth2/token";
            $value = ['grant_type' => "client_credentials"
            ];
            $header = array('Authorization'=>'Basic ' .base64_encode($this->token.":".$this->tokensecret),
            "Content-Type"=>"application/x-www-form-urlencoded;charset=UTF-8");
            $response = $this->client->post($url, ['query' => $value,'headers' => $header]);
            $result = json_decode($response->getBody()->getContents());

            $this->accesstoken = $result->access_token;
        }
        catch (RequestException $e) {
            $response = $this->statusCodeHandling($e);
            return $response;
        }
    }
    protected function callTwitteAPIr($method,$request,$post = [])
    {
        try{
            $this->prepareAccessToken();
            $url = self::API_URL . $request;
            $header = array('Authorization'=>'Bearer ' . $this->accesstoken);
            $response = $this->client->request($method,$url, array('query' => $post,'headers' => $header));
            return json_decode($response->getBody()->getContents());
        } catch (RequestException $e) {
            $response = $this->StatusCodeHandling($e);
            return $response;
        }
    }
    protected function statusCodeHandling($e)
    {
        $response = array("statuscode" => $e->getResponse()->getStatusCode(),
        "error" => json_decode($e->getResponse()->getBody(true)->getContents()));
        return $response;
    }
}

This is the base class that is responsible for the generation of access tokens for Twitter API and API related requests.

I will now explain some aspects of this class.

First I initialized the namespace, and then the Guzzle classes. Next, I created a constant for Twitter API URL, various variables, and a constructor that call Guzzle\Client.

const API_URL = "https://api.twitter.com/1.1";

    protected $client;
    protected $token;
    protected $tokensecret;
    protected $accesstoken;
    public function __construct()
    {
        $this->client = new \GuzzleHttp\Client();
    }

Next, I created functions for preparing the access token and calling the user-generated Twitter API requests.

public function setToken($token,$secret)
    {
        $this->token = $token;
        $this->tokensecret = $secret;
    }

Next, I created functions for preparing the access token and calling the user-generated Twitter API requests.

  protected function prepareAccessToken()
    {
        try{
            $url = "https://api.twitter.com/oauth2/token";
            $value = ['grant_type' => "client_credentials"
            ];
            $header = array('Authorization'=>'Basic ' .base64_encode($this->token.":".$this->tokensecret),
            "Content-Type"=>"application/x-www-form-urlencoded;charset=UTF-8");
            $response = $this->client->post($url, ['query' => $value,'headers' => $header]);
            $result = json_decode($response->getBody()->getContents());

            $this->accesstoken = $result->access_token;
        }
        catch (RequestException $e) {
            $response = $this->statusCodeHandling($e);
            return $response;
        }
    }
    protected function callTwitter($method,$request,$post = [])
    {
        try{
            $this->prepareAccessToken();
            $url = self::API_URL . $request;
            $header = array('Authorization'=>'Bearer ' . $this->accesstoken);
            $response = $this->client->request($method,$url, array('query' => $post,'headers' => $header));
            return json_decode($response->getBody()->getContents());
        } catch (RequestException $e) {
            $response = $this->StatusCodeHandling($e);
            return $response;
        }
    }
    protected function statusCodeHandling($e)
    {
        $response = array("statuscode" => $e->getResponse()->getStatusCode(),
        "error" => json_decode($e->getResponse()->getBody(true)->getContents()));
        return $response;
    }

In addition, I have created a function for handling the error codes.

Now create a new folder in the local Git repo folder and name it Search. In this folder create a new file, name it Search.php and paste the following code in it:

namespace Twitter\Search;
/**
 * 
 */
class Search extends \Twitter\Base
{

    public function search($value)
    {
        try{
            $url = "/search/tweets.json";
            $response = $this->callTwitter("get",$url,$value);
            return $response;
        } catch (RequestException $e) {
            $response = $this->StatusCodeHandling($e);
            return $response;
        }
    }    
}

Let me explain the code in some detail.

First, I gave a namespace for the class and then extend it to the Base class. Next, I created a function Search() to perform search via Twitter Search API. Search() accepts the variable $value that contains the parameters of the search query as defined in the Twitter API.

public function search($value)
    {
        try{
            $url = "/search/tweets.json";
            $response = $this->callTwitter("get",$url,$value);
            return $response;
        } catch (RequestException $e) {
            $response = $this->StatusCodeHandling($e);
            return $response;
        }
    }

Next, I will add the namespace in composer.json file that will be included in the autoload.php. Open composer.json file and add the following lines to it:

,
"autoload": {
        "psr-4": {
            "Twitter\\" : "Twitter/"
        }
    }

The new composer.json file will look like this:

{
    "require": {
        "guzzlehttp/guzzle": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "Twitter\\" : "Twitter/"
        }
    }
}

Now run the following command.

$ composer dump-autoload

Get the Keys

The next step is to get the consumer key and the consumer secret key for Twitter API.

To get the keys, login to https://apps.twitter.com/. Create a new application in order to get the consumer keys.

Test the Project

Now that I have all the necessary components of the project, I could now test them to make sure that everything is working properly.

In the root of the Git folder, create a new file with the name index.php, and paste the following code in it:

include "vendor/autoload.php";

use Twitter\Search\Search;

$search = new Search();
$search->setTokken("afmowdkHQTZVZWMxx0dh5Bz4r","fuJ4HNNTkI27qZEFdv3g4MEMXdVoZHUIVXCwmDbqXBRWMrEQAU");
$value = ["q" => "ahmed khan"];
echo "<pre>";
print_r($search->search($value));
echo "</pre>";

Now run index.php on the localhost. If you get the result array, things are working correctly!

Now that the project is completed and working perfectly, I will now convert it into a Composer package.

Create the Composer Package

Open composer.json and replace the code with the following:

{
    "name": "ahmed.khan/twittersdkphp", //naming our package
    "description": "A PHP-SDK for Twitter Search API", // Description about package
    "keywords": ["twitter search", "search"], // Keywords
    "type": "package", //Since it's a package  we define its type package
    "require": {
        "guzzlehttp/guzzle": "^6.2"
    },
    "license": "MIT",
    "authors": [{ //Your information
        "name": "Ahmed Khan", 
        "email": "ahmedkhan_847@hotmail.com"
    }],
    "autoload": {
        "psr-4": {
            "Twitter\\": "Twitter/"
        }
    },
    "version": "1.0.0", //Version of the package
    "minimum-stability": "dev" //Stability

}

All the schema can be found here. Now push all the code to the Git repository. Once it is done, go to https://packagist.org/. Login with GitHub and then click Submit in the top menu. Paste the URL of the Git repository and click the Check button.

Wait for the verification process to finish and then click the Submit button.

Once done, the following message will appear:

"The packagist is not auto-updated"

In order to resolve this issue, go to the GitHub account and the page to the settings of the related repository. Click Integrations & Services > Add Service, find and click Packagist.

You might be prompted to enter your GitHub password. In the next window, add your Packagist username and token. Check the Active box and then click the Add service button.

Once the service has been added, click the Test service button.

Now refresh the page and observe that the error is gone!

Now that the package has been created, I will now test it!

Test the Composer Package

Now on a separate machine or a server, run the following command to install the package via Composer.

$ composer require "ahmedkhan847/twittersdkphp:dev-master"

You will notice that the package is being installed.

Conclusion

In this tutorial, I created a Composer package based on Twitter Search API. I also submitted the package to Packagist, and then tested it by installing the package via Composer. You can find this complete package on Githuband on Packagist. If you face any issue during the process, leave a comment below.

Top comments (0)