Skip to content

Commit

Permalink
Merge branch 'develop' into extend-validation
Browse files Browse the repository at this point in the history
  • Loading branch information
mjauvin committed Oct 15, 2023
2 parents 72176b6 + de146af commit 9a51c15
Show file tree
Hide file tree
Showing 37 changed files with 1,459 additions and 243 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4

[.github/workflows/**.{yml,yaml}]
indent_size = 2
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
max-parallel: 6
matrix:
operatingSystem: [ubuntu-latest, windows-latest]
phpVersion: ['8.0', '8.1']
phpVersion: ['8.0', '8.1', '8.2']
fail-fast: false
runs-on: ${{ matrix.operatingSystem }}
name: ${{ matrix.operatingSystem }} / PHP ${{ matrix.phpVersion }}
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@

"assetic/framework": "~3.0",
"doctrine/dbal": "^2.6",
"enshrined/svg-sanitize": "^0.15",
"enshrined/svg-sanitize": "~0.16",
"laravel/framework": "^9.1",
"laravel/tinker": "^2.7",
"league/csv": "~9.1",
"nesbot/carbon": "^2.0",
"nikic/php-parser": "^4.10",
"scssphp/scssphp": "~1.0",
"symfony/console": ">=6.0.9 <6.3.0",
"symfony/yaml": "^6.0",
"twig/twig": "~3.0",
"wikimedia/less.php": "~3.0",
Expand Down
35 changes: 0 additions & 35 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@ parameters:
count: 1
path: src/Auth/Models/Group.php

-
message: "#^Call to an undefined method Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\:\\:afterValidate\\(\\)\\.$#"
count: 1
path: src/Auth/Models/Group.php

-
message: "#^Call to an undefined method Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\:\\:beforeValidate\\(\\)\\.$#"
count: 1
path: src/Auth/Models/Group.php

-
message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)\\:\\:getOriginalEncryptableValues\\(\\)\\.$#"
count: 1
Expand All @@ -30,31 +20,11 @@ parameters:
count: 1
path: src/Auth/Models/Role.php

-
message: "#^Call to an undefined method Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\:\\:afterValidate\\(\\)\\.$#"
count: 1
path: src/Auth/Models/Role.php

-
message: "#^Call to an undefined method Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\:\\:beforeValidate\\(\\)\\.$#"
count: 1
path: src/Auth/Models/Role.php

-
message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\User\\)\\:\\:getOriginalEncryptableValues\\(\\)\\.$#"
count: 1
path: src/Auth/Models/User.php

-
message: "#^Call to an undefined method Winter\\\\Storm\\\\Auth\\\\Models\\\\User\\:\\:afterValidate\\(\\)\\.$#"
count: 1
path: src/Auth/Models/User.php

-
message: "#^Call to an undefined method Winter\\\\Storm\\\\Auth\\\\Models\\\\User\\:\\:beforeValidate\\(\\)\\.$#"
count: 1
path: src/Auth/Models/User.php

-
message: "#^Parameter \\#1 \\$app of class Illuminate\\\\Database\\\\DatabaseManager constructor expects Illuminate\\\\Contracts\\\\Foundation\\\\Application, Illuminate\\\\Contracts\\\\Container\\\\Container given\\.$#"
count: 1
Expand Down Expand Up @@ -755,11 +725,6 @@ parameters:
count: 1
path: src/Extension/Extendable.php

-
message: "#^Conditional return type uses subject type TCacheValue which is not part of PHPDoc @template tags\\.$#"
count: 1
path: src/Halcyon/MemoryRepository.php

-
message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue\\:\\:queue\\(\\)$#"
count: 1
Expand Down
5 changes: 5 additions & 0 deletions src/Console/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ abstract class Command extends BaseCommand implements SignalableCommandInterface
use Traits\HandlesCleanup;
use Traits\ProvidesAutocompletion;

/**
* @var \Winter\Storm\Foundation\Application
*/
protected $laravel;

/**
* @var array List of commands that this command replaces (aliases)
*/
Expand Down
20 changes: 14 additions & 6 deletions src/Database/Attach/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Winter\Storm\Exception\ApplicationException;
use Winter\Storm\Network\Http;
use Winter\Storm\Support\Facades\File as FileHelper;
use Winter\Storm\Support\Svg;

/**
* File attachment model
Expand Down Expand Up @@ -834,31 +835,38 @@ protected function putFile($sourcePath, $destinationFileName = null)
$destinationFileName = $this->disk_name;
}

$destinationPath = $this->getStorageDirectory() . $this->getPartitionDirectory();
$destinationFolder = $this->getStorageDirectory() . $this->getPartitionDirectory();
$destinationPath = $destinationFolder . $destinationFileName;

// Filter SVG files
if (pathinfo($destinationPath, PATHINFO_EXTENSION) === 'svg') {
file_put_contents($sourcePath, Svg::extract($sourcePath));
}

if (!$this->isLocalStorage()) {
return $this->copyLocalToStorage($sourcePath, $destinationPath . $destinationFileName);
return $this->copyLocalToStorage($sourcePath, $destinationPath);
}

/*
* Using local storage, tack on the root path and work locally
* this will ensure the correct permissions are used.
*/
$destinationPath = $this->getLocalRootPath() . '/' . $destinationPath;
$destinationFolder = $this->getLocalRootPath() . '/' . $destinationFolder;
$destinationPath = $destinationFolder . $destinationFileName;

/*
* Verify the directory exists, if not try to create it. If creation fails
* because the directory was created by a concurrent process then proceed,
* otherwise trigger the error.
*/
if (
!FileHelper::isDirectory($destinationPath) &&
!FileHelper::makeDirectory($destinationPath, 0777, true, true)
!FileHelper::isDirectory($destinationFolder) &&
!FileHelper::makeDirectory($destinationFolder, 0777, true, true)
) {
trigger_error(error_get_last()['message'], E_USER_WARNING);
}

return FileHelper::copy($sourcePath, $destinationPath . $destinationFileName);
return FileHelper::copy($sourcePath, $destinationPath);
}

/**
Expand Down
158 changes: 158 additions & 0 deletions src/Database/Behaviors/Encryptable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php namespace Winter\Storm\Database\Behaviors;

use App;
use Illuminate\Contracts\Encryption\Encrypter;
use Winter\Storm\Database\Model;
use Winter\Storm\Exception\ApplicationException;
use Winter\Storm\Extension\ExtensionBase;

/**
* Encryptable model behavior
*
* Usage:
*
* In the model class definition:
*
* public $implement = [
* \Winter\Storm\Database\Behaviors\Encryptable::class,
* ];
*
* /**
* * List of attributes to encrypt.
* * /
* protected array $encryptable = ['api_key', 'api_secret'];
*
* Dynamically attached to third party model:
*
* TargetModel::extend(function ($model) {
* $model->addDynamicProperty('encryptable', ['encrypt_this']);
* $model->extendClassWith(\Winter\Storm\Database\Behaviors\Encryptable::class);
* });
*
* >**NOTE**: Encrypted attributes will be serialized and unserialized
* as a part of the encryption / decryption process. Do not make an
* attribute that is encryptable also jsonable at the same time as the
* jsonable process will attempt to decode a value that has already been
* unserialized by the encrypter.
*
*/
class Encryptable extends ExtensionBase
{
protected Model $model;

/**
* List of attribute names which should be encrypted
*
* protected array $encryptable = [];
*/

/**
* Encrypter instance.
*/
protected ?Encrypter $encrypterInstance = null;

/**
* List of original attribute values before they were encrypted.
*/
protected array $originalEncryptableValues = [];

public function __construct($parent)
{
$this->model = $parent;
$this->bootEncryptable();
}

/**
* Boot the encryptable trait for a model.
*/
public function bootEncryptable(): void
{
$isEncryptable = $this->model->extend(function () {
/** @var Model $this */
return $this->propertyExists('encryptable');
});

if (!$isEncryptable) {
throw new ApplicationException(sprintf(
'You must define an $encryptable property on the %s class to use the Encryptable behavior.',
get_class($this->model)
));
}

/*
* Encrypt required fields when necessary
*/
$this->model->bindEvent('model.beforeSetAttribute', function ($key, $value) {
if (in_array($key, $this->getEncryptableAttributes()) && !is_null($value)) {
return $this->makeEncryptableValue($key, $value);
}
});
$this->model->bindEvent('model.beforeGetAttribute', function ($key) {
if (in_array($key, $this->getEncryptableAttributes()) && array_get($this->model->attributes, $key) != null) {
return $this->getEncryptableValue($key);
}
});
}

/**
* Encrypts an attribute value and saves it in the original locker.
*/
public function makeEncryptableValue(string $key, mixed $value): string
{
$this->originalEncryptableValues[$key] = $value;
return $this->getEncrypter()->encrypt($value);
}

/**
* Decrypts an attribute value
*/
public function getEncryptableValue(string $key): mixed
{
$attributes = $this->model->getAttributes();
return isset($attributes[$key])
? $this->getEncrypter()->decrypt($attributes[$key])
: null;
}

/**
* Returns a collection of fields that will be encrypted.
*/
public function getEncryptableAttributes(): array
{
return $this->model->extend(function () {
return $this->encryptable ?? [];
});
}

/**
* Returns the original values of any encrypted attributes.
*/
public function getOriginalEncryptableValues(): array
{
return $this->originalEncryptableValues;
}

/**
* Returns the original values of any encrypted attributes.
*/
public function getOriginalEncryptableValue(string $attribute): mixed
{
return array_get($this->originalEncryptableValues, $attribute, null);
}

/**
* Provides the encrypter instance.
*/
public function getEncrypter(): Encrypter
{
return (!is_null($this->encrypterInstance)) ? $this->encrypterInstance : App::make('encrypter');
}

/**
* Sets the encrypter instance.
*/
public function setEncrypter(Encrypter $encrypter): void
{
$this->encrypterInstance = $encrypter;
}
}
2 changes: 2 additions & 0 deletions src/Database/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ public function upsert(array $values, $uniqueBy, $update = null)
return 0;
}

$this->clearDuplicateCache();

if (!is_array(reset($values))) {
$values = [$values];
}
Expand Down
Loading

0 comments on commit 9a51c15

Please sign in to comment.