diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f54dc58..816f93d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- php_version: [8.1, 8.2, 8.3]
+ php_version: [8.1, 8.2, 8.3, 8.4]
composer_flags: ['', '--prefer-lowest']
steps:
@@ -20,7 +20,7 @@ jobs:
extensions: xdebug
- name: Install dependencies
- uses: php-actions/composer@v5
+ uses: php-actions/composer@v6
with:
php_version: ${{ matrix.php_version }}
args: ${{ matrix.composer_flags }}
@@ -44,4 +44,3 @@ jobs:
# run: |
# composer global require php-coveralls/php-coveralls
# ~/.composer/vendor/bin/php-coveralls -v
-
diff --git a/.gitignore b/.gitignore
index 188e4c4..3882089 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
vendor
composer.lock
/tests/Unit/logs
+.phpunit.result.cache
diff --git a/composer.json b/composer.json
index 0cf2c1c..6e44d49 100644
--- a/composer.json
+++ b/composer.json
@@ -4,36 +4,41 @@
"license": "MIT",
"require": {
"php": ">=8.1",
- "php-di/php-di": "^6.4.0",
- "rareloop/router": "^6.0.2",
- "psr/container": "^1.1.2",
- "psr/http-message": "^1.1",
+ "php-di/php-di": "^7.1.1",
+ "rareloop/router": "^6.0.3",
+ "psr/container": "^2.0.2",
+ "psr/http-message": "^2",
"psr/http-server-middleware": "^1.0.2",
- "blast/facades": "^1.0",
- "timber/timber": "^2.3.0",
- "monolog/monolog": "^2.9.1",
- "http-interop/response-sender": "^1.0",
- "symfony/debug": "^4.4.44",
- "illuminate/collections": "^8.53.1||^9.52.16",
+ "timber/timber": "^2.3.3",
+ "monolog/monolog": "^3.9",
+ "illuminate/collections": "^10.49",
"statamic/stringy": "^3.1.3",
- "laminas/laminas-diactoros": "^2.25.2",
- "rareloop/psr7-server-request-extension": "^2.1.0",
- "mmeyer2k/dcrypt": "^8.3.1",
+ "laminas/laminas-diactoros": "^3.6.0",
+ "rareloop/psr7-server-request-extension": "^2.2.0",
"spatie/macroable": "^1.0.1",
- "mindplay/middleman": "^3.1.0",
- "psr/log": "^1.1.4",
- "laminas/laminas-zendframework-bridge": "^1.7",
- "symfony/var-dumper": "^5.0||^6.3.6"
+ "mindplay/middleman": "^4.0.4",
+ "psr/log": "^2.0.0",
+ "symfony/var-dumper": "^6.4.26",
+ "spatie/ignition": "^1.15.1",
+ "laminas/laminas-httphandlerrunner": "^2.13",
+ "symfony/error-handler": "^6.4.26",
+ "illuminate/support": "^10.49",
+ "illuminate/pipeline": "^10.49",
+ "spatie/backtrace": "^1.8.1",
+ "illuminate/conditionable": "^10.49",
+ "guzzlehttp/guzzle": "^7.10",
+ "spatie/flare-client-php": "^1.10.1"
},
"require-dev": {
- "phpunit/phpunit": "^9.6.13",
- "php-coveralls/php-coveralls": "^2.6",
- "mockery/mockery": "^1.6.6",
- "brain/monkey": "^2.6.1",
- "squizlabs/php_codesniffer": "^3.7.2",
- "php-mock/php-mock": "^2.4.1",
- "mikey179/vfsstream": "1.6.11",
- "dms/phpunit-arraysubset-asserts": "^0.3.1"
+ "phpunit/phpunit": "^9.6.29",
+ "php-coveralls/php-coveralls": "^2.8",
+ "mockery/mockery": "^1.6.12",
+ "brain/monkey": "^2.6.2",
+ "squizlabs/php_codesniffer": "^3.13.4",
+ "php-mock/php-mock": "^2.6.2",
+ "mikey179/vfsstream": "^1.6.12",
+ "dms/phpunit-arraysubset-asserts": "^0.5.0",
+ "antecedent/patchwork": "^2.2.3"
},
"autoload": {
"psr-4": {
@@ -46,8 +51,9 @@
}
},
"config": {
+ "preferred-install": "dist",
"allow-plugins": {
"composer/installers": true
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Application.php b/src/Application.php
index 22f7443..599f9fd 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -3,15 +3,13 @@
namespace Rareloop\Lumberjack;
use Closure;
-use DI\ContainerBuilder;
+use DI\Container;
use Illuminate\Support\Collection;
-use Interop\Container\ContainerInterface as InteropContainerInterface;
+use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
-use Rareloop\Router\Invoker;
-use function Http\Response\send;
-class Application implements ContainerInterface, InteropContainerInterface
+class Application implements ContainerInterface
{
private $container;
private $loadedProviders = [];
@@ -24,7 +22,7 @@ class Application implements ContainerInterface, InteropContainerInterface
public function __construct($basePath = false)
{
- $this->container = ContainerBuilder::buildDevContainer();
+ $this->container = new Container();
$this->bind(Application::class, $this);
@@ -152,7 +150,7 @@ private function isSingletonClassBind($id)
*
* @return bool
*/
- public function has($id)
+ public function has($id): bool
{
return $this->container->has($id);
}
@@ -234,7 +232,7 @@ public function bootstrapWith(array $bootstrappers)
*
* @return boolean
*/
- public function hasRequestBeenHandled() : bool
+ public function hasRequestBeenHandled(): bool
{
return $this->requestHandled;
}
@@ -265,17 +263,17 @@ public function detectWhenRequestHasNotBeenHandled()
if ($this->has('__wp-controller-miss-template') && $this->has('__wp-controller-miss-controller')) {
wp_die(
'Loaded template ' .
- $this->get('__wp-controller-miss-template') .
- ' but couldn\'t find class ' .
- $this->get('__wp-controller-miss-controller') .
- ''
+ $this->get('__wp-controller-miss-template') .
+ ' but couldn\'t find class ' .
+ $this->get('__wp-controller-miss-controller') .
+ ''
);
}
}
});
}
- public function shutdown(ResponseInterface $response = null)
+ public function shutdown(?ResponseInterface $response = null)
{
if ($response) {
global $wp;
@@ -284,13 +282,13 @@ public function shutdown(ResponseInterface $response = null)
// If we're handling a WordPressController response at this point then WordPress will already have
// sent headers as it happens earlier in the lifecycle. For this scenario we need to do a bit more
// work to make sure that duplicate headers are not sent back.
- send($this->removeSentHeadersAndMoveIntoResponse($response));
+ (new SapiEmitter())->emit($this->removeSentHeadersAndMoveIntoResponse($response));
}
die();
}
- protected function removeSentHeadersAndMoveIntoResponse(ResponseInterface $response) : ResponseInterface
+ protected function removeSentHeadersAndMoveIntoResponse(ResponseInterface $response): ResponseInterface
{
// 1. Format the previously sent headers into an array of [key, value]
// 2. Remove all headers from the output that we find
diff --git a/src/Bootstrappers/RegisterExceptionHandler.php b/src/Bootstrappers/RegisterExceptionHandler.php
index b4ff838..64e946d 100644
--- a/src/Bootstrappers/RegisterExceptionHandler.php
+++ b/src/Bootstrappers/RegisterExceptionHandler.php
@@ -2,16 +2,18 @@
namespace Rareloop\Lumberjack\Bootstrappers;
-use DI\NotFoundException;
use Error;
use ErrorException;
-use Psr\Http\Message\ResponseInterface;
+use DI\NotFoundException;
+use function Http\Response\send;
+use Rareloop\Router\Responsable;
use Rareloop\Lumberjack\Application;
+use Psr\Http\Message\ResponseInterface;
+use Laminas\Diactoros\ServerRequestFactory;
+use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use Rareloop\Lumberjack\Exceptions\HandlerInterface;
-use Rareloop\Router\Responsable;
use Symfony\Component\Debug\Exception\FatalErrorException;
-use Zend\Diactoros\ServerRequestFactory;
-use function Http\Response\send;
+use Symfony\Component\ErrorHandler\Error\FatalError;
/**
* Determine whether or not we should be in debug mode or not
@@ -70,7 +72,7 @@ public function handleException($e)
public function send(ResponseInterface $response)
{
- @send($response);
+ @(new SapiEmitter())->emit($response);
}
protected function getExceptionHandler(): HandlerInterface
@@ -122,16 +124,14 @@ public function handleShutdown()
*
* @param array $error
* @param int|null $traceOffset
- * @return \Symfony\Component\Debug\Exception\FatalErrorException
+ * @return \Symfony\Component\ErrorHandler\Error\FatalError
*/
protected function fatalExceptionFromError(array $error, $traceOffset = null)
{
- return new FatalErrorException(
+ return new FatalError(
$error['message'],
- $error['type'],
0,
- $error['file'],
- $error['line'],
+ $error,
$traceOffset
);
}
diff --git a/src/Bootstrappers/RegisterFacades.php b/src/Bootstrappers/RegisterFacades.php
index d5b34a2..53628a0 100644
--- a/src/Bootstrappers/RegisterFacades.php
+++ b/src/Bootstrappers/RegisterFacades.php
@@ -2,8 +2,8 @@
namespace Rareloop\Lumberjack\Bootstrappers;
-use Blast\Facades\FacadeFactory;
use Rareloop\Lumberjack\Application;
+use Rareloop\Lumberjack\FacadeFactory;
class RegisterFacades
{
diff --git a/src/Config.php b/src/Config.php
index 86b0ea3..9ea2e60 100644
--- a/src/Config.php
+++ b/src/Config.php
@@ -9,7 +9,7 @@ class Config
{
private $data = [];
- public function __construct(string $path = null)
+ public function __construct(?string $path = null)
{
if ($path) {
$this->load($path);
diff --git a/src/Contracts/QueryBuilder.php b/src/Contracts/QueryBuilder.php
index 848720d..8797b6f 100644
--- a/src/Contracts/QueryBuilder.php
+++ b/src/Contracts/QueryBuilder.php
@@ -16,7 +16,7 @@ public function offset($offset): QueryBuilder;
public function orderBy($orderBy, string $order = QueryBuilder::ASC): QueryBuilder;
- public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, string $type = null): QueryBuilder;
+ public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, ?string $type = null): QueryBuilder;
public function whereIdIn(array $ids): QueryBuilder;
diff --git a/src/Dcrypt/AesCbc.php b/src/Dcrypt/AesCbc.php
new file mode 100644
index 0000000..bcf544c
--- /dev/null
+++ b/src/Dcrypt/AesCbc.php
@@ -0,0 +1,42 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * Symmetric AES-256-CBC encryption functions powered by OpenSSL.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class AesCbc extends OpensslBridge
+{
+ /**
+ * AES-256 cipher identifier that will be passed to openssl
+ *
+ * @var string
+ */
+ const CIPHER = 'aes-256-cbc';
+
+ /**
+ * Specify sha256 for message authentication
+ *
+ * @var string
+ */
+ const CHKSUM = 'sha256';
+}
diff --git a/src/Dcrypt/AesCtr.php b/src/Dcrypt/AesCtr.php
new file mode 100644
index 0000000..8ed8814
--- /dev/null
+++ b/src/Dcrypt/AesCtr.php
@@ -0,0 +1,35 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * Symmetric AES-256-CTR encryption functions powered by OpenSSL.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class AesCtr extends AesCbc
+{
+ /**
+ * AES-256 cipher identifier that will be passed to openssl
+ *
+ * @var string
+ */
+ const CIPHER = 'aes-256-ctr';
+}
diff --git a/src/Dcrypt/Hash.php b/src/Dcrypt/Hash.php
new file mode 100644
index 0000000..1177d46
--- /dev/null
+++ b/src/Dcrypt/Hash.php
@@ -0,0 +1,193 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * An opaque 512 bit iterative hash function.
+ *
+ * 16 bytes => iv
+ * 12 bytes => cost checksum
+ * 4 bytes => cost
+ * 32 bytes => hmac
+ *
+ * ivivivivivivivivsssssssssssscosthmachmachmachmachmachmachmachmac
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class Hash
+{
+ const ALGO = 'sha256';
+
+ /**
+ * Internal function used to build the actual hash.
+ *
+ * @param string $input Data to hash
+ * @param string $password Password to use in HMAC call
+ * @param int $cost Number of iterations to use
+ * @param string|null $salt Initialization vector to use in HMAC calls
+ * @return string
+ */
+ private static function build(string $input, string $password, int $cost, ?string $salt = null): string
+ {
+ // Generate salt if needed
+ $salt = $salt ?? \random_bytes(16);
+
+ // Verify and normalize cost value
+ $cost = self::cost($cost);
+
+ // Create key to use for hmac operations
+ $key = self::hmac($salt, $password, self::ALGO);
+
+ // Perform hash iterations. Get a 32 byte output value
+ $hash = self::ihmac($input, $key, $cost, self::ALGO);
+
+ // Return the salt + cost blob + hmac
+ return $salt . self::costHash($cost, $salt, $password) . $hash;
+ }
+
+ /**
+ * Return a normalized cost value.
+ *
+ * @param int $cost Number of iterations to use.
+ * @return int
+ */
+ private static function cost(int $cost): int
+ {
+ return $cost % \pow(2, 32);
+ }
+
+ /**
+ * Performs hashing functions
+ *
+ * @param int $cost
+ * @param string $salt
+ * @param string $password
+ * @return string
+ */
+ private static function costHash(int $cost, string $salt, string $password): string
+ {
+ // Hash and return first 12 bytes
+ $hash = Str::substr(self::hmac($cost, $salt, self::ALGO), 0, 12);
+
+ // Convert cost to base 256 then encrypt with OTP stream cipher
+ $cost = Otp::crypt(self::dec2bin($cost), $password);
+
+ return $hash . $cost;
+ }
+
+ /**
+ * Perform a raw iterative HMAC operation with a configurable algo.
+ *
+ * @param string $data Data to hash.
+ * @param string $key Key to use to authenticate the hash.
+ * @param int $iter Number of times to iteratate the hash
+ * @param string $algo Name of algo (sha256 or sha512 recommended)
+ * @return string
+ */
+ public static function ihmac(string $data, string $key, int $iter, string $algo = 'sha256'): string
+ {
+ // Can't perform negative iterations
+ $iter = \abs($iter);
+
+ // Perform iterative hmac calls
+ // Make sure $iter value of 0 is handled
+ for ($i = 0; $i <= $iter; $i++) {
+ $data = self::hmac($data . $i . $iter, $key, $algo);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Perform a single hmac iteration. This adds an extra layer of safety because hash_hmac can return false if algo
+ * is not valid. Return type hint will throw an exception if this happens.
+ *
+ * @param string $data Data to hash.
+ * @param string $key Key to use to authenticate the hash.
+ * @param string $algo Name of algo
+ * @return string
+ */
+ public static function hmac(string $data, string $key, string $algo): string
+ {
+ return \hash_hmac($algo, $data, $key, true);
+ }
+
+ /**
+ * Hash an input string into a salted 512 byte hash.
+ *
+ * @param string $input Data to hash.
+ * @param string $password HMAC validation password.
+ * @param int $cost Cost value of the hash.
+ * @return string
+ */
+ public static function make(string $input, string $password, int $cost = 250000): string
+ {
+ return self::build($input, $password, $cost, null);
+ }
+
+ /**
+ * Check the validity of a hash.
+ *
+ * @param string $input Input to test.
+ * @param string $hash Known hash to validate against.
+ * @param string $password HMAC password to use during iterative hash.
+ * @return boolean
+ */
+ public static function verify(string $input, string $hash, string $password): bool
+ {
+ // Get the salt value from the decrypted prefix
+ $salt = Str::substr($hash, 0, 16);
+
+ // Get the encrypted cost bytes
+ $cost = self::bin2dec(Otp::crypt(Str::substr($hash, 28, 4), $password));
+
+ // Get the entire cost+hash blob for comparison
+ $blob = Str::substr($hash, 16, 16);
+
+ if (!Str::equal(self::costHash($cost, $salt, $password), $blob)) {
+ return false;
+ }
+
+ // Return the boolean equivalence
+ return Str::equal($hash, self::build($input, $password, $cost, $salt));
+ }
+
+ /**
+ * Turns an integer into a 4 byte binary representation
+ *
+ * @param int $dec Integer to convert to binary
+ * @return string
+ */
+ private static function dec2bin(int $dec): string
+ {
+ return \hex2bin(\str_pad(\dechex($dec), 8, '0', STR_PAD_LEFT));
+ }
+
+ /**
+ * Reverses dec2bin
+ *
+ * @param string $bin Binary string to convert to decimal
+ * @return string
+ */
+ private static function bin2dec(string $bin): string
+ {
+ return \hexdec(\bin2hex($bin));
+ }
+}
diff --git a/src/Dcrypt/OpensslBridge.php b/src/Dcrypt/OpensslBridge.php
new file mode 100644
index 0000000..ebe329b
--- /dev/null
+++ b/src/Dcrypt/OpensslBridge.php
@@ -0,0 +1,191 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * Provides functionality common to the dcrypt AES block ciphers. Extend this class to customize your cipher suite.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class OpensslBridge
+{
+ /**
+ * This string is used when hashing to ensure cross compatibility between
+ * dcrypt\mcrypt and dcrypt\aes. Since v7, this is only needed for backwards
+ * compatibility with older versions
+ */
+ const RIJNDA = 'rijndael-128';
+
+ /**
+ * Decrypt cyphertext
+ *
+ * @param string $data Cyphertext to decrypt
+ * @param string $pass Password that should be used to decrypt input data
+ * @param int $cost Number of extra HMAC iterations to perform on key
+ * @return string
+ */
+ public static function decrypt(string $data, string $pass, int $cost = 0): string
+ {
+ // Find the IV at the beginning of the cypher text
+ $ivr = Str::substr($data, 0, self::ivsize());
+
+ // Gather the checksum portion of the ciphertext
+ $sum = Str::substr($data, self::ivsize(), self::cksize());
+
+ // Gather message portion of ciphertext after iv and checksum
+ $msg = Str::substr($data, self::ivsize() + self::cksize());
+
+ // Derive key from password
+ $key = self::key($pass, $ivr, $cost);
+
+ // Calculate verification checksum
+ $chk = self::checksum($msg, $ivr, $key);
+
+ // Verify HMAC before decrypting
+ self::checksumVerify($chk, $sum);
+
+ // Decrypt message and return
+ return OpensslWrapper::decrypt($msg, static::CIPHER, $key, $ivr);
+ }
+
+ /**
+ * Encrypt plaintext
+ *
+ * @param string $data Plaintext string to encrypt.
+ * @param string $pass Password used to encrypt data.
+ * @param int $cost Number of extra HMAC iterations to perform on key
+ * @return string
+ */
+ public static function encrypt(string $data, string $pass, int $cost = 0): string
+ {
+ // Generate IV of appropriate size.
+ $ivr = \random_bytes(self::ivsize());
+
+ // Derive key from password
+ $key = self::key($pass, $ivr, $cost);
+
+ // Encrypt the plaintext
+ $msg = OpensslWrapper::encrypt($data, static::CIPHER, $key, $ivr);
+
+ // Create the cypher text prefix (iv + checksum)
+ $pre = $ivr . self::checksum($msg, $ivr, $key);
+
+ // Return prefix + cyphertext
+ return $pre . $msg;
+ }
+
+ /**
+ * Create a message authentication checksum.
+ *
+ * @param string $data Ciphertext that needs a checksum.
+ * @param string $iv Initialization vector.
+ * @param string $key HMAC key
+ * @return string
+ */
+ private static function checksum(string $data, string $iv, string $key): string
+ {
+ // Prevent multiple potentially large string concats by hmac-ing the input data
+ // by itself first...
+ $sum = Hash::hmac($data, $key, static::CHKSUM);
+
+ // Then add the other input elements together before performing the final hash
+ $sum = $sum . $iv . self::mode() . self::RIJNDA;
+
+ // ... then hash other elements with previous hmac and return
+ return Hash::hmac($sum, $key, static::CHKSUM);
+ }
+
+ /**
+ * Transform password into key and perform iterative HMAC (if specified)
+ *
+ * @param string $pass Encryption key
+ * @param string $iv Initialization vector
+ * @param int $cost Number of HMAC iterations to perform on key
+ * @return string
+ */
+ private static function key(string $pass, string $iv, int $cost): string
+ {
+ // Create the authentication string to be hashed
+ $data = $iv . self::RIJNDA . self::mode();
+
+ return Hash::ihmac($data, $pass, $cost, static::CHKSUM);
+ }
+
+ /**
+ * Verify checksum during decryption step and throw error if mismatching.
+ *
+ * @param string $calculated
+ * @param string $supplied
+ * @throws \InvalidArgumentException
+ */
+ private static function checksumVerify(string $calculated, string $supplied)
+ {
+ if (!Str::equal($calculated, $supplied)) {
+ $e = 'Decryption can not proceed due to invalid cyphertext checksum.';
+ throw new \InvalidArgumentException($e);
+ }
+ }
+
+ /**
+ * Return the encryption mode string. This function is really only needed for backwards
+ * compatibility.
+ *
+ * @return string
+ */
+ private static function mode(): string
+ {
+ // To prevent legacy blobs from not decoding, these ciphers (which were implemented before 8.3) have hard coded
+ // return values. Luckily, this integrates gracefully with overloading.
+ $legacy = [
+ 'bf-cbc' => 'cbc',
+ 'bf-ofb' => 'ofb',
+ 'aes-256-cbc' => 'cbc',
+ 'aes-256-ctr' => 'ctr',
+ ];
+
+ $cipher = \strtolower(static::CIPHER);
+
+ if (isset($legacy[$cipher])) {
+ return $legacy[$cipher];
+ }
+
+ return $cipher;
+ }
+
+ /**
+ * Calculate checksum size
+ *
+ * @return int
+ */
+ private static function cksize(): int
+ {
+ return Str::hashSize(static::CHKSUM);
+ }
+
+ /**
+ * Get IV size
+ *
+ * @return int
+ */
+ private static function ivsize(): int
+ {
+ return \openssl_cipher_iv_length(static::CIPHER);
+ }
+}
diff --git a/src/Dcrypt/OpensslWrapper.php b/src/Dcrypt/OpensslWrapper.php
new file mode 100644
index 0000000..bea6638
--- /dev/null
+++ b/src/Dcrypt/OpensslWrapper.php
@@ -0,0 +1,67 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+class OpensslWrapper
+{
+
+ /**
+ * OpenSSL encrypt wrapper function
+ *
+ * @param string $data Data to decrypt
+ * @param string $method Cipher method to use
+ * @param string $key Key string
+ * @param string $iv Initialization vector
+ * @return string
+ */
+ public static function encrypt(string $data, string $method, string $key, string $iv): string
+ {
+ $ret = \openssl_encrypt($data, $method, $key, 1, $iv);
+
+ return self::returnOrException($ret);
+ }
+
+ /**
+ * OpenSSL decrypt wrapper function
+ *
+ * @param string $data Data to decrypt
+ * @param string $method Cipher method to use
+ * @param string $key Key string
+ * @param string $iv Initialization vector
+ * @return string
+ */
+ public static function decrypt(string $data, string $method, string $key, string $iv): string
+ {
+ $ret = \openssl_decrypt($data, $method, $key, 1, $iv);
+
+ return self::returnOrException($ret);
+ }
+
+ /**
+ * Throw an exception if openssl function returns false
+ *
+ * @param string|bool $data
+ * @return string
+ * @throws \Exception
+ */
+ private static function returnOrException($data): string
+ {
+ if ($data === false) {
+ throw new \Exception('OpenSSL failed to encrypt/decrypt message.');
+ }
+
+ return $data;
+ }
+}
diff --git a/src/Dcrypt/Otp.php b/src/Dcrypt/Otp.php
new file mode 100644
index 0000000..2836e4f
--- /dev/null
+++ b/src/Dcrypt/Otp.php
@@ -0,0 +1,50 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * A one time pad stream encryption class.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link http://en.wikipedia.org/wiki/Stream_cipher
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class Otp
+{
+ /**
+ * Encrypt or decrypt a binary input string.
+ *
+ * @param string $input Input data to encrypt
+ * @param string $password Encryption/decryption key to use on input
+ * @param string $algo Hashing algo to generate keystream
+ * @return string
+ */
+ public static function crypt(string $input, string $password, string $algo = 'sha512'): string
+ {
+ $chunks = \str_split($input, Str::hashSize($algo));
+
+ $length = Str::strlen($input);
+
+ foreach ($chunks as $i => &$chunk) {
+ $chunk = $chunk ^ \hash_hmac($algo, $password . $length, $i, true);
+ }
+
+ return \implode($chunks);
+ }
+}
diff --git a/src/Dcrypt/Pkcs7.php b/src/Dcrypt/Pkcs7.php
new file mode 100644
index 0000000..37fc9ca
--- /dev/null
+++ b/src/Dcrypt/Pkcs7.php
@@ -0,0 +1,76 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * Provides PKCS #7 padding functionality.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class Pkcs7
+{
+ /**
+ * PKCS #7 padding function.
+ *
+ * @param string $input String to pad
+ * @param int $blocksize Block size in bytes
+ * @return string
+ */
+ public static function pad(string $input, int $blocksize): string
+ {
+ // Determine the padding string that needs to be appended.
+ $pad = self::paddingString(Str::strlen($input), $blocksize);
+
+ // Return input + padding
+ return $input . $pad;
+ }
+
+ /**
+ * Create the padding string that will be appended to the input.
+ *
+ * @param int $inputsize Size of the input in bytes
+ * @param int $blocksize Blocksize in bytes
+ * @return string
+ */
+ private static function paddingString(int $inputsize, int $blocksize): string
+ {
+ // Determine the amount of padding to use
+ $pad = $blocksize - ($inputsize % $blocksize);
+
+ // Create and return the padding string
+ return \str_repeat(\chr($pad), $pad);
+ }
+
+ /**
+ * PKCS #7 unpadding function.
+ *
+ * @param string $input Padded string to unpad
+ * @return string
+ */
+ public static function unpad(string $input): string
+ {
+ // Determine the padding size by converting the final byte of the
+ // input to its decimal value
+ $padsize = \ord(Str::substr($input, -1));
+
+ // Return string minus the padding amount
+ return Str::substr($input, 0, Str::strlen($input) - $padsize);
+ }
+}
diff --git a/src/Dcrypt/Rc4.php b/src/Dcrypt/Rc4.php
new file mode 100644
index 0000000..8587437
--- /dev/null
+++ b/src/Dcrypt/Rc4.php
@@ -0,0 +1,76 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * An implementation of RC4 symmetric encryption.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link http://en.wikipedia.org/wiki/Stream_cipher
+ * @link https://en.wikipedia.org/wiki/RC4
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class Rc4
+{
+ /**
+ * Perform (en/de)cryption
+ *
+ * @param string $str String to be encrypted
+ * @param string $key Key to use for encryption
+ * @return string
+ */
+ public static function crypt(string $str, string $key): string
+ {
+ $s = self::initializeState($key);
+ $i = $j = 0;
+ $res = '';
+ $size = Str::strlen($str);
+ for ($y = 0; $y < $size; $y++) {
+ $i = ($i + 1) % 256;
+ $j = ($j + $s[$i]) % 256;
+ $x = $s[$i];
+ $s[$i] = $s[$j];
+ $s[$j] = $x;
+ $res .= $str[$y] ^ \chr($s[($s[$i] + $s[$j]) % 256]);
+ }
+
+ return $res;
+ }
+
+ /**
+ * Create the initial byte matrix that will be used for swaps. This code
+ * is identical between RC4 and Spritz.
+ *
+ * @param string $key
+ * @return array
+ */
+ protected static function initializeState(string $key): array
+ {
+ $s = \range(0, 255);
+ $j = 0;
+ foreach (\range(0, 255) as $i) {
+ $j = ($j + $s[$i] + \ord($key[$i % Str::strlen($key)])) % 256;
+ $x = $s[$i];
+ $s[$i] = $s[$j];
+ $s[$j] = $x;
+ }
+
+ return $s;
+ }
+}
diff --git a/src/Dcrypt/Spritz.php b/src/Dcrypt/Spritz.php
new file mode 100644
index 0000000..78d6726
--- /dev/null
+++ b/src/Dcrypt/Spritz.php
@@ -0,0 +1,59 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * An implementation of Spritz symmetric encryption.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link http://en.wikipedia.org/wiki/Stream_cipher
+ * @link https://en.wikipedia.org/wiki/RC4
+ * @link http://people.csail.mit.edu/rivest/pubs/RS14.pdf
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class Spritz extends Rc4
+{
+ /**
+ * Perform (en/de)cryption
+ *
+ * @param string $str String to be encrypted
+ * @param string $key Key to use for encryption
+ * @return string
+ */
+ public static function crypt(string $str, string $key): string
+ {
+ $s = self::initializeState($key);
+ $i = $j = $k = $z = 0;
+ $w = 1;
+ $res = '';
+ $size = Str::strlen($str);
+ for ($y = 0; $y < $size; $y++) {
+ $i = ($i + $w) % 256;
+ $j = ($k + $s[($j + $s[$i]) % 256]) % 256;
+ $k = ($i + $k + $s[$j]) % 256;
+ $x = $s[$i];
+ $s[$i] = $s[$j];
+ $s[$j] = $x;
+ $z = $s[($j + $s[($i + $s[($z + $k) % 256]) % 256]) % 256];
+ $res .= $str[$y] ^ \chr($z);
+ }
+
+ return $res;
+ }
+}
diff --git a/src/Dcrypt/Str.php b/src/Dcrypt/Str.php
new file mode 100644
index 0000000..b9f1b89
--- /dev/null
+++ b/src/Dcrypt/Str.php
@@ -0,0 +1,92 @@
+
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ */
+
+namespace Rareloop\Lumberjack\Dcrypt;
+
+/**
+ * Provides time-safe string comparison facilities, and safe string operations
+ * on systems that have mb_* function overloading enabled.
+ *
+ * The functions in this class were inspired by the symfony's StringUtils class.
+ *
+ * @category Dcrypt
+ * @package Dcrypt
+ * @author Michael Meyer (mmeyer2k)
+ * @license http://opensource.org/licenses/MIT The MIT License (MIT)
+ * @link https://github.com/mmeyer2k/dcrypt
+ * @link https://github.com/symfony/Security/blob/master/Core/Util/StringUtils.php
+ * @link https://php.net/manual/en/mbstring.overload.php
+ * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
+ */
+class Str
+{
+ /**
+ * Compares two strings in constant time. Strings are hashed before
+ * comparison so information is not leaked when strings are not of
+ * equal length.
+ *
+ * @param string $known The string of known length to compare against
+ * @param string $given The string that the user can control
+ * @return bool
+ */
+ public static function equal(string $known, string $given): bool
+ {
+ // Create some entropy
+ $nonce = \random_bytes(32);
+
+ // We hash the 2 inputs at this point because hash_equals is still
+ // vulnerable to timing attacks when the inputs have different sizes.
+ // Inputs are also cast to string like in symfony stringutils.
+ $known = Hash::hmac($known, $nonce, 'sha256');
+ $given = Hash::hmac($given, $nonce, 'sha256');
+
+ return \hash_equals($known, $given);
+ }
+
+ /**
+ * Determine the length of the output of a given hash algorithm in bytes.
+ *
+ * @param string $algo Name of algorithm to look up
+ * @return int
+ */
+ public static function hashSize(string $algo): int
+ {
+ return self::strlen(\hash($algo, 'hash me', true));
+ }
+
+ /**
+ * Returns the number of bytes in a string.
+ *
+ * @param string $string The string whose length we wish to obtain
+ * @return int
+ */
+ public static function strlen(string $string): int
+ {
+ return \mb_strlen($string, '8bit');
+ }
+
+ /**
+ * Returns part of a string.
+ *
+ * @param string $string The string whose length we wish to obtain
+ * @param int $start
+ * @param int $length
+ *
+ * @return string the extracted part of string; or FALSE on failure, or an empty string.
+ */
+ public static function substr(string $string, int $start, ?int $length = null): string
+ {
+ return \mb_substr($string, $start, $length, '8bit');
+ }
+}
diff --git a/src/Encrypter.php b/src/Encrypter.php
index 12e3049..034da2a 100644
--- a/src/Encrypter.php
+++ b/src/Encrypter.php
@@ -2,7 +2,7 @@
namespace Rareloop\Lumberjack;
-use Dcrypt\AesCbc;
+use Rareloop\Lumberjack\Dcrypt\AesCbc;
use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract;
class Encrypter implements EncrypterContract
diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php
index 045b6f1..cc5409b 100644
--- a/src/Exceptions/Handler.php
+++ b/src/Exceptions/Handler.php
@@ -3,13 +3,12 @@
namespace Rareloop\Lumberjack\Exceptions;
use Exception;
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
+use Spatie\Ignition\Ignition;
use Rareloop\Lumberjack\Application;
+use Psr\Http\Message\ResponseInterface;
use Rareloop\Lumberjack\Facades\Config;
-use Symfony\Component\Debug\ExceptionHandler as SymfonyExceptionHandler;
-use Symfony\Component\Debug\Exception\FlattenException;
-use Zend\Diactoros\Response\HtmlResponse;
+use Laminas\Diactoros\Response\HtmlResponse;
+use Psr\Http\Message\ServerRequestInterface;
class Handler implements HandlerInterface
{
@@ -34,13 +33,22 @@ public function report(Exception $e)
}
}
- public function render(ServerRequestInterface $request, Exception $e) : ResponseInterface
+ public function render(ServerRequestInterface $request, Exception $e): ResponseInterface
{
- $e = FlattenException::create($e);
+ $isDebug = Config::get('app.debug', false) === true;
+
+ $ignition = Ignition::make()
+ ->shouldDisplayException($isDebug)
+ ->runningInProductionEnvironment(!$isDebug)
+ ->register();
+
+ ob_start();
+
+ $ignition->handleException($e);
- $handler = new SymfonyExceptionHandler(Config::get('app.debug', false));
+ $html = ob_get_clean();
- return new HtmlResponse($handler->getHtml($e), $e->getStatusCode(), $e->getHeaders());
+ return new HtmlResponse($html);
}
protected function shouldNotReport(Exception $e)
diff --git a/src/FacadeFactory.php b/src/FacadeFactory.php
new file mode 100644
index 0000000..eb09caf
--- /dev/null
+++ b/src/FacadeFactory.php
@@ -0,0 +1,25 @@
+get($accessor), $name], ...$arguments);
+ }
+}
diff --git a/src/Facades/AbstractFacade.php b/src/Facades/AbstractFacade.php
new file mode 100644
index 0000000..72b81c4
--- /dev/null
+++ b/src/Facades/AbstractFacade.php
@@ -0,0 +1,20 @@
+get(static::accessor());
+ }
+
+ abstract protected static function accessor();
+}
diff --git a/src/Facades/Config.php b/src/Facades/Config.php
index 174a692..bfd4a26 100644
--- a/src/Facades/Config.php
+++ b/src/Facades/Config.php
@@ -2,8 +2,6 @@
namespace Rareloop\Lumberjack\Facades;
-use Blast\Facades\AbstractFacade;
-
class Config extends AbstractFacade
{
protected static function accessor()
diff --git a/src/Facades/Log.php b/src/Facades/Log.php
index 324c66b..d7bbf8b 100644
--- a/src/Facades/Log.php
+++ b/src/Facades/Log.php
@@ -2,8 +2,6 @@
namespace Rareloop\Lumberjack\Facades;
-use Blast\Facades\AbstractFacade;
-
class Log extends AbstractFacade
{
protected static function accessor()
diff --git a/src/Facades/MiddlewareAliases.php b/src/Facades/MiddlewareAliases.php
index cf8bf7d..72d1080 100644
--- a/src/Facades/MiddlewareAliases.php
+++ b/src/Facades/MiddlewareAliases.php
@@ -2,8 +2,6 @@
namespace Rareloop\Lumberjack\Facades;
-use Blast\Facades\AbstractFacade;
-
class MiddlewareAliases extends AbstractFacade
{
protected static function accessor()
diff --git a/src/Facades/Router.php b/src/Facades/Router.php
index 6859dec..85fa666 100644
--- a/src/Facades/Router.php
+++ b/src/Facades/Router.php
@@ -2,8 +2,6 @@
namespace Rareloop\Lumberjack\Facades;
-use Blast\Facades\AbstractFacade;
-
class Router extends AbstractFacade
{
protected static function accessor()
diff --git a/src/Facades/Session.php b/src/Facades/Session.php
index cf9b053..2b28d9f 100644
--- a/src/Facades/Session.php
+++ b/src/Facades/Session.php
@@ -2,8 +2,6 @@
namespace Rareloop\Lumberjack\Facades;
-use Blast\Facades\AbstractFacade;
-
class Session extends AbstractFacade
{
protected static function accessor()
diff --git a/src/Http/MiddlewareAliasStore.php b/src/Http/MiddlewareAliasStore.php
index c9c2682..80fbc9d 100644
--- a/src/Http/MiddlewareAliasStore.php
+++ b/src/Http/MiddlewareAliasStore.php
@@ -32,7 +32,7 @@ public function get(string $name)
return $middleware;
}
- protected function parseName($name) : array
+ protected function parseName($name): array
{
list($name, $params) = array_pad(explode(':', $name), 2, '');
@@ -41,7 +41,7 @@ protected function parseName($name) : array
return [$name, $params];
}
- public function has(string $name) : bool
+ public function has(string $name): bool
{
list($name, $params) = $this->parseName($name);
diff --git a/src/Http/Responses/RedirectResponse.php b/src/Http/Responses/RedirectResponse.php
index d51db77..a6d992d 100644
--- a/src/Http/Responses/RedirectResponse.php
+++ b/src/Http/Responses/RedirectResponse.php
@@ -3,7 +3,7 @@
namespace Rareloop\Lumberjack\Http\Responses;
use Rareloop\Lumberjack\Helpers;
-use Zend\Diactoros\Response\RedirectResponse as DiactorosRedirectResponse;
+use Laminas\Diactoros\Response\RedirectResponse as DiactorosRedirectResponse;
class RedirectResponse extends DiactorosRedirectResponse
{
diff --git a/src/Http/Responses/TimberResponse.php b/src/Http/Responses/TimberResponse.php
index 06a31cb..8ac161e 100644
--- a/src/Http/Responses/TimberResponse.php
+++ b/src/Http/Responses/TimberResponse.php
@@ -6,7 +6,7 @@
use Rareloop\Lumberjack\Contracts\Arrayable;
use Rareloop\Lumberjack\Exceptions\TwigTemplateNotFoundException;
use Timber\Timber;
-use Zend\Diactoros\Response\HtmlResponse;
+use Laminas\Diactoros\Response\HtmlResponse;
class TimberResponse extends HtmlResponse
{
@@ -21,7 +21,7 @@ public function __construct($twigTemplate, $context, $status = 200, array $heade
parent::__construct($template, $status, $headers);
}
- private function flattenContextToArrays(array $context) : array
+ private function flattenContextToArrays(array $context): array
{
// Recursively walk the array, when we find something that implements the Arrayable interface
// flatten it to an array. Because we're passing by reference by updating what the value of
diff --git a/src/Http/ServerRequest.php b/src/Http/ServerRequest.php
index 5135e39..3274e66 100644
--- a/src/Http/ServerRequest.php
+++ b/src/Http/ServerRequest.php
@@ -5,7 +5,7 @@
use Psr\Http\Message\ServerRequestInterface;
use Rareloop\Psr7ServerRequestExtension\InteractsWithInput;
use Rareloop\Psr7ServerRequestExtension\InteractsWithUri;
-use Zend\Diactoros\ServerRequest as DiactorosServerRequest;
+use Laminas\Diactoros\ServerRequest as DiactorosServerRequest;
class ServerRequest extends DiactorosServerRequest
{
@@ -34,7 +34,7 @@ public static function fromRequest(ServerRequestInterface $request)
);
}
- public function ajax() : bool
+ public function ajax(): bool
{
if (!$this->hasHeader('X-Requested-With')) {
return false;
@@ -43,7 +43,7 @@ public function ajax() : bool
return 'XMLHttpRequest' === $this->getHeader('X-Requested-With')[0];
}
- public function getMethod() : string
+ public function getMethod(): string
{
return strtoupper(parent::getMethod());
}
diff --git a/src/Providers/EncryptionServiceProvider.php b/src/Providers/EncryptionServiceProvider.php
index ec762cc..87b7c3f 100644
--- a/src/Providers/EncryptionServiceProvider.php
+++ b/src/Providers/EncryptionServiceProvider.php
@@ -2,11 +2,8 @@
namespace Rareloop\Lumberjack\Providers;
-use Rareloop\Lumberjack\Application;
use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract;
use Rareloop\Lumberjack\Encrypter;
-use Rareloop\Lumberjack\Facades\Config;
-use Rareloop\Lumberjack\Session\SessionManager;
class EncryptionServiceProvider extends ServiceProvider
{
@@ -15,9 +12,11 @@ class EncryptionServiceProvider extends ServiceProvider
public function register()
{
if ($this->app->has('config')) {
- $encryptionKey = $this->app->get('config')->get('app.key');
+ $encrypter = function () {
+ $encryptionKey = $this->app->get('config')->get('app.key');
- $encrypter = new Encrypter($encryptionKey);
+ return new Encrypter($encryptionKey);
+ };
$this->app->bind(EncrypterContract::class, $encrypter);
$this->app->bind('encrypter', $encrypter);
diff --git a/src/Providers/LogServiceProvider.php b/src/Providers/LogServiceProvider.php
index b04a0b3..a6cf812 100644
--- a/src/Providers/LogServiceProvider.php
+++ b/src/Providers/LogServiceProvider.php
@@ -7,6 +7,7 @@
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\ErrorLogHandler;
+use Monolog\Level;
class LogServiceProvider extends ServiceProvider
{
@@ -44,9 +45,9 @@ private function shouldUseErrorLogHandler()
return $config && $config->get('app.logs.path') === false && $config->get('app.logs.enabled') === true;
}
- private function getLogLevel()
+ private function getLogLevel(): Level
{
- $logLevel = Logger::DEBUG;
+ $logLevel = Level::Debug;
if ($this->app->has('config')) {
$logLevel = $this->app->get('config')->get('app.logs.level', $logLevel);
diff --git a/src/Providers/RouterServiceProvider.php b/src/Providers/RouterServiceProvider.php
index 5a61ee5..81f82e1 100644
--- a/src/Providers/RouterServiceProvider.php
+++ b/src/Providers/RouterServiceProvider.php
@@ -9,7 +9,7 @@
use Rareloop\Lumberjack\Http\Router;
use Rareloop\Lumberjack\Http\ServerRequest;
use Rareloop\Router\MiddlewareResolver as MiddlewareResolverInterface;
-use Zend\Diactoros\ServerRequestFactory;
+use Laminas\Diactoros\ServerRequestFactory;
class RouterServiceProvider extends ServiceProvider
{
diff --git a/src/Providers/WordPressControllersServiceProvider.php b/src/Providers/WordPressControllersServiceProvider.php
index 29170f5..49a206f 100644
--- a/src/Providers/WordPressControllersServiceProvider.php
+++ b/src/Providers/WordPressControllersServiceProvider.php
@@ -9,7 +9,7 @@
use Rareloop\Router\ResponseFactory;
use Psr\Http\Message\RequestInterface;
use Rareloop\Lumberjack\Http\Middleware\PasswordProtected;
-use Zend\Diactoros\ServerRequestFactory;
+use Laminas\Diactoros\ServerRequestFactory;
use Rareloop\Router\ProvidesControllerMiddleware;
class WordPressControllersServiceProvider extends ServiceProvider
@@ -94,7 +94,7 @@ function ($request) use ($controller, $methodName) {
];
$dispatcher = $this->createDispatcher($middlewares);
- return $dispatcher->dispatch($request);
+ return $dispatcher->handle($request);
}
private function createDispatcher(array $middlewares): Dispatcher
diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php
index 384fee6..f51ad99 100644
--- a/src/QueryBuilder.php
+++ b/src/QueryBuilder.php
@@ -82,7 +82,7 @@ public function orderBy($orderBy, string $order = QueryBuilder::ASC): QueryBuild
return $this;
}
- public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, string $type = null): QueryBuilderContract
+ public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, ?string $type = null): QueryBuilderContract
{
$order = strtoupper($order);
diff --git a/src/Session/EncryptedStore.php b/src/Session/EncryptedStore.php
index 856b7c1..d6388f0 100644
--- a/src/Session/EncryptedStore.php
+++ b/src/Session/EncryptedStore.php
@@ -17,7 +17,7 @@ public function __construct(
SessionHandlerInterface $handler,
Encrypter $encrypter,
$id = null,
- HandlerInterface $exceptionHandler = null
+ ?HandlerInterface $exceptionHandler = null
) {
$this->encrypter = $encrypter;
$this->exceptionHandler = $exceptionHandler;
@@ -32,10 +32,14 @@ protected function prepareForStorage($data)
protected function prepareForUnserialize($data)
{
+ if ($data === '') {
+ return '';
+ }
+
try {
return $this->encrypter->decrypt($data);
} catch (Exception $e) {
- $this->exceptionHandler->report($e);
+ $this->exceptionHandler?->report($e);
return '';
}
}
diff --git a/src/Session/FileSessionHandler.php b/src/Session/FileSessionHandler.php
index 80d5a48..56f3b5e 100644
--- a/src/Session/FileSessionHandler.php
+++ b/src/Session/FileSessionHandler.php
@@ -46,7 +46,7 @@ public function write($sessionId, $data)
{
try {
file_put_contents($this->getFilepath($sessionId), $data);
- } catch (Exception $e) {
+ } catch (Exception) {
Log::error('Failed to create session on disk');
}
diff --git a/src/Session/SessionManager.php b/src/Session/SessionManager.php
index 0926a03..6359bae 100644
--- a/src/Session/SessionManager.php
+++ b/src/Session/SessionManager.php
@@ -5,6 +5,7 @@
use Rareloop\Lumberjack\Application;
use Rareloop\Lumberjack\Config;
use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract;
+use Rareloop\Lumberjack\Exceptions\HandlerInterface;
use Rareloop\Lumberjack\Manager;
class SessionManager extends Manager
@@ -54,8 +55,9 @@ protected function buildSession($handler)
if ($this->config->get('session.encrypt')) {
$encrypter = $this->app->get(EncrypterContract::class);
+ $exceptionHandler = $this->app->get(HandlerInterface::class);
- return new EncryptedStore($this->name, $handler, $encrypter, $sessionId);
+ return new EncryptedStore($this->name, $handler, $encrypter, $sessionId, $exceptionHandler);
}
return new Store($this->name, $handler, $sessionId);
diff --git a/src/ViewModel.php b/src/ViewModel.php
index a7a9a31..9ae3fa2 100644
--- a/src/ViewModel.php
+++ b/src/ViewModel.php
@@ -9,7 +9,7 @@
abstract class ViewModel implements Arrayable
{
- public function toArray() : array
+ public function toArray(): array
{
$propertyKeyValues = collect($this->validPropertyNames())
->mapWithKeys(function ($method) {
@@ -31,7 +31,7 @@ public function toArray() : array
return array_merge($propertyKeyValues, $methodKeyValues);
}
- protected function validMethodNames() : array
+ protected function validMethodNames(): array
{
$class = new ReflectionClass(static::class);
return collect($class->getMethods(ReflectionMethod::IS_PUBLIC))
@@ -44,7 +44,7 @@ protected function validMethodNames() : array
->all();
}
- protected function validPropertyNames() : array
+ protected function validPropertyNames(): array
{
$class = new ReflectionClass(static::class);
@@ -58,7 +58,7 @@ protected function validPropertyNames() : array
->all();
}
- protected function ignoredMethods() : array
+ protected function ignoredMethods(): array
{
return [
'toArray',
diff --git a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php
index c19f006..ff38c42 100644
--- a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php
+++ b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php
@@ -14,10 +14,9 @@
use Rareloop\Lumberjack\Exceptions\HandlerInterface;
use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration;
use Rareloop\Router\Responsable;
-use Symfony\Component\Debug\Exception\FatalErrorException;
-use Zend\Diactoros\Response;
-use Zend\Diactoros\Response\TextResponse;
-use Zend\Diactoros\ServerRequest;
+use Laminas\Diactoros\Response;
+use Laminas\Diactoros\Response\TextResponse;
+use Laminas\Diactoros\ServerRequest;
/**
* @runTestsInSeparateProcesses
diff --git a/tests/Unit/Bootstrappers/RegisterFacadesTest.php b/tests/Unit/Bootstrappers/RegisterFacadesTest.php
index 71653c9..5a2f234 100644
--- a/tests/Unit/Bootstrappers/RegisterFacadesTest.php
+++ b/tests/Unit/Bootstrappers/RegisterFacadesTest.php
@@ -2,11 +2,10 @@
namespace Rareloop\Lumberjack\Test\Bootstrappers;
-use Blast\Facades\FacadeFactory;
-use Mockery;
use PHPUnit\Framework\TestCase;
use Rareloop\Lumberjack\Application;
use Rareloop\Lumberjack\Bootstrappers\RegisterFacades;
+use Rareloop\Lumberjack\FacadeFactory;
class RegisterFacadesTest extends TestCase
{
diff --git a/tests/Unit/Dcrypt/AesCbcTest.php b/tests/Unit/Dcrypt/AesCbcTest.php
new file mode 100644
index 0000000..e7a4cb7
--- /dev/null
+++ b/tests/Unit/Dcrypt/AesCbcTest.php
@@ -0,0 +1,49 @@
+expectException(\InvalidArgumentException::class);
+
+ $input = 'AAAAAAAA';
+ $key = 'AAAAAAAA';
+ $encrypted = AesCbc::encrypt($input, $key, 10);
+ $this->assertEquals($input, AesCbc::decrypt($encrypted, $key, 10));
+
+ $corrupt = self::swaprandbyte($encrypted);
+ AesCbc::decrypt($corrupt, $key, 10);
+ }
+
+ public function testEngine()
+ {
+ $this->expectException(\InvalidArgumentException::class);
+
+ $input = 'AAAAAAAA';
+ $key = 'AAAAAAAA';
+
+ $encrypted = AesCbc::encrypt($input, $key);
+ $this->assertEquals($input, AesCbc::decrypt($encrypted, $key));
+
+ // Perform a validation by replacing a random byte to make sure
+ // the decryption fails. After enough successful runs,
+ // all areas of the cypher text will have been tested
+ // for integrity
+ $corrupt = self::swaprandbyte($encrypted);
+ AesCbc::decrypt($corrupt, $key);
+ }
+
+ public function testVector()
+ {
+ $input = 'hello world';
+ $pass = 'password';
+ $vector = \base64_decode('eZu2DqB2gYhdA2YkjagLNJJVMVo1BbpJ75tW/PO2bGIY98XHD+Gp+YlO5cv/rHzo45LHMCxL2qOircdST1w5hg==');
+
+ $this->assertEquals($input, AesCbc::decrypt($vector, $pass));
+ }
+}
diff --git a/tests/Unit/Dcrypt/AesCtrTest.php b/tests/Unit/Dcrypt/AesCtrTest.php
new file mode 100644
index 0000000..e640a2f
--- /dev/null
+++ b/tests/Unit/Dcrypt/AesCtrTest.php
@@ -0,0 +1,44 @@
+input, $this->key, 10);
+ $this->assertEquals($this->input, AesCtr::decrypt($encrypted, $this->key, 10));
+ }
+
+ public function testEngine()
+ {
+ $encrypted = AesCtr::encrypt($this->input, $this->key);
+ $this->assertEquals($this->input, AesCtr::decrypt($encrypted, $this->key));
+ }
+
+ public function testCorrupt()
+ {
+ $this->expectException(\InvalidArgumentException::class);
+
+ $encrypted = AesCtr::encrypt($this->input, $this->key);
+
+ // Perform a validation by replacing a random byte to make sure
+ // the decryption fails. After enough successful runs,
+ // all areas of the cypher text will have been tested
+ // for integrity
+ $corrupt = self::swaprandbyte($encrypted);
+ AesCtr::decrypt($corrupt, $this->key);
+ }
+
+ public function testVector()
+ {
+ $input = 'hello world';
+ $pass = 'password';
+ $vector = \base64_decode('Vpbd71CIVcRPALeSg126DhRKYozXlbusn/eSSxrQPtzj/U7hOhlN8D21Y0gmlmUKorpoXuDS6bklvD8=');
+ $this->assertEquals($input, AesCtr::decrypt($vector, $pass));
+ }
+}
diff --git a/tests/Unit/Dcrypt/HashTest.php b/tests/Unit/Dcrypt/HashTest.php
new file mode 100644
index 0000000..3e1ba86
--- /dev/null
+++ b/tests/Unit/Dcrypt/HashTest.php
@@ -0,0 +1,64 @@
+assertNotEquals('aaaa', Hash::ihmac('aaaa', 'bbbb', 0));
+ $this->assertNotEquals('aaaa', Hash::ihmac('aaaa', 'bbbb', -1));
+ }
+
+ public function testBadCost()
+ {
+ $this->assertEquals(64, strlen(Hash::make('test', '1234', 0)));
+ }
+
+ public function testLength()
+ {
+ $this->assertEquals(64, strlen(Hash::make('test', '1234')));
+ }
+
+ public function testCycle()
+ {
+ $input = 'input test';
+ $key = 'key123';
+ $hash = Hash::make($input, $key, 1);
+ $this->assertTrue(Hash::verify($input, $hash, $key));
+ }
+
+ public function testHmacAlgoFailure()
+ {
+ $this->expectException(\ValueError::class);
+
+ Hash::hmac('test', '1234', 'an algo that does not exist');
+ }
+
+ public function testFail()
+ {
+ $input = str_repeat('A', rand(0, 10000));
+ $key = str_repeat('A', rand(10, 100));
+ $cost = 1;
+
+ $output = Hash::make($input, $key, $cost);
+ $this->assertTrue(Hash::verify($input, $output, $key));
+
+ for ($i = 0; $i < 10; $i++) {
+ $corrupt = self::swaprandbyte($output);
+ $this->assertFalse(Hash::verify($input, $corrupt, $key));
+ }
+ }
+
+ public function testVector()
+ {
+ $input = 'hello world';
+ $key = 'password';
+ $vector = base64_decode('dvvWMEFPCo9EV+l+htGGcoK5Uj8zrh6bfxCh16NOjJxuugObuidTQ3+R3qiyZLnHl7zRxSmfHRasEJQpTymZDw==');
+ $this->assertTrue(Hash::verify($input, $vector, $key));
+ }
+}
diff --git a/tests/Unit/Dcrypt/OtpTest.php b/tests/Unit/Dcrypt/OtpTest.php
new file mode 100644
index 0000000..38b910a
--- /dev/null
+++ b/tests/Unit/Dcrypt/OtpTest.php
@@ -0,0 +1,38 @@
+assertEquals(strlen($input), strlen($encrypted));
+ $this->assertNotEquals($input, $encrypted);
+
+ /*
+ * Test decryption
+ */
+ $decrypted = Otp::crypt($encrypted, $key);
+ $this->assertEquals($input, $decrypted);
+ }
+ }
+
+ public function testVector()
+ {
+ $input = 'hello world';
+ $pass = 'password';
+ $vector = base64_decode('Cf6ULwbiZEbJr1w=');
+
+ $this->assertEquals($input, Otp::crypt($vector, $pass));
+ }
+}
diff --git a/tests/Unit/Dcrypt/Pkcs7Test.php b/tests/Unit/Dcrypt/Pkcs7Test.php
new file mode 100644
index 0000000..6f8ed2e
--- /dev/null
+++ b/tests/Unit/Dcrypt/Pkcs7Test.php
@@ -0,0 +1,19 @@
+assertEquals(Pkcs7::pad('aaaabbbb', 3), "aaaabbbb\x01");
+
+ $this->assertEquals(Pkcs7::pad('aaaabbbb', 4), "aaaabbbb\x04\x04\x04\x04");
+
+ $this->assertEquals(Pkcs7::unpad("aaaabbbb\x01"), "aaaabbbb");
+
+ $this->assertEquals(Pkcs7::unpad("aaaabbbb\x04\x04\x04\x04"), "aaaabbbb");
+ }
+}
diff --git a/tests/Unit/Dcrypt/Rc4Test.php b/tests/Unit/Dcrypt/Rc4Test.php
new file mode 100644
index 0000000..eb1dd49
--- /dev/null
+++ b/tests/Unit/Dcrypt/Rc4Test.php
@@ -0,0 +1,39 @@
+assertEquals(strlen($input), strlen($encrypted));
+ $this->assertNotEquals($input, $encrypted);
+
+ /*
+ * Test decryption
+ */
+ $decrypted = Rc4::crypt($encrypted, $key);
+ $this->assertEquals($input, $decrypted);
+ }
+
+ public function testVector()
+ {
+ /*
+ * Test that known cypher text decrypts properly
+ */
+ $cyphertext = hex2bin('140ad3d278a229ff3c487d');
+ $plain = 'Hello World';
+ $key = 'asdf';
+
+ $this->assertEquals($plain, Rc4::crypt($cyphertext, $key));
+ }
+}
diff --git a/tests/Unit/Dcrypt/SpritzTest.php b/tests/Unit/Dcrypt/SpritzTest.php
new file mode 100644
index 0000000..d2bc421
--- /dev/null
+++ b/tests/Unit/Dcrypt/SpritzTest.php
@@ -0,0 +1,27 @@
+assertEquals(strlen($input), strlen($encrypted));
+ $this->assertNotEquals($input, $encrypted);
+
+ /*
+ * Test decryption
+ */
+ $decrypted = Spritz::crypt($encrypted, $key);
+ $this->assertEquals($input, $decrypted);
+ }
+}
diff --git a/tests/Unit/Dcrypt/StrTest.php b/tests/Unit/Dcrypt/StrTest.php
new file mode 100644
index 0000000..19022e0
--- /dev/null
+++ b/tests/Unit/Dcrypt/StrTest.php
@@ -0,0 +1,19 @@
+assertTrue(Str::equal('2222', '2222', true));
+ $this->assertFalse(Str::equal('2222', '3333', true));
+
+ // Test without hash_equals
+ $this->assertTrue(Str::equal('2222', '2222', false));
+ $this->assertFalse(Str::equal('2222', '3333', false));
+ }
+}
diff --git a/tests/Unit/Dcrypt/SwapTest.php b/tests/Unit/Dcrypt/SwapTest.php
new file mode 100644
index 0000000..8267ea3
--- /dev/null
+++ b/tests/Unit/Dcrypt/SwapTest.php
@@ -0,0 +1,15 @@
+assertFalse($orig === self::swaprandbyte($orig));
+ $this->assertEquals(levenshtein($orig, self::swaprandbyte($orig)), 1);
+ }
+ }
+}
diff --git a/tests/Unit/Dcrypt/TestSupport.php b/tests/Unit/Dcrypt/TestSupport.php
new file mode 100644
index 0000000..dcf1c8f
--- /dev/null
+++ b/tests/Unit/Dcrypt/TestSupport.php
@@ -0,0 +1,30 @@
+container = new Container();
+ $this->container->set(FooFacade::accessor(), new Foo());
+ }
+
+ /** @test */
+ public function can_initiate_facades()
+ {
+ FacadeFactory::setContainer($this->container);
+ $this->assertInstanceOf(ContainerInterface::class, FacadeFactory::getContainer());
+ }
+
+ /** @test */
+ public function can_get_facade_instance()
+ {
+ FacadeFactory::setContainer($this->container);
+
+ $instance = FooFacade::__instance();
+
+ $this->assertInstanceOf(FooInterface::class, $instance);
+ $this->assertInstanceOf(Foo::class, $instance);
+ }
+
+ /** @test */
+ public function can_swap_instances()
+ {
+ FacadeFactory::setContainer($this->container);
+
+ $instance = FooFacade::__instance();
+
+ $this->assertInstanceOf(FooInterface::class, $instance);
+ $this->assertInstanceOf(Foo::class, $instance);
+
+ $this->container->set(FooFacade::accessor(), 'bar');
+
+ $instance = FooFacade::__instance();
+
+ $this->assertEquals('bar', $instance);
+ }
+
+ public function can_call_functions()
+ {
+ FacadeFactory::setContainer($this->container);
+
+ $this->assertEquals('bar', forward_static_call([FooFacade::class, 'foo']));
+ $this->assertEquals('bar', call_user_func('FooFacade::class::foo'));
+ $this->assertEquals('bar', FooFacade::foo());
+ }
+}
diff --git a/tests/Unit/Facades/LogTest.php b/tests/Unit/Facades/LogTest.php
index 4ae6019..9105726 100644
--- a/tests/Unit/Facades/LogTest.php
+++ b/tests/Unit/Facades/LogTest.php
@@ -2,10 +2,10 @@
namespace Rareloop\Lumberjack\Test\Facades;
-use Blast\Facades\FacadeFactory;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use Rareloop\Lumberjack\Application;
+use Rareloop\Lumberjack\FacadeFactory;
use Rareloop\Lumberjack\Facades\Log as LogFacade;
class LogTest extends TestCase
diff --git a/tests/Unit/Facades/MiddlewareAliasesTest.php b/tests/Unit/Facades/MiddlewareAliasesTest.php
index 5cf4da6..cd847a0 100644
--- a/tests/Unit/Facades/MiddlewareAliasesTest.php
+++ b/tests/Unit/Facades/MiddlewareAliasesTest.php
@@ -2,9 +2,9 @@
namespace Rareloop\Lumberjack\Test\Facades;
-use Blast\Facades\FacadeFactory;
use PHPUnit\Framework\TestCase;
use Rareloop\Lumberjack\Application;
+use Rareloop\Lumberjack\FacadeFactory;
use Rareloop\Lumberjack\Facades\MiddlewareAliases;
use Rareloop\Lumberjack\Http\MiddlewareAliasStore;
diff --git a/tests/Unit/Facades/RouterTest.php b/tests/Unit/Facades/RouterTest.php
index 71ee82a..ebc39f6 100644
--- a/tests/Unit/Facades/RouterTest.php
+++ b/tests/Unit/Facades/RouterTest.php
@@ -2,9 +2,9 @@
namespace Rareloop\Lumberjack\Test\Facades;
-use Blast\Facades\FacadeFactory;
use PHPUnit\Framework\TestCase;
use Rareloop\Lumberjack\Application;
+use Rareloop\Lumberjack\FacadeFactory;
use Rareloop\Lumberjack\Facades\Router as RouterFacade;
use Rareloop\Lumberjack\Http\Router;
diff --git a/tests/Unit/Facades/SessionTest.php b/tests/Unit/Facades/SessionTest.php
index af7103e..95c7c3f 100644
--- a/tests/Unit/Facades/SessionTest.php
+++ b/tests/Unit/Facades/SessionTest.php
@@ -2,9 +2,9 @@
namespace Rareloop\Lumberjack\Test\Facades;
-use Blast\Facades\FacadeFactory;
use PHPUnit\Framework\TestCase;
use Rareloop\Lumberjack\Application;
+use Rareloop\Lumberjack\FacadeFactory;
use Rareloop\Lumberjack\Facades\Session;
use Rareloop\Lumberjack\Session\SessionManager;
use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler;
diff --git a/tests/Unit/Facades/Stubs/Foo.php b/tests/Unit/Facades/Stubs/Foo.php
new file mode 100644
index 0000000..d5ba726
--- /dev/null
+++ b/tests/Unit/Facades/Stubs/Foo.php
@@ -0,0 +1,11 @@
+once()->andReturn(false);
- $app = new Application(__DIR__.'/../');
+ $app = new Application(__DIR__ . '/../');
$lumberjack = new Lumberjack($app);
$lumberjack->bootstrap();
@@ -37,7 +38,7 @@ public function log_object_is_always_registered()
* @codingStandardsIgnoreLine */
function default_handler_is_in_memory_stream()
{
- $app = new Application(__DIR__.'/../');
+ $app = new Application(__DIR__ . '/../');
$config = new Config;
$app->bind('config', $config);
@@ -52,7 +53,7 @@ function default_handler_is_in_memory_stream()
/** @test */
public function default_log_warning_level_is_debug()
{
- $app = new Application(__DIR__.'/../');
+ $app = new Application(__DIR__ . '/../');
$config = new Config;
$app->bind('config', $config);
@@ -61,13 +62,13 @@ public function default_log_warning_level_is_debug()
RegisterProviders::class,
]);
- $this->assertSame(Logger::DEBUG, $app->get('logger')->getHandlers()[0]->getLevel());
+ $this->assertSame(Level::Debug, $app->get('logger')->getHandlers()[0]->getLevel());
}
/** @test */
public function stream_is_used_when_path_is_set_but_logging_is_disabled()
{
- $app = new Application(__DIR__.'/../');
+ $app = new Application(__DIR__ . '/../');
$config = new Config;
$config->set('app.logs.enabled', false);
@@ -84,17 +85,17 @@ public function stream_is_used_when_path_is_set_but_logging_is_disabled()
/** @test */
public function log_warning_level_can_be_set_in_config()
{
- $app = new Application(__DIR__.'/../');
+ $app = new Application(__DIR__ . '/../');
$config = new Config;
- $config->set('app.logs.level', Logger::ERROR);
+ $config->set('app.logs.level', Level::Error);
$app->bind('config', $config);
$app->bootstrapWith([
RegisterProviders::class,
]);
- $this->assertSame(Logger::ERROR, $app->get('logger')->getHandlers()[0]->getLevel());
+ $this->assertSame(Level::Error, $app->get('logger')->getHandlers()[0]->getLevel());
}
/** @test */
diff --git a/tests/Unit/Providers/RouterServiceProviderTest.php b/tests/Unit/Providers/RouterServiceProviderTest.php
index 780f363..5f7b813 100644
--- a/tests/Unit/Providers/RouterServiceProviderTest.php
+++ b/tests/Unit/Providers/RouterServiceProviderTest.php
@@ -18,10 +18,10 @@
use Rareloop\Lumberjack\Providers\RouterServiceProvider;
use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration;
use Rareloop\Router\MiddlewareResolver;
-use Zend\Diactoros\Request;
-use Zend\Diactoros\Response\HtmlResponse;
-use Zend\Diactoros\Response\TextResponse;
-use Zend\Diactoros\ServerRequest;
+use Laminas\Diactoros\Request;
+use Laminas\Diactoros\Response\HtmlResponse;
+use Laminas\Diactoros\Response\TextResponse;
+use Laminas\Diactoros\ServerRequest;
class RouterServiceProviderTest extends TestCase
{
@@ -79,8 +79,7 @@ public function configured_router_can_resolve_middleware_aliases()
$store->set('middleware-key', new RSPAddHeaderMiddleware('X-Key', 'abc'));
$request = new ServerRequest([], [], '/test/123', 'GET');
- $router->get('/test/123', function () {
- })->middleware('middleware-key');
+ $router->get('/test/123', function () {})->middleware('middleware-key');
$response = $router->match($request);
$this->assertTrue($response->hasHeader('X-Key'));
diff --git a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php
index f588181..713c84d 100644
--- a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php
+++ b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php
@@ -22,8 +22,8 @@
use Rareloop\Lumberjack\Providers\WordPressControllersServiceProvider;
use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration;
use Rareloop\Router\Responsable;
-use Zend\Diactoros\Response\TextResponse;
-use Zend\Diactoros\ServerRequest;
+use Laminas\Diactoros\Response\TextResponse;
+use Laminas\Diactoros\ServerRequest;
use \Mockery;
use Rareloop\Lumberjack\Http\Middleware\PasswordProtected;
diff --git a/tests/Unit/Providers/includes/single.php b/tests/Unit/Providers/includes/single.php
index e69de29..b3d9bbc 100644
--- a/tests/Unit/Providers/includes/single.php
+++ b/tests/Unit/Providers/includes/single.php
@@ -0,0 +1 @@
+shouldReceive('read')->andReturn($previousSessionValue);
+ $handler->shouldReceive('read')->andReturn(@serialize(['foo' => 'bar']));
$errorHandler = Mockery::mock(HandlerInterface::class);
$errorHandler->shouldReceive('report')->once();
@@ -73,11 +72,40 @@ public function unexpected_session_data_is_handled_gracefully($previousSessionVa
$this->assertSame(null, $store->get('foo'));
}
- public function unexpectedSessionData()
+ /**
+ * @test
+ */
+ public function gracefully_handle_case_with_no_exception_handler()
{
- return [
- [@serialize(['foo' => 'bar'])],
- [''],
- ];
+ $encryptionKey = 'encryption-key';
+
+ // Use a mock handler to fake a previously stored state
+ $handler = Mockery::mock(NullSessionHandler::class . '[read]');
+ $handler->shouldReceive('read')->andReturn(@serialize(['foo' => 'bar']));
+
+ $store = new EncryptedStore('session-name', $handler, new Encrypter($encryptionKey), 'session-id');
+ $store->start();
+
+ $this->assertSame(null, $store->get('foo'));
+ }
+
+ /**
+ * @test
+ */
+ public function empty_session_data_is_ignored()
+ {
+ $encryptionKey = 'encryption-key';
+
+ // Use a mock handler to fake a previously stored state
+ $handler = Mockery::mock(NullSessionHandler::class . '[read]');
+ $handler->shouldReceive('read')->andReturn('');
+
+ $errorHandler = Mockery::mock(HandlerInterface::class);
+ $errorHandler->shouldNotHaveReceived('report');
+
+ $store = new EncryptedStore('session-name', $handler, new Encrypter($encryptionKey), 'session-id', $errorHandler);
+ $store->start();
+
+ $this->assertSame(null, $store->get('foo'));
}
}
diff --git a/tests/Unit/Session/SessionManagerTest.php b/tests/Unit/Session/SessionManagerTest.php
index c46a2c9..f1e34aa 100644
--- a/tests/Unit/Session/SessionManagerTest.php
+++ b/tests/Unit/Session/SessionManagerTest.php
@@ -3,17 +3,20 @@
namespace Rareloop\Lumberjack\Test;
use Mockery;
+use ReflectionClass;
+use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\TestCase;
-use Rareloop\Lumberjack\Application;
use Rareloop\Lumberjack\Config;
-use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract;
use Rareloop\Lumberjack\Encrypter;
+use Rareloop\Lumberjack\Application;
+use Rareloop\Lumberjack\Session\Store;
+use Rareloop\Lumberjack\Exceptions\Handler;
use Rareloop\Lumberjack\Session\EncryptedStore;
-use Rareloop\Lumberjack\Session\FileSessionHandler;
use Rareloop\Lumberjack\Session\SessionManager;
-use Rareloop\Lumberjack\Session\Store;
+use Rareloop\Lumberjack\Session\FileSessionHandler;
+use Rareloop\Lumberjack\Exceptions\HandlerInterface;
use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler;
-use org\bovigo\vfs\vfsStream;
+use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract;
class SessionManagerTest extends TestCase
{
@@ -93,12 +96,22 @@ public function can_create_an_unencrypted_store()
/** @test */
public function can_create_an_encrypted_store()
{
- $app = $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true);
+ $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true);
$app->bind(EncrypterContract::class, new Encrypter('encryption-key'));
+ $handler = Mockery::mock(Handler::class);
+ $app->bind(HandlerInterface::class, $handler);
+
$manager = new SessionManager($app);
- $this->assertInstanceOf(EncryptedStore::class, $manager->driver());
+ $driver = $manager->driver();
+ $this->assertInstanceOf(EncryptedStore::class, $driver);
+
+ $reflection = new ReflectionClass($driver);
+ $property = $reflection->getProperty('exceptionHandler');
+ $property->setAccessible(true);
+
+ $this->assertInstanceOf(HandlerInterface::class, $property->getValue($driver));
}
}