<?php
/**
 * Author: Nil Portugués Calderó <contact@nilportugues.com>
 * Date: 6/3/14
 * Time: 12:07 AM.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace NilPortugues\Sql\QueryBuilder\Syntax;

/**
 * Class SyntaxFactory.
 */
final class SyntaxFactory
{
    /**
     * Creates a collection of Column objects.
     *
     * @param array      $arguments
     * @param Table|null $table
     *
     * @return array
     */
    public static function createColumns(array &$arguments, $table = null)
    {
        $createdColumns = [];

        foreach ($arguments as $index => $column) {
            if (!is_object($column)) {
                $newColumn = array($column);
                $column = self::createColumn($newColumn, $table);
                if (!is_numeric($index)) {
                    $column->setAlias($index);
                }

                $createdColumns[] = $column;
            } else if ($column instanceof Column) {
                $createdColumns[] = $column;
            }
        }

        return \array_filter($createdColumns);
    }

    /**
     * Creates a Column object.
     *
     * @param array      $argument
     * @param null|Table $table
     *
     * @return Column
     */
    public static function createColumn(array &$argument, $table = null)
    {
        $columnName = \array_values($argument);
        $columnName = $columnName[0];

        $columnAlias = \array_keys($argument);
        $columnAlias = $columnAlias[0];

        if (\is_numeric($columnAlias) || \strpos($columnName, '*') !== false) {
            $columnAlias = null;
        }

        return new Column($columnName, (string) $table, $columnAlias);
    }

    /**
     * Creates a Table object.
     *
     * @param string[] $table
     *
     * @return Table
     */
    public static function createTable($table)
    {
        $tableName = $table;
        if (\is_array($table)) {
            $tableName = \current($table);
            $tableAlias = \key($table);
        }

        $newTable = new Table($tableName);

        if (isset($tableAlias) && !is_numeric($tableAlias)) {
            $newTable->setAlias($tableAlias);
        }

        return $newTable;
    }
}


<?php
/**
 * Author: Nil Portugués Calderó <contact@nilportugues.com>
 * Date: 12/24/14
 * Time: 12:30 PM.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace NilPortugues\Sql\QueryBuilder\Manipulation;

/**
 * Class AbstractCreationalQuery.
 */
abstract class AbstractCreationalQuery extends AbstractBaseQuery
{
    /**
     * @var array
     */
    protected $values = [];

    /**
     * @param string $table
     * @param array  $values
     */
    public function __construct($table = null, array $values = null)
    {
        if (isset($table)) {
            $this->setTable($table);
        }

        if (!empty($values)) {
            $this->setValues($values);
        }
    }

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

    /**
     * @param array $values
     *
     * @return $this
     */
    public function setValues(array $values)
    {
        $this->values = \array_filter($values, function($value) {
            if (is_int($value)) {
                return true;
            }
            return $value;
        });

        return $this;
    }
}


<?php
/**
 * Author: Nil Portugués Calderó <contact@nilportugues.com>
 * Date: 6/3/14
 * Time: 12:07 AM.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace NilPortugues\Sql\QueryBuilder\Manipulation;

use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
use NilPortugues\Sql\QueryBuilder\Syntax\QueryPartInterface;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
use NilPortugues\Sql\QueryBuilder\Syntax\Table;
use NilPortugues\Sql\QueryBuilder\Syntax\Where;
// Builder injects itself into query for convestion to SQL string.
use NilPortugues\Sql\QueryBuilder\Builder\BuilderInterface;

/**
 * Class AbstractBaseQuery.
 */
abstract class AbstractBaseQuery implements QueryInterface, QueryPartInterface
{
    /**
     * @var string
     */
    protected $comment = '';

    /**
     * @var \NilPortugues\Sql\QueryBuilder\Builder\BuilderInterface
     */
    protected $builder;

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

    /**
     * @var string
     */
    protected $whereOperator = 'AND';

    /**
     * @var Where
     */
    protected $where;

    /**
     * @var array
     */
    protected $joins = [];

    /**
     * @var int
     */
    protected $limitStart;

    /**
     * @var int
     */
    protected $limitCount;

    /**
     * @var array
     */
    protected $orderBy = [];

    /**
     * @return Where
     */
    protected function filter()
    {
        if (!isset($this->where)) {
            $this->where = QueryFactory::createWhere($this);
        }

        return $this->where;
    }

    /**
     * Stores the builder that created this query.
     *
     * @param BuilderInterface $builder
     *
     * @return $this
     */
    final public function setBuilder(BuilderInterface $builder)
    {
        $this->builder = $builder;

        return $this;
    }

    /**
     * @return BuilderInterface
     *
     * @throws \RuntimeException when builder has not been injected
     */
    final public function getBuilder()
    {
        if (!$this->builder) {
            throw new \RuntimeException('Query builder has not been injected with setBuilder');
        }

        return $this->builder;
    }

    /**
     * Converts this query into an SQL string by using the injected builder.
     *
     * @return string
     */
    public function __toString()
    {
        try {
            return $this->getSql();
        } catch (\Exception $e) {
            return \sprintf('[%s] %s', \get_class($e), $e->getMessage());
        }
    }

    /**
     * Converts this query into an SQL string by using the injected builder.
     * Optionally can return the SQL with formatted structure.
     *
     * @param bool $formatted
     *
     * @return string
     */
    public function getSql($formatted = false)
    {
        if ($formatted) {
            return $this->getBuilder()->writeFormatted($this);
        }

        return $this->getBuilder()->write($this);
    }

    /**
     * @return string
     */
    abstract public function partName();

    /**
     * @return Where
     */
    public function getWhere()
    {
        return $this->where;
    }

    /**
     * @return Table
     */
    public function getTable()
    {
        $newTable = array($this->table);

        return \is_null($this->table) ? null : SyntaxFactory::createTable($newTable);
    }

    /**
     * @param string $table
     *
     * @return $this
     */
    public function setTable($table)
    {
        $this->table = (string) $table;

        return $this;
    }

    /**
     * @param string $whereOperator
     *
     * @return Where
     */
    public function where($whereOperator = 'AND')
    {
        if (!isset($this->where)) {
            $this->where = $this->filter();
        }

        $this->where->conjunction($whereOperator);

        return $this->where;
    }

    /**
     * @return string
     */
    public function getWhereOperator()
    {
        if (!isset($this->where)) {
            $this->where = $this->filter();
        }

        return $this->where->getConjunction();
    }

    /**
     * @param string $column
     * @param string $direction
     * @param null   $table
     *
     * @return $this
     */
    public function orderBy($column, $direction = OrderBy::ASC, $table = null)
    {
        $newColumn = array($column);
        $column = SyntaxFactory::createColumn($newColumn, \is_null($table) ? $this->getTable() : $table);
        $this->orderBy[] = new OrderBy($column, $direction);

        return $this;
    }

    /**
     * @return int
     */
    public function getLimitCount()
    {
        return $this->limitCount;
    }

    /**
     * @return int
     */
    public function getLimitStart()
    {
        return $this->limitStart;
    }

    /**
     * @param string $comment
     *
     * @return $this
     */
    public function setComment($comment)
    {
        // Make each line of the comment prefixed with "--",
        // and remove any trailing whitespace.
        $comment = '-- '.str_replace("\n", "\n-- ", \rtrim($comment));

        // Trim off any trailing "-- ", to ensure that the comment is valid.
        $this->comment = \rtrim($comment, '- ');

        if ($this->comment) {
            $this->comment .= "\n";
        }

        return $this;
    }

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


<?php
/**
 * Author: Nil Portugués Calderó <contact@nilportugues.com>
 * Date: 6/4/14
 * Time: 12:02 AM.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;

/**
 * Class PlaceholderWriter.
 */
class PlaceholderWriter
{
    /**
     * @var int
     */
    protected $counter = 1;

    /**
     * @var array
     */
    protected $placeholders = [];

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

    /**
     * @return $this
     */
    public function reset()
    {
        $this->counter = 1;
        $this->placeholders = [];

        return $this;
    }

    /**
     * @param $value
     *
     * @return string
     */
    public function add($value)
    {
        $placeholderKey = ':v'.$this->counter;
        $this->placeholders[$placeholderKey] = $this->setValidSqlValue($value);

        ++$this->counter;

        return $placeholderKey;
    }

    /**
     * @param $value
     *
     * @return string
     */
    protected function setValidSqlValue($value)
    {
        $value = $this->writeNullSqlString($value);
        $value = $this->writeStringAsSqlString($value);
        $value = $this->writeBooleanSqlString($value);

        return $value;
    }

    /**
     * @param $value
     *
     * @return string
     */
    protected function writeNullSqlString($value)
    {
        if (\is_null($value) || (\is_string($value) && empty($value))) {
            $value = $this->writeNull();
        }

        return $value;
    }

    /**
     * @return string
     */
    protected function writeNull()
    {
        return 'NULL';
    }

    /**
     * @param string $value
     *
     * @return string
     */
    protected function writeStringAsSqlString($value)
    {
        if (\is_string($value)) {
            $value = $this->writeString($value);
        }

        return $value;
    }

    /**
     * @param string $value
     *
     * @return string
     */
    protected function writeString($value)
    {
        return $value;
    }

    /**
     * @param string $value
     *
     * @return string
     */
    protected function writeBooleanSqlString($value)
    {
        if (\is_bool($value)) {
            $value = $this->writeBoolean($value);
        }

        return $value;
    }

    /**
     * @param bool $value
     *
     * @return string
     */
    protected function writeBoolean($value)
    {
        $value = \filter_var($value, FILTER_VALIDATE_BOOLEAN);

        return ($value) ? '1' : '0';
    }
}


<?php
/**
 * Author: Nil Portugués Calderó <contact@nilportugues.com>
 * Date: 12/24/14
 * Time: 12:55 PM.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;

use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Syntax\QueryPartInterface;

/**
 * Class AbstractSetWriter.
 */
abstract class AbstractSetWriter
{
    /**
     * @var GenericBuilder
     */
    protected $writer;

    /**
     * @param GenericBuilder $writer
     */
    public function __construct(GenericBuilder $writer)
    {
        $this->writer = $writer;
    }

    /**
     * @param QueryPartInterface $setClass
     * @param string             $setOperation
     * @param $glue
     *
     * @return string
     */
    protected function abstractWrite(QueryPartInterface $setClass, $setOperation, $glue)
    {
        $selects = [];

        foreach ($setClass->$setOperation() as $select) {
            $selects[] = $this->writer->write($select, false);
        }

        return \implode("\n".$glue."\n", $selects);
    }
}


<?php
/**
 * Author: Nil Portugués Calderó <contact@nilportugues.com>
 * Date: 6/3/14
 * Time: 12:07 AM.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace NilPortugues\Sql\QueryBuilder\Syntax;

use NilPortugues\Sql\QueryBuilder\Manipulation\QueryException;

/**
 * Class Column.
 */
class Column implements QueryPartInterface
{
    const ALL = '*';

    /**
     * @var Table
     */
    protected $table;

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

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

    /**
     * @param string $name
     * @param string $table
     * @param string $alias
     */
    public function __construct($name, $table, $alias = '')
    {
        $this->setName($name);
        $this->setTable($table);
        $this->setAlias($alias);
    }

    /**
     * @return string
     */
    public function partName()
    {
        return 'COLUMN';
    }

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

    /**
     * @param string $name
     *
     * @return $this
     */
    public function setName($name)
    {
        $this->name = (string) $name;

        return $this;
    }

    /**
     * @return Table
     */
    public function getTable()
    {
        return $this->table;
    }

    /**
     * @param string $table
     *
     * @return $this
     */
    public function setTable($table)
    {
        $newTable = array($table);
        $this->table = SyntaxFactory::createTable($newTable);

        return $this;
    }

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

    /**
     * @param null|string $alias
     *
     * @return $this
     *
     * @throws QueryException
     */
    public function setAlias($alias)
    {
        if (0 == \strlen($alias)) {
            $this->alias = null;

            return $this;
        }

        if ($this->isAll()) {
            throw new QueryException("Can't use alias because column name is ALL (*)");
        }

        $this->alias = (string) $alias;

        return $this;
    }

    /**
     * Check whether column name is '*' or not.
     *
     * @return bool
     */
    public function isAll()
    {
        return $this->getName() == self::ALL;
    }
}


<?php
/**
 * Author: Nil Portugués Calderó <contact@nilportugues.com>
 * Date: 12/25/14
 * Time: 11:41 AM.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace NilPortugues\Sql\QueryBuilder\Manipulation;

use NilPortugues\Sql\QueryBuilder\Syntax\Where;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;

/**
 * Class JoinQuery.
 */
class JoinQuery
{
    const JOIN_LEFT = 'LEFT';
    const JOIN_RIGHT = 'RIGHT';
    const JOIN_INNER = 'INNER';
    const JOIN_CROSS = 'CROSS';

    /**
     * @var Where
     */
    protected $joinCondition;

    /**
     * @var bool
     */
    protected $isJoin = false;

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

    /**
     * @var array
     */
    protected $joins = [];

    /**
     * @var Select
     */
    protected $select;

    /**
     * @param Select $select
     */
    public function __construct(Select $select)
    {
        $this->select = $select;
    }

    /**
     * @param string $table
     *
     * @return $this
     */
    public function setTable($table)
    {
        $this->select->setTable($table);

        return $this;
    }

    /**
     * @param string   $table
     * @param mixed    $selfColumn
     * @param mixed    $refColumn
     * @param string[] $columns
     *
     * @return Select
     */
    public function leftJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
    {
        return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_LEFT);
    }

    /**
     * @param string   $table
     * @param mixed    $selfColumn
     * @param mixed    $refColumn
     * @param string[] $columns
     * @param string   $joinType
     *
     * @return Select
     */
    public function join(
        $table,
        $selfColumn = null,
        $refColumn = null,
        $columns = [],
        $joinType = null
    ) {
        if (!isset($this->joins[$table])) {
            $select = QueryFactory::createSelect($table);
            $select->setColumns($columns);
            $select->setJoinType($joinType);
            $select->setParentQuery($this->select);
            $this->addJoin($select, $selfColumn, $refColumn);
        }

        return $this->joins[$table];
    }

    /**
     * @param Select $select
     * @param mixed  $selfColumn
     * @param mixed  $refColumn
     *
     * @return Select
     */
    public function addJoin(Select $select, $selfColumn, $refColumn)
    {
        $select->isJoin(true);
        $table = $select->getTable()->getName();

        if (!isset($this->joins[$table])) {
            if (!$selfColumn instanceof Column) {
                $newColumn = array($selfColumn);
                $selfColumn = SyntaxFactory::createColumn(
                    $newColumn,
                    $this->select->getTable()
                );
            }

            $select->joinCondition()->equals($refColumn, $selfColumn);
            $this->joins[$table] = $select;
        }

        return $this->joins[$table];
    }

    /**
     * Transforms Select in a joint.
     *
     * @param bool $isJoin
     *
     * @return $this
     */
    public function setJoin($isJoin = true)
    {
        $this->isJoin = $isJoin;

        return $this;
    }

    /**
     * @param string   $table
     * @param mixed    $selfColumn
     * @param mixed    $refColumn
     * @param string[] $columns
     *
     * @internal param null $selectClass
     *
     * @return Select
     */
    public function rightJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
    {
        return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_RIGHT);
    }

    /**
     * @param string   $table
     * @param mixed    $selfColumn
     * @param mixed    $refColumn
     * @param string[] $columns
     *
     * @return Select
     */
    public function crossJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
    {
        return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_CROSS);
    }

    /**
     * @param string   $table
     * @param mixed    $selfColumn
     * @param mixed    $refColumn
     * @param string[] $columns
     *
     * @return Select
     */
    public function innerJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
    {
        return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_INNER);
    }

    /**
     * Alias to joinCondition.
     *
     * @return Where
     */
    public function on()
    {
        return $this->joinCondition();
    }

    /**
     * WHERE constrains used for the ON clause of a (LEFT/RIGHT/INNER/CROSS) JOIN.
     *
     * @return Where
     */
    public function joinCondition()
    {
        if (!isset($this->joinCondition)) {
            $this->joinCondition = QueryFactory::createWhere($this->select);
        }

        return $this->joinCondition;
    }

    /**
     * @return bool
     */
    public function isJoinSelect()
    {
        return $this->isJoin;
    }

    /**
     * @return bool
     */
    public function isJoin()
    {
        return $this->isJoin;
    }

    /**
     * @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
     */
    public function getJoinCondition()
    {
        return $this->joinCondition;
    }

    /**
     * @param \NilPortugues\Sql\QueryBuilder\Syntax\Where $joinCondition
     *
     * @return $this
     */
    public function setJoinCondition($joinCondition)
    {
        $this->joinCondition = $joinCondition;

        return $this;
    }

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

    /**
     * @param string $joinType
     *
     * @return $this
     */
    public function setJoinType($joinType)
    {
        $this->joinType = $joinType;

        return $this;
    }

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

    /**
     * @param array $joins
     *
     * @return $this
     */
    public function setJoins($joins)
    {
        $this->joins = $joins;

        return $this;
    }

    /**
     * @return array
     */
    public function getAllJoins()
    {
        $joins = $this->joins;

        foreach ($this->joins as $join) {
            $joins = \array_merge($joins, $join->getAllJoins());
        }

        return $joins;
    }
}


<?php

declare(strict_types=1);

namespace Rinvex\Cacheable;

use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Builder;

/**
 * @mixin \Illuminate\Database\Query\Builder
 */
class EloquentBuilder extends Builder
{
    /**
     * Execute the query as a "select" statement.
     *
     * @param array $columns
     *
     * @return \Illuminate\Database\Eloquent\Collection|static[]
     */
    public function get($columns = ['*'])
    {
        $builder = $this->applyScopes();

        $closure = function () use ($builder, $columns) {
            // If we actually found models we will also eager load any relationships that
            // have been specified as needing to be eager loaded, which will solve the
            // n+1 query issue for the developers to avoid running a lot of queries.
            if (count($models = $builder->getModels($columns)) > 0) {
                $models = $builder->eagerLoadRelations($models);
            }

            return $builder->model->newCollection($models);
        };

        // Check if cache is enabled
        if ($builder->model->getCacheLifetime()) {
            return $builder->model->cacheQuery($builder, $columns, $closure);
        }

        // Cache disabled, just execute query & return result
        $results = call_user_func($closure);

        // We're done, let's clean up!
        $builder->model->resetCacheConfig();

        return $results;
    }

    /**
     * Get an array with the values of a given column.
     *
     * @param string      $column
     * @param string|null $key
     *
     * @return \Illuminate\Support\Collection
     */
    public function pluck($column, $key = null): Collection
    {
        $builder = $this->toBase();

        $closure = function () use ($builder, $column, $key) {
            $results = $builder->pluck($column, $key);

            // If the model has a mutator for the requested column, we will spin through
            // the results and mutate the values so that the mutated version of these
            // columns are returned as you would expect from these Eloquent models.
            if (! $this->model->hasGetMutator($column) &&
                ! $this->model->hasCast($column) &&
                ! in_array($column, $this->model->getDates())) {
                return $results;
            }

            return $results->map(function ($value) use ($column) {
                return $this->model->newFromBuilder([$column => $value])->{$column};
            });
        };

        // Check if cache is enabled
        if ($this->model->getCacheLifetime()) {
            return $this->model->cacheQuery($builder, (array) $column, $closure);
        }

        // Cache disabled, just execute query & return result
        $results = call_user_func($closure);

        // We're done, let's clean up!
        $this->model->resetCacheConfig();

        return $results;
    }
}


<?php

declare(strict_types=1);

namespace Rinvex\Cacheable;

use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

trait CacheableEloquent
{
    /**
     * Register an updated model event with the dispatcher.
     *
     * @param \Closure|string $callback
     *
     * @return void
     */
    abstract public static function updated($callback);

    /**
     * Register a created model event with the dispatcher.
     *
     * @param \Closure|string $callback
     *
     * @return void
     */
    abstract public static function created($callback);

    /**
     * Register a deleted model event with the dispatcher.
     *
     * @param \Closure|string $callback
     *
     * @return void
     */
    abstract public static function deleted($callback);

    /**
     * Boot the cacheable eloquent trait for a model.
     *
     * @return void
     */
    public static function bootCacheableEloquent(): void
    {
        static::updated(function (Model $cachedModel) {
            ! $cachedModel->isCacheClearEnabled() || $cachedModel::forgetCache();
        });

        static::created(function (Model $cachedModel) {
            ! $cachedModel->isCacheClearEnabled() || $cachedModel::forgetCache();
        });

        static::deleted(function (Model $cachedModel) {
            ! $cachedModel->isCacheClearEnabled() || $cachedModel::forgetCache();
        });
    }

    /**
     * Store the given cache key for the given model by mimicking cache tags.
     *
     * @param string $modelName
     * @param string $cacheKey
     *
     * @return void
     */
    protected static function storeCacheKey(string $modelName, string $cacheKey): void
    {
        $keysFile = storage_path('framework/cache/data/rinvex.cacheable.json');
        $cacheKeys = static::getCacheKeys($keysFile);

        if (! isset($cacheKeys[$modelName]) || ! in_array($cacheKey, $cacheKeys[$modelName])) {
            $cacheKeys[$modelName][] = $cacheKey;
            file_put_contents($keysFile, json_encode($cacheKeys));
        }
    }

    /**
     * Get cache keys from the given file.
     *
     * @param string $file
     *
     * @return array
     */
    protected static function getCacheKeys($file): array
    {
        if (! file_exists($file)) {
            $dir = dirname($file);
            is_dir($dir) || mkdir($dir);
            file_put_contents($file, null);
        }

        return json_decode(file_get_contents($file), true) ?: [];
    }

    /**
     * Flush cache keys of the given model by mimicking cache tags.
     *
     * @param string $modelName
     *
     * @return array
     */
    protected static function flushCacheKeys(string $modelName): array
    {
        $flushedKeys = [];
        $keysFile = storage_path('framework/cache/data/rinvex.cacheable.json');
        $cacheKeys = static::getCacheKeys($keysFile);

        if (isset($cacheKeys[$modelName])) {
            $flushedKeys = $cacheKeys[$modelName];

            unset($cacheKeys[$modelName]);

            file_put_contents($keysFile, json_encode($cacheKeys));
        }

        return $flushedKeys;
    }

    /**
     * Set the model cache lifetime.
     *
     * @param int $cacheLifetime
     *
     * @return $this
     */
    public function setCacheLifetime(int $cacheLifetime)
    {
        $this->cacheLifetime = $cacheLifetime;

        return $this;
    }

    /**
     * Get the model cache lifetime.
     *
     * @return int
     */
    public function getCacheLifetime(): int
    {
        return $this->cacheLifetime ?? -1;
    }

    /**
     * Set the model cache driver.
     *
     * @param string $cacheDriver
     *
     * @return $this
     */
    public function setCacheDriver($cacheDriver)
    {
        $this->cacheDriver = $cacheDriver;

        return $this;
    }

    /**
     * Get the model cache driver.
     *
     * @return string|null
     */
    public function getCacheDriver(): ?string
    {
        return $this->cacheDriver ?? null;
    }

    /**
     * Determine if model cache clear is enabled.
     *
     * @return bool
     */
    public function isCacheClearEnabled(): bool
    {
        return $this->cacheClearEnabled ?? true;
    }

    /**
     * Forget the model cache.
     *
     * @return void
     */
    public static function forgetCache()
    {
        static::fireCacheFlushEvent('cache.flushing');

        // Flush cache tags
        if (method_exists(app('cache')->getStore(), 'tags')) {
            app('cache')->tags(static::class)->flush();
        } else {
            // Flush cache keys, then forget actual cache
            foreach (static::flushCacheKeys(static::class) as $cacheKey) {
                app('cache')->forget($cacheKey);
            }
        }

        static::fireCacheFlushEvent('cache.flushed', false);
    }

    /**
     * Fire the given event for the model.
     *
     * @param string $event
     * @param bool   $halt
     *
     * @return mixed
     */
    protected static function fireCacheFlushEvent($event, $halt = true)
    {
        if (! isset(static::$dispatcher)) {
            return true;
        }

        // We will append the names of the class to the event to distinguish it from
        // other model events that are fired, allowing us to listen on each model
        // event set individually instead of catching event for all the models.
        $event = "eloquent.{$event}: ".static::class;

        $method = $halt ? 'until' : 'dispatch';

        return static::$dispatcher->{$method}($event, static::class);
    }

    /**
     * Reset cached model to its defaults.
     *
     * @return $this
     */
    public function resetCacheConfig()
    {
        ! $this->cacheDriver || $this->cacheDriver = null;
        ! $this->cacheLifetime || $this->cacheLifetime = -1;

        return $this;
    }

    /**
     * Generate unique cache key.
     *
     * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $builder
     * @param array                                                                    $columns
     *
     * @return string
     */
    protected function generateCacheKey($builder, array $columns): string
    {
        $query = $builder instanceof Builder ? $builder->getQuery() : $builder;
        $vars = [
            'aggregate' => $query->aggregate,
            'columns' => $query->columns,
            'distinct' => $query->distinct,
            'from' => $query->from,
            'joins' => $query->joins,
            'wheres' => $query->wheres,
            'groups' => $query->groups,
            'havings' => $query->havings,
            'orders' => $query->orders,
            'limit' => $query->limit,
            'offset' => $query->offset,
            'unions' => $query->unions,
            'unionLimit' => $query->unionLimit,
            'unionOffset' => $query->unionOffset,
            'unionOrders' => $query->unionOrders,
            'lock' => $query->lock,
        ];

        return md5(json_encode([
            $vars,
            $columns,
            static::class,
            $this->getCacheDriver(),
            $this->getCacheLifetime(),
            $builder instanceof Builder ? $builder->getEagerLoads() : null,
            $builder->getBindings(),
            $builder->toSql(),
        ]));
    }

    /**
     * Cache given callback.
     *
     * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $builder
     * @param array                                                                    $columns
     * @param \Closure                                                                 $closure
     *
     * @return mixed
     */
    public function cacheQuery($builder, array $columns, Closure $closure)
    {
        $modelName = $this->getMorphClass();
        $lifetime = $this->getCacheLifetime();
        $cacheKey = $this->generateCacheKey($builder, $columns);

        // Switch cache driver on runtime
        if ($driver = $this->getCacheDriver()) {
            app('cache')->setDefaultDriver($driver);
        }

        // We need cache tags, check if default driver supports it
        if (method_exists(app('cache')->getStore(), 'tags')) {
            $result = $lifetime === -1 ? app('cache')->tags($modelName)->rememberForever($cacheKey, $closure) : app('cache')->tags($modelName)->remember($cacheKey, $lifetime, $closure);

            return $result;
        }

        $result = $lifetime === -1 ? app('cache')->rememberForever($cacheKey, $closure) : app('cache')->remember($cacheKey, $lifetime, $closure);

        // Default cache driver doesn't support tags, let's do it manually
        static::storeCacheKey($modelName, $cacheKey);

        // We're done, let's clean up!
        $this->resetCacheConfig();

        return $result;
    }

    /**
     * Create a new Eloquent query builder for the model.
     *
     * @param \Illuminate\Database\Query\Builder $query
     *
     * @return \Illuminate\Database\Eloquent\Builder|static
     */
    public function newEloquentBuilder($query)
    {
        return new EloquentBuilder($query);
    }
}


<?php

namespace com\realexpayments\hpp\sdk\utils;

use com\realexpayments\hpp\sdk\domain\HppRequest;
use com\realexpayments\hpp\sdk\domain\HppResponse;
use com\realexpayments\hpp\sdk\RealexValidationException;
use com\realexpayments\hpp\sdk\RXPLogger;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Logger;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Validator\ValidatorInterface;

/**
 * Class validates HPP request and response objects.
 *
 * @author vicpada
 */
class ValidationUtils {

	/**
	 * @var Logger logger
	 */
	private static $logger;
	private static $initialised = false;

	/**
	 * @var ValidatorInterface validator
	 */
	private static $validator;

	/**
	 * Method validates HPP request object using JSR-303 bean validation.
	 *
	 * @param HppRequest|HppResponse $hppRequest
	 */
	public static function validate( HppRequest $hppRequest ) {
		self::Initialise();

		$violations = self::$validator->validate( $hppRequest );

		if ( $violations->count() > 0 ) {
			$validationMessages = array();

			foreach ( $violations as $violation ) {

				/* @var ConstraintViolationInterface $violation */
				$validationMessages[] = $violation->getMessage();
			}

			$message = "HppRequest failed validation with the following errors:";
			foreach ( $validationMessages as $validationMessage ) {
				$message .= $validationMessage . '.';
			}

			self::$logger->info( $message );
			throw new RealexValidationException( "HppRequest failed validation", $validationMessages );
		}

	}

	private static function Initialise() {
		if ( self::$initialised ) {
			return;
		}

		$file   = self::getVendorFile();
		$loader = require $file;

		AnnotationRegistry::registerLoader( array( $loader, 'loadClass' ) );

		self::$logger = RXPLogger::getLogger( __CLASS__ );

		self::$validator = Validation::createValidatorBuilder()
		                             ->enableAnnotationMapping()
		                             ->getValidator();

		self::$initialised = true;
	}

	private static function getVendorFile() {
		$vendor_dir = __DIR__ . "/../../../../../vendor";
		$file       = $vendor_dir . '/autoload.php';

		if ( ! file_exists( $file ) ) {
			$vendor_dir = __DIR__ . "/../../../../../../../../vendor";
			$file       = $vendor_dir . '/autoload.php';
		}

		return $file;
	}

	/**
	 * Method validates HPP response hash.
	 *
	 * @param HppResponse $hppResponse
	 * @param string $secret
	 */
	public static function validateResponse( HppResponse $hppResponse, $secret ) {
		self::Initialise();

		if ( ! $hppResponse->isHashValid( $secret ) ) {
			self::$logger->error( "HppResponse contains an invalid security hash." );
			throw new RealexValidationException( "HppResponse contains an invalid security hash", array( "HppResponse contains an invalid security hash" ) );
		}
	}
}


<?php

namespace com\realexpayments\hpp\sdk\validators;

use com\realexpayments\hpp\sdk\domain\HppRequest;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * Supplementary data cannot be longer than 255
 *
 * @package com\realexpayments\hpp\sdk\validators
 * @author vicpada
 *
 */
class SupplementaryDataLengthValidator extends ConstraintValidator {

	private $maxLength = 255;

	/**
	 * Checks if the passed value is valid.
	 *
	 * @param HppRequest $hppRequest
	 * @param Constraint $constraint
	 *
	 * @api
	 */
	public function validate( $hppRequest, Constraint $constraint ) {

		foreach ( $hppRequest->getSupplementaryData() as $supplementaryData ) {
			if ( strlen( $supplementaryData ) > $this->maxLength ) {
				$this->context->buildViolation( $constraint->message )
				              ->atPath( 'supplementaryData' )
				              ->addViolation();
			}
		}
	}
}


<?php

namespace com\realexpayments\hpp\sdk;

use com\realexpayments\hpp\sdk\domain\HppRequest;
use com\realexpayments\hpp\sdk\domain\HppResponse;
use com\realexpayments\hpp\sdk\utils\JsonUtils;
use com\realexpayments\hpp\sdk\utils\ValidationUtils;
use Exception;
use Logger;

/**
 *

 * RealexHpp class for converting HPP requests and responses to and from JSON.
 * This class is also responsible for validating inputs, generating defaults and encoding parameter values.
 *

 *

 * Creating Request JSON for Realex JS SDK
 *

 * $hppRequest = (new HppRequest())->addMerchantId("merchantId")->addAmount(100)...addAutoSettleFlag(true);
 * $realexHpp = new RealexHpp("mySecret");
 * $json = $realexHpp->requestToJson($hppRequest);
 *

 *

 *

 * Consuming Response JSON from Realex JS SDK
 *

 * $realexHpp = new RealexHpp("mySecret");
 * $hppResponse = $realexHpp->responseFromJson($responseJson);
 *

 *

 * @author vicpada
 *
 */
class RealexHpp
{

    /**
     * @var Logger logger
     */
    private $logger;

    /**
     * Character set to use for encoding/decoding.
     */
    const ENCODING_CHARSET = "UTF-8";

    /**
     * @var string  The shared secret issued by Realex. Used to create the SHA-1 hash in the request and
     * to verify the validity of the XML response.
     */
    private $secret;

    /**
     * RealexHpp constructor
     *
     * @param string $secret
     */
    public function __construct($secret)
    {
        $this->logger = RXPLogger::getLogger(__CLASS__);
        $this->secret = $secret;
    }

    /**
     *

     * Method produces JSON from HppRequest object.
     * Carries out the following actions:
     *

     *
Validates inputs

     *
Generates defaults for security hash, order ID and time stamp (if required)

     *
Base64 encodes inputs

     *
Serialises request object to JSON

     *

     *

     *
     * @param HppRequest $hppRequest
     * @param bool $encoded true if the JSON values will be encoded.
     * @return HppRequest
     */
    public function requestToJson(HppRequest $hppRequest, $encoded = true)
    {

        $this->logger->info("Converting HppRequest to JSON.");

        $json = null;

        //generate defaults
        $this->logger->debug("Generating defaults.");
        $hppRequest->generateDefaults($this->secret);

        //validate request
        $this->logger->debug("Validating request.");
        ValidationUtils::validate($hppRequest);

        // build request
        $this->logger->debug("Encoding object.");
        try {
            if ($encoded === true) {
                $hppRequest = $hppRequest->encode(self::ENCODING_CHARSET);
            }
            else {
                $hppRequest = $hppRequest->formatRequest(self::ENCODING_CHARSET);
            }
        } catch (Exception $e) {
            $this->logger->error("Exception encoding HPP request.", $e);
            throw new RealexException("Exception encoding HPP request.", $e);
        }

        //convert to JSON
        $this->logger->debug("Converting to JSON.");
        $json = JsonUtils::toJson($hppRequest);

        return $json;
    }

    /**
     *

     * Method produces HppRequest object from JSON.
     * Carries out the following actions:
     *

     *
Deserialises JSON to request object

     *
Decodes Base64 inputs

     *
Validates inputs

     *

     *

     *
     * @param string $json
     * @param bool $encoded true if the JSON values have been encoded.
     * @return HppRequest
     */
    public function  requestFromJson($json, $encoded = true)
    {
        $this->logger->info("Converting JSON to HppRequest.");

        //convert to HppRequest from JSON
        $hppRequest = JsonUtils::fromJsonHppRequest($json);

        //decode if necessary
        if ($encoded) {
            $this->logger->debug("Decoding object.");
            try {
                $hppRequest = $hppRequest->decode(self::ENCODING_CHARSET);
            } catch (Exception $e) {
                $this->logger->error("Exception encoding HPP request.", $e);
                throw new RealexException("Exception decoding HPP request.", $e);
            }
        }

        //validate HPP request
        $this->logger->debug("Validating request.");
        ValidationUtils::validate($hppRequest);

        return $hppRequest;
    }

    /**
     *

     * Method produces JSON from HppResponse object.
     * Carries out the following actions:
     *

     *
Validates inputs

     *
Generates defaults for security hash, order ID and time stamp (if required)

     *
Base64 encodes inputs

     *
Serialises response object to JSON

     *

     *

     *
     * @param HppResponse $hppResponse
     * @return string
     */
    public function responseToJson(HppResponse $hppResponse)
    {

        $this->logger->info("Converting HppResponse to JSON.");

        $json = null;

        //generate hash
        $this->logger->debug("Generating hash.");
        $hppResponse->hash($this->secret);

        //encode
        $this->logger->debug("Encoding object.");
        try {
            $hppResponse = $hppResponse->encode(self::ENCODING_CHARSET);
        } catch (Exception $e) {
            $this->logger->error("Exception encoding HPP response.", $e);
            throw new RealexException("Exception encoding HPP response.", $e);
        }

        //convert to JSON
        $this->logger->debug("Converting to JSON.");
        $json = JsonUtils::toJson($hppResponse);

        return $json;
    }

    /**
     *

     * Method produces HppResponse object from JSON.
     * Carries out the following actions:
     *

     *
Deserialises JSON to response object

     *
Decodes Base64 inputs

     *
Validates hash

     *

     *

     *
     * @param string $json
     * @param bool $encoded
     * @return HppResponse
     */
    public function responseFromJson($json, $encoded = true)
    {
        $this->logger->info("Converting JSON to HppResponse.");

        //convert to HppResponse from JSON
        $hppResponse = JsonUtils::fromJsonHppResponse($json);

        //decode if necessary
        if ($encoded) {
            $this->logger->debug("Decoding object.");
            try {
                $hppResponse = $hppResponse->decode(self::ENCODING_CHARSET);
            } catch (Exception $e) {
                $this->logger->error("Exception decoding HPP response.", $e);
                throw new RealexException("Exception decoding HPP response.", $e);
            }
        }

        //validate HPP response hash
        $this->logger->debug("Validating response hash.");
        ValidationUtils::validateResponse($hppResponse, $this->secret);

        return $hppResponse;
    }

}


<?php

namespace com\realexpayments\hpp\sdk\domain;

use com\realexpayments\hpp\sdk\utils\GenerationUtils;
use com\realexpayments\hpp\sdk\validators as AssertHPP;
use com\realexpayments\hpp\sdk\validators\ValidationMessages;
use Symfony\Component\Validator\Constraints as Assert;

/**
 *

 * Class representing a request to be sent to HPP.
 *

 *

 * Helper methods are provided (prefixed with 'add') for object creation.
 *

 *

 * Example usage:
 *

 * $request = (new HppRequest())
 *    ->addAmount(100)
 *    ->addCurrency("EUR")
 *    ->addMerchantId("merchantId");
 *

 *

 *
 *

 * HPP Version and HPP Select Stored Card
 *

 *

 * Example usage:
 *

 * $hppRequest = new HppRequest();
 * $hppRequest
 *   ->addAmount("1001")
 *   ->addCurrency("EUR")
 *   ->addAccount("accountId")
 *   ->addMerchantId("merchantId")
 *   ->addAutoSettleFlag("1")
 *   ->addPayerExists("1")
 *   ->addPayerReference("payerRef")
 *   ->addHppSelectStoredCard("storedCardRef");
 *

 *

 *
 *

 * HPP Post Dimension and  HPP Post Response
 *

 *

 * Example usage:
 *

 * $hppRequest = new HppRequest();
 * $hppRequest
 *   ->addAmount("1001")
 *   ->addCurrency("EUR")
 *   ->addAccount("accountId")
 *   ->addMerchantId("merchantId")
 *   ->addAutoSettleFlag("1")
 *   ->addPayerExists("payerRef")
 *   ->addPayerReference("payerRef")
 *   ->addPostDimensions("https://www.example.com")
 *   ->addPostResponse("https://www.example.com");
 *

 *

 * @author vicpada
 * @AssertHPP\OtbAmount
 * @AssertHPP\SupplementaryDataLength
 * @AssertHPP\SupplementaryDataPattern
 */
class HppRequest {

	/**
	 * @var String The merchant or client ID supplied by Realex Payments – note this is not the merchant number
	 * supplied by your bank.
	 *
	 * @Assert\Length(min = 1, max = 50, minMessage = ValidationMessages::hppRequest_merchantId_size, maxMessage = ValidationMessages::hppRequest_merchantId_size)
	 * @Assert\NotBlank(message= ValidationMessages::hppRequest_merchantId_size)
	 * @Assert\Regex(pattern="/^[a-zA-Z0-9\.]*$/", message=ValidationMessages::hppRequest_merchantId_pattern )
	 */
	private $merchantId;

	/**
	 * @var String The sub-account to use for this transaction. If not present, the default sub-account will be used.
	 *
	 * @Assert\Length(min = 0, max = 30, maxMessage = ValidationMessages::hppRequest_account_size)
	 * @Assert\Regex(pattern="/^[a-zA-Z0-9\s]*$/", message=ValidationMessages::hppRequest_account_pattern )
	 */
	private $account;

	/**
	 * @var String A unique alphanumeric id that’s used to identify the transaction. No spaces are allowed.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_orderId_size)
	 * @Assert\Regex(pattern="/^[a-zA-Z0-9_\-]*$/", message=ValidationMessages::hppRequest_orderId_pattern )
	 */
	private $orderId;

	/**
	 * @var String Total amount to authorise in the lowest unit of the currency – i.e. 100 euro would be entered as 10000.
	 * If there is no decimal in the currency (e.g. JPY Yen) then contact Realex Payments. No decimal points are allowed.
	 * Amount should be set to 0 for OTB transactions (i.e. where validate card only is set to 1).
	 *
	 * @Assert\Length(min = 1, max = 11, minMessage = ValidationMessages::hppRequest_amount_size, maxMessage = ValidationMessages::hppRequest_amount_size)
	 * @Assert\NotBlank(message= ValidationMessages::hppRequest_amount_size)
	 * @Assert\Regex(pattern="/^[0-9]*$/", message=ValidationMessages::hppRequest_amount_pattern )
	 */
	private $amount;

	/**
	 * @var String A three-letter currency code (Eg. EUR, GBP). A list of currency codes can be provided
	 * by your account manager.
	 *
	 * @Assert\Length(min = 3, max = 3, exactMessage = ValidationMessages::hppRequest_currency_size)
	 * @Assert\NotBlank(message= ValidationMessages::hppRequest_currency_size)
	 * @Assert\Regex(pattern="/^[a-zA-Z]*$/", message=ValidationMessages::hppRequest_currency_pattern )
	 */
	private $currency;

	/**
	 * @var String Date and time of the transaction. Entered in the following format: YYYYMMDDHHMMSS.
	 * Must be within 24 hours of the current time.
	 *
	 * @Assert\Length(min = 14, max = 14, exactMessage = ValidationMessages::hppRequest_timestamp_size)
	 * @Assert\NotBlank(message= ValidationMessages::hppRequest_timestamp_size)
	 * @Assert\Regex(pattern="/^[0-9]*$/", message=ValidationMessages::hppRequest_timestamp_pattern )
	 */
	private $timeStamp;

	/**
	 * @var String A digital signature generated using the SHA-1 algorithm.
	 *
	 * @Assert\Length(min = 40, max =40, exactMessage = ValidationMessages::hppRequest_hash_size)
	 * @Assert\NotBlank(message= ValidationMessages::hppRequest_hash_size)
	 * @Assert\Regex(pattern="/^[a-f0-9]*$/", message=ValidationMessages::hppRequest_hash_pattern )
	 */
	private $hash;

	/**
	 * @var String Used to signify whether or not you wish the transaction to be captured in the next batch.
	 * If set to "1" and assuming the transaction is authorised then it will automatically be settled in the next batch.
	 * If set to "0" then the merchant must use the RealControl application to manually settle the transaction.
	 * This option can be used if a merchant wishes to delay the payment until after the goods have been shipped.
	 * Transactions can be settled for up to 115% of the original amount and must be settled within a certain period
	 * of time agreed with your issuing bank.
	 *
	 * @Assert\Regex(pattern="/(?i)^on$|^off$|^$|^multi$|^1$|^0$/", message=ValidationMessages::hppRequest_autoSettleFlag_pattern )
	 *
	 */
	private $autoSettleFlag;

	/**
	 * @var String A freeform comment to describe the transaction.
	 * @Assert\Length(min = 0, max = 255, maxMessage = ValidationMessages::hppRequest_comment1_size, charset="ISO-8859-1")
	 * @Assert\Regex(pattern="/^[\s \x{0020}-\x{003B} \x{003D} \x{003F}-\x{007E} \x{00A1}-\x{00FF}\x{20AC}\x{201A}\x{0192}\x{201E}\x{2026}\x{2020}\x{2021}\x{02C6}\x{2030}\x{0160}\x{2039}\x{0152}\x{017D}\x{2018}\x{2019}\x{201C}\x{201D}\x{2022}\x{2013}\x{2014}\x{02DC}\x{2122}\x{0161}\x{203A}\x{0153}\x{017E}\x{0178}]*$/iu", message=ValidationMessages::hppRequest_comment1_pattern )
	 */
	private $commentOne;

	/**
	 * @var String A freeform comment to describe the transaction.
	 *
	 * @Assert\Length(min = 0, max = 255, maxMessage = ValidationMessages::hppRequest_comment2_size, charset="ISO-8859-1")
	 * @Assert\Regex(pattern="/^[\s \x{0020}-\x{003B} \x{003D} \x{003F}-\x{007E} \x{00A1}-\x{00FF}\x{20AC}\x{201A}\x{0192}\x{201E}\x{2026}\x{2020}\x{2021}\x{02C6}\x{2030}\x{0160}\x{2039}\x{0152}\x{017D}\x{2018}\x{2019}\x{201C}\x{201D}\x{2022}\x{2013}\x{2014}\x{02DC}\x{2122}\x{0161}\x{203A}\x{0153}\x{017E}\x{0178}]*$/iu", message=ValidationMessages::hppRequest_comment2_pattern )
	 */
	private $commentTwo;

	/**
	 * @var String Used to signify whether or not you want a Transaction Suitability Score for this transaction.
	 * Can be "0" for no and "1" for yes.
	 *
	 * @Assert\Length(min = 0, max = 1, maxMessage = ValidationMessages::hppRequest_returnTss_size)
	 * @Assert\Regex(pattern="/^[01]*$/", message=ValidationMessages::hppRequest_returnTss_pattern )
	 */
	private $returnTss;

	/**
	 * @var String The postcode or ZIP of the shipping address.
	 *
	 * @Assert\Length(min = 0, max = 30, maxMessage = ValidationMessages::hppRequest_shippingCode_size)
	 * @Assert\Regex(pattern="/^[A-Za-z0-9\,\.\-\/\| ]*$/", message=ValidationMessages::hppRequest_shippingCode_pattern )
	 */
	private $shippingCode;

	/**
	 * @var String The country of the shipping address.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_shippingCountry_size)
	 * @Assert\Regex(pattern="/^[A-Za-z0-9\,\.\- ]*$/", message=ValidationMessages::hppRequest_shippingCountry_pattern )
	 */
	private $shippingCountry;

	/**
	 * @var String The postcode or ZIP of the billing address.
	 *
	 * @Assert\Length(min = 0, max = 60, maxMessage = ValidationMessages::hppRequest_billingCode_size)
	 * @Assert\Regex(pattern="/^[A-Za-z0-9\,\.\-\/\| ]*$/", message=ValidationMessages::hppRequest_billingCode_pattern )
	 */
	private $billingCode;

	/**
	 * @var String The country of the billing address.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_billingCountry_size)
	 * @Assert\Regex(pattern="/^[A-Za-z0-9\,\.\- ]*$/", message=ValidationMessages::hppRequest_billingCountry_pattern )
	 */
	private $billingCountry;

	/**
	 * @var String The customer number of the customer. You can send in any additional information about
	 * the transaction in this field, which will be visible under the transaction in the RealControl application.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_customerNumber_size)
	 * @Assert\Regex(pattern="/^[a-zA-Z0-9\.\_\-\,\+\@ \s]*$/", message=ValidationMessages::hppRequest_customerNumber_pattern )
	 */
	private $customerNumber;

	/**
	 * @var String A variable reference also associated with this customer. You can send in any additional
	 * information about the transaction in this field, which will be visible under the transaction in
	 * the RealControl application.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_variableReference_size)
	 * @Assert\Regex(pattern="/^[a-zA-Z0-9\.\_\-\,\+\@ \s]*$/", message=ValidationMessages::hppRequest_variableReference_pattern )
	 */
	private $variableReference;

	/**
	 * @var String A product id associated with this product. You can send in any additional information about
	 * the transaction in this field, which will be visible under the transaction in the RealControl application.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_productId_size)
	 * @Assert\Regex(pattern="/^[a-zA-Z0-9\.\_\-\,\+\@ \s]*$/", message=ValidationMessages::hppRequest_productId_pattern )
	 */
	private $productId;

	/**
	 * @var String Used to set what language HPP is displayed in. Currently HPP is available in English, Spanish and
	 * German, with other languages to follow. If the field is not sent in, the default language is the language
	 * that is set in your account configuration. This can be set by your account manager.
	 *
	 * @Assert\Regex(pattern="/^[a-zA-Z]{2}$|^[a-zA-Z]{0}$/", message=ValidationMessages::hppRequest_language_pattern )
	 */
	private $language;

	/**
	 * @var String Used to set what text is displayed on the payment button for card transactions.
	 * If this field is not sent in, "Pay Now" is displayed on the button by default.
	 *
	 * @Assert\Length(min = 0, max = 25, maxMessage = ValidationMessages::hppRequest_cardPaymentButtonText_size)
	 * @Assert\Regex(pattern = "/^[\x{00C0}\x{00C1}\x{00C2}\x{00C3}\x{00C4}\x{00C5}\x{00C6}\x{00C7}\x{00C8}\x{00C9}\x{00CA}\x{00CB}\x{00CC}\x{00CD}\x{00CE}\x{00CF}\x{00D0}\x{00D1}\x{00D2}\x{00D3}\x{00D4}\x{00D5}\x{00D6}\x{00D7}\x{00D8}\x{00D9}\x{00DA}\x{00DB}\x{00DC}\x{00DD}\x{00DE}\x{00DF}\x{00E0}\x{00E1}\x{00E2}\x{00E3}\x{00E4}\x{00E5}\x{00E6}\x{00E7}\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{00EC}\x{00ED}\x{00EE}\x{00EF}\x{00F0}\x{00F1}\x{00F2}\x{00F3}\x{00F4}\x{00F5}\x{00F6}\x{00F7}\x{00F8}\x{00A4}\x{00F9}\x{00FA}\x{00FB}\x{00FC}\x{00FD}\x{00FE}\x{00FF}\x{0152}\x{017D}\x{0161}\x{0153}\x{017E}\x{0178}\x{00A5}a-zA-Z0-9\'\,\+\x{0022}\.\_\-\&\/\@\!\?\%\()\*\:\x{00A3}\$\&\x{20AC}\#\[\]\|\=\\\x{201C}\x{201D}\x{201C} ]*$/iu" , message=ValidationMessages::hppRequest_cardPaymentButtonText_pattern )
	 */
	private $cardPaymentButtonText;

	/**
	 * @var string Enable card storage.
	 *
	 * @Assert\Length(min = 0, max = 1, maxMessage = ValidationMessages::hppRequest_cardStorageEnable_size)
	 * @Assert\Regex(pattern = "/^[10]*$/" , message=ValidationMessages::hppRequest_cardStorageEnable_pattern )
	 */
	private $cardStorageEnable;

	/**
	 * @var string Offer to save the card.
	 *
	 * @Assert\Length(min = 0, max = 1, maxMessage = ValidationMessages::hppRequest_offerSaveCard_size)
	 * @Assert\Regex(pattern = "/^[10]*$/" , message=ValidationMessages::hppRequest_offerSaveCard_pattern )
	 */
	private $offerSaveCard;

	/**
	 * @var string The payer reference.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_payerReference_size)
	 * @Assert\Regex(pattern = "/^[A-Za-z0-9\_\-\\ ]*$/" , message=ValidationMessages::hppRequest_payerReference_pattern )
	 */
	private $payerReference;

	/**
	 * @var string The payment reference.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_paymentReference_size)
	 * @Assert\Regex(pattern = "/^[A-Za-z0-9\_\-]*$/" , message=ValidationMessages::hppRequest_paymentReference_pattern )
	 */
	private $paymentReference;

	/**
	 * @var string Flag to indicate if the payer exists.
	 *
	 * @Assert\Length(min = 0, max = 1, maxMessage = ValidationMessages::hppRequest_payerExists_size)
	 * @Assert\Regex(pattern = "/^[102]*$/" , message=ValidationMessages::hppRequest_payerExists_pattern )
	 */
	private $payerExists;

	/**
	 * @var string[] Supplementary data to be sent to Realex Payments. This will be returned in the HPP response.
	 * Fields will be 255 char max
	 */
	private $supplementaryData = array();

	/**
	 * @var string Used to identify an OTB transaction.
	 *
	 * @Assert\Length(min = 0, max = 1, maxMessage = ValidationMessages::hppRequest_validateCardOnly_size)
	 * @Assert\Regex(pattern="/^[01]*$/", message=ValidationMessages::hppRequest_validateCardOnly_pattern )
	 */
	private $validateCardOnly;

	/**
	 * @var string Transaction level configuration to enable/disable a DCC request.
	 * (Only if the merchant is configured).
	 *
	 * @Assert\Length(min = 0, max = 1, maxMessage = ValidationMessages::hppRequest_dccEnable_size)
	 * @Assert\Regex(pattern="/^[01]*$/", message=ValidationMessages::hppRequest_dccEnable_pattern )
	 */
	private $dccEnable;

	/**
	 * @var string Used to indicate the SDK version. Can only have the value 1 or 2. Passing 2 enables the new skin for the HPP.
	 * It also allows HPP_SELECT_STORED_CARD to be sent.
	 *
	 * @Assert\Length(min = 0, max = 1, maxMessage = ValidationMessages::hppRequest_hppVersion_size)
	 * @Assert\Regex(pattern="/^[1-2]*$/", message=ValidationMessages::hppRequest_hppVersion_pattern )
	 */
	private $hppVersion;

	/**
	 * @var string Must contain the Payer reference of the customer whose cards the merchant wishes
	 * to display on the HPP. If sent correctly, all of the customer's saved cards will be displayed and they can choose
	 * which one they wish to complete the payment with.
	 *
	 * @Assert\Length(min = 0, max = 50, maxMessage = ValidationMessages::hppRequest_hppSelectStoredCard_size)
	 * @Assert\Regex(pattern="/^[A-Za-z0-9\_\-\\ ]*$/", message=ValidationMessages::hppRequest_hppSelectStoredCard_pattern )
	 */
	private $hppSelectStoredCard;

	/**
	 * @var string This field should contain the domain of the page hosting the iFrame calling HPP. If sent correctly,
	 * every time the height or width of the card form changes (e.g. an error message is displayed),
	 * the HPP will send this back as a JSON string to the parent iFrame.
	 * This is to facilitate developers who wish to resize their iFrame accordingly on increases or decreases of the HPP form’s size.
	 * @Assert\Length(min = 0, max = 255, maxMessage = ValidationMessages::hppRequest_postDimensions_size, charset="ISO-8859-1")
	 * @Assert\Regex(pattern="/^[\s \x{0020}-\x{003B} \x{003D} \x{003F}-\x{007E} \x{00A1}-\x{00FF}\x{20AC}\x{201A}\x{0192}\x{201E}\x{2026}\x{2020}\x{2021}\x{02C6}\x{2030}\x{0160}\x{2039}\x{0152}\x{017D}\x{2018}\x{2019}\x{201C}\x{201D}\x{2022}\x{2013}\x{2014}\x{02DC}\x{2122}\x{0161}\x{203A}\x{0153}\x{017E}\x{0178}]*$/iu", message=ValidationMessages::hppRequest_postDimensions_pattern )

	 */
	private $postDimensions ;

	/**
	 * @var string This field should contain the domain of the page hosting the iFrame calling HPP. If sent correctly,
	 * when the transaction is complete the HPP will send the response as a
	 * Base64 encoded JSON string to the parent iFrame.
	 * This is to facilitate developers who wish to not have to rely on a Response URL
	 * to accept the transaction response and would prefer to have the parent iFrame capture it
	 *
	 * @Assert\Length(min = 0, max = 255, maxMessage = ValidationMessages::hppRequest_postResponse_size, charset="ISO-8859-1")
	 * @Assert\Regex(pattern="/^[\s \x{0020}-\x{003B} \x{003D} \x{003F}-\x{007E} \x{00A1}-\x{00FF}\x{20AC}\x{201A}\x{0192}\x{201E}\x{2026}\x{2020}\x{2021}\x{02C6}\x{2030}\x{0160}\x{2039}\x{0152}\x{017D}\x{2018}\x{2019}\x{201C}\x{201D}\x{2022}\x{2013}\x{2014}\x{02DC}\x{2122}\x{0161}\x{203A}\x{0153}\x{017E}\x{0178}]*$/iu", message=ValidationMessages::hppRequest_postResponse_pattern )
	 */

	private $postResponse;

	/**
	 * Getter for merchantId
	 *
	 * @return String
	 */
	public function getMerchantId() {
		return $this->merchantId;
	}

	/**
	 * Setter for merchantId
	 *
	 * @param String $merchantId
	 */
	public function setMerchantId( $merchantId ) {
		$this->merchantId = $merchantId;
	}

	/**
	 * Getter for account
	 *
	 * @return String
	 */
	public function getAccount() {
		return $this->account;
	}

	/**
	 * Setter for account
	 *
	 * @param String $account
	 */
	public function setAccount( $account ) {
		$this->account = $account;
	}

	/**
	 * Getter for orderId
	 *
	 * @return String
	 */
	public function getOrderId() {
		return $this->orderId;
	}

	/**
	 * Setter for orderId
	 *
	 * @param String $orderId
	 */
	public function setOrderId( $orderId ) {
		$this->orderId = $orderId;
	}

	/**
	 * Getter for amount
	 *
	 * @return String
	 */
	public function getAmount() {
		return $this->amount;
	}

	/**
	 * Setter for amount
	 *
	 * @param String $amount
	 */
	public function setAmount( $amount ) {
		$this->amount = $amount;
	}

	/**
	 * Getter for currency
	 *
	 * @return String
	 */
	public function getCurrency() {
		return $this->currency;
	}

	/**
	 * Setter for currency
	 *
	 * @param String $currency
	 */
	public function setCurrency( $currency ) {
		$this->currency = $currency;
	}

	/**
	 * Getter for timeStamp
	 *
	 * @return String
	 */
	public function getTimeStamp() {
		return $this->timeStamp;
	}

	/**
	 * Setter for timeStamp
	 *
	 * @param String $timeStamp
	 */
	public function setTimeStamp( $timeStamp ) {
		$this->timeStamp = $timeStamp;
	}

	/**
	 * Getter for hash
	 *
	 * @return String
	 */
	public function getHash() {
		return $this->hash;
	}

	/**
	 * Setter for hash
	 *
	 * @param String $hash
	 */
	public function setHash( $hash ) {
		$this->hash = $hash;
	}

	/**
	 * Getter for autoSettleFlag
	 *
	 * @return String
	 */
	public function getAutoSettleFlag() {
		return $this->autoSettleFlag;
	}

	/**
	 * Setter for autoSettleFlag
	 *
	 * @param String $autoSettleFlag
	 */
	public function setAutoSettleFlag( $autoSettleFlag ) {
		$this->autoSettleFlag = $autoSettleFlag;
	}

	/**
	 * Getter for commentOne
	 *
	 * @return String
	 */
	public function getCommentOne() {
		return $this->commentOne;
	}

	/**
	 * Setter for commentOne
	 *
	 * @param String $commentOne
	 */
	public function setCommentOne( $commentOne ) {
		$this->commentOne = $commentOne;
	}

	/**
	 * Getter for commentTwo
	 *
	 * @return String
	 */
	public function getCommentTwo() {
		return $this->commentTwo;
	}

	/**
	 * Setter for commentTwo
	 *
	 * @param String $commentTwo
	 */
	public function setCommentTwo( $commentTwo ) {
		$this->commentTwo = $commentTwo;
	}

	/**
	 * Getter for returnTss
	 *
	 * @return String
	 */
	public function getReturnTss() {
		return $this->returnTss;
	}

	/**
	 * Setter for returnTss
	 *
	 * @param String $returnTss
	 */
	public function setReturnTss( $returnTss ) {
		$this->returnTss = $returnTss;
	}

	/**
	 * Getter for shippingCode
	 *
	 * @return String
	 */
	public function getShippingCode() {
		return $this->shippingCode;
	}

	/**
	 * Setter for shippingCode
	 *
	 * @param String $shippingCode
	 */
	public function setShippingCode( $shippingCode ) {
		$this->shippingCode = $shippingCode;
	}

	/**
	 * Getter for shippingCountry
	 *
	 * @return String
	 */
	public function getShippingCountry() {
		return $this->shippingCountry;
	}

	/**
	 * Setter for shippingCountry
	 *
	 * @param String $shippingCountry
	 */
	public function setShippingCountry( $shippingCountry ) {
		$this->shippingCountry = $shippingCountry;
	}

	/**
	 * Getter for billingCode
	 *
	 * @return String
	 */
	public function getBillingCode() {
		return $this->billingCode;
	}

	/**
	 * Setter for billingCode
	 *
	 * @param String $billingCode
	 */
	public function setBillingCode( $billingCode ) {
		$this->billingCode = $billingCode;
	}

	/**
	 * Getter for billingCountry
	 *
	 * @return String
	 */
	public function getBillingCountry() {
		return $this->billingCountry;
	}

	/**
	 * Setter for billingCountry
	 *
	 * @param String $billingCountry
	 */
	public function setBillingCountry( $billingCountry ) {
		$this->billingCountry = $billingCountry;
	}

	/**
	 * Getter for customerNumber
	 *
	 * @return String
	 */
	public function getCustomerNumber() {
		return $this->customerNumber;
	}

	/**
	 * Setter for customerNumber
	 *
	 * @param String $customerNumber
	 */
	public function setCustomerNumber( $customerNumber ) {
		$this->customerNumber = $customerNumber;
	}

	/**
	 * Getter for variableReference
	 *
	 * @return String
	 */
	public function getVariableReference() {
		return $this->variableReference;
	}

	/**
	 * Setter for variableReference
	 *
	 * @param String $variableReference
	 */
	public function setVariableReference( $variableReference ) {
		$this->variableReference = $variableReference;
	}

	/**
	 * Getter for productId
	 *
	 * @return String
	 */
	public function getProductId() {
		return $this->productId;
	}

	/**
	 * Setter for productId
	 *
	 * @param String $productId
	 */
	public function setProductId( $productId ) {
		$this->productId = $productId;
	}

	/**
	 * Getter for language
	 *
	 * @return String
	 */
	public function getLanguage() {
		return $this->language;
	}

	/**
	 * Setter for language
	 *
	 * @param String $language
	 */
	public function setLanguage( $language ) {
		$this->language = $language;
	}

	/**
	 * Getter for cardPaymentButtonText
	 *
	 * @return String
	 */
	public function getCardPaymentButtonText() {
		return $this->cardPaymentButtonText;
	}

	/**
	 * Setter for cardPaymentButtonText
	 *
	 * @param String $cardPaymentButtonText
	 */
	public function setCardPaymentButtonText( $cardPaymentButtonText ) {
		$this->cardPaymentButtonText = $cardPaymentButtonText;
	}

	/**
	 * Getter for supplementaryData
	 *
	 * @return string[]
	 */
	public function getSupplementaryData() {
		return $this->supplementaryData;
	}

	/**
	 * Setter for supplementaryData
	 *
	 *

	 * Example usage:
	 *

	 * $data = array();
	 * $data["key1"] = "value1";
	 * $data["key2"] = "value2";
	 *
	 * $request = (new HppRequest());
	 * $request->setSupplementaryData($data);
	 *

	 *

	 *
	 * @param string[] $supplementaryData
	 */
	public function setSupplementaryData( array $supplementaryData ) {
		$this->supplementaryData = $supplementaryData;
	}

	/**
	 * Getter for validateCardOnly
	 *
	 * @return String
	 */
	public function getValidateCardOnly() {
		return $this->validateCardOnly;
	}

	/**
	 * Setter for validateCardOnly
	 *
	 * @param String $validateCardOnly
	 */
	public function setValidateCardOnly( $validateCardOnly ) {
		$this->validateCardOnly = $validateCardOnly;
	}

	/**
	 * Getter for dccEnable
	 *
	 * @return String
	 */
	public function getDccEnable() {
		return $this->dccEnable;
	}

	/**
	 * Setter for dccEnable
	 *
	 * @param String $dccEnable
	 */
	public function setDccEnable( $dccEnable ) {
		$this->dccEnable = $dccEnable;
	}

	/**
	 * Getter for cardStorageEnable
	 *
	 * @return mixed
	 */
	public function getCardStorageEnable() {
		return $this->cardStorageEnable;
	}

	/**
	 * Setter for cardStorageEnable
	 *
	 * @param mixed $cardStorageEnable
	 */
	public function setCardStorageEnable( $cardStorageEnable ) {
		$this->cardStorageEnable = $cardStorageEnable;
	}

	/**
	 * Getter for offerSaveCard
	 *
	 * @return string
	 */
	public function getOfferSaveCard() {
		return $this->offerSaveCard;
	}

	/**
	 * Setter for offerSaveCard
	 *
	 * @param string $offerSaveCard
	 */
	public function setOfferSaveCard( $offerSaveCard ) {
		$this->offerSaveCard = $offerSaveCard;
	}

	/**
	 * Getter for payerReference
	 *
	 * @return string
	 */
	public function getPayerReference() {
		return $this->payerReference;
	}

	/**
	 * Setter for payerReference
	 *
	 * @param string $payerReference
	 */
	public function setPayerReference( $payerReference ) {
		$this->payerReference = $payerReference;
	}

	/**
	 * Getter for paymentReference
	 *
	 * @return string
	 */
	public function getPaymentReference() {
		return $this->paymentReference;
	}

	/**
	 * Setter for paymentReference
	 *
	 * @param string $paymentReference
	 */
	public function setPaymentReference( $paymentReference ) {
		$this->paymentReference = $paymentReference;
	}

	/**
	 * Getter for payerExists
	 *
	 * @return string
	 */
	public function getPayerExists() {
		return $this->payerExists;
	}

	/**
	 * Setter for payerExists
	 *
	 * @param string $payerExists
	 */
	public function setPayerExists( $payerExists ) {
		$this->payerExists = $payerExists;
	}

	/**
	 * Helper method for adding a merchantId
	 *
	 * @param String $merchantId
	 *
	 * @return HppRequest
	 */
	public function addMerchantId( $merchantId ) {
		$this->merchantId = $merchantId;

		return $this;
	}

	/**
	 * Helper method for adding a account
	 *
	 * @param String $account
	 *
	 * @return HppRequest
	 */
	public function addAccount( $account ) {
		$this->account = $account;

		return $this;
	}

	/**
	 * Helper method for adding a orderId
	 *
	 * @param String $orderId
	 *
	 * @return HppRequest
	 */
	public function addOrderId( $orderId ) {
		$this->orderId = $orderId;

		return $this;
	}

	/**
	 * Helper method for adding a amount
	 *
	 * @param String $amount
	 *
	 * @return HppRequest
	 */
	public function addAmount( $amount ) {
		$this->amount = $amount;

		return $this;
	}

	/**
	 * Helper method for adding a currency
	 *
	 * @param String $currency
	 *
	 * @return HppRequest
	 */
	public function addCurrency( $currency ) {
		$this->currency = $currency;

		return $this;
	}

	/**
	 * Helper method for adding a timeStamp
	 *
	 * @param String $timeStamp
	 *
	 * @return HppRequest
	 */
	public function addTimeStamp( $timeStamp ) {
		$this->timeStamp = $timeStamp;

		return $this;
	}

	/**
	 * Helper method for adding a hash
	 *
	 * @param String $hash
	 *
	 * @return HppRequest
	 */
	public function addHash( $hash ) {
		$this->hash = $hash;

		return $this;
	}

	/**
	 * Helper method for adding a autoSettleFlag
	 *
	 * @param String|bool $autoSettleFlag
	 *
	 * @return HppRequest
	 */
	public function addAutoSettleFlag( $autoSettleFlag ) {
		if ( is_bool( $autoSettleFlag ) ) {
			$this->autoSettleFlag = $autoSettleFlag ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->autoSettleFlag = $autoSettleFlag;
		}

		return $this;
	}

	/**
	 * Helper method for adding a commentOne
	 *
	 * @param String $commentOne
	 *
	 * @return HppRequest
	 */
	public function addCommentOne( $commentOne ) {
		$this->commentOne = $commentOne;

		return $this;
	}

	/**
	 * Helper method for adding a commentTwo
	 *
	 * @param String $commentTwo
	 *
	 * @return HppRequest
	 */
	public function addCommentTwo( $commentTwo ) {
		$this->commentTwo = $commentTwo;

		return $this;
	}

	/**
	 * Helper method for adding a returnTss
	 *
	 * @param String|bool $returnTss
	 *
	 * @return HppRequest
	 */
	public function addReturnTss( $returnTss ) {
		if ( is_bool( $returnTss ) ) {
			$this->returnTss = $returnTss ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->returnTss = $returnTss;
		}

		return $this;
	}

	/**
	 * Helper method for adding a shippingCode
	 *
	 * @param String $shippingCode
	 *
	 * @return HppRequest
	 */
	public function addShippingCode( $shippingCode ) {
		$this->shippingCode = $shippingCode;

		return $this;
	}

	/**
	 * Helper method for adding a shippingCountry
	 *
	 * @param String $shippingCountry
	 *
	 * @return HppRequest
	 */
	public function addShippingCountry( $shippingCountry ) {
		$this->shippingCountry = $shippingCountry;

		return $this;
	}

	/**
	 * Helper method for adding a billingCode
	 *
	 * @param String $billingCode
	 *
	 * @return HppRequest
	 */
	public function addBillingCode( $billingCode ) {
		$this->billingCode = $billingCode;

		return $this;
	}

	/**
	 * Helper method for adding a billingCountry
	 *
	 * @param String $billingCountry
	 *
	 * @return HppRequest
	 */
	public function addBillingCountry( $billingCountry ) {
		$this->billingCountry = $billingCountry;

		return $this;
	}

	/**
	 * Helper method for adding a customerNumber
	 *
	 * @param String $customerNumber
	 *
	 * @return HppRequest
	 */
	public function addCustomerNumber( $customerNumber ) {
		$this->customerNumber = $customerNumber;

		return $this;
	}

	/**
	 * Helper method for adding a variableReference
	 *
	 * @param String $variableReference
	 *
	 * @return HppRequest
	 */
	public function addVariableReference( $variableReference ) {
		$this->variableReference = $variableReference;

		return $this;
	}

	/**
	 * Helper method for adding a productId
	 *
	 * @param String $productId
	 *
	 * @return HppRequest
	 */
	public function addProductId( $productId ) {
		$this->productId = $productId;

		return $this;
	}

	/**
	 * Helper method for adding a language
	 *
	 * @param String $language
	 *
	 * @return HppRequest
	 */
	public function addLanguage( $language ) {
		$this->language = $language;

		return $this;
	}

	/**
	 * Helper method for adding a cardPaymentButtonText
	 *
	 * @param String $cardPaymentButtonText
	 *
	 * @return HppRequest
	 */
	public function addCardPaymentButtonText( $cardPaymentButtonText ) {
		$this->cardPaymentButtonText = $cardPaymentButtonText;

		return $this;
	}

	/**
	 * Helper method for adding a supplementaryData
	 *
	 *

	 * Example usage:
	 *

	 * $data = array();
	 * $data["key1"] = "value1";
	 * $data["key2"] = "value2";
	 *
	 * $request = (new HppRequest())
	 *    ->addAmount(100)
	 *    ->addCurrency("EUR")
	 *    ->addMerchantId("merchantId");
	 *    ->addSupplementaryData($data);
	 *

	 *

	 *
	 * @param string[] $supplementaryData
	 *
	 * @return HppRequest
	 */
	public function addSupplementaryData( array $supplementaryData ) {
		$this->supplementaryData = $supplementaryData;

		return $this;
	}

	/**
	 * Helper method for adding a validateCardOnly
	 *
	 * @param String|bool $validateCardOnly
	 *
	 * @return HppRequest
	 */
	public function addValidateCardOnly( $validateCardOnly ) {
		if ( is_bool( $validateCardOnly ) ) {
			$this->validateCardOnly = $validateCardOnly ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->validateCardOnly = $validateCardOnly;
		}

		return $this;
	}

	/**
	 * Helper method for adding a dccEnable
	 *
	 * @param String $dccEnable
	 *
	 * @return HppRequest
	 */
	public function addDccEnable( $dccEnable ) {
		if ( is_bool( $dccEnable ) ) {
			$this->dccEnable = $dccEnable ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->dccEnable = $dccEnable;
		}

		return $this;
	}

	/**
	 * Helper method for adding a cardStorageEnable
	 *
	 * @param mixed|bool $cardStorageEnable
	 *
	 * @return HppRequest
	 */
	public function addCardStorageEnable( $cardStorageEnable ) {
		if ( is_bool( $cardStorageEnable ) ) {
			$this->cardStorageEnable = $cardStorageEnable ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->cardStorageEnable = $cardStorageEnable;
		}

		return $this;
	}

	/**
	 * Helper method for adding a offerSaveCard
	 *
	 * @param string|bool $offerSaveCard
	 *
	 * @return HppRequest
	 */
	public function addOfferSaveCard( $offerSaveCard ) {
		if ( is_bool( $offerSaveCard ) ) {
			$this->offerSaveCard = $offerSaveCard ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->offerSaveCard = $offerSaveCard;
		}

		return $this;
	}

	/**
	 * Helper method for adding a payerReference
	 *
	 * @param string $payerReference
	 *
	 * @return HppRequest
	 */
	public function addPayerReference( $payerReference ) {
		$this->payerReference = $payerReference;

		return $this;
	}

	/**
	 * Helper method for adding a paymentReference
	 *
	 * @param string $paymentReference
	 *
	 * @return HppRequest
	 */
	public function addPaymentReference( $paymentReference ) {
		$this->paymentReference = $paymentReference;

		return $this;
	}

	/**
	 * Helper method for adding a payerExists
	 *
	 * @param string|bool $payerExists
	 *
	 * @return HppRequest
	 */
	public function addPayerExists( $payerExists ) {
		if ( is_bool( $payerExists ) ) {
			$this->payerExists = $payerExists ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->payerExists = $payerExists;
		}

		return $this;
	}

	/**
	 * Helper method for adding the Hpp Version
	 *
	 * @param string $hppVersion
	 *
	 * @return HppRequest
	 */
	public function addHppVersion( $hppVersion ){
		if ( is_bool( $hppVersion ) ) {
			$this->cardStorageEnable = $hppVersion ? Flag::TRUE : Flag::FALSE;
		} else {
			$this->hppVersion = $hppVersion;
		}

		return $this;
	}

	/**
	 * Helper method for setting the Hpp Version
	 *
	 * @param string $hppVersion
	 *
	 * @return void
	 */
	public function setHppVersion( $hppVersion ){
		$this->hppVersion = $hppVersion;

	}

	/**
	 * Helper method for adding the Hpp Version
	 *
	 * @return string
	 */
	public function getHppVersion(){
		return $this->hppVersion ;

	}

	/**
	 * Helper method for adding the Hpp Select Stored Card
	 *
	 * @param string $hppSelectStoredCard
	 *
	 * @return HppRequest
	 */
	public function addHppSelectStoredCard( $hppSelectStoredCard ){
		$this->hppSelectStoredCard = $hppSelectStoredCard;

		return $this;
	}

	/**
	 * Helper method for setting the Hpp Select Stored Card
	 *
	 * @param string $hppSelectStoredCard
	 *
	 * @return void
	 */
	public function setHppSelectStoredCard( $hppSelectStoredCard ){
		$this->hppSelectStoredCard = $hppSelectStoredCard;
	}

	/**
	 * Helper method for adding the Hpp Select Stored Card
	 *
	 * @return string
	 */
	public function getHppSelectStoredCard( ){
		return $this->hppSelectStoredCard ;

	}

	/**
	 * Helper method for adding the Hpp Post Dimension
	 *
	 * @param string $postDimensions
	 *
	 * @return HppRequest
	 */
	public function addPostDimensions( $postDimensions ){
		$this->postDimensions = $postDimensions;

		return $this;
	}

	/**
	 * Helper method for setting the Hpp Post Dimension
	 *
	 * @param string $postDimensions
	 *
	 * @return void
	 */
	public function setPostDimensions( $postDimensions ){
		$this->postDimensions = $postDimensions;

	}

	/**
	 * Helper method for adding the Hpp Post Dimension
	 *
	 * @return string
	 */
	public function getPostDimensions( ){
		return $this->postDimensions ;

	}

	/**
	 * Helper method for adding the Hpp Post Dimension
	 *
	 * @param string $postResponse
	 *
	 * @return HppRequest
	 */
	public function addPostResponse(  $postResponse ){
		$this->postResponse = $postResponse;

		return $this;
	}

	/**
	 * Helper method for setting the Hpp Post Dimension
	 *
	 * @param string $postResponse
	 *
	 * @return void
	 */
	public function setPostResponse( $postResponse ){
		$this->postResponse = $postResponse;

	}

	/**
	 * Helper method for adding the Hpp Post Dimension
	 *
	 * @return string
	 */
	public function getPostResponse(){
		return $this->postResponse ;

	}

	/**
	 * Generates default values for fields such as hash, timestamp and order ID.
	 *
	 * @param String $secret
	 *
	 * @return HppRequest
	 */
	public function generateDefaults( $secret ) {

		//generate timestamp if not set
		if ( is_null( $this->timeStamp ) ) {
			$this->timeStamp = GenerationUtils::generateTimestamp();
		}

		//generate order ID if not set
		if ( is_null( $this->orderId ) ) {
			$this->orderId = GenerationUtils::generateOrderId();
		}

		//generate hash
		$this->hash( $secret );

		return $this;
	}

	/**
	 * Creates the security hash from a number of fields and the shared secret.
	 *
	 * @param string $secret
	 *
	 * @return HppRequest
	 */
	public function hash( $secret ) {

		//check for any null values and set them to empty string for hashing
		$timeStamp        = null == $this->timeStamp ? "" : $this->timeStamp;
		$merchantId       = null == $this->merchantId ? "" : $this->merchantId;
		$orderId          = null == $this->orderId ? "" : $this->orderId;
		$amount           = null == $this->amount ? "" : $this->amount;
		$currency         = null == $this->currency ? "" : $this->currency;
		$payerReference   = null == $this->payerReference ? "" : $this->payerReference;
		$paymentReference = null == $this->paymentReference ? "" : $this->paymentReference;
		$hppSelectStoredCard = null == $this->hppSelectStoredCard ? "" : $this->hppSelectStoredCard;

        // Override payerRef with hppSelectStoredCard if present.
		$payRefORStoredCard =  empty($hppSelectStoredCard) ?  $payerReference : $hppSelectStoredCard;

        //create String to hash
		if ( $this->cardStorageEnable ) {
			$toHash = $timeStamp
				. "."
				. $merchantId
				. "."
				. $orderId
				. "."
				. $amount
				. "."
				. $currency
				. "."
				. $payerReference
				. "."
				. $paymentReference;
		} else	if ($payRefORStoredCard && empty($paymentReference) ) {
			$toHash = $timeStamp
			. "."
			. $merchantId
			. "."
			. $orderId
			. "."
			. $amount
			. "."
			. $currency
			. "."
			. $payRefORStoredCard
			. ".";

		} else	if ( $payRefORStoredCard && !empty($paymentReference) ) {
			$toHash = $timeStamp
			. "."
			. $merchantId
			. "."
			. $orderId
			. "."
			. $amount
			. "."
			. $currency
			. "."
			. $payRefORStoredCard
			. "."
			. $paymentReference;

		}else {
			$toHash = $timeStamp
				. "."
				. $merchantId
				. "."
				. $orderId
				. "."
				. $amount
				. "."
				. $currency;
		}

		$this->hash = GenerationUtils::generateHash( $toHash, $secret );

		return $this;

	}

	/**
	 * Base64 encodes all Hpp Request values.
	 *
	 * @param string $charSet
	 *
	 * @return HppRequest
	 */
	public function encode( $charSet ) {
		$this->account               = base64_encode( $this->account );
		$this->amount                = base64_encode( $this->amount );
		$this->autoSettleFlag        = base64_encode( $this->autoSettleFlag );
		$this->billingCode           = base64_encode( $this->billingCode );
		$this->billingCountry        = base64_encode( $this->billingCountry );
		$this->cardPaymentButtonText = base64_encode( $this->cardPaymentButtonText );
		$this->cardStorageEnable     = base64_encode( $this->cardStorageEnable );
		$this->commentOne            = base64_encode( $this->commentOne );
		$this->commentTwo            = base64_encode( $this->commentTwo );
		$this->currency              = base64_encode( $this->currency );
		$this->customerNumber        = base64_encode( $this->customerNumber );
		$this->hash                  = base64_encode( $this->hash );
		$this->language              = base64_encode( $this->language );
		$this->merchantId            = base64_encode( $this->merchantId );
		$this->offerSaveCard         = base64_encode( $this->offerSaveCard );
		$this->orderId               = base64_encode( $this->orderId );
		$this->payerExists           = base64_encode( $this->payerExists );
		$this->payerReference        = base64_encode( $this->payerReference );
		$this->paymentReference      = base64_encode( $this->paymentReference );
		$this->productId             = base64_encode( $this->productId );
		$this->returnTss             = base64_encode( $this->returnTss );
		$this->shippingCode          = base64_encode( $this->shippingCode );
		$this->shippingCountry       = base64_encode( $this->shippingCountry );
		$this->timeStamp             = base64_encode( $this->timeStamp );
		$this->variableReference     = base64_encode( $this->variableReference );
		$this->validateCardOnly 	 = base64_encode( $this->validateCardOnly );
		$this->dccEnable         	 = base64_encode( $this->dccEnable );
		$this->hppVersion     		 = base64_encode( $this->hppVersion );
		$this->hppSelectStoredCard    = base64_encode( $this->hppSelectStoredCard );
		$this->postResponse   		 = base64_encode( $this->postResponse );
		$this->postDimensions   	 = base64_encode( $this->postDimensions );

		if ( is_array( $this->supplementaryData ) ) {
			foreach ( $this->supplementaryData as $key => $value ) {
				$this->supplementaryData[ $key ] = base64_encode( $value );
			}
		}

		return $this;
	}

	/**
	 * Base64 decodes all Hpp Request values.
	 *
	 * @param string $charSet
	 *
	 * @return HppRequest
	 */
	public function decode( $charSet ) {
		$this->account               = base64_decode( $this->account );
		$this->amount                = base64_decode( $this->amount );
		$this->autoSettleFlag        = base64_decode( $this->autoSettleFlag );
		$this->billingCode           = base64_decode( $this->billingCode );
		$this->billingCountry        = base64_decode( $this->billingCountry );
		$this->cardPaymentButtonText = base64_decode( $this->cardPaymentButtonText );
		$this->cardStorageEnable     = base64_decode( $this->cardStorageEnable );
		$this->commentOne            = base64_decode( $this->commentOne );
		$this->commentTwo            = base64_decode( $this->commentTwo );
		$this->currency              = base64_decode( $this->currency );
		$this->customerNumber        = base64_decode( $this->customerNumber );
		$this->hash                  = base64_decode( $this->hash );
		$this->language              = base64_decode( $this->language );
		$this->merchantId            = base64_decode( $this->merchantId );
		$this->offerSaveCard         = base64_decode( $this->offerSaveCard );
		$this->orderId               = base64_decode( $this->orderId );
		$this->payerExists           = base64_decode( $this->payerExists );
		$this->payerReference        = base64_decode( $this->payerReference );
		$this->paymentReference      = base64_decode( $this->paymentReference );
		$this->productId             = base64_decode( $this->productId );
		$this->returnTss             = base64_decode( $this->returnTss );
		$this->shippingCode          = base64_decode( $this->shippingCode );
		$this->shippingCountry       = base64_decode( $this->shippingCountry );
		$this->timeStamp             = base64_decode( $this->timeStamp );
		$this->variableReference     = base64_decode( $this->variableReference );
		$this->validateCardOnly      = base64_decode( $this->validateCardOnly );
		$this->dccEnable       		 = base64_decode( $this->dccEnable );
		$this->hppVersion     		 = base64_decode( $this->hppVersion );
		$this->hppSelectStoredCard   	= base64_decode( $this->hppSelectStoredCard );
		$this->postResponse   		= base64_decode( $this->postResponse );
		$this->postDimensions   	= base64_decode( $this->postDimensions );

		if ( is_array( $this->supplementaryData ) ) {
			foreach ( $this->supplementaryData as $key => $value ) {
				$this->supplementaryData[ $key ] = base64_decode( $value );
			}
		}

		return $this;
	}

	/**
	 * Format (non-encoded) request, remove null values
	 *
	 * @param string $charSet
	 *
	 * @return HppRequest
	 */
	public function formatRequest($charSet)
	{
	    $this->account = $this->nullToEmptyString($this->account);
	    $this->amount = $this->nullToEmptyString($this->amount);
	    $this->autoSettleFlag = $this->nullToEmptyString($this->autoSettleFlag);
	    $this->billingCode = $this->nullToEmptyString($this->billingCode);
	    $this->billingCountry = $this->nullToEmptyString($this->billingCountry);
	    $this->cardPaymentButtonText = $this->nullToEmptyString($this->cardPaymentButtonText);
	    $this->cardStorageEnable = $this->nullToEmptyString($this->cardStorageEnable);
	    $this->commentOne = $this->nullToEmptyString($this->commentOne);
	    $this->commentTwo = $this->nullToEmptyString($this->commentTwo);
	    $this->currency = $this->nullToEmptyString($this->currency);
	    $this->customerNumber = $this->nullToEmptyString($this->customerNumber);
	    $this->hash = $this->nullToEmptyString($this->hash);
	    $this->language = $this->nullToEmptyString($this->language);
	    $this->merchantId = $this->nullToEmptyString($this->merchantId);
	    $this->offerSaveCard = $this->nullToEmptyString($this->offerSaveCard);
	    $this->orderId = $this->nullToEmptyString($this->orderId);
	    $this->payerExists = $this->nullToEmptyString($this->payerExists);
	    $this->payerReference = $this->nullToEmptyString($this->payerReference);
	    $this->paymentReference = $this->nullToEmptyString($this->paymentReference);
	    $this->productId = $this->nullToEmptyString($this->productId);
	    $this->returnTss = $this->nullToEmptyString($this->returnTss);
	    $this->shippingCode = $this->nullToEmptyString($this->shippingCode);
	    $this->shippingCountry = $this->nullToEmptyString($this->shippingCountry);
	    $this->timeStamp = $this->nullToEmptyString($this->timeStamp);
	    $this->variableReference = $this->nullToEmptyString($this->variableReference);
	    $this->validateCardOnly = $this->nullToEmptyString($this->validateCardOnly);
	    $this->dccEnable = $this->nullToEmptyString($this->dccEnable);
	    $this->hppVersion = $this->nullToEmptyString($this->hppVersion);
	    $this->hppSelectStoredCard = $this->nullToEmptyString($this->hppSelectStoredCard);
	    $this->postResponse = $this->nullToEmptyString($this->postResponse);
	    $this->postDimensions = $this->nullToEmptyString($this->postDimensions);

	    if (is_array($this->supplementaryData)) {
	        foreach ($this->supplementaryData as $key => $value) {
	            $this->supplementaryData[$key] = $this->nullToEmptyString($value);
	        }
	    }

	    return $this;
	}

	/**
	 * Convert null values to empty strings
	 *
	 * @param string request $parameter
	 *
	 * @return $parameter
	 */
	public function nullToEmptyString($parameter)
	{
	    if (is_null($parameter)) {
	        $parameter = "";
	    }

	    return $parameter;
	}

	/**
	 * @return string The class name
	 */
	public static function GetClassName() {
		return __CLASS__;
	}

}


<?php

namespace com\realexpayments\hpp\sdk\validators;

use com\realexpayments\hpp\sdk\domain\Flag;
use com\realexpayments\hpp\sdk\domain\HppRequest;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * OTB amount validator. Amount must be set to 0 for OTB transactions.
 * For OTB transactions the validate card only flag is set to 1.
 *
 * @package com\realexpayments\hpp\sdk\validators
 * @author vicpada
 *
 */
class OtbAmountValidator extends ConstraintValidator
{

    /**
     * Validates OTB
     *
     * @param HppRequest $hppRequest
     * @param Constraint $constraint
     *
     * @api
     */
    public function validate($hppRequest, Constraint $constraint)
    {

        //if validate card only flag is true (1), then ensure the amount is set to 0
        if (Flag::TRUE == $hppRequest->getValidateCardOnly()) {
            if ($hppRequest->getAmount() != "0") {
                $this->context->buildViolation($constraint->message)
                    ->atPath('amount')
                    ->addViolation();

            }
        }

    }
}


<?php

namespace com\realexpayments\hpp\sdk\validators;

use com\realexpayments\hpp\sdk\domain\HppRequest;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * Supplementary should conform to defined character
 *
 * @package com\realexpayments\hpp\sdk\validators
 * @author vicpada
 *
 */
class SupplementaryDataPatternValidator extends ConstraintValidator {

	/**
	 * Checks if the passed value is valid.
	 *
	 * @param HppRequest $hppRequest
	 * @param Constraint $constraint
	 *
	 * @api
	 */
	public function validate( $hppRequest, Constraint $constraint ) {

		foreach ( $hppRequest->getSupplementaryData() as $supplementaryData ) {
			if ( ! preg_match( $constraint->pattern, $supplementaryData ) ) {
				$this->context->buildViolation( $constraint->message )
				              ->atPath( 'supplementaryData' )
				              ->addViolation();
			}
		}
	}
}


<?php

namespace com\realexpayments\hpp\sdk;

use Logger;

/**
 * Class RXPLogger. Wraps initialisation of the logging framework
 * @package com\realexpayments\remote\sdk
 */
class RXPLogger {

	/**
	 * @var bool
	 */
	private static $initialised = false;

	/**
	 * @param string $className
	 *
	 * @return Logger
	 */
	public static function GetLogger( $className ) {
		if ( ! self::IsInitialised() ) {
			self::Initialise();
		}

		$logger = Logger::getLogger( $className );

		return $logger;

	}

	private static function Initialise() {

		$path = $_SERVER['DOCUMENT_ROOT'] . '/config.xml';
		if ( file_exists( $path ) ) {
			Logger::configure( $path );
		} else {
			Logger::configure( __DIR__ . '/config.xml' );
		}

		self::$initialised = true;
	}

	private static function IsInitialised() {
		return self::$initialised;
	}

}


<?php

namespace com\realexpayments\hpp\sdk\utils;

use DateTime;

/**
 * Utils for the auto-generation of fields, for example the SHA1 hash.
 * @package com\realexpayments\hpp\sdk\utils
 *
 * @author vicpada
 */
class GenerationUtils {

	/**
	 * Each message sent to Realex should have a hash, attached. For a message using the remote
	 * interface this is generated using the This is generated from the TIMESTAMP, MERCHANT_ID,
	 * ORDER_ID, AMOUNT, and CURRENCY fields concatenated together with "." in between each field.
	 * This confirms the message comes
	 * from the client and
	 * Generate a hash, required for all messages sent to IPS to prove it was not tampered with.
	 *

	 * Hashing takes a string as input, and produce a fixed size number (160 bits for SHA-1 which
	 * this implementation uses). This number is a hash of the input, and a small change in the
	 * input results in a substantial change in the output. The functions are thought to be secure
	 * in the sense that it requires an enormous amount of computing power and time to find a string
	 * that hashes to the same value. In others words, there's no way to decrypt a secure hash.
	 * Given the larger key size, this implementation uses SHA-1 which we prefer that you, but Realex
	 * has retained compatibilty with MD5 hashing for compatibility with older systems.
	 *

	 *

	 * To construct the hash for the remote interface follow this procedure:
	 *
	 * To construct the hash for the remote interface follow this procedure:
	 * Form a string by concatenating the above fields with a period ('.') in the following order
	 *

	 * (TIMESTAMP.MERCHANT_ID.ORDER_ID.AMOUNT.CURRENCY)
	 *

	 * Like so (where a field is empty an empty string "" is used):
	 *

	 * (20120926112654.thestore.ORD453-11.29900.EUR)
	 *

	 * Get the hash of this string (SHA-1 shown below).
	 *

	 * (b3d51ca21db725f9c7f13f8aca9e0e2ec2f32502)
	 *

	 * Create a new string by concatenating this string and your shared secret using a period.
	 *

	 * (b3d51ca21db725f9c7f13f8aca9e0e2ec2f32502.mysecret )
	 *

	 * Get the hash of this value. This is the value that you send to Realex Payments.
	 *

	 * (3c3cac74f2b783598b99af6e43246529346d95d1)
	 *
	 * This method takes the pre-built string of concatenated fields and the secret and returns the
	 * SHA-1 hash to be placed in the request sent to Realex.
	 *
	 * @param string $toHash
	 * @param string $secret
	 * @return string The hash as a hex string
	 */
	public static function generateHash( $toHash, $secret ) {

		//first pass hashes the String of required fields
		$toHashFirstPass = sha1( $toHash );

		//second pass takes the first hash, adds the secret and hashes again
		$toHashSecondPass = $toHashFirstPass . "." . $secret;

		return sha1( $toHashSecondPass );
	}

	/**
	 * Generate the current datetimestamp in the string formaat (YYYYMMDDHHSS) required in a
	 * request to Realex.
	 *
	 * @return string current timestamp in YYYYMMDDHHSS format
	 */
	public static function generateTimestamp() {

		$date = new DateTime();

		return $date->format( "YmdHis" );

	}

	/**
	 * Order Id for a initial request should be unique per client ID. This method generates a unique
	 * order Id using the Java UUID class and then convert it to base64 to shorten the length to 22
	 * characters. Order Id for a subsequent request (void, rebate, settle ect.) should use the
	 * order Id of the initial request.
	 *
	 * * the order ID uses the Java UUID (universally unique identifier) so in theory it may not
	 * be unique but the odds of this are extremely remote (see
	 *
	 * http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates)
	 *
	 */
	public static function generateOrderId() {

		$uuid                 = self::getGuid();
		$mostSignificantBits  = substr( $uuid, 0, 8 );
		$leastSignificantBits = substr( $uuid, 23, 8 );

		return substr( base64_encode( $mostSignificantBits . $leastSignificantBits ), 0, 22 );
	}

	private static function getGuid() {
		if ( function_exists( 'com_create_guid' ) ) {
			return trim( com_create_guid(), '{}' );
		} else {

			mt_srand( (double) microtime() * 10000 );//optional for php 4.2.0 and up.
			$charId = strtoupper( md5( uniqid( rand(), true ) ) );
			$hyphen = chr( 45 );// "-"
			$uuid   = chr( 123 )// "{"
			          . substr( $charId, 0, 8 ) . $hyphen
			          . substr( $charId, 8, 4 ) . $hyphen
			          . substr( $charId, 12, 4 ) . $hyphen
			          . substr( $charId, 16, 4 ) . $hyphen
			          . substr( $charId, 20, 12 )
			          . chr( 125 );// "}"
			return $uuid;
		}

	}
}


<?php

namespace com\realexpayments\hpp\sdk\utils;

use com\realexpayments\hpp\sdk\domain\HppRequest;
use com\realexpayments\hpp\sdk\domain\HppResponse;
use com\realexpayments\hpp\sdk\RXPLogger;
use Logger;

class JsonUtils
{

    /**
     * @var Logger logger
     */
    private static $logger;
    private static $initialised = false;

    /**
     * @var iMapper[] logger
     */
    private static $mappers;

    /**
     * Method serialises HppRequest or  HppResponse  to JSON.
     *
     * @param $hppObject
     * @return string
     */
    public static function toJson($hppObject)
    {
        self::Initialise();

        $mapper = self::$mappers[get_class($hppObject)];
        return $mapper->WriteValueAsString($hppObject);

    }

    /**
     * Method deserialises JSON to HppRequest.
     *
     * @param $json
     * @return HppRequest
     */
    public static function fromJsonHppRequest($json)
    {
        self::Initialise();

        $mapper = self::$mappers[HppRequest::GetClassName()];
        return $mapper->ReadValue($json);
    }

    /**
     * Method deserialises JSON to HppResponse.
     *
     * @param $json
     * @return HppResponse
     */
    public static function fromJsonHppResponse($json)
    {
        self::Initialise();

        $mapper = self::$mappers[HppResponse::GetClassName()];
        return $mapper->ReadValue($json);
    }

    private static function Initialise()
    {
        if (self::$initialised) {
            return;
        }

        self::$logger = RXPLogger::getLogger(__CLASS__);

        self::$mappers = array();

        self::$mappers[HppRequest::GetClassName()] = new RequestMapper();
        self::$mappers[HppResponse::GetClassName()] = new ResponseMapper();

        self::$initialised = true;
    }
}