<?php
/*
 * This file is part of the Laravel Facebook Account-Kit package.
 *
 * (c) Adeniyi Ibraheem <ibonly01@gmail.com>
 * (c) Surajudeen AKande <surajudeen.akande@andela.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Ibonly\FacebookAccountKit;

use GuzzleHttp\Client;
use Illuminate\Support\Facades\Config;

class AccountKit
{
    /**
     * @var \GuzzleHttp\Client HttpClient
     */
    protected $client;

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

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

    /**
     * AccountKit constructor.
     */
    public function __construct()
    {
        $this->client = new Client();
        $this->accessTokenUrl = 'https://graph.accountkit.com/v1.2/access_token';
        $this->meTokenUrl = 'https://graph.accountkit.com/v1.2/me?access_token=';
    }

    /**
     * Set Token Url
     *
     * @param string $code
     * @param string $appId
     * @param string $appSecret
     *
     * @return string
     */
    private function tokenUrl($code, $appId, $appSecret)
    {
        return $this->accessTokenUrl.'?grant_type=authorization_code&code='.$code.'&access_token=AA|'.$appId.'|'.$appSecret;
    }

    /**
     * Get App Id
     *
     * @return string
     */
    private function getFacebookAppID()
    {
        return Config::get('accountKit.appId');
    }

    /**
     * Get App Secret
     *
     * @return string
     */
    private function getFacebookAppSecret()
    {
        return Config::get('accountKit.appSecret');
    }

    /**
     * Set Token endpoint
     *
     * @param string $code
     *
     * @return string
     */
    private function tokenExchangeEndPoint($code)
    {
        return $this->tokenUrl($code, $this->getFacebookAppID(), $this->getFacebookAppSecret());
    }

    /**
     * Make Request To AccountKit
     *
     * @param string $url
     *
     * @return mixed
     */
    private function getContentBody($url)
    {
        $data = $this->client->request('GET', $url);

        return json_decode($data->getBody());
    }

    /**
     * Get User Data
     *
     * @param string $code
     *
     * @return mixed
     */
    private function getData($code)
    {
        $url = $this->tokenExchangeEndPoint($code);

        return $this->getContentBody($url);
    }

    /**
     * Get Access token
     *
     * @param string $code
     *
     * @return string
     */
    private function getAccessToken($code)
    {
        return $this->getData($code)->access_token;
    }

    /**
     * Set User Token Endpoint
     *
     * @param string $code
     *
     * @return string
     */
    private function meEndPoint($code)
    {
        return $this->meTokenUrl . '' . $this->getAccessToken($code);
    }

    /**
     * Get Returned Data
     *
     * @param string $code
     *
     * @return mixed
     */
    public function data($code)
    {
        return $this->getContentBody($this->meEndPoint($code));
    }
}


<?php
/*
 * This file is part of the Laravel Facebook Account-Kit package.
 *
 * (c) Adeniyi Ibraheem <ibonly01@gmail.com>
 * (c) Surajudeen AKande <surajudeen.akande@andela.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Ibonly\FacebookAccountKit;

class FacebookAccountKit extends AccountKit
{
    /**
     * Get All User Data
     *
     * @param string $code
     * @return array
     */
    public function accountKitData($code)
    {
        $data = $this->data($code);

        $output = [
            'id' => $data->id,
            'phoneNumber' => '',
            'email' => '',
        ];

        if (array_key_exists('phone', $data)) {
            $output['phoneNumber'] = $data->phone->number ?? null;
        }

        if (array_key_exists('email', $data)) {
            $output['email'] = $data->email->address ?? null;
        }

        return $output;
    }
}


<?php

namespace Litecms\Contact\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Routing\Router;
use Litecms\Contact\Models\Contact;
use Request;
use Route;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * This namespace is applied to the controller routes in your routes file.
     *
     * In addition, it is set as the URL generator's root namespace.
     *
     * @var string
     */
    protected $namespace = 'Litecms\Contact\Http\Controllers';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @param   \Illuminate\Routing\Router  $router
     * @return void
     */
    public function boot()
    {
        parent::boot();

        if (Request::is('*/contact/contact/*')) {
            Route::bind('contact', function ($contact) {
                $contactrepo = $this->app->make('Litecms\Contact\Interfaces\ContactRepositoryInterface');
                return $contactrepo->findorNew($contact);
            });
        }

    }

    /**
     * Define the routes for the package.
     *
     * @return void
     */
    public function map()
    {
        $this->mapWebRoutes();
    }

    /**
     * Define the "web" routes for the package.
     *
     * These routes all receive session state, CSRF protection, etc.
     *
     * @return void
     */
    protected function mapWebRoutes()
    {
        Route::group([
            'middleware' => 'web',
            'namespace' => $this->namespace,
            'prefix' => trans_setlocale(),
        ], function ($router) {
            require (__DIR__ . '/../../routes/web.php');
        });
    }

}


<?php

namespace Litecms\Contact;

use Illuminate\Support\ServiceProvider;

class ContactServiceProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Bootstrap the application events.
     *
     * @return void
     */
    public function boot()
    {
        // Load view
        $this->loadViewsFrom(__DIR__ . '/../resources/views', 'contact');

        // Load translation
        $this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'contact');

        // Load migrations
        $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');

        // Call pblish redources function
        $this->publishResources();

    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->mergeConfigFrom(__DIR__.'/../config/config.php', 'litecms.contact');

        // Bind facade
        $this->app->bind('contact', function ($app) {
            return $this->app->make('Litecms\Contact\Contact');
        });

        // Bind Contact to repository
        $this->app->bind(
            'Litecms\Contact\Interfaces\ContactRepositoryInterface',
            \Litecms\Contact\Repositories\Eloquent\ContactRepository::class
        );

        $this->app->register(\Litecms\Contact\Providers\AuthServiceProvider::class);
        $this->app->register(\Litecms\Contact\Providers\EventServiceProvider::class);
        $this->app->register(\Litecms\Contact\Providers\RouteServiceProvider::class);
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['contact'];
    }

    /**
     * Publish resources.
     *
     * @return void
     */
    private function publishResources()
    {
        // Publish configuration file
        $this->publishes([__DIR__ . '/../config/config.php' => config_path('litecms/contact.php')], 'config');

        // Publish admin view
        $this->publishes([__DIR__ . '/../resources/views' => base_path('resources/views/vendor/contact')], 'view');

        // Publish language files
        $this->publishes([__DIR__ . '/../resources/lang' => base_path('resources/lang/vendor/contact')], 'lang');

        // Publish public
        $this->publishes([__DIR__ . '/../public' => public_path('/')], 'public');
   }
}


<?php

namespace Litecms\Contact\Http\Controllers;

use App\Http\Controllers\ResourceController as BaseController;
use Litecms\Contact\Http\Requests\ContactRequest;
use Litecms\Contact\Interfaces\ContactRepositoryInterface;
use Litecms\Contact\Models\Contact;

/**
 * Resource controller class for contact.
 */
class ContactResourceController extends BaseController
{

    /**
     * Initialize contact resource controller.
     *
     * @param type ContactRepositoryInterface $contact
     *
     * @return null
     */
    public function __construct(ContactRepositoryInterface $contact)
    {
        parent::__construct();
        $this->repository = $contact;
        $this->repository
            ->pushCriteria(\Litepie\Repository\Criteria\RequestCriteria::class)
            ->pushCriteria(\Litecms\Contact\Repositories\Criteria\ContactResourceCriteria::class);
    }

    /**
     * Display a list of contact.
     *
     * @return Response
     */
    public function index(ContactRequest $request)
    {

        if ($this->response->typeIs('json')) {
            $pageLimit = $request->input('pageLimit');
            $data      = $this->repository
                ->setPresenter(\Litecms\Contact\Repositories\Presenter\ContactListPresenter::class)
                ->getDataTable($pageLimit);
            return $this->response
                ->data($data)
                ->output();
        }

        $contacts = $this->repository->paginate();

        $this->response->theme->asset()->container('footer')->add('gmap', 'https://maps.googleapis.com/maps/api/js?key=' . config('litecms.contact.gmapapi'));

        return $this->response->setMetaTitle(trans('contact::contact.names'))
            ->view('contact::contact.index', true)
            ->data(compact('contacts'))
            ->output();
    }

    /**
     * Display contact.
     *
     * @param Request $request
     * @param Model   $contact
     *
     * @return Response
     */
    public function show(ContactRequest $request, Contact $contact)
    {

        if ($contact->exists) {
            $view = 'contact::contact.show';
        } else {
            $view = 'contact::contact.new';
        }

        return $this->response->setMetaTitle(trans('app.view') . ' ' . trans('contact::contact.name'))
            ->data(compact('contact'))
            ->view($view)
            ->output();
    }

    /**
     * Show the form for creating a new contact.
     *
     * @param Request $request
     *
     * @return Response
     */
    public function create(ContactRequest $request)
    {

        $contact = $this->repository->newInstance([]);
        return $this->response->setMetaTitle(trans('app.new') . ' ' . trans('contact::contact.name'))
            ->view('contact::contact.create', true)
            ->data(compact('contact'))
            ->output();
    }

    /**
     * Create new contact.
     *
     * @param Request $request
     *
     * @return Response
     */
    public function store(ContactRequest $request)
    {
        try {
            $attributes              = $request->all();
            $attributes['user_id']   = user_id();
            $attributes['user_type'] = user_type();
            $contact                 = $this->repository->create($attributes);

            return $this->response->message(trans('messages.success.created', ['Module' => trans('contact::contact.name')]))
                ->code(204)
                ->status('success')
                ->url(guard_url('contact/contact/' . $contact->getRouteKey()))
                ->redirect();
        } catch (Exception $e) {
            return $this->response->message($e->getMessage())
                ->code(400)
                ->status('error')
                ->url(guard_url('contact/contact'))
                ->redirect();
        }

    }

    /**
     * Show contact for editing.
     *
     * @param Request $request
     * @param Model   $contact
     *
     * @return Response
     */
    public function edit(ContactRequest $request, Contact $contact)
    {
        return $this->response->setMetaTitle(trans('app.edit') . ' ' . trans('contact::contact.name'))
            ->view('contact::contact.edit', true)
            ->data(compact('contact'))
            ->output();
    }

    /**
     * Update the contact.
     *
     * @param Request $request
     * @param Model   $contact
     *
     * @return Response
     */
    public function update(ContactRequest $request, Contact $contact)
    {
        try {
            $attributes = $request->all();

            $contact->update($attributes);
            return $this->response->message(trans('messages.success.updated', ['Module' => trans('contact::contact.name')]))
                ->code(204)
                ->status('success')
                ->url(guard_url('contact/contact/' . $contact->getRouteKey()))
                ->redirect();
        } catch (Exception $e) {
            return $this->response->message($e->getMessage())
                ->code(400)
                ->status('error')
                ->url(guard_url('contact/contact/' . $contact->getRouteKey()))
                ->redirect();
        }

    }

    /**
     * Remove the contact.
     *
     * @param Model   $contact
     *
     * @return Response
     */
    public function destroy(ContactRequest $request, Contact $contact)
    {
        try {

            $contact->delete();
            return $this->response->message(trans('messages.success.deleted', ['Module' => trans('contact::contact.name')]))
                ->code(202)
                ->status('success')
                ->url(guard_url('contact/contact'))
                ->redirect();

        } catch (Exception $e) {

            return $this->response->message($e->getMessage())
                ->code(400)
                ->status('error')
                ->url(guard_url('contact/contact/' . $contact->getRouteKey()))
                ->redirect();
        }

    }

}


<?php

namespace Litecms\Contact;

use User;

class Contact
{
    /**
     * $contact object.
     */
    protected $contact;

    /**
     * Constructor.
     */
    public function __construct(\Litecms\Contact\Interfaces\ContactRepositoryInterface $contact)
    {
        $this->contact = $contact;
    }

    /**
     * Returns count of contact.
     *
     * @param array $filter
     *
     * @return int
     */
    public function count()
    {
        return  $this->contact->count();
    }

    /**
     * Make gadget View
     *
     * @param string $view
     *
     * @param int $count
     *
     * @return View
     */
    public function gadget($view = 'admin.contact.gadget', $count = 10)
    {

        if (User::hasRole('user')) {
            $this->contact->pushCriteria(new \Litecms\Litecms\Repositories\Criteria\ContactUserCriteria());
        }

        $contact = $this->contact->scopeQuery(function ($query) use ($count) {
            return $query->orderBy('id', 'DESC')->take($count);
        })->all();

        return view('contact::' . $view, compact('contact'))->render();
    }
    /**
     * Returns field of contact.
     *
     * @param array $filter
     *
     * @return int
     */
    public function get($field)
    {
        $data=  $this->contact->scopeQuery(function($query){
            return $query->orderBy('id','DESC');
        })->first([$field]);
        return $data[$field];
    }
}


<?php

namespace Litecms\Contact\Policies;

use App\User;
use Litecms\Contact\Models\Contact;

class ContactPolicy
{

    /**
     * Determine if the given user can view the contact.
     *
     * @param User $user
     * @param Contact $contact
     *
     * @return bool
     */
    public function view(User $user, Contact $contact)
    {
        if ($user->canDo('contact.contact.view') && $user->isAdmin()) {
            return true;
        }
    }

    /**
     * Determine if the given user can create a contact.
     *
     * @param User $user
     * @param Contact $contact
     *
     * @return bool
     */
    public function create(User $user)
    {
        return  $user->canDo('contact.contact.create');
    }

    /**
     * Determine if the given user can update the given contact.
     *
     * @param User $user
     * @param Contact $contact
     *
     * @return bool
     */
    public function update(User $user, Contact $contact)
    {
        if ($user->canDo('contact.contact.update') && $user->isAdmin()) {
            return true;
        }
    }

    /**
     * Determine if the given user can delete the given contact.
     *
     * @param User $user
     * @param Contact $contact
     *
     * @return bool
     */
    public function destroy(User $user, Contact $contact)
    {
        if ($user->canDo('contact.contact.delete') && $user->isAdmin()) {
            return true;
        }
    }

    /**
     * Determine if the user can perform a given action ve.
     *
     * @param [type] $user    [description]
     * @param [type] $ability [description]
     *
     * @return [type] [description]
     */
    public function before($user, $ability)
    {
        if ($user->isSuperuser()) {
            return true;
        }
    }
}


<?php

namespace Litecms\Contact\Http\Controllers;

use App\Http\Controllers\PublicController as BaseController;
use Illuminate\Http\Request as Request;
use Litecms\Contact\Interfaces\ContactRepositoryInterface;
use Mail;

class ContactPublicController extends BaseController
{
    // use ContactWorkflow;

    /**
     * Constructor.
     *
     * @param type \Litecms\Contact\Interfaces\ContactRepositoryInterface $contact
     *
     * @return type
     */
    public function __construct(ContactRepositoryInterface $contact)
    {
        $this->repository = $contact;
        parent::__construct();
    }

    /**
     * Show contact's list.
     *
     * @param string $slug
     *
     * @return response
     */
    protected function index()
    {

        $this->response->theme->asset()->container('footer')->add('gmap', 'https://maps.googleapis.com/maps/api/js?key=' . config('litecms.contact.gmapapi'));

        $contact = $this->repository
            ->pushCriteria(app('Litepie\Repository\Criteria\RequestCriteria'))
            ->scopeQuery(function ($query) {
                return $query->orderBy('id', 'DESC');
            })->first();

        return $this->response->setMetaTitle(trans('contact::contact.names'))
            ->view('contact::index')
            ->populate(false)
            ->data(compact('contact'))
            ->output();
    }

    /**
     * Send mail
     *
     * @param string $slug
     *
     * @return response
     */
    public function sendMail(Request $request)
    {

        $data = $request->all();
       Mail::send('contact::emails.message', ['data' => $data], function ($message) use ($data) {
           $message->from($data['email']);
           $message->to('swathy@renfos.com')->subject('Contact Enquiry');
       });
           return $this->response->message('Success! Your message send successfully.')
               ->code(204)
               ->status('success')
               ->url(url('/contacts'))
               ->redirect();

    }

}


<?php

namespace Dusterio\LinkPreview\Parsers;

use Dusterio\LinkPreview\Contracts\LinkInterface;
use Dusterio\LinkPreview\Contracts\PreviewInterface;
use Dusterio\LinkPreview\Contracts\ReaderInterface;
use Dusterio\LinkPreview\Contracts\ParserInterface;
use Dusterio\LinkPreview\Exceptions\ConnectionErrorException;
use Dusterio\LinkPreview\Models\Link;
use Dusterio\LinkPreview\Readers\HttpReader;
use Dusterio\LinkPreview\Models\HtmlPreview;
use Symfony\Component\DomCrawler\Crawler;

/**
 * Class HtmlParser
 */
class HtmlParser extends BaseParser implements ParserInterface
{
    /**
     * Supported HTML tags
     *
     * @var array
     */
    private $tags = [
        'cover' => [
            ['selector' => 'meta[property="twitter:image"]', 'attribute' => 'content'],
            ['selector' => 'meta[property="og:image"]', 'attribute' => 'content'],
            ['selector' => 'meta[itemprop="image"]', 'attribute' => 'content'],
        ],

        'title' => [
            ['selector' => 'meta[property="twitter:title"]', 'attribute' => 'content'],
            ['selector' => 'meta[property="og:title"]', 'attribute' => 'content'],
            ['selector' => 'meta[itemprop="name"]', 'attribute' => 'content'],
            ['selector' => 'title']
        ],

        'description' => [
            ['selector' => 'meta[property="twitter:description"]', 'attribute' => 'content'],
            ['selector' => 'meta[property="og:description"]', 'attribute' => 'content'],
            ['selector' => 'meta[itemprop="description"]', 'attribute' => 'content'],
            ['selector' => 'meta[name="description"]', 'attribute' => 'content'],
        ],

        'video' => [
            ['selector' => 'meta[property="twitter:player:stream"]', 'attribute' => 'content'],
            ['selector' => 'meta[property="og:video"]', 'attribute' => 'content'],
        ],

        'videoType' => [
            ['selector' => 'meta[property="twitter:player:stream:content_type"]', 'attribute' => 'content'],
            ['selector' => 'meta[property="og:video:type"]', 'attribute' => 'content'],
        ],
    ];

    /**
     * Smaller images will be ignored
     * @var int
     */
    private $imageMinimumWidth = 300;
    private $imageMinimumHeight = 300;

    /**
     * @param ReaderInterface $reader
     * @param PreviewInterface $preview
     */
    public function __construct(ReaderInterface $reader = null, PreviewInterface $preview = null)
    {
        $this->setReader($reader ?: new HttpReader());
        $this->setPreview($preview ?: new HtmlPreview());
    }

    /**
     * @inheritdoc
     */
    public function __toString()
    {
        return 'general';
    }

    /**
     * @param int $width
     * @param int $height
     */
    public function setMinimumImageDimension($width, $height)
    {
        $this->imageMinimumWidth = $width;
        $this->imageMinimumHeight = $height;
    }

    /**
     * @inheritdoc
     */
    public function canParseLink(LinkInterface $link)
    {
        return !filter_var($link->getUrl(), FILTER_VALIDATE_URL) === false;
    }

    /**
     * @inheritdoc
     */
    public function parseLink(LinkInterface $link)
    {
        $link = $this->readLink($link);

        if (!$link->isUp()) throw new ConnectionErrorException();

        if ($link->isHtml()) {
            $this->getPreview()->update($this->parseHtml($link));
        } else if ($link->isImage()) {
            $this->getPreview()->update($this->parseImage($link));
        }

        return $this;
    }

    /**
     * @param LinkInterface $link
     * @return array
     */
    protected function parseImage(LinkInterface $link)
    {
        return [
            'cover' => $link->getEffectiveUrl(),
            'images' => [
                $link->getEffectiveUrl()
            ]
        ];
    }

    /**
     * Extract required data from html source
     * @param LinkInterface $link
     * @return array
     */
    protected function parseHtml(LinkInterface $link)
    {
        $images = [];

        try {
            $parser = new Crawler();
	    $parser->addHtmlContent($link->getContent());

            // Parse all known tags
            foreach($this->tags as $tag => $selectors) {
                foreach($selectors as $selector) {
                    if ($parser->filter($selector['selector'])->count() > 0) {
                        if (isset($selector['attribute'])) {
                            ${$tag} = $parser->filter($selector['selector'])->first()->attr($selector['attribute']);
                        } else {
                            ${$tag} = $parser->filter($selector['selector'])->first()->text();
                        }

                        break;
                    }
                }

                // Default is empty string
                if (!isset(${$tag})) ${$tag} = '';
            }

            // Parse all images on this page
            foreach($parser->filter('img') as $image) {
                if (!$image->hasAttribute('src')) continue;
                if (filter_var($image->getAttribute('src'), FILTER_VALIDATE_URL) === false) continue;

                // This is not bulletproof, actual image maybe bigger than tags
                if ($image->hasAttribute('width') && $image->getAttribute('width') < $this->imageMinimumWidth) continue;
                if ($image->hasAttribute('height') && $image->getAttribute('height') < $this->imageMinimumHeight) continue;

                $images[] = $image->getAttribute('src');
            }
        } catch (\InvalidArgumentException $e) {
            // Ignore exceptions
        }

        $images = array_unique($images);

        if (!isset($cover) && count($images)) $cover = $images[0];

        return compact('cover', 'title', 'description', 'images', 'video', 'videoType');
    }
}


<?php

namespace Dusterio\LinkPreview;

use Dusterio\LinkPreview\Contracts\ParserInterface;
use Dusterio\LinkPreview\Contracts\PreviewInterface;
use Dusterio\LinkPreview\Parsers\HtmlParser;
use Dusterio\LinkPreview\Parsers\YouTubeParser;
use Dusterio\LinkPreview\Parsers\VimeoParser;
use Dusterio\LinkPreview\Models\Link;
use Dusterio\LinkPreview\Exceptions\UnknownParserException;

class Client
{
    /**
     * @var ParserInterface[]
     */
    private $parsers = [];

    /**
     * @var Link $link
     */
    private $link;

    /**
     * @param string $url Request address
     */
    public function __construct($url = null)
    {
        if ($url) $this->setUrl($url);
        $this->addDefaultParsers();
    }

    /**
     * Try to get previews from as many parsers as possible
     * @return PreviewInterface[]
     */
    public function getPreviews()
    {
        $parsed = [];

        foreach ($this->getParsers() as $name => $parser) {
            if ($parser->canParseLink($this->link))
                $parsed[$name] = $parser->parseLink($this->link)->getPreview();
        }

        return $parsed;
    }

    /**
     * Get a preview from a single parser
     * @param string $parserId
     * @throws UnknownParserException
     * @return PreviewInterface|boolean
     */
    public function getPreview($parserId)
    {
        if (array_key_exists($parserId, $this->getParsers())) {
            $parser = $this->getParsers()[$parserId];
        } else throw new UnknownParserException();

        return $parser->parseLink($this->link)->getPreview();
    }

    /**
     * Add parser to the beginning of parsers list
     *
     * @param ParserInterface $parser
     * @return $this
     */
    public function addParser(ParserInterface $parser)
    {
        $this->parsers = [(string) $parser => $parser] + $this->parsers;

        return $this;
    }

    /**
     * @param $id
     * @return bool|ParserInterface
     */
    public function getParser($id)
    {
        return isset($this->parsers[$id]) ? $this->parsers[$id] : false;
    }

    /**
     * Get parsers
     * @return ParserInterface[]
     */
    public function getParsers()
    {
        return $this->parsers;
    }

    /**
     * Set parsers
     * @param ParserInterface[] $parsers
     * @return $this
     */
    public function setParsers($parsers)
    {
        $this->parsers = $parsers;

        return $this;
    }

    /**
     * @return string
     */
    public function getUrl()
    {
        return (!empty($this->link->getEffectiveUrl())) ? $this->link->getEffectiveUrl() : $this->link->getUrl();
    }

    /**
     * Set target url
     *
     * @param string $url Website url to parse
     * @return $this
     */
    public function setUrl($url)
    {
        $this->link = new Link($url);

        return $this;
    }

    /**
     * Remove parser from parsers list
     *
     * @param string $name Parser name
     * @return $this
     */
    public function removeParser($name)
    {
        if (in_array($name, $this->parsers, false)) {
            unset($this->parsers[$name]);
        }

        return $this;
    }

    /**
     * Add default parsers
     * @return void
     */
    protected function addDefaultParsers()
    {
        $this->addParser(new HtmlParser());
        $this->addParser(new YouTubeParser());
        $this->addParser(new VimeoParser());
    }
}


<?php

namespace HnhDigital\HelperCollection\Middleware;

use URL;

class ForceHttps
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure                 $next
     *
     * @return mixed
     */
    public function handle($request, \Closure $next)
    {
        if (config('app.force_https', false)) {
            $request->server->set('HTTPS', true);
            app('url')->forceScheme('https');

            if (class_exists('\\Roumen\\Asset\\Asset')) {
                \Roumen\Asset\Asset::$secure = true;
            }
        }

        return $next($request);
    }
}


<?php

namespace HnhDigital\HelperCollection;

class Color
{
    /**
     * Contrast the given hex based color.
     *
     * @return array
     */
    public function contrastFromHex($hex)
    {
        $hash = '';

        if (substr($hex, 0, 1) === '#') {
            $hash = '#';
            $hex = substr($hex, 1);
        }

        $color = $this->contrastFromRGB(...sscanf($hex, '%02x%02x%02x'));

        return sprintf('%s%02x%02x%02x', $hash, $color['r'], $color['g'], $color['b']);
    }

    /**
     * Contrast the given RGB based color.
     *
     * @param string $r
     * @param string $g
     * @param string $b
     *
     * @return array
     */
    public function contrastFromRGB($r, $g, $b)
    {
        return [
            'r' => ($r < 128) ? 255 : 0,
            'g' => ($g < 128) ? 255 : 0,
            'b' => ($b < 128) ? 255 : 0,
        ];
    }
}


<?php

namespace HnhDigital\HelperCollection\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as ParentVerifyCsrfToken;

class VerifyCsrfToken extends ParentVerifyCsrfToken
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure                 $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        try {
            return parent::handle($request, $next);
        } catch (\Exception $exception) {
            if ($request->ajax()) {
                header('X-FORCE_FRONTEND_REDIRECT: 1');
                echo array_get($_SERVER, 'HTTP_REFERER', array_get($_SERVER, 'HTTP_ORIGIN', secure_url('')));
                exit();
            }

            return $next($request);
        }
    }
}


<?php

namespace HnhDigital\HelperCollection;

class FileSystem
{
    /**
     * Enclose the provided string with slashes.
     *
     * @param string $path
     *
     * @return string
     */
    public function enclosePath($path)
    {
        // Add starting slash.
        if (substr($path, 0, 1) !== '/') {
            $path = '/'.$path;
        }

        // Add trailing slash.
        if (substr($path, -1, 1) !== '/') {
            $path .= '/';
        }

        return $path;
    }
}


<?php

namespace HnhDigital\HelperCollection;

class Database
{
    private static $static_unit_interval = [
        '1d' => [1, 'DAY'],
        '7d' => [7, 'DAY'],
        '1m' => [1, 'MONTH'],
        '1y' => [1, 'YEAR'],
    ];

    /**
     * Converts a unit/interval string to the correct INTERVAL constant.
     *
     * eg 1y = [1, YEAR]
     *
     * @return array|null
     */
    public function parseUnitInterval($unit_interval)
    {
        if (array_has(self::$static_unit_interval, $unit_interval)) {
            return array_get(self::$static_unit_interval, $unit_interval);
        }

        $unit = preg_replace('/([A-Za-z]*?)/', '', $unit_interval);
        $interval = preg_replace('/([0-9]*?)/', '', $unit_interval);

        $interval = $this->convertInterval($interval);

        if ($unit > 0 && $interval !== false) {
            return [$unit, $interval];
        }

        return [null, null];
    }

    /**
     * Convert the interval string to the mysql constant.
     *
     * @param string $interval
     *
     * @return string
     */
    public function convertInterval($interval)
    {
        switch (strtolower($interval)) {
            case 'y':
            case 'year':
                $interval = 'YEAR';
                break;
            case 'm':
            case 'month':
                $interval = 'MONTH';
                break;
            case 'd':
            case 'day':
                $interval = 'DAY';
                break;
            case 'h':
            case 'hour':
                $interval = 'HOUR';
                break;
            case 'i':
            case 'min':
            case 'minute':
                $iterval = 'MINUTE';
                break;
            case 's':
            case 'sec':
            case 'second':
                $interval = 'SECOND';
                break;
            default:
                $interval = false;
        }

        return $interval;
    }
}


<?php

namespace HnhDigital\HelperCollection\Response;

use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;

class MacroServiceProvider extends ServiceProvider
{
    /**
     * Register the application's response macros.
     *
     * @return void
     */
    public function boot()
    {
        // Providesd the ability to download a file from a given path.
        // Will also return a not modified header if file has not changed.
        Response::macro('downloadAndCache', function ($file_path) {
            if (!empty(request()->header('If-Modified-Since'))) {
                $cache_last_modified = strtotime(request()->header('If-Modified-Since'));
                $file_last_modified = filemtime($file_path);
                if ($file_last_modified == $cache_last_modified) {
                    header('HTTP/1.1 304 Not Modified');
                    exit();
                }
            }

            return Response::file($file_path);
        });
    }
}


<?php

namespace HnhDigital\HelperCollection;

use Carbon\Carbon;
use DateTimeZone;

class Timezone
{
    /**
     * Region name and mask.
     *
     * @var array
     */
    private static $regions = [
        'Africa'     => DateTimeZone::AFRICA,
        'America'    => DateTimeZone::AMERICA,
        'Arctic'     => DateTimeZone::ARCTIC,
        'Antarctica' => DateTimeZone::ANTARCTICA,
        'Asia'       => DateTimeZone::ASIA,
        'Atlantic'   => DateTimeZone::ATLANTIC,
        'Australia'  => DateTimeZone::AUSTRALIA,
        'Europe'     => DateTimeZone::EUROPE,
        'Indian'     => DateTimeZone::INDIAN,
        'Pacific'    => DateTimeZone::PACIFIC,
    ];

    /**
     * Returns a region grouped array.
     *
     * @param array $config
     *
     * @return array
     */
    public static function data($config = [])
    {
        foreach (self::$regions as $region => $mask) {
            $zones = DateTimeZone::listIdentifiers($mask);

            foreach ($zones as $timezone) {

                // Time at this timezone.
                $time = Carbon::now($timezone);

                // Convert timezone to human readable.
                $offset = '('.app('Human')->timeOffset($time->offsetHours).')';

                // Default name.
                $name = $timezone;

                // Remove the region from the name.
                if (array_get($config, 'remove-region', false)) {
                    $name = substr($timezone, strlen($region) + 1);
                }

                // Remove the offset.
                if (array_get($config, 'remove-offset', false)) {
                    $offset = '';
                }

                // Show the time.
                $current_time = '';
                if (array_get($config, 'show-time', false)) {
                    $current_time = $time->format(array_get($config, 'show-time-format', 'g:ia'));
                }

                // Place entry.
                array_set($timezones, strtoupper($region).'.'.$timezone, sprintf('%s %s %s',
                    $name,
                    $offset,
                    $current_time
                ));
            }
        }

        return $timezones;
    }

    /**
     * Return a flat array.
     *
     * @param array $config
     *
     * @return array
     */
    public static function optionsArray($config = [])
    {
        $data = self::data($config);

        $result = [];

        foreach ($data as $name => $zones) {
            if (array_get($config, 'include-region', false)) {
                $result[] = [array_get($config, 'region-value', null), $name];
            }
            foreach ($zones as $timezone => $zone) {
                $result[] = [$timezone, $zone];
            }
        }

        return $result;
    }
}


<?php

namespace HnhDigital\HelperCollection;

class LaravelModel
{
    /**
     * Get the table keys based on the relation.
     *
     * @param Relation $relation
     *
     * @return array
     */
    private function getModelRelation($relation)
    {
        $method = basename(str_replace('\\', '/', get_class($relation)));

        switch ($method) {
            case 'BelongsTo':
            case 'HasMany':
            case 'HasOne':
                $model = $relation->getRelated();
                break;
            default:
                $model = $relation;
        }

        $table = $model->getTable();

        switch ($method) {
            case 'BelongsTo':
            case 'BelongsToMany':
                $parent_key = $relation->getQualifiedForeignKey();
                $foreign_key = $relation->getQualifiedOwnerKeyName();
            break;
            case 'HasMany':
                $parent_key = $relation->getQualifiedParentKeyName();
                $foreign_key = $relation->getQualifiedForeignKeyName();
            break;
            case 'HasOne':
                $parent_key = $table.'.'.$relation->getParentKey();
                $foreign_key = $table.'.'.$relation->getForeignKeyName();
            break;
        }

        return [
            'model'       => $model,
            'method'      => $method,
            'table'       => $table,
            'parent_key'  => $parent_key,
            'foreign_key' => $foreign_key,
        ];
    }

    /**
     * This determines the foreign key relations automatically to prevent the need to figure out the columns.
     *
     * @param string $relation_name
     * @param string $operator
     * @param string $type
     * @param bool   $where
     *
     * @return Builder
     *
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
     * @SuppressWarnings(PHPMD.LongVariable)
     * @SuppressWarnings(PHPMD.StaticAccess)
     */
    public function modelJoin(&$query, &$primary_model, &$relationships, $operator = '=', $type = 'left', $where = false)
    {
        if (is_string($relationships)) {
            $relationships = [$relationships => []];
        }

        if (!is_array($relationships)) {
            $relationships = [$relationships];
        }

        if (empty($query->columns)) {
            $query->selectRaw('DISTINCT '.$primary_model->getTable().'.*');
        }

        foreach ($relationships as $relation_name => &$relationship) {
            if (empty($relationship)) {
                $relationship = $this->getModelRelation($primary_model->$relation_name());
            }

            // Required variables.
            $model = array_get($relationship, 'model');
            $method = array_get($relationship, 'method');
            $table = array_get($relationship, 'table');
            $parent_key = array_get($relationship, 'parent_key');
            $foreign_key = array_get($relationship, 'foreign_key');

            // Add the columns from the other table.
            // @todo do we need this?
            //$this->query->addSelect(new Expression("`$table`.*"));
            $query->join($table, $parent_key, $operator, $foreign_key, $type, $where);

            // The join above is to the intimidatory table. This joins the query to the actual model.
            if ($method === 'BelongsToMany') {
                $related_foreign_key = $model->getQualifiedRelatedKeyName();
                $related_relation = $model->getRelated();
                $related_table = $related_relation->getTable();
                $related_qualified_key_name = $related_relation->getQualifiedKeyName();
                $query->join($related_table, $related_qualified_key_name, $operator, $related_foreign_key, $type, $where);
            }
        }

        // Group by the original model.
        $query->groupBy($primary_model->getQualifiedKeyName());
    }
}


<?php

namespace HnhDigital\HelperCollection;

class Aws
{
    /**
     * Client connection details.
     *
     * @return array
     */
    public function client($override = [])
    {
        return [
            'version'     => array_get($override, 'version', config('hnhdigital.aws.version')),
            'region'      => array_get($override, 'region', config('hnhdigital.aws.region')),
            'credentials' => [
                'key'    => array_get($override, 'key', config('hnhdigital.aws.key')),
                'secret' => array_get($override, 'secret', config('hnhdigital.aws.secret')),
            ],
        ];
    }
}


<?php

namespace HnhDigital\HelperCollection;

class SemanticVersion
{
    /**
     * Convert a semantic version string to a number.
     *
     * @param string $value
     *
     * @return array
     */
    public static function toNumber($value)
    {
        $version_array = explode('-', $value, 2);
        $version = array_pad(explode('.', $version_array[0]), 4, '00000');
        $version_extra = array_get($version_array, 1, '');

        if (strlen($version[0]) == 8) {
            $version_number = (int) $version[0] * 100 + array_get($version, 1, 0);
        } else {
            $version_number = '';
            foreach ($version as $value) {
                $version_number .= str_pad($value, 5, '0', STR_PAD_LEFT);
            }
            $version_number = (int) $version_number;
        }

        return [$version_number, $version_extra];
    }
}


<?php

namespace HnhDigital\HelperCollection\Traits;

use Carbon\Carbon;

trait JobTrait
{
    /**
     * Scope the task in the job.
     *
     * @param Builer $query
     *
     * @return void
     */
    public function scopeTask($query, $task)
    {
        $query->whereRaw(sprintf(
            'payload->"$.data.command" LIKE \'%%%s%%\'',
            self::encodeQueryValue($task)
        ));
    }

    /**
     * Scope the model in the job.
     *
     * @param Builer $query
     *
     * @return void
     */
    public function scopeModel($query, $model, $key)
    {
        $query->whereRaw(sprintf(
            'payload->"$.data.command" LIKE \'%%id\\\\";s:36:\\\\"%s\\\\";%%\'',
            $key
        ));
    }

    /**
     * Scope a given key value.
     *
     * @param Builer $query
     * @param string $key
     * @param string $value
     * @param string $class_name
     * @param string $data_type
     *
     * @return void
     */
    public function scopeLookupKeyValue($query, $key, $value, $class_name = '', $data_type = 's')
    {
        // Lookup serialized array.
        if (is_array($value)) {
            $value = serialize($value);
            $value = self::encodeQueryValue(addslashes($value));

            $sql = sprintf(
                'payload->"$.data.command" LIKE \'%%%s\\\\\\\\";%s%%\'',
                $key,
                $value
            );

            $query->whereRaw($sql);

            return;
        }

        if ($value === false) {
            $data = sprintf(
                $data_type.':%s:\\\\\\\\"%s\\\\\\\\"',
                mb_strlen($class_name),
                self::encodeQueryValue($class_name)
            );

            $sql = sprintf(
                'payload->"$.data.command" LIKE \'%%%s\\\\\\\\";%s%%\'',
                $key,
                $data
            );
        } elseif ($value !== false) {
            if (stripos($value, '%') === false) {
                $value_length = mb_strlen($value);
            } else {
                $value_length = '%';
            }

            $data = sprintf(
                's:%s:\\\\\\\\"%s\\\\\\\\"',
                $value_length,
                $value
            );

            if (is_numeric($value)) {
                $data = sprintf('i:%s', $value);
            }

            $sql = sprintf(
                'payload->"$.data.command" LIKE \'%%%s\\\\\\\\";%s;%%\'',
                $key,
                $data
            );
        }

        $query->whereRaw($sql);
    }

    /**
     * Get the available at value as Carbon.
     *
     * @param string $value
     *
     * @return Carbon
     */
    public function getAvailableAtAttribute($value)
    {
        return Carbon::createFromTimestamp($value);
    }

    /**
     * Get the reserved at value as Carbon.
     *
     * @param string $value
     *
     * @return Carbon
     */
    public function getReservedAtAttribute($value)
    {
        return Carbon::createFromTimestamp($value);
    }

    /**
     * Get the created at value as Carbon.
     *
     * @param string $value
     *
     * @return Carbon
     */
    public function getCreatedAtAttribute($value)
    {
        return Carbon::createFromTimestamp($value);
    }

    /**
     * Set available_at attribute.
     *
     * @param string $value
     * @param $this
     */
    public function setAvailableAtAttribute($value)
    {
        $this->attributes['available_at'] = empty($value) ? 0 : $this->asDateTime($value)->timestamp;

        return $this;
    }

    /**
     * Get the payload.
     *
     * @param string $value
     *
     * @return string
     */
    public function getPayloadAttribute($value)
    {
        $value = json_decode($value);

        data_set($value, 'data.command', unserialize(data_get($value, 'data.command')));

        return $value;
    }

    /**
     * Encode a value.
     *
     * @param string $value
     *
     * @return string
     */
    public static function encodeQueryValue($value)
    {
        return str_replace('\\', '\\\\\\\\', $value);
    }
}


<?php

namespace HnhDigital\HelperCollection;

class Human
{
    /**
     * Consecutive names for byte units.
     *
     * @var array
     */
    public $units_of_bytes = [
        'B',
        'KB',
        'MB',
        'GB',
        'TB',
        'PB',
        'EB',
        'ZB',
        'YB',
    ];

    /**
     * Convert bytes to a word.
     *
     * @param int $bytes
     * @param int $decimals
     * @param int $mod
     *
     * @return string
     */
    public function bytes($bytes, $decimals = 2, $mod = 1024)
    {
        $factor = floor((strlen($bytes) - 1) / 3);
        $byte_name = $this->units_of_bytes[$factor];
        if ($mod == 1000) {
            $byte_name = str_replace('B', 'bit', $byte_name);
        }

        return sprintf("%.{$decimals}f %s", $bytes / pow($mod, $factor), $byte_name);
    }

    /**
     * Convert an integer timeset to pretty.
     *
     * @param int $offset
     *
     * @return string
     */
    public function timeOffset($offset)
    {
        $offset_whole = round($offset);
        $offset_decimal = abs($offset - $offset_whole) * 60;

        return (substr($offset, 0, 1) != '-' ? '+' : '').$offset_whole.':'.str_pad($offset_decimal, 2, '0', STR_PAD_LEFT);
    }

    /**
     * Convert seconds to words.
     *
     * @param int   $seconds
     * @param array $options
     *
     * @return string
     */
    public function seconds($seconds, $options = [])
    {
        // Seconds.
        if ($seconds < 60) {
            $time = $seconds;
            $name = 'second';
        }

        // Minutes.
        if ($seconds >= 60) {
            $time = round($seconds / 60, 0);
            $name = 'minute';
        }

        // Hours.
        if ($time >= 60) {
            $time = round($time / 60, 0);
            $name = 'hour';
        }

        // Days.
        if ($time >= 24) {
            $time = round($time / 24, 0);
            $name = 'day';
        }

        if (array_has($options, 'abbrev')) {
            switch ($name) {
                case 'second':
                    $name = 'sec';
                    break;
                case 'minute':
                    $name = 'min';
                    break;
            }
        }

        if (array_has($options, 'single')) {
            $name = substr($name, 0, 1);
        }

        if ($time == 1) {
            $time = '';
            $period = $name;
        } else {
            $period = str_plural($name, $time);
        }

        return sprintf('%s %s', $time, $period);
    }

    /**
     * Truncate a string.
     *
     * @param string $long_text
     * @param int    $length
     * @param array  $options
     *
     * @return string
     */
    public function truncate($long_text, $length, $options = [])
    {
        $short_text = mb_substr($long_text, 0, $length);

        if ($short_text === $long_text) {
            return $long_text;
        }

        $short_text .= '...';

        if (array_get($options, 'html', false)) {
            $short_text .= ' (truncated)';
        }

        if (array_get($options, 'html', false)) {
            $short_text = sprintf('%s', htmlspecialchars($long_text), $short_text);
        }

        return $short_text;
    }

    /**
     * File upload error codes for humans.
     *
     * @param int $code
     *
     * @return string
     */
    public static function getFileUploadErrorMessage($code)
    {
        switch ($code) {
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                return 'The uploaded file exceeds the maximum file size';
            case UPLOAD_ERR_PARTIAL:
                return 'The uploaded file was only partially uploaded';
            case UPLOAD_ERR_NO_FILE:
               return 'No file was uploaded';
            case UPLOAD_ERR_NO_TMP_DIR:
            case UPLOAD_ERR_CANT_WRITE:
            case UPLOAD_ERR_EXTENSION:
                return 'Server side error occurred';
        }

        return 'Unknown upload error';
    }
}


<?php

namespace HnhDigital\HelperCollection;

/*
 * This file is part of Laravel Navigation Builder.
 *
 * (c) Rocco Howard <rocco@hnh.digital>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Blade;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;

/**
 * This is the service provider class.
 *
 * @author Rocco Howard <rocco@hnh.digital>
 */
class ServiceProvider extends BaseServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        if ($this->app['config']->get('hnhdigital.blade.enabled')) {
            $this->registerBladeDirectives();
        }
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->mergeConfigFrom(__DIR__.'/regex.php', 'hnhdigital.regex');

        $this->app->singleton('Aws', function () {
            return new Aws();
        });

        $this->app->singleton('Color', function () {
            return new Color();
        });

        $this->app->singleton('Database', function () {
            return new Database();
        });

        $this->app->singleton('FileSystem', function () {
            return new FileSystem();
        });

        $this->app->singleton('Human', function () {
            return new Human();
        });

        $this->app->singleton('LaravelModel', function () {
            return new LaravelModel();
        });

        $this->app->singleton('SemVer', function () {
            return new SemanticVersion();
        });

        $this->app->singleton('Timezone', function () {
            return new Timezone();
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['Aws', 'Color', 'Database', 'FileSystem', 'Human', 'LaravelModel', 'SemVer', 'Timezone'];
    }

    /**
     * Register blade directives.
     *
     * @return void
     */
    private function registerBladeDirectives()
    {
        // Start capturing.
        if (!$this->app['config']->has('hnhdigital.blade.disable.capture')) {
            blade::directive('capture', function () {
                return '<?php ob_start(); ?>';
            });

            // Stop capturing.
            blade::directive('endcapture', function ($name) {
                if (substr($name, 0, 1) == '$') {
                    $name = substr($text, 1);
                }
                $name = trim($name, "'\"");

                return '<?php $'.$name.' = ob_get_clean(); ?>';
            });
        }

        // Call.
        if (!$this->app['config']->has('hnhdigital.blade.disable.call')) {
            blade::directive('call', function ($call) {
                $name = trim($name, "'\"");

                return "<?php $call; ?>";
            });
        }

        // CSRF.
        // @todo Remove when dropping L5.5 support.
        if (!$this->app['config']->has('hnhdigital.blade.disable.csrf')) {
            blade::directive('csrf', function () {
                return '<?= csrf_field(); ?>';
            });
        }

        // Single line of php
        if (!$this->app['config']->has('hnhdigital.blade.disable.raw')) {
            blade::directive('raw', function ($raw) {
                return "<?php $raw; ?>";
            });
        }

        // Multiple lines of php.
        if (!$this->app['config']->has('hnhdigital.blade.disable.php')) {
            blade::directive('php', function ($raw) {
                return "<?php\n$raw\n?>";
            });
        }

        // Share with view.
        if (!$this->app['config']->has('hnhdigital.blade.disable.share')) {
            blade::directive('share', function ($share) {
                return "<?php view()->share($share); ?>";
            });
        }

        // Use.
        if (!$this->app['config']->has('hnhdigital.blade.disable.use')) {
            blade::directive('use', function ($use) {
                return "<?php use $use; ?>";
            });
        }

        // Route.
        blade::directive('route', function ($use) {
            return "<?= route($use) ?>";
        });

        if (!$this->app['config']->has('hnhdigital.blade.disable.locale')) {
            // Format number.
            blade::directive('locale_currency_symbol', function () {
                return '<?= locale_currency_symbol() ?>';
            });

            // Format number.
            blade::directive('locale_format_number', function ($args) {
                return "<?= locale_format_number($args) ?>";
            });

            // Format currency.
            blade::directive('float_format_currency', function ($args) {
                return "<?= float_format_currency($args) ?>";
            });
        }

        if (!$this->app['config']->has('hnhdigital.blade.disable.datetime')) {
            // Format date using user format and timezone.
            blade::directive('user_timedate', function ($args) {
                return "<?= user_timedate($args) ?>";
            });

            // Format date using user format and timezone.
            blade::directive('user_time', function ($args) {
                return "<?= user_time($args) ?>";
            });

            // Format date using user format and timezone.
            blade::directive('user_date', function ($args) {
                return "<?= user_date($args) ?>";
            });
        }

        // Various text helper directives.
        if (!$this->app['config']->has('hnhdigital.blade.disable.text')) {
            foreach (['__', 'camel_case', 'kebab_case', 'snake_case', 'studly_case', 'str_plural', 'str_tense', 'title_case'] as $function_name) {
                blade::directive($function_name, function ($text) use ($function_name) {
                    $text = implode(',', array_map(function ($value) {
                        $value = trim($value, "'\" ");
                        if (substr($value, 0, 1) !== '$') {
                            $value = "'$value'";
                        }

                        return $value;
                    }, explode(',', $text)));

                    return "<?= $function_name($text); ?>";
                });
            }

            // @str_upper => strtoupper
            blade::directive('str_upper', function ($text) use ($function_name) {
                if (substr($text, 0, 1) !== '$') {
                    $text = "'$text'";
                }

                return "<?= strtoupper($text); ?>";
            });

            // @str_lower => strtolower
            blade::directive('str_lower', function ($text) use ($function_name) {
                if (substr($text, 0, 1) !== '$') {
                    $text = "'$text'";
                }

                return "<?= strtolower($text); ?>";
            });
        }
    }
}


<?php
namespace SelvinOrtiz\Dot;

/**
 * Class Dot
 *
 * @package SelvinOrtiz\Dot
 */
class Dot
{
    /**
     * Returns whether or not the $key exists within $arr
     *
     * @param array  $arr
     * @param string $key
     *
     * @return bool
     */
    public static function has($arr, $key)
    {
        if (strpos($key, '.') !== false && count(($keys = explode('.', $key)))) {
            foreach ($keys as $key) {
                if (!array_key_exists($key, $arr)) {
                    return false;
                }

                $arr = $arr[$key];
            }

            return true;
        }

        return array_key_exists($key, $arr);
    }

    /**
     * Returns he value of $key if found in $arr or $default
     *
     * @param array       $arr
     * @param string      $key
     * @param null|mixed  $default
     *
     * @return mixed
     */
    public static function get($arr, $key, $default = null)
    {
        if (strpos($key, '.') !== false && count(($keys = explode('.', $key)))) {
            foreach ($keys as $key) {
                if (!array_key_exists($key, $arr)) {
                    return $default;
                }

                $arr = $arr[$key];
            }

            return $arr;
        }

        return array_key_exists($key, $arr) ? $arr[$key] : $default;
    }

    /**
     * Sets the $value identified by $key inside $arr
     *
     * @param array  &$arr
     * @param string $key
     * @param mixed  $value
     */
    public static function set(array &$arr, $key, $value)
    {
        if (strpos($key, '.') !== false && ($keys = explode('.', $key)) && count($keys)) {
            while (count($keys) > 1) {
                $key = array_shift($keys);

                if (!isset($arr[$key]) || !is_array($arr[$key])) {
                    $arr[$key] = [];
                }

                $arr = &$arr[$key];
            }

            $arr[array_shift($keys)] = $value;
        } else {
            $arr[$key] = $value;
        }
    }

    /**
     * Deletes a $key and its value from the $arr
     *
     * @param  array &$arr
     * @param string $key
     */
    public static function delete(array &$arr, $key)
    {
        if (strpos($key, '.') !== false && ($keys = explode('.', $key)) && count($keys)) {
            while (count($keys) > 1) {
                $arr = &$arr[array_shift($keys)];
            }

            unset($arr[array_shift($keys)]);
        } else {
            unset($arr[$key]);
        }
    }
}


<?php

/**
 * Lightweight API interface with the Amazon Simple Notification Service
 *
 * @author Chris Barr <chris.barr@ntlworld.com>
 * @link http://aws.amazon.com/sns/
 * @link http://docs.amazonwebservices.com/sns/latest/api/
 */
class AmazonSNS {
	/** @var string $access_key */
	private $access_key;
	/** @var string $secret_key */
	private $secret_key;

	/** @var string $protocol */
	private $protocol = 'https://'; // http is allowed
	/** @var string $endpoint */
	private $endpoint = ''; // Defaults to us-east-1

	/** @var array $endpoints */
	private $endpoints = array(
		'us-east-2' => 'sns.us-east-2.amazonaws.com',
		'us-east-1' => 'sns.us-east-1.amazonaws.com',
		'us-west-1' => 'sns.us-west-1.amazonaws.com',
		'us-west-2' => 'sns.us-west-2.amazonaws.com',
		'ap-northeast-1' => 'sns.ap-northeast-1.amazonaws.com',
		'ap-northeast-2' => 'sns.ap-northeast-2.amazonaws.com',
		'ap-northeast-3' => 'sns.ap-northeast-3.amazonaws.com',
		'ap-south-1' => 'sns.ap-south-1.amazonaws.com',
		'ap-southeast-1' => 'sns.ap-southeast-1.amazonaws.com',
		'ap-southeast-2' => 'sns.ap-southeast-2.amazonaws.com',
		'ca-central-1' => 'sns.ca-central-1.amazonaws.com',
		'cn-north-1' => 'sns.cn-north-1.amazonaws.com.cn',
		'cn-northwest-1' => 'sns.cn-northwest-1.amazonaws.com.cn',
		'eu-central-1' => 'sns.eu-central-1.amazonaws.com',
		'eu-west-1' => 'sns.eu-west-1.amazonaws.com',
		'eu-west-2' => 'sns.eu-west-2.amazonaws.com',
		'eu-west-3' => 'sns.eu-west-3.amazonaws.com',
		'sa-east-1' => 'sns.sa-east-1.amazonaws.com',
		'us-gov-west-1' => 'sns.us-gov-west-1.amazonaws.com',
	);

	/**
	 * Instantiate the object - set access_key and secret_key and set default region
	 *
	 * @param string $access_key
	 * @param string $secret_key
	 * @param string $region [optional]
	 * @throws InvalidArgumentException
	 */
	public function __construct($access_key, $secret_key, $region = 'us-east-1') {
		$this->access_key = $access_key;
		$this->secret_key = $secret_key;

		if(empty($this->access_key) || empty($this->secret_key)) {
			throw new InvalidArgumentException('Must define Amazon access key and secret key');
		}

		$this->setRegion($region);
	}

	/**
	 * Set the SNS endpoint/region
	 *
	 * @link http://docs.amazonwebservices.com/general/latest/gr/index.html?rande.html
	 * @param string $region
	 * @return string
	 * @throws InvalidArgumentException
	 */
	public function setRegion($region) {
		if(!isset($this->endpoints[$region])) {
			throw new InvalidArgumentException('Region unrecognised');
		}

		return $this->endpoint = $this->protocol . $this->endpoints[$region];
	}

	/**
	 * Manually set the AWS API endpoint
	 *
	 * @param string $endpoint
	 */
	public function setEndpoint($endpoint) {
		$this->endpoint = $endpoint;
	}

	//
	// Public interface functions
	//

	/**
	 * Add permissions to a topic
	 *
	 * Example:
	 * 	$AmazonSNS->addPermission('topic:arn:123', 'New Permission', array('987654321000' => 'Publish', '876543210000' => array('Publish', 'SetTopicAttributes')));
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_AddPermission.html
	 * @param string $topicArn
	 * @param string $label Unique name of permissions
	 * @param array $permissions [optional] Array of permissions - member ID as keys, actions as values
	 * @return bool
	 * @throws InvalidArgumentException
	 */
	public function addPermission($topicArn, $label, $permissions = array()) {
		if(empty($topicArn) || empty($label)) {
			throw new InvalidArgumentException('Must supply TopicARN and a Label for this permission');
		}

		// Add standard params as normal
		$params = array(
			'TopicArn' => $topicArn,
			'Label' => $label
		);

		// Compile permissions into separate sequential arrays
		$memberFlatArray = array();
		$permissionFlatArray = array();

		foreach($permissions as $member => $permission) {
			if(is_array($permission)) {
				// Array of permissions
				foreach($permission as $singlePermission) {
					$memberFlatArray[] = $member;
					$permissionFlatArray[] = $singlePermission;
				}
			}
			else {
				// Just a single permission
				$memberFlatArray[] = $member;
				$permissionFlatArray[] = $permission;
			}
		}

		// Dummy check
		if(count($memberFlatArray) !== count($permissionFlatArray)) {
			// Something went wrong
			throw new InvalidArgumentException('Mismatch of permissions to users');
		}

		// Finally add to params
		for($x = 1; $x <= count($memberFlatArray); $x++) {
			$params['ActionName.member.' . $x] = $permissionFlatArray[$x];
			$params['AWSAccountID.member.' . $x] = $memberFlatArray[$x];
		}

		// Finally send request
		$this->_request('AddPermission', $params);

		return true;
	}

	/**
	 * Confirm a subscription to a topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_ConfirmSubscription.html
	 * @param string $topicArn
	 * @param string $token
	 * @param bool|null $authenticateOnUnsubscribe [optional]
	 * @return string - SubscriptionARN
	 * @throws InvalidArgumentException
	 */
	public function confirmSubscription($topicArn, $token, $authenticateOnUnsubscribe = null) {
		if(empty($topicArn) || empty($token)) {
			throw new InvalidArgumentException('Must supply a TopicARN and a Token to confirm subscription');
		}

		$params = array(
			'TopicArn' => $topicArn,
			'Token' => $token
		);

		if(!is_null($authenticateOnUnsubscribe)) {
			$params['AuthenticateOnUnsubscribe'] = $authenticateOnUnsubscribe;
		}

		$resultXml = $this->_request('ConfirmSubscription', $params);

		return strval($resultXml->ConfirmSubscriptionResult->SubscriptionArn);
	}

	/**
	 * Create an SNS topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_CreateTopic.html
	 * @param string $name
	 * @return string - TopicARN
	 * @throws InvalidArgumentException
	 */
	public function createTopic($name) {
		if(empty($name)) {
			throw new InvalidArgumentException('Must supply a Name to create topic');
		}

		$resultXml = $this->_request('CreateTopic', array('Name' => $name));

		return strval($resultXml->CreateTopicResult->TopicArn);
	}

	/**
	 * Delete an SNS topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_DeleteTopic.html
	 * @param string $topicArn
	 * @return bool
	 * @throws InvalidArgumentException
	 */
	public function deleteTopic($topicArn) {
		if(empty($topicArn)) {
			throw new InvalidArgumentException('Must supply a TopicARN to delete a topic');
		}

		$this->_request('DeleteTopic', array('TopicArn' => $topicArn));

		return true;
	}

	/**
	 * Get the attributes of a topic like owner, ACL, display name
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_GetTopicAttributes.html
	 * @param string $topicArn
	 * @return array
	 * @throws InvalidArgumentException
	 */
	public function getTopicAttributes($topicArn) {
		if(empty($topicArn)) {
			throw new InvalidArgumentException('Must supply a TopicARN to get topic attributes');
		}

		$resultXml = $this->_request('GetTopicAttributes', array('TopicArn' => $topicArn));

		// Get attributes
		$attributes = $resultXml->GetTopicAttributesResult->Attributes->entry;

		// Unfortunately cannot use _processXmlToArray here, so process manually
		$returnArray = array();

		// Process into array
		foreach($attributes as $attribute) {
			// Store attribute key as array key
			$returnArray[strval($attribute->key)] = strval($attribute->value);
		}

		return $returnArray;
	}

	/**
	 * List subscriptions that user is subscribed to
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_ListSubscriptions.html
	 * @param string|null $nextToken [optional] Token to retrieve next page of results
	 * @return array
	 */
	public function listSubscriptions($nextToken = null) {
		$params = array();

		if(!is_null($nextToken)) {
			$params['NextToken'] = $nextToken;
		}

		$resultXml = $this->_request('ListSubscriptions', $params);

		// Get subscriptions
		$subs = $resultXml->ListSubscriptionsResult->Subscriptions->member;

		$return = ['members' => $this->_processXmlToArray($subs)];

		if(isset($resultXml->ListSubscriptionsResult->NextToken)) {
			$return['nextToken'] = strval($resultXml->ListSubscriptionsResult->NextToken);
		}

		return $return;
	}

	/**
	 * List subscribers to a topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_ListSubscriptionsByTopic.html
	 * @param string $topicArn
	 * @param string|null $nextToken [optional] Token to retrieve next page of results
	 * @return array
	 * @throws InvalidArgumentException
	 */
	public function listSubscriptionsByTopic($topicArn, $nextToken = null) {
		if(empty($topicArn)) {
			throw new InvalidArgumentException('Must supply a TopicARN to show subscriptions to a topic');
		}

		$params = array(
			'TopicArn' => $topicArn
		);

		if(!is_null($nextToken)) {
			$params['NextToken'] = $nextToken;
		}

		$resultXml = $this->_request('ListSubscriptionsByTopic', $params);

		// Get subscriptions
		$subs = $resultXml->ListSubscriptionsByTopicResult->Subscriptions->member;

		$return = ['members' => $this->_processXmlToArray($subs)];

		if(isset($resultXml->ListSubscriptionsByTopicResult->NextToken)) {
			$return['nextToken'] = strval($resultXml->ListSubscriptionsByTopicResult->NextToken);
		}

		return $return;
	}

	/**
	 * List SNS topics
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_ListTopics.html
	 * @param string|null $nextToken [optional] Token to retrieve next page of results
	 * @return array
	 */
	public function listTopics($nextToken = null) {
		$params = array();

		if(!is_null($nextToken)) {
			$params['NextToken'] = $nextToken;
		}

		$resultXml = $this->_request('ListTopics', $params);

		// Get Topics
		$topics = $resultXml->ListTopicsResult->Topics->member;

		return $this->_processXmlToArray($topics);
	}

	/**
	 * Publish a message to a topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_Publish.html
	 * @param string $topicArn
	 * @param string $message
	 * @param string $subject [optional] Used when sending emails
	 * @param string $messageStructure [optional] Used when you want to send a different message for each protocol.If you set MessageStructure to json, the value of the Message parameter must: be a syntactically valid JSON object; and contain at least a top-level JSON key of "default" with a value that is a string.
	 * @return string
	 * @throws InvalidArgumentException
	 */
	public function publish($topicArn, $message, $subject = '', $messageStructure = '') {
		if(empty($topicArn) || empty($message)) {
			throw new InvalidArgumentException('Must supply a TopicARN and Message to publish to a topic');
		}

		$params = array(
			'TopicArn' => $topicArn,
			'Message' => $message
		);

		if(!empty($subject)) {
			$params['Subject'] = $subject;
		}

		if(!empty($messageStructure)) {
			$params['MessageStructure'] = $messageStructure;
		}

		$resultXml = $this->_request('Publish', $params);

		return strval($resultXml->PublishResult->MessageId);
	}

	/**
	 * Remove a set of permissions indentified by topic and label that was used when creating permissions
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_RemovePermission.html
	 * @param string $topicArn
	 * @param string $label
	 * @return bool
	 * @throws InvalidArgumentException
	 */
	public function removePermission($topicArn, $label) {
		if(empty($topicArn) || empty($label)) {
			throw new InvalidArgumentException('Must supply a TopicARN and Label to remove a permission');
		}

		$this->_request('RemovePermission', array('Label' => $label));

		return true;
	}

	/**
	 * Set a single attribute on a topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_SetTopicAttributes.html
	 * @param string $topicArn
	 * @param string $attrName
	 * @param mixed $attrValue
	 * @return bool
	 * @throws InvalidArgumentException
	 */
	public function setTopicAttributes($topicArn, $attrName, $attrValue) {
		if(empty($topicArn) || empty($attrName) || empty($attrValue)) {
			throw new InvalidArgumentException('Must supply a TopicARN, AttributeName and AttributeValue to set a topic attribute');
		}

		$this->_request('SetTopicAttributes', array(
			'TopicArn' => $topicArn,
			'AttributeName' => $attrName,
			'AttributeValue' => $attrValue
		));

		return true;
	}

	/**
	 * Subscribe to a topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_Subscribe.html
	 * @param string $topicArn
	 * @param string $protocol - http/https/email/email-json/sms/sqs
	 * @param string $endpoint
	 * @return string $SubscriptionArn
	 * @throws InvalidArgumentException
	 */
	public function subscribe($topicArn, $protocol, $endpoint) {
		if(empty($topicArn) || empty($protocol) || empty($endpoint)) {
			throw new InvalidArgumentException('Must supply a TopicARN, Protocol and Endpoint to subscribe to a topic');
		}

		$response = $this->_request('Subscribe', array(
			'TopicArn' => $topicArn,
			'Protocol' => $protocol,
			'Endpoint' => $endpoint
		));

		return strval($response->SubscribeResult->SubscriptionArn);
	}

	/**
	 * Unsubscribe a user from a topic
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_Unsubscribe.html
	 * @param string $subscriptionArn
	 * @return bool
	 * @throws InvalidArgumentException
	 */
	public function unsubscribe($subscriptionArn) {
		if(empty($subscriptionArn)) {
			throw new InvalidArgumentException('Must supply a SubscriptionARN to unsubscribe from a topic');
		}

		$this->_request('Unsubscribe', array('SubscriptionArn' => $subscriptionArn));

		return true;
	}

	/**
	 * Create Platform endpoint
	 *
	 * @link http://docs.aws.amazon.com/sns/latest/api/API_CreatePlatformEndpoint.html
	 * @param string $platformApplicationArn
	 * @param string $token
	 * @param string $userData
	 * @return string
	 * @throws InvalidArgumentException
	 */
	public function createPlatformEndpoint($platformApplicationArn, $token, $userData = null) {
		if(empty($platformApplicationArn) || empty($token)) {
			throw new InvalidArgumentException('Must supply a PlatformApplicationArn & Token to create platform endpoint');
		}

		$attributes = array(
			'PlatformApplicationArn' => $platformApplicationArn,
			'Token' => $token
		);

		if(!empty($userData)) {
			$attributes['CustomUserData'] = $userData;
		}

		$response = $this->_request('CreatePlatformEndpoint', $attributes);

		return strval($response->CreatePlatformEndpointResult->EndpointArn);
	}

	/**
	 * Delete endpoint
	 *
	 * @link http://docs.aws.amazon.com/sns/latest/api/API_DeleteEndpoint.html
	 * @param string $deviceArn
	 *
	 * @return bool
	 * @throws InvalidArgumentException
	 */
	public function deleteEndpoint($deviceArn) {
		if(empty($deviceArn)) {
			throw new InvalidArgumentException('Must supply a DeviceARN to remove platform endpoint');
		}

		$this->_request('DeleteEndpoint', array(
			'EndpointArn' => $deviceArn
		));

		return true;
	}

	/**
	 * Publish a message to an Endpoint
	 *
	 * @link http://docs.amazonwebservices.com/sns/latest/api/API_Publish.html
	 * @param string $deviceArn
	 * @param string $message
	 * @return string
	 * @throws InvalidArgumentException
	 */
	public function publishToEndpoint($deviceArn, $message) {
		if (empty($deviceArn) || empty($message)) {
			throw new InvalidArgumentException('Must supply DeviceArn and Message');
		}

		$resultXml = $this->_request('Publish', array(
			'TargetArn' => $deviceArn,
			'Message' => $message)
		);

		return strval($resultXml->PublishResult->MessageId);
	}

	//
	// Private functions
	//

	/**
	 * Perform and process a cURL request
	 *
	 * @param string $action
	 * @param array $params [optional]
	 * @return SimpleXMLElement
	 * @throws SNSException|APIException
	 */
	private function _request($action, $params = array()) {
		// Add in required params
		$params['Action'] = $action;
		$params['AWSAccessKeyId'] = $this->access_key;
		$params['Timestamp'] = gmdate('Y-m-d\TH:i:s.000\Z');
		$params['SignatureVersion'] = 2;
		$params['SignatureMethod'] = 'HmacSHA256';

		// Sort and encode into string
		uksort($params, 'strnatcmp');
		$queryString = '';
		foreach ($params as $key => $val) {
			$queryString .= "&{$key}=".rawurlencode($val);
		}
		$queryString = substr($queryString, 1);

		// Form request string
		$requestString = "GET\n"
			. $this->endpoint."\n"
			. "/\n"
			. $queryString;

		// Create signature - Version 2
		$params['Signature'] = base64_encode(
			hash_hmac('sha256', $requestString, $this->secret_key, true)
		);

		// Finally create request
		$request = $this->endpoint . '/?' . http_build_query($params, '', '&');

		// Instantiate cUrl and perform request
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $request);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

		$output = curl_exec($ch);
		$info = curl_getinfo($ch);

		// Close cUrl
		curl_close($ch);

		// Load XML response
		$xmlResponse = simplexml_load_string($output);

		// Check return code
		if($this->_checkGoodResponse($info['http_code']) === false) {
			// Response not in 200 range
			if(isset($xmlResponse->Error)) {
				// Amazon returned an XML error
				throw new SNSException(strval($xmlResponse->Error->Code) . ': ' . strval($xmlResponse->Error->Message), $info['http_code']);
			}
			else {
				// Some other problem
				throw new APIException('There was a problem executing this request', $info['http_code']);
			}
		}
		else {
			// All good
			return $xmlResponse;
		}
	}

	/**
	 * Check the curl response code - anything in 200 range
	 *
	 * @param int $code
	 * @return bool
	 */
	private function _checkGoodResponse($code) {
		return floor($code / 100) == 2;
	}

	/**
	 * Transform the standard AmazonSNS XML array format into a normal array
	 *
	 * @param SimpleXMLElement $xmlArray
	 * @return array
	 */
	private function _processXmlToArray(SimpleXMLElement $xmlArray) {
		$returnArray = array();

		// Process into array
		foreach($xmlArray as $xmlElement) {
			$elementArray = array();

			// Loop through each element
			foreach($xmlElement as $key => $element) {
				// Use strval() to make sure no SimpleXMLElement objects remain
				$elementArray[$key] = strval($element);
			}

			// Store array of elements
			$returnArray[] = $elementArray;
		}

		return $returnArray;
	}
}

// Exception thrown if there's a problem with the API
class APIException extends Exception {}

// Exception thrown if Amazon returns an error
class SNSException extends Exception {}


<?php

declare(strict_types=1);

/**
 * @license     http://opensource.org/licenses/mit-license.php MIT
 * @link        https://github.com/nicoSWD
 * @author      Nicolas Oelgart <nico@oelgart.com>
 */
namespace nicoSWD\Rule;

use Exception;
use nicoSWD\Rule\Evaluator\EvaluatorInterface;

class Rule
{
    /** @var string */
    private $rule;
    /** @var Parser\Parser */
    private $parser;
    /** @var string */
    private $parsedRule = '';
    /** @var string */
    private $error = '';
    /** @var object */
    private static $container;

    public function __construct(string $rule, array $variables = [])
    {
        if (!isset(self::$container)) {
            self::$container = require __DIR__ . '/container.php';
        }

        $this->parser = self::$container->parser($variables);
        $this->rule = $rule;
    }

    public function isTrue(): bool
    {
        /** @var EvaluatorInterface $evaluator */
        $evaluator = self::$container->evaluator();

        return $evaluator->evaluate(
            $this->parsedRule ?:
            $this->parser->parse($this->rule)
        );
    }

    public function isFalse(): bool
    {
        return !$this->isTrue();
    }

    /**
     * Tells whether a rule is valid (as in "can be parsed without error") or not.
     */
    public function isValid(): bool
    {
        try {
            $this->parsedRule = $this->parser->parse($this->rule);
        } catch (Exception $e) {
            $this->error = $e->getMessage();
            return false;
        }

        return true;
    }

    public function getError(): string
    {
        return $this->error;
    }
}


<?php

namespace Qutee\Persistor;

/**
 * Redis
 *
 * @author anorgan
 */
class Redis implements PersistorInterface
{

    /**
     * Default name for Redis key (queue name)
     */
    const DEFAULT_PREFIX = 'qutee:';

    /**
     *
     * @var array
     */
    private $_options = array();

    /**
     *
     * @var \Redis
     */
    private $_redis;

    /**
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->_options;
    }

    /**
     *
     * @param array $options
     *
     * @return \Qutee\Persistor\Redis
     */
    public function setOptions(array $options)
    {
        $this->_options = $options;

        return $this;
    }

    /**
     * Add task to queue
     *
     * @param \Qutee\Task $task
     *
     * @return \Qutee\Persistor\Redis
     */
    public function addTask(\Qutee\Task $task)
    {
        $queue = $this->_createQueueName($task->getPriority());

        // Remember all the queues we are using
        $this->_getRedis()->sadd('queues', $queue);

        $key = $this->_createKey($task);

        // Check if the task is unique and already exists
        if ($task->isUnique() && $this->_getRedis()->get($key) !== false) {
            return $this;
        }

        // Remember that we set the task to the queue
        $this->_getRedis()->set($key, $queue);

        // Add task to queue
        $data = serialize(array('task' => serialize($task), 'key' => $key));
        $this->_getRedis()->rpush($queue, $data);

        return $this;
    }

    /**
     *
     * @param int $priority
     *
     * @return \Qutee\Task|null
     */
    public function getTask($priority = null)
    {
        if (null !== $priority) {
            // Get only the requested priority queue
            $queues = (array) $this->_createQueueName($priority);
        } else {
            // Get all queues
            $queues = (array) $this->_getRedis()->smembers('queues');
        }

        $data   = $this->_getRedis()->blpop($queues, 10);
        if (empty($data)) {
            return null;
        }

        list($queue, $taskData) = $data;
        $taskData = unserialize($taskData);

        // Forget we ever had this one
        // @todo events should fire methods: before processing, processing, afterFailedProcessing, afterSuccessfulProcessing
        $this->_getRedis()->del($taskData['key']);

        return unserialize($taskData['task']);
    }

    /**
     *
     * @param int $priority
     *
     * @return array
     */
    public function getTasks($priority = null)
    {
        $tasks  = array();

        if (null !== $priority) {
            // Get only the requested priority queue
            $queues = (array) $this->_createQueueName($priority);
        } else {
            // Get all queues
            $queues = (array) $this->_getRedis()->smembers('queues');
        }

        foreach ($queues as $queue) {
            $tasks += (array) $this->_getRedis()->lrange($queue, 0, 1000);
        }

        foreach ($tasks as $k => $data) {
            $data       = unserialize($data);
            $tasks[$k]  = unserialize($data['task']);
        }

        return $tasks;
    }

    /**
     * Clear queue
     *
     * @return boolean
     */
    public function clear()
    {
        return $this->_getRedis()->flushDB();
    }

    /**
     * Create queue name, which is in fact a priority filter
     *
     * @param int $priority
     *
     * @return string
     */
    protected function _createQueueName($priority)
    {
        return 'queue:priority_'. $priority;
    }

    /**
     *
     * @param \Qutee\Task $task
     *
     * @return string
     */
    protected function _createKey(\Qutee\Task $task)
    {
        if ($task->isUnique()) {
            $key = sprintf('task:%s:%s', $task->getName(), $task->getUniqueId());
        } else {
            $key = sprintf('task:%s:%s', $task->getName(), uniqid('', true));
        }

        return $key;
    }

    /**
     *
     * @return \Redis|\Predis\Client
     *
     * @throws \RuntimeException
     */
    protected function _getRedis()
    {
        if (null === $this->_redis) {

            $host = isset($this->_options['host']) ? $this->_options['host'] : '127.0.0.1';
            $port = isset($this->_options['port']) ? $this->_options['port'] : 6379;

            if (extension_loaded('redis')) {
                $redis = new \Redis;
                $redis->connect($host, $port);
                $redis->select(isset($this->_options['database']) ? $this->_options['database'] : 0);
                $redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_NONE);

                if (isset($this->_options['prefix'])) {
                    // Enforce trailing semicollon
                    $prefix = rtrim($this->_options['prefix'], ':') .':';
                } else {
                    $prefix = self::DEFAULT_PREFIX;
                }

                $redis->setOption(\Redis::OPT_PREFIX, $prefix);
            } elseif (class_exists('\\Predis\\Client')) {
                $redis = new \Predis\Client(array(
                    'scheme' => 'tcp',
                    'host'   => $host,
                    'port'   => $port
                ));
            } else {
                throw new \RuntimeException('Redis persistor requires redis extension or predis client library.');
            }

            $this->_redis = $redis;
        }

        return $this->_redis;
    }

}


<?php

namespace Qutee;

use Qutee\Task;

/**
 * Queue
 *
 * @author anorgan
 */
class Queue
{
    const EVENT_ADD_TASK = 'qutee.queue.add_task';
    const EVENT_CLEAR_ALL_TASKS = 'qutee.queue.clear_all_tasks';

    /**
     *
     * @var \Qutee\Persistor\PersistorInterface
     */
    protected $_persistor;

    /**
     *
     * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
     */
    protected $_eventDispatcher;

    /**
     *
     * @var \Qutee\Queue
     */
    static protected $_instance;

    public function __construct()
    {
        self::$_instance = $this;
    }

    /**
     *
     * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
     */
    public function getEventDispatcher()
    {
        if (null === $this->_eventDispatcher) {
            $this->_eventDispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher;
        }
        return $this->_eventDispatcher;
    }

    /**
     *
     * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
     *
     * @return \Qutee\Queue
     */
    public function setEventDispatcher(\Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher)
    {
        $this->_eventDispatcher = $eventDispatcher;
        return $this;
    }

    /**
     *
     * @return \Qutee\Persistor\PersistorInterface
     */
    public function getPersistor()
    {
        return $this->_persistor;
    }

    /**
     *
     * @param \Qutee\Persistor\PersistorInterface $persistor
     *
     * @return \Qutee\Queue
     */
    public function setPersistor(\Qutee\Persistor\PersistorInterface $persistor)
    {
        $this->_persistor = $persistor;

        return $this;
    }

    /**
     *
     * @param \Qutee\Task $task
     *
     * @return \Qutee\Queue
     */
    public function addTask(Task $task)
    {
        $this->getPersistor()->addTask($task);

        $event = new Event($this);
        $event->setTask($task);

        $this->getEventDispatcher()->dispatch(self::EVENT_ADD_TASK, $event);

        return $this;
    }

    /**
     *
     * @param int $priority
     *
     * @return \Qutee\Task
     */
    public function getTask($priority = null)
    {
        return $this->getPersistor()->getTask($priority);
    }

    /**
     *
     * @param int $priority
     *
     * @return array
     */
    public function getTasks($priority = null)
    {
        return $this->getPersistor()->getTasks($priority);
    }

    /**
     * Clear all tasks
     *
     * @return boolean
     */
    public function clear()
    {
        if ($this->getPersistor()->clear()) {

            $event = new Event($this);

            $this->getEventDispatcher()->dispatch(self::EVENT_CLEAR_ALL_TASKS, $event);
            return true;
        }

        return false;
    }

    /**
     * Create queue
     *
     * @param array $config:
     *  persistor: name of the persistor adapter
     *  options:   array with options for the persistor
     *
     * @return \Qutee\Queue
     * @throws \InvalidArgumentException
     */
    static public function factory($config = array())
    {
        if (isset($config['persistor'])) {
            $persistorClass = 'Qutee\\Persistor\\'. ucfirst($config['persistor']);
            if (class_exists($persistorClass)) {
                $persistor = new $persistorClass;
            } elseif (class_exists($config['persistor'])) {
                $persistor = new $config['persistor'];
            }

            if (!isset($persistor) || !is_object($persistor)) {
                throw new \InvalidArgumentException(sprintf('Persistor "%s" doesn\'t exist', $config['persistor']));
            } elseif (!($persistor instanceof Persistor\PersistorInterface)) {
                throw new \InvalidArgumentException(sprintf('Persistor "%s" does not implement Persistor\PersistorInterface', $config['persistor']));
            }

            if (isset($config['options'])) {
                $persistor->setOptions($config['options']);
            }
        } else {
            // Default persistor
            $persistor = new \Qutee\Persistor\Memory;
        }

        $queue = new self;
        $queue->setPersistor($persistor);

        return $queue;
    }

    static public function setInstance($instance)
    {
        self::$_instance = $instance;
    }

    /**
     *
     * @return \Qutee\Queue
     */
    static public function get()
    {
        if (null === self::$_instance) {
            throw new Exception('Queue not created');
        }

        return self::$_instance;
    }
}


<?php

namespace Qutee\Persistor;

/**
 * Memory
 *
 * @author anorgan
 */
class Memory implements PersistorInterface
{

    /**
     *
     * @var array
     */
    private $_options = array();

    /**
     * Array for storage
     *
     * @var array
     */
    private $_storage = array();

    /**
     * Array for unique tasks
     *
     * @var array
     */
    private $_uniqueTasks = array();

    /**
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->_options;
    }

    /**
     *
     * @param array $options
     *
     * @return \Qutee\Persistor\Memory
     */
    public function setOptions(array $options)
    {
        $this->_options = $options;

        return $this;
    }

    /**
     * Add task to queue
     *
     * @param \Qutee\Task $task
     *
     * @return \Qutee\Persistor\Memory
     */
    public function addTask(\Qutee\Task $task)
    {
        $key = $this->_createKey($task);

        // Check if the task is unique and already exists
        if ($task->isUnique() && array_key_exists($key, $this->_uniqueTasks)) {
            return $this;
        }

        $this->_uniqueTasks[$key] = true;

        $this->_storage[] = serialize($task);

        return $this;
    }

    /**
     *
     * @param int $priority
     *
     * @return \Qutee\Task
     */
    public function getTask($priority = null)
    {
        if (null !== $priority) {
            foreach ($this->_storage as $k => $task) {
                $task = unserialize($task);
                if ($task->getPriority() == $priority) {
                    unset($this->_storage[$k]);
                    return $task;
                }
            }
        } else {
            $task = array_shift($this->_storage);

            if (null !== $task) {
                $task = unserialize($task);
            }

            return $task;
        }
    }

    /**
     *
     * @param int $priority
     *
     * @return array
     */
    public function getTasks($priority = null)
    {
        $tasks = array();
        foreach ($this->_storage as $task) {
            $task = unserialize($task);
            if (null !== $priority && $task->getPriority() != $priority) {
                continue;
            }

            $tasks[] = $task;
        }

        return $tasks;
    }

    /**
     * Clear queue
     *
     * @return boolean
     */
    public function clear()
    {
        $this->_storage = array();

        return true;
    }

    /**
     *
     * @param \Qutee\Task $task
     *
     * @return string
     */
    protected function _createKey(\Qutee\Task $task)
    {
        if ($task->isUnique()) {
            $key = sprintf('task:%s:%s', $task->getName(), $task->getUniqueId());
        } else {
            $key = sprintf('task:%s:%s', $task->getName(), uniqid('', true));
        }

        return $key;
    }
}


<?php

namespace Qutee\Persistor;

use Pheanstalk\Pheanstalk;
use Pheanstalk\PheanstalkInterface;
use Qutee\Task;

/**
 * Beanstalk
 *
 * @author anorgan
 */
class Beanstalk implements PersistorInterface
{

    const TUBE_NAME = 'qutee';

    /**
     *
     * @var array
     */
    private $_options = array();

    /**
     *
     * @var Pheanstalk
     */
    private $_client;

    /**
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->_options;
    }

    /**
     *
     * @param array $options
     *
     * @return \Qutee\Persistor\Beanstalk
     */
    public function setOptions(array $options)
    {
        $this->_options = $options;

        return $this;
    }

    /**
     * Add task to queue
     *
     * @param Task $task
     *
     * @return \Qutee\Persistor\Beanstalk
     */
    public function addTask(Task $task)
    {
        // Add task to queue
        $data = serialize($task);
        $this->_getClient()->putInTube(self::TUBE_NAME, $data, $this->_convertPriority($task->getPriority()));

        return $this;
    }

    /**
     *
     * @param int $priority
     *
     * @return Task|null
     */
    public function getTask($priority = null)
    {
        $data   = $this->_getClient()->reserveFromTube(self::TUBE_NAME, 10);

        if (empty($data)) {
            return null;
        }

        /* @var $task Task */
        $task = unserialize($data->getData());

        if (null !== $priority && $task->getPriority() !== $priority) {
            // These is not the task you are looking for :)
            $this->_getClient()->release($data);

            return null;
        }

        $this->_getClient()->delete($data);

        return $task;
    }

    /**
     *
     * @param int $priority
     *
     * @return array
     */
    public function getTasks($priority = null)
    {
        throw new \BadMethodCallException('Method not supported for Beanstalk persistor');
    }

    /**
     * Clear queue
     *
     * @return boolean
     */
    public function clear()
    {
        while ($job = $this->_getClient()->peekReady(self::TUBE_NAME)) {
            $this->_getClient()->delete($job);
        }
    }

    /**
     *
     * @return Pheanstalk
     *
     * @throws \RuntimeException
     */
    protected function _getClient()
    {
        if (null === $this->_client) {

            $host = isset($this->_options['host']) ? $this->_options['host'] : '127.0.0.1';
            $port = isset($this->_options['port']) ? $this->_options['port'] : 11300;
            $connectTimeout = isset($this->_options['connect_timeout']) ? $this->_options['connect_timeout'] : null;

            $this->_client = new Pheanstalk($host, $port, $connectTimeout);
        }

        return $this->_client;
    }

    /**
     * QuTee - Higher the number, higher the priority;
     * Beanstalkd - Lower the number, higher the priority
     *
     * @param int $priority
     *
     * @return int
     */
    protected function _convertPriority($priority)
    {
        if (!is_numeric($priority)) {
            throw new \Exeption('Error while converting priority, agrument is '
                    . 'not a number, '. gettype($priority) .' sent');
        }

        switch ($priority) {
            case Task::PRIORITY_LOW:
                // Least urgent Beanstalkd priority
                return 4294967295;

            case Task::PRIORITY_NORMAL:
                return PheanstalkInterface::DEFAULT_PRIORITY;

            case Task::PRIORITY_HIGH:
                return 1024;

            default:
                return 512;
        }
    }
}