austinheap/laravel-database-encryption

View on GitHub
src/EncryptionHelper.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * src/EncryptionHelper.php.
 *
 * @author      Austin Heap <me@austinheap.com>
 * @version     v0.2.1
 */
declare(strict_types=1);

namespace AustinHeap\Database\Encryption;

use Illuminate\Support\Facades\Config;
use RuntimeException;

/**
 * EncryptionHelper.
 *
 * @link        https://github.com/austinheap/laravel-database-encryption
 * @link        https://packagist.org/packages/austinheap/laravel-database-encryption
 * @link        https://austinheap.github.io/laravel-database-encryption/classes/AustinHeap.Database.Encryption.EncryptionHelper.html
 */
class EncryptionHelper extends EncryptionDefaults
{
    /**
     * Internal version number.
     *
     * @var string
     */
    public const VERSION = '0.1.2';

    /**
     * Private enable flag cache.
     *
     * @var null|bool
     */
    private $enabledCache = null;

    /**
     * Private versioning flag cache.
     *
     * @var null|bool
     */
    private $versioningCache = null;

    /**
     * Private version parts cache.
     *
     * @var null|array
     */
    private $versionPartsCache = null;

    /**
     * Private control characters cache.
     *
     * @var null|array
     */
    private $controlCharactersCache = null;

    /**
     * Private prefix cache.
     *
     * @var null|string
     */
    private $prefixCache = null;

    /**
     * Private header prefix cache.
     *
     * @var null|string
     */
    private $headerPrefixCache = null;

    /**
     * EncryptionHelper constructor.
     */
    public function __construct()
    {
        $this->reset();
    }

    /**
     * Reset the class; mostly the cache.
     *
     * @return EncryptionHelper
     */
    public function reset(): self
    {
        $this->enabledCache =
        $this->versioningCache =
        $this->versionPartsCache =
        $this->controlCharactersCache =
        $this->prefixCache =
        $this->headerPrefixCache = null;

        return $this;
    }

    /**
     * Get the package version.
     *
     * @return string
     */
    public function getVersion(): string
    {
        throw_if(! defined('LARAVEL_DATABASE_ENCRYPTION_VERSION'), RuntimeException::class,
                 'The provider did not boot.');

        return LARAVEL_DATABASE_ENCRYPTION_VERSION;
    }

    /**
     * Set the enabled flag.
     *
     * @return bool
     */
    public function setEnabled(?bool $value = null): self
    {
        $this->enabledCache = $value;

        return $this;
    }

    /**
     * Check the enabled flag.
     *
     * @return bool
     */
    public function isEnabled(): bool
    {
        if (is_null($this->enabledCache)) {
            $enabled = Config('database-encryption.enabled', null);
            $this->enabledCache = ! is_null($enabled) && is_bool($enabled) ? $enabled : self::isEnabledDefault();
        }

        return $this->enabledCache;
    }

    /**
     * Set the enabled flag inverse.
     *
     * @return bool
     */
    public function setDisabled(?bool $value = null): self
    {
        return $this->setEnabled(is_bool($value) ? ! $value : null);
    }

    /**
     * Check the enabled flag inverse.
     *
     * @return bool
     */
    public function isDisabled(): bool
    {
        return ! $this->isEnabled();
    }

    /**
     * Set the versioning flag.
     *
     * @return null|bool
     */
    public function setVersioning(?bool $value = null): self
    {
        $this->reset();
        $this->versioningCache = $value;

        return $this;
    }

    /**
     * Check the versioning flag.
     *
     * @return bool
     */
    public function isVersioning(): bool
    {
        if (is_null($this->versioningCache)) {
            $versioning = Config('database-encryption.versioning', null);
            $this->versioningCache = ! is_null($versioning) && is_bool($versioning) ? $versioning : self::isVersioningDefault();
        }

        return $this->versioningCache;
    }

    /**
     * Set the enabled flag inverse.
     *
     * @return null|bool
     */
    public function setVersionless(?bool $value = null): self
    {
        return $this->setVersioning(is_bool($value) ? ! $value : null);
    }

    /**
     * Check the versioning flag inverse.
     *
     * @return bool
     */
    public function isVersionless(): bool
    {
        return ! $this->isVersioning();
    }

    /**
     * Get the package version in parts.
     *
     * @return array
     */
    public function getVersionParts(?int $padding = null): array
    {
        if (! is_array($this->versionPartsCache)) {
            $this->versionPartsCache = [];
        }

        $padding = is_int($padding) && $padding === 0 ? null : $padding;
        $key = 'padding-'.(is_null($padding) ? 'null' : (string) $padding);

        if (! array_key_exists($key, $this->versionPartsCache)) {
            $parts = explode('.', $this->getVersion());

            $this->versionPartsCache[$key] = array_map(function ($part) use ($padding) {
                $part = (string) $part;

                if (is_null($padding)) {
                    return $part;
                }

                $length = strlen(is_string($part) ? $part : (string) $part);

                return $length == $padding ? $part : str_repeat('0', $padding - $length).$part;
            }, $parts);
        }

        return $this->versionPartsCache[$key];
    }

    /**
     * Get the package version for a prefix.
     *
     * @return string
     */
    public function getVersionForPrefix(int $padding = 2, string $glue = '-'): string
    {
        return 'VERSION-'.implode($glue, $this->getVersionParts($padding));
    }

    /**
     * Set the configured header prefix.
     *
     * @return EncryptionHelper
     */
    public function setHeaderPrefix(?string $value = null): self
    {
        throw_if(is_string($value) && strlen(trim($value)) == 0, RuntimeException::class,
                 'Cannot use empty string as header prefix.');

        $this->headerPrefixCache = $value;

        return $this;
    }

    /**
     * Get the configured header prefix.
     *
     * @return string
     */
    public function getHeaderPrefix(): string
    {
        if (is_null($this->headerPrefixCache)) {
            $characters = $this->getControlCharacters();

            $this->headerPrefixCache = $characters['header']['start']['string'].
                                       $characters['prefix']['start']['string'].
                                       $this->getPrefix().
                                       $characters['prefix']['stop']['string'];
        }

        return $this->headerPrefixCache;
    }

    /**
     * Build a header string, optionally with an object.
     *
     * @return string
     */
    public function buildHeader($object = null): string
    {
        $characters = $this->getControlCharacters();

        return $characters['header']['start']['string'].
               $characters['prefix']['start']['string'].
               $this->getPrefix().
               $characters['prefix']['stop']['string'].
               $characters['field']['start']['string'].
               'version'.
               $characters['field']['delimiter']['string'].
               self::getVersionForPrefix().
               $characters['field']['stop']['string'].
               (is_null($object) ? '' :
                   $characters['field']['start']['string'].
                   'type'.
                   $characters['field']['delimiter']['string'].
                   gettype($object).'['.(is_object($object) ? get_class($object) : 'native').']'.
                   $characters['field']['stop']['string']
               ).
               $characters['header']['stop']['string'];
    }

    /**
     * Set the configured prefix.
     *
     * @return EncryptionHelper
     */
    public function setPrefix(?string $value = null): self
    {
        throw_if(is_string($value) && strlen(trim($value)) == 0, RuntimeException::class,
                 'Cannot use empty string as prefix.');

        $this->prefixCache = is_string($value) && $this->isVersioning() ?
            str_replace('%VERSION%', $this->getVersionForPrefix(), $value) :
            $value;

        return $this;
    }

    /**
     * Get the configured prefix.
     *
     * @return string
     */
    public function getPrefix(): string
    {
        if (is_null($this->prefixCache)) {
            $prefix = Config::get('database-encryption.prefix', null);
            $prefix = ! empty($prefix) && is_string($prefix) ? $prefix : self::getPrefixDefault();

            $this->prefixCache = $this->isVersioning() ?
                str_replace('%VERSION%', $this->getVersionForPrefix(), $prefix) :
                $prefix;
        }

        return $this->prefixCache;
    }

    /**
     * Get the configured control characters.
     *
     * @return array
     */
    public function getControlCharacters(?string $type = null): array
    {
        $defaults = self::getControlCharactersDefault();
        $characters = self::getControlCharactersDefault();

        if (! is_null($type)) {
            throw_if(! array_key_exists($type, $characters),
                     'Control characters do not exist for $type: "'.(empty($type) ? '(empty)' : $type).'".');

            return $characters[$type];
        }

        return $characters;
    }

    /**
     * Get the singleton of this class.
     *
     * @return EncryptionHelper
     */
    public static function getInstance(): self
    {
        return EncryptionFacade::getInstance();
    }
}