diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml
new file mode 100644
index 000000000..04c516d82
--- /dev/null
+++ b/.github/workflows/code-analysis.yaml
@@ -0,0 +1,63 @@
+name: Tests
+
+on:
+ push:
+ branches:
+ - "wip/1.2"
+ pull_request:
+ branches:
+ - "wip/1.2"
+
+jobs:
+ codeAnalysis:
+ runs-on: ubuntu-latest
+ name: Code Analysis
+ env:
+ extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
+ key: winter-storm-cache-v1.2
+ steps:
+ - name: Cancel previous incomplete runs
+ uses: styfle/cancel-workflow-action@0.8.0
+ with:
+ access_token: ${{ github.token }}
+
+ - name: Checkout changes
+ uses: actions/checkout@v2
+
+ - name: Setup extension cache
+ id: extcache
+ uses: shivammathur/cache-extensions@v1
+ with:
+ php-version: '8.0'
+ extensions: ${{ env.extensions }}
+ key: ${{ env.key }}
+
+ - name: Cache extensions
+ uses: actions/cache@v2
+ with:
+ path: ${{ steps.extcache.outputs.dir }}
+ key: ${{ steps.extcache.outputs.key }}
+ restore-keys: ${{ steps.extcache.outputs.key }}
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.0'
+ extensions: ${{ env.extensions }}
+
+ - name: Setup dependency cache
+ id: composercache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache dependencies
+ uses: actions/cache@v2
+ with:
+ path: ${{ steps.composercache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install Composer dependencies
+ run: composer install --no-interaction --no-progress --no-scripts
+
+ - name: Analyse code
+ run: ./vendor/bin/phpstan analyse
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 72810423a..59d2e7f9d 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -22,6 +22,11 @@ jobs:
extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
key: winter-storm-cache-v1.2
steps:
+ - name: Cancel previous incomplete runs
+ uses: styfle/cancel-workflow-action@0.8.0
+ with:
+ access_token: ${{ github.token }}
+
- name: Checkout changes
uses: actions/checkout@v2
@@ -44,7 +49,6 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.phpVersion }}
- tools: composer:v2
extensions: ${{ env.extensions }}
- name: Setup dependency cache
diff --git a/composer.json b/composer.json
index 00ac1f4a8..163a5a278 100644
--- a/composer.json
+++ b/composer.json
@@ -56,6 +56,7 @@
"php-parallel-lint/php-parallel-lint": "^1.0",
"meyfa/phpunit-assert-gd": "^2.0.0|^3.0.0",
"dms/phpunit-arraysubset-asserts": "^0.1.0|^0.2.1",
+ "nunomaduro/larastan": "^2.0.1",
"orchestra/testbench": "^7.1.0"
},
"suggest": {
@@ -83,7 +84,10 @@
"classmap": [
"tests/TestCase.php",
"tests/DbTestCase.php"
- ]
+ ],
+ "psr-4": {
+ "Winter\\Storm\\Tests\\": "tests/"
+ }
},
"scripts": {
"test": [
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index f7d16d946..48ed245f7 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -9,12 +9,13 @@
+
+
+
-
+
*/src/Auth/Migrations/*\.php
*/src/Database/Migrations/*\.php
*/tests/*
@@ -28,6 +29,9 @@
*/tests/*
+
+
+
src/
tests/
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 000000000..fd99a24a5
--- /dev/null
+++ b/phpstan-baseline.neon
@@ -0,0 +1,801 @@
+parameters:
+ ignoreErrors:
+ -
+ message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)\\:\\:getOriginalEncryptableValues\\(\\)\\.$#"
+ count: 1
+ path: src/Auth/Models/Group.php
+
+ -
+ message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)\\:\\:getOriginalHashValues\\(\\)\\.$#"
+ 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
+ path: src/Auth/Models/Role.php
+
+ -
+ message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)\\:\\:getOriginalHashValues\\(\\)\\.$#"
+ 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 \\$disk of static method Winter\\\\Storm\\\\Filesystem\\\\Filesystem\\:\\:isLocalDisk\\(\\) expects Illuminate\\\\Filesystem\\\\FilesystemAdapter, Illuminate\\\\Contracts\\\\Filesystem\\\\Filesystem given\\.$#"
+ count: 1
+ path: src/Database/Attach/File.php
+
+ -
+ message: "#^Access to an undefined property Winter\\\\Storm\\\\Database\\\\Model\\:\\:\\$purgeable\\.$#"
+ count: 4
+ path: src/Database/Behaviors/Purgeable.php
+
+ -
+ message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Model\\:\\:purgeAttributes\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Behaviors/Purgeable.php
+
+ -
+ message: "#^Parameter \\#1 \\$app of class Illuminate\\\\Database\\\\DatabaseManager constructor expects Illuminate\\\\Contracts\\\\Foundation\\\\Application, Illuminate\\\\Contracts\\\\Container\\\\Container given\\.$#"
+ count: 1
+ path: src/Database/Capsule/Manager.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface\\:\\:getName\\(\\)\\.$#"
+ count: 1
+ path: src/Database/MemoryCache.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$is_bind\\.$#"
+ count: 1
+ path: src/Database/Model.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$master_field\\.$#"
+ count: 1
+ path: src/Database/Model.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$pivot_data\\.$#"
+ count: 1
+ path: src/Database/Model.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$slave_id\\.$#"
+ count: 1
+ path: src/Database/Model.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$slave_type\\.$#"
+ count: 1
+ path: src/Database/Model.php
+
+ -
+ message: "#^Method Winter\\\\Storm\\\\Database\\\\Model\\:\\:getDeferredBindingRecords\\(\\) should return Winter\\\\Storm\\\\Database\\\\Collection but returns Illuminate\\\\Database\\\\Eloquent\\\\Collection\\\\.$#"
+ count: 1
+ path: src/Database/Model.php
+
+ -
+ message: "#^Return type \\(Winter\\\\Storm\\\\Database\\\\Pivot\\) of method Winter\\\\Storm\\\\Database\\\\Model\\:\\:newPivot\\(\\) should be compatible with return type \\(Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Pivot\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:newPivot\\(\\)$#"
+ count: 1
+ path: src/Database/Model.php
+
+ -
+ message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Model\\:\\:errors\\(\\)\\.$#"
+ count: 1
+ path: src/Database/ModelException.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:deleteCancel\\(\\)\\.$#"
+ count: 2
+ path: src/Database/Models/DeferredBinding.php
+
+ -
+ message: "#^Parameter \\#1 \\$haystack of function str_contains expects string, int given\\.$#"
+ count: 1
+ path: src/Database/MorphPivot.php
+
+ -
+ message: "#^Parameter \\#1 \\$ids of method Winter\\\\Storm\\\\Database\\\\Pivot\\:\\:newQueryForRestoration\\(\\) expects array\\\\|string, int given\\.$#"
+ count: 1
+ path: src/Database/MorphPivot.php
+
+ -
+ message: "#^Parameter \\#2 \\$string of function explode expects string, int given\\.$#"
+ count: 1
+ path: src/Database/MorphPivot.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getLeftColumnName\\(\\)\\.$#"
+ count: 1
+ path: src/Database/NestedTreeScope.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#"
+ count: 2
+ path: src/Database/Query/Grammars/MySqlGrammar.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#"
+ count: 2
+ path: src/Database/Query/Grammars/PostgresGrammar.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#"
+ count: 2
+ path: src/Database/Query/Grammars/SQLiteGrammar.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#"
+ count: 2
+ path: src/Database/Query/Grammars/SqlServerGrammar.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface\\:\\:getName\\(\\)\\.$#"
+ count: 1
+ path: src/Database/QueryBuilder.php
+
+ -
+ message: "#^Property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$orders \\(array\\) does not accept null\\.$#"
+ count: 1
+ path: src/Database/QueryBuilder.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/AttachMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachMany.php
+
+ -
+ message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachMany\\:\\:getRelationExistenceQueryForSelfJoin\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachMany.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachOne\\:\\:getRelationExistenceQueryForSelfJoin\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Call to private method delete\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne\\\\.$#"
+ count: 3
+ path: src/Database/Relations/AttachOne.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindEventOnce\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getRelationDefinition\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsTo.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$sessionKey\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindDeferred\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindEventOnce\\(\\)\\.$#"
+ count: 2
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:fireEvent\\(\\)\\.$#"
+ count: 4
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getRelationDefinition\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:newRelationPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:reloadRelations\\(\\)\\.$#"
+ count: 2
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:unbindDeferred\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:lists\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:flushDuplicateCache\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects array, int\\|null given\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$columns \\(array\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects string, array given\\.$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#"
+ count: 1
+ path: src/Database/Relations/BelongsToMany.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Call to private method whereNotIn\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^If condition is always true\\.$#"
+ count: 1
+ path: src/Database/Relations/HasMany.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/HasManyThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/HasManyThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasManyThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasManyThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasManyThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasManyThrough.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/HasOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOne.php
+
+ -
+ message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasOne\\\\.$#"
+ count: 2
+ path: src/Database/Relations/HasOne.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOneThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/HasOneThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOneThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOneThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOneThrough.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/HasOneThrough.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphMany\\\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Call to private method whereNotIn\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphMany\\\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^If condition is always true\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphMany.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/MorphOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphOne.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphOne.php
+
+ -
+ message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne\\\\.$#"
+ count: 2
+ path: src/Database/Relations/MorphOne.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindEventOnce\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/MorphTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphTo.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphTo.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#"
+ count: 3
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:lists\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:flushDuplicateCache\\(\\)\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects array, int\\|null given\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$columns \\(array\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects string, array given\\.$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#"
+ count: 1
+ path: src/Database/Relations/MorphToMany.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getSortOrderColumn\\(\\)\\.$#"
+ count: 1
+ path: src/Database/SortableScope.php
+
+ -
+ message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$children\\.$#"
+ count: 1
+ path: src/Database/TreeCollection.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getParentId\\(\\)\\.$#"
+ count: 1
+ path: src/Database/TreeCollection.php
+
+ -
+ message: "#^Winter\\\\Storm\\\\Extension\\\\Extendable\\:\\:extendableCall\\(\\) calls parent\\:\\:__call\\(\\) but Winter\\\\Storm\\\\Extension\\\\Extendable does not extend any class\\.$#"
+ count: 1
+ path: src/Extension/Extendable.php
+
+ -
+ message: "#^Winter\\\\Storm\\\\Extension\\\\Extendable\\:\\:extendableGet\\(\\) calls parent\\:\\:__get\\(\\) but Winter\\\\Storm\\\\Extension\\\\Extendable does not extend any class\\.$#"
+ count: 1
+ path: src/Extension/Extendable.php
+
+ -
+ message: "#^Winter\\\\Storm\\\\Extension\\\\Extendable\\:\\:extendableSet\\(\\) calls parent\\:\\:__set\\(\\) but Winter\\\\Storm\\\\Extension\\\\Extendable does not extend any class\\.$#"
+ count: 1
+ path: src/Extension/Extendable.php
+
+ -
+ message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Foundation\\\\Application\\:\\:getCachedClassesPath\\(\\)\\.$#"
+ count: 1
+ path: src/Foundation/Console/ClearCompiledCommand.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
+ path: src/Mail/Mailer.php
+
+ -
+ message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:queue\\(\\)$#"
+ count: 1
+ path: src/Mail/Mailer.php
+
+ -
+ message: "#^Parameter \\#2 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:queueOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:queueOn\\(\\)$#"
+ count: 1
+ path: src/Mail/Mailer.php
+
+ -
+ message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue\\:\\:later\\(\\)$#"
+ count: 1
+ path: src/Mail/Mailer.php
+
+ -
+ message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:later\\(\\)$#"
+ count: 1
+ path: src/Mail/Mailer.php
+
+ -
+ message: "#^Parameter \\#3 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:laterOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:laterOn\\(\\)$#"
+ count: 1
+ path: src/Mail/Mailer.php
+
+ -
+ message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue\\:\\:queue\\(\\)$#"
+ count: 1
+ path: src/Support/Testing/Fakes/MailFake.php
+
+ -
+ message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:queue\\(\\)$#"
+ count: 1
+ path: src/Support/Testing/Fakes/MailFake.php
+
+ -
+ message: "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer\\:\\:send\\(\\)$#"
+ count: 1
+ path: src/Support/Testing/Fakes/MailFake.php
+
+ -
+ message: "#^Call to function is_null\\(\\) with Closure will always evaluate to false\\.$#"
+ count: 1
+ path: src/Validation/Factory.php
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 000000000..d36bb09c3
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,15 @@
+includes:
+ - ./vendor/nunomaduro/larastan/extension.neon
+ - phpstan-baseline.neon
+
+parameters:
+ paths:
+ - src
+ level: 5
+ excludePaths:
+ # Exclude PHP Parser files
+ - src/Parse/PHP/ArrayFile.php
+ - src/Parse/PHP/ArrayPrinter.php
+ databaseMigrationsPath:
+ - src/Auth/Migrations
+ - src/Database/Migrations
diff --git a/src/Argon/Argon.php b/src/Argon/Argon.php
index 843485006..a4cff3666 100644
--- a/src/Argon/Argon.php
+++ b/src/Argon/Argon.php
@@ -1,11 +1,11 @@
createUserModel();
+ /** @var \Winter\Storm\Database\Builder */
$query = $model->newQuery();
$this->extendUserQuery($query);
@@ -139,6 +140,7 @@ public function register(array $credentials, $activate = false, $autoLogin = tru
// Prevents revalidation of the password field
// on subsequent saves to this model object
+ /** @phpstan-ignore-next-line */
$user->password = null;
if ($autoLogin) {
@@ -159,6 +161,7 @@ public function hasUser()
/**
* Sets the user
+ * @phpstan-param Models\User $user
*/
public function setUser(Authenticatable $user)
{
@@ -244,6 +247,7 @@ public function findUserByCredentials(array $credentials)
}
}
+ /** @var Models\User */
$user = $query->first();
if (!$this->validateUserModel($user)) {
throw new AuthenticationException('A user was not found with the given credentials.');
@@ -338,7 +342,10 @@ public function findThrottleByUserId($userId, $ipAddress = null)
});
}
- if (!$throttle = $query->first()) {
+ /** @var Models\Throttle|null */
+ $throttle = $query->first();
+
+ if (!$throttle) {
$throttle = $this->createThrottleModel();
$throttle->user_id = $userId;
if ($ipAddress) {
@@ -361,7 +368,7 @@ public function findThrottleByUserId($userId, $ipAddress = null)
* @param array $credentials The user login details
* @param bool $remember Store a non-expire cookie for the user
* @throws AuthenticationException If authentication fails
- * @return Models\User The successfully logged in user
+ * @return bool If authentication was successful
*/
public function attempt(array $credentials = [], $remember = false)
{
@@ -383,7 +390,7 @@ public function validate(array $credentials = [])
* Validate a user's credentials, method used internally.
*
* @param array $credentials
- * @return User
+ * @return Models\User|null
*/
protected function validateInternal(array $credentials = [])
{
@@ -413,7 +420,9 @@ protected function validateInternal(array $credentials = [])
/*
* If throttling is enabled, check they are not locked out first and foremost.
*/
- if ($this->useThrottle) {
+ $useThrottle = $this->useThrottle;
+
+ if ($useThrottle) {
$throttle = $this->findThrottleByLogin($credentials[$loginName], $this->ipAddress);
$throttle->check();
}
@@ -425,14 +434,15 @@ protected function validateInternal(array $credentials = [])
$user = $this->findUserByCredentials($credentials);
}
catch (AuthenticationException $ex) {
- if ($this->useThrottle) {
+ if ($useThrottle) {
$throttle->addLoginAttempt();
}
+ $user = null;
throw $ex;
}
- if ($this->useThrottle) {
+ if ($useThrottle) {
$throttle->clearLoginAttempts();
}
@@ -621,6 +631,7 @@ public function onceUsingId($id)
* Logs in the given user and sets properties
* in the session.
* @throws AuthenticationException If the user is not activated and $this->requireActivation = true
+ * @phpstan-param Models\User $user
*/
public function login(Authenticatable $user, $remember = true)
{
@@ -656,7 +667,7 @@ public function login(Authenticatable $user, $remember = true)
*
* @param mixed $id
* @param bool $remember
- * @return \Illuminate\Contracts\Auth\Authenticatable
+ * @return \Illuminate\Contracts\Auth\Authenticatable|false
*/
public function loginUsingId($id, $remember = false)
{
@@ -714,7 +725,7 @@ public function logout()
* Impersonates the given user and sets properties in the session but not the cookie.
*
* @param Models\User $impersonatee
- * @throws Exception If the current user is not permitted to impersonate the provided user
+ * @throws AuthorizationException If the current user is not permitted to impersonate the provided user
* @return void
*/
public function impersonate($impersonatee)
@@ -828,7 +839,7 @@ public function isImpersonator()
/**
* Get the original user doing the impersonation
*
- * @return mixed Returns the User model for the impersonator if able, false if not
+ * @return Models\User|false Returns the User model for the impersonator if able, `false` if not
*/
public function getImpersonator()
{
@@ -845,7 +856,10 @@ public function getImpersonator()
return $this->impersonator;
}
- return $this->impersonator = $this->createUserModel()->find($impersonatorId);
+ /** @var Models\User|false */
+ $impersonator = $this->createUserModel()->find($impersonatorId) ?? false;
+
+ return $this->impersonator = $impersonator;
}
/**
diff --git a/src/Auth/Migrations/2013_10_01_000001_Db_Users.php b/src/Auth/Migrations/2013_10_01_000001_Db_Users.php
index 2d957959b..b09769518 100644
--- a/src/Auth/Migrations/2013_10_01_000001_Db_Users.php
+++ b/src/Auth/Migrations/2013_10_01_000001_Db_Users.php
@@ -23,6 +23,7 @@ public function up()
$table->timestamp('activated_at')->nullable();
$table->timestamp('last_login')->nullable();
$table->integer('role_id')->unsigned()->nullable()->index();
+ $table->boolean('is_superuser')->default(0);
$table->timestamps();
});
}
diff --git a/src/Auth/Models/Group.php b/src/Auth/Models/Group.php
index 21d5e06cb..2c3da8fa9 100644
--- a/src/Auth/Models/Group.php
+++ b/src/Auth/Models/Group.php
@@ -4,6 +4,8 @@
/**
* Group model
+ *
+ * @method \Winter\Storm\Database\Relations\BelongsToMany users() Users relation.
*/
class Group extends Model
{
@@ -29,7 +31,7 @@ class Group extends Model
];
/**
- * @var array The attributes that aren't mass assignable.
+ * @var string[]|bool The attributes that aren't mass assignable.
*/
protected $guarded = [];
diff --git a/src/Auth/Models/Preferences.php b/src/Auth/Models/Preferences.php
index a24537789..8e21119e1 100644
--- a/src/Auth/Models/Preferences.php
+++ b/src/Auth/Models/Preferences.php
@@ -6,6 +6,15 @@
/**
* User Preferences model
+ *
+ * @property string|array|null $value Represents the value of the preference.
+ * @property string|null $namespace Represents the namespace of the preference.
+ * @property string|null $group Represents the group of the preference.
+ * @property string|null $item Represents the item name of the preference.
+ * @property int|null $user_id Represents the user ID that this preference belongs to.
+ *
+ * @method static \Winter\Storm\Database\QueryBuilder applyKeyAndUser($key, $user = null) Scope to find a setting record
+ * for the specified module (or plugin) name, setting name and user.
*/
class Preferences extends Model
{
@@ -26,7 +35,7 @@ class Preferences extends Model
protected $jsonable = ['value'];
/**
- * @var \Winter\Storm\Auth\Models\User A user who owns the preferences
+ * @var \Winter\Storm\Auth\Models\User|null A user who owns the preferences
*/
public $userContext;
@@ -38,6 +47,7 @@ class Preferences extends Model
public function resolveUser($user)
{
$user = Manager::instance()->getUser();
+
if (!$user) {
throw new AuthException('User is not logged in');
}
@@ -63,7 +73,9 @@ public static function forUser($user = null)
*/
public function get($key, $default = null)
{
- if (!($user = $this->userContext)) {
+ $user = $this->userContext;
+
+ if (!$user) {
return $default;
}
@@ -74,6 +86,7 @@ public function get($key, $default = null)
}
$record = static::findRecord($key, $user);
+
if (!$record) {
return static::$cache[$cacheKey] = $default;
}
@@ -91,11 +104,14 @@ public function get($key, $default = null)
*/
public function set($key, $value)
{
- if (!$user = $this->userContext) {
+ $user = $this->userContext;
+
+ if (!$user) {
return false;
}
$record = static::findRecord($key, $user);
+
if (!$record) {
list($namespace, $group, $item) = $this->parseKey($key);
$record = new static;
@@ -120,11 +136,14 @@ public function set($key, $value)
*/
public function reset($key)
{
- if (!$user = $this->userContext) {
+ $user = $this->userContext;
+
+ if (!$user) {
return false;
}
$record = static::findRecord($key, $user);
+
if (!$record) {
return false;
}
@@ -139,7 +158,7 @@ public function reset($key)
/**
* Returns a record
- * @return self
+ * @return self|null
*/
public static function findRecord($key, $user = null)
{
@@ -148,8 +167,8 @@ public static function findRecord($key, $user = null)
/**
* Scope to find a setting record for the specified module (or plugin) name, setting name and user.
+ * @param \Winter\Storm\Database\QueryBuilder $query
* @param string $key Specifies the setting key value, for example 'backend:items.perpage'
- * @param mixed $default The default value to return if the setting doesn't exist in the DB.
* @param mixed $user An optional user object.
* @return mixed Returns the found record or null.
*/
diff --git a/src/Auth/Models/Role.php b/src/Auth/Models/Role.php
index c1781326b..efa8f3359 100644
--- a/src/Auth/Models/Role.php
+++ b/src/Auth/Models/Role.php
@@ -5,6 +5,8 @@
/**
* Role model
+ *
+ * @property array $permissions Permissions array.
*/
class Role extends Model
{
@@ -44,7 +46,7 @@ class Role extends Model
protected $allowedPermissionsValues = [0, 1];
/**
- * @var array The attributes that aren't mass assignable.
+ * @var string[]|bool The attributes that aren't mass assignable.
*/
protected $guarded = [];
@@ -167,6 +169,7 @@ public function hasAnyAccess(array $permissions)
public function setPermissionsAttribute($permissions)
{
$permissions = json_decode($permissions, true);
+
foreach ($permissions as $permission => $value) {
if (!in_array($value = (int) $value, $this->allowedPermissionsValues)) {
throw new InvalidArgumentException(sprintf(
diff --git a/src/Auth/Models/Throttle.php b/src/Auth/Models/Throttle.php
index cfa4c16d0..a4cdbea58 100644
--- a/src/Auth/Models/Throttle.php
+++ b/src/Auth/Models/Throttle.php
@@ -6,6 +6,9 @@
/**
* Throttle model
+ *
+ * @property \Winter\Storm\Auth\Models\User|null $user Related user.
+ * @method \Winter\Storm\Database\Relations\BelongsTo user() User relation.
*/
class Throttle extends Model
{
@@ -50,7 +53,7 @@ class Throttle extends Model
/**
* Returns the associated user with the throttler.
- * @return User
+ * @return \Illuminate\Database\Eloquent\Model|null
*/
public function getUser()
{
diff --git a/src/Auth/Models/User.php b/src/Auth/Models/User.php
index fe22d414b..8e7f020ad 100644
--- a/src/Auth/Models/User.php
+++ b/src/Auth/Models/User.php
@@ -1,13 +1,19 @@
The attributes that should be hidden for arrays.
*/
protected $hidden = ['password', 'reset_password_code', 'activation_code', 'persist_code'];
/**
- * @var array The attributes that aren't mass assignable.
+ * @var string[]|bool The attributes that aren't mass assignable.
*/
protected $guarded = ['is_superuser', 'reset_password_code', 'activation_code', 'persist_code', 'role_id'];
@@ -150,7 +156,7 @@ public function afterLogin()
/**
* Delete the user groups
- * @return bool
+ * @return void
*/
public function afterDelete()
{
@@ -341,7 +347,7 @@ public function getGroups()
/**
* Returns the role assigned to this user.
- * @return Winter\Storm\Auth\Models\Role
+ * @return \Winter\Storm\Auth\Models\Role|null
*/
public function getRole()
{
@@ -404,8 +410,9 @@ public function getMergedPermissions()
{
if (!$this->mergedPermissions) {
$permissions = [];
+ $role = $this->getRole();
- if (($role = $this->getRole()) && is_array($role->permissions)) {
+ if ($role && is_array($role->permissions)) {
$permissions = array_merge($permissions, $role->permissions);
}
@@ -564,6 +571,7 @@ public function hasAnyAccess(array $permissions)
public function setPermissionsAttribute($permissions)
{
$permissions = json_decode($permissions, true) ?: [];
+
foreach ($permissions as $permission => &$value) {
if (!in_array($value = (int) $value, $this->allowedPermissionsValues)) {
throw new InvalidArgumentException(sprintf(
@@ -632,7 +640,7 @@ public function getRememberToken()
/**
* Set the token value for the "remember me" session.
- * @param string $value
+ * @param string|null $value
* @return void
*/
public function setRememberToken($value)
diff --git a/src/Config/FileLoader.php b/src/Config/FileLoader.php
index f1395c9cc..52c59f09e 100644
--- a/src/Config/FileLoader.php
+++ b/src/Config/FileLoader.php
@@ -210,14 +210,15 @@ protected function getPackagePath($package, $group, $env = null)
/**
* Get the configuration path for a namespace.
*
- * @param string $namespace
+ * @param string|null $namespace
* @return string|null
*/
- protected function getPath($namespace)
+ protected function getPath($namespace = null)
{
if (is_null($namespace)) {
return $this->defaultPath;
- } elseif (isset($this->hints[$namespace])) {
+ }
+ if (isset($this->hints[$namespace])) {
return $this->hints[$namespace];
}
@@ -237,10 +238,10 @@ public function addNamespace($namespace, $hint)
}
/**
- * Add a new namespace to the loader.
+ * Registers an alias for a given namespace.
*
- * @param string $namespace
- * @param string $alias
+ * @param string $namespace
+ * @param string $alias
* @return void
*/
public function registerNamespaceAlias(string $namespace, string $alias)
diff --git a/src/Config/LoaderInterface.php b/src/Config/LoaderInterface.php
index 7345546f3..7b5f4502b 100644
--- a/src/Config/LoaderInterface.php
+++ b/src/Config/LoaderInterface.php
@@ -31,6 +31,15 @@ public function exists($group, $namespace = null);
*/
public function addNamespace($namespace, $hint);
+ /**
+ * Registers an alias for a given namespace.
+ *
+ * @param string $namespace
+ * @param string $alias
+ * @return void
+ */
+ public function registerNamespaceAlias(string $namespace, string $alias);
+
/**
* Returns all registered namespaces with the config
* loader.
diff --git a/src/Config/Repository.php b/src/Config/Repository.php
index 43985b5ca..719802dfe 100644
--- a/src/Config/Repository.php
+++ b/src/Config/Repository.php
@@ -2,6 +2,7 @@
use Closure;
use ArrayAccess;
+use Illuminate\Config\Repository as BaseRepository;
use Illuminate\Contracts\Config\Repository as RepositoryContract;
/**
@@ -9,7 +10,7 @@
*
* @author Alexey Bobkov, Samuel Georges
*/
-class Repository implements ArrayAccess, RepositoryContract
+class Repository extends BaseRepository implements ArrayAccess, RepositoryContract
{
use \Winter\Storm\Support\Traits\KeyParser;
@@ -115,6 +116,27 @@ public function get($key, $default = null)
return array_get($this->items[$collection], $item, $default);
}
+ /**
+ * Get many configuration values.
+ *
+ * @param array $keys
+ * @return array
+ */
+ public function getMany($keys)
+ {
+ $config = [];
+
+ foreach ($keys as $key => $default) {
+ if (is_numeric($key)) {
+ [$key, $default] = [$default, null];
+ }
+
+ $config[$key] = $this->get($key, $default);
+ }
+
+ return $config;
+ }
+
/**
* Set a given configuration value.
*
@@ -148,48 +170,6 @@ public function set($key, $value = null)
}
}
- /**
- * Prepend a value onto an array configuration value.
- *
- * @param string $key
- * @param mixed $value
- * @return void
- */
- public function prepend($key, $value)
- {
- $array = $this->get($key);
-
- array_unshift($array, $value);
-
- $this->set($key, $array);
- }
-
- /**
- * Push a value onto an array configuration value.
- *
- * @param string $key
- * @param mixed $value
- * @return void
- */
- public function push($key, $value)
- {
- $array = $this->get($key);
-
- $array[] = $value;
-
- $this->set($key, $array);
- }
-
- /**
- * Get all of the configuration items for the application.
- *
- * @return array
- */
- public function all()
- {
- return $this->items;
- }
-
/**
* Load the configuration group for the key.
*
diff --git a/src/Console/Command.php b/src/Console/Command.php
index c8aaf0166..b3a3c3af8 100644
--- a/src/Console/Command.php
+++ b/src/Console/Command.php
@@ -97,6 +97,9 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti
$dataType = 'Option';
$suggestionType = 'Options';
break;
+ default:
+ // This should not be possible to ever be triggered given the type is hardcoded above
+ throw new \Exception('Invalid input type being parsed during completion');
}
if (!empty($data)) {
foreach ($data as $name => $value) {
diff --git a/src/Cookie/CookieValuePrefix.php b/src/Cookie/CookieValuePrefix.php
deleted file mode 100644
index b2d106790..000000000
--- a/src/Cookie/CookieValuePrefix.php
+++ /dev/null
@@ -1,47 +0,0 @@
-disableFor($except);
}
-
- /**
- * Shift gracefully to unserialized cookies
- * @todo Remove entire method if year >= 2021 or build >= 475
- */
- protected function decryptCookie($name, $cookie)
- {
- if (is_array($cookie)) {
- return $this->decryptArray($cookie);
- }
-
- try {
- $result = $this->encrypter->decrypt($cookie, true);
- if (!is_string($result)) {
- $result = json_encode($result);
- }
- }
- catch (\Exception $ex) {
- $result = $this->encrypter->decrypt($cookie, false);
- }
-
- return $result;
- }
-
- /**
- * Shift gracefully to unserialized cookies
- * @todo Remove entire method if year >= 2021 or build >= 475
- */
- protected function decryptArray(array $cookie)
- {
- $decrypted = [];
-
- foreach ($cookie as $key => $value) {
- if (is_string($value)) {
- try {
- $result = $this->encrypter->decrypt($value, true);
- if (!is_string($result)) {
- $result = json_encode($result);
- }
- $decrypted[$key] = $result;
- }
- catch (\Exception $ex) {
- $decrypted[$key] = $this->encrypter->decrypt($value, false);
- }
- }
- }
-
- return $decrypted;
- }
-
- /**
- * Decrypt the cookies on the request.
- *
- * @param \Symfony\Component\HttpFoundation\Request $request
- * @return \Symfony\Component\HttpFoundation\Request
- */
- protected function decrypt(Request $request)
- {
- foreach ($request->cookies as $key => $cookie) {
- if ($this->isDisabled($key)) {
- continue;
- }
-
- try {
- // Decrypt the request-provided cookie
- $decryptedValue = $this->decryptCookie($key, $cookie);
-
- // Verify that the decrypted value belongs to this cookie key, use null if it fails
- $value = CookieValuePrefix::getVerifiedValue($key, $decryptedValue, $this->encrypter->getKey());
-
- /**
- * If the cookie is for the session and the value is a valid Session ID,
- * then allow it to pass through even if the validation failed (most likely
- * because the upgrade just occurred)
- *
- * The cookie will be adjusted on the next request
- * @todo Remove if year >= 2021 or build >= 475
- */
- if (empty($value) && $key === Config::get('session.cookie') && Session::isValidId($decryptedValue)) {
- $value = $decryptedValue;
- }
-
- // Set the verified cookie value on the request
- $request->cookies->set($key, $value);
- } catch (DecryptException $e) {
- $request->cookies->set($key, null);
- }
- }
-
- return $request;
- }
-
- /**
- * Encrypt the cookies on an outgoing response.
- *
- * @param \Symfony\Component\HttpFoundation\Response $response
- * @return \Symfony\Component\HttpFoundation\Response
- */
- protected function encrypt(Response $response)
- {
- foreach ($response->headers->getCookies() as $cookie) {
- if ($this->isDisabled($cookie->getName())) {
- continue;
- }
-
- $response->headers->setCookie($this->duplicate(
- $cookie,
- $this->encrypter->encrypt(
- // Prefix the cookie value to verify that it belongs to the current cookie
- CookieValuePrefix::create($cookie->getName(), $this->encrypter->getKey()) . $cookie->getValue(),
- static::serialized($cookie->getName())
- )
- ));
- }
-
- return $response;
- }
}
diff --git a/src/Database/Attach/BrokenImage.php b/src/Database/Attach/BrokenImage.php
index 7da30e53e..54cd08d50 100644
--- a/src/Database/Attach/BrokenImage.php
+++ b/src/Database/Attach/BrokenImage.php
@@ -1,6 +1,6 @@
[],
];
/**
- * @var array The attributes that are mass assignable.
+ * @var string[] The attributes that are mass assignable.
*/
protected $fillable = [
'file_name',
@@ -47,17 +53,17 @@ class File extends Model
];
/**
- * @var array The attributes that aren't mass assignable.
+ * @var string[] The attributes that aren't mass assignable.
*/
protected $guarded = [];
/**
- * @var array Known image extensions.
+ * @var string[] Known image extensions.
*/
public static $imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
/**
- * @var array Hidden fields from array/json access
+ * @var array Hidden fields from array/json access
*/
protected $hidden = ['attachment_type', 'attachment_id', 'is_public'];
@@ -93,14 +99,12 @@ class File extends Model
/**
* Creates a file object from a file an uploaded file.
- * @param Symfony\Component\HttpFoundation\File\UploadedFile $uploadedFile
+ *
+ * @param UploadedFile $uploadedFile The uploaded file.
+ * @return static
*/
public function fromPost($uploadedFile)
{
- if ($uploadedFile === null) {
- return;
- }
-
$this->file_name = $uploadedFile->getClientOriginalName();
$this->file_size = $uploadedFile->getSize();
$this->content_type = $uploadedFile->getMimeType();
@@ -120,13 +124,12 @@ public function fromPost($uploadedFile)
/**
* Creates a file object from a file on the disk.
+ *
+ * @param string $filePath The path to the file.
+ * @return static
*/
public function fromFile($filePath)
{
- if ($filePath === null) {
- return;
- }
-
$file = new FileObj($filePath);
$this->file_name = $file->getFilename();
$this->file_size = $file->getSize();
@@ -141,17 +144,12 @@ public function fromFile($filePath)
/**
* Creates a file object from raw data.
*
- * @param $data string Raw data
- * @param $filename string Filename
- *
- * @return $this
+ * @param string $data The raw data.
+ * @param string $filename The name of the file.
+ * @return static
*/
public function fromData($data, $filename)
{
- if ($data === null) {
- return;
- }
-
$tempPath = temp_path($filename);
FileHelper::put($tempPath, $data);
@@ -163,9 +161,10 @@ public function fromData($data, $filename)
/**
* Creates a file object from url
- * @param $url string URL
- * @param $filename string Filename
- * @return $this
+ *
+ * @param string $url The URL to retrieve and store.
+ * @param string|null $filename The name of the file. If null, the filename will be extracted from the URL.
+ * @return static
*/
public function fromUrl($url, $filename = null)
{
@@ -182,7 +181,8 @@ public function fromUrl($url, $filename = null)
// Get the filename from the path
$filename = pathinfo($filePath)['filename'];
- // Attempt to detect the extension from the reported Content-Type, fall back to the original path extension if not able to guess
+ // Attempt to detect the extension from the reported Content-Type, fall back to the original path extension
+ // if not able to guess
$mimesToExt = array_flip($this->autoMimeTypes);
if (!empty($data->headers['Content-Type']) && isset($mimesToExt[$data->headers['Content-Type']])) {
$ext = $mimesToExt[$data->headers['Content-Type']];
@@ -203,6 +203,7 @@ public function fromUrl($url, $filename = null)
/**
* Helper attribute for getPath.
+ *
* @return string
*/
public function getPathAttribute()
@@ -212,6 +213,7 @@ public function getPathAttribute()
/**
* Helper attribute for getExtension.
+ *
* @return string
*/
public function getExtensionAttribute()
@@ -221,6 +223,8 @@ public function getExtensionAttribute()
/**
* Used only when filling attributes.
+ *
+ * @param mixed $value
* @return void
*/
public function setDataAttribute($value)
@@ -230,7 +234,10 @@ public function setDataAttribute($value)
/**
* Helper attribute for get image width.
- * @return string
+ *
+ * Returns `null` if this file is not an image.
+ *
+ * @return string|int|null
*/
public function getWidthAttribute()
{
@@ -239,11 +246,16 @@ public function getWidthAttribute()
return $dimensions[0];
}
+
+ return null;
}
/**
* Helper attribute for get image height.
- * @return string
+ *
+ * Returns `null` if this file is not an image.
+ *
+ * @return string|int|null
*/
public function getHeightAttribute()
{
@@ -252,10 +264,13 @@ public function getHeightAttribute()
return $dimensions[1];
}
+
+ return null;
}
/**
* Helper attribute for file size in human format.
+ *
* @return string
*/
public function getSizeAttribute()
@@ -270,33 +285,34 @@ public function getSizeAttribute()
/**
* Outputs the raw file contents.
*
- * @param string $disposition The Content-Disposition to set, defaults to inline
- * @param bool $returnResponse Defaults to false, returns a Response object instead of directly outputting to the browser
- * @return Response | void
+ * @param string $disposition The Content-Disposition to set, defaults to `inline`
+ * @param bool $returnResponse Defaults to `false`, returns a Response object instead of directly outputting to the
+ * browser
+ * @return \Illuminate\Http\Response|void
*/
public function output($disposition = 'inline', $returnResponse = false)
{
$response = response($this->getContents())->withHeaders([
'Content-type' => $this->getContentType(),
'Content-Disposition' => $disposition . '; filename="' . $this->file_name . '"',
- 'Cache-Control' => 'private, no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
+ 'Cache-Control' => 'private, no-store, no-cache, must-revalidate, max-age=0',
'Accept-Ranges' => 'bytes',
'Content-Length' => $this->file_size,
]);
if ($returnResponse) {
return $response;
- } else {
- $response->sendHeaders();
- $response->sendContent();
}
+
+ $response->sendHeaders();
+ $response->sendContent();
}
/**
* Outputs the raw thumbfile contents.
*
- * @param integer $width
- * @param integer $height
+ * @param int $width
+ * @param int $height
* @param array $options [
* 'mode' => 'auto',
* 'offset' => [0, 0],
@@ -306,8 +322,9 @@ public function output($disposition = 'inline', $returnResponse = false)
* 'extension' => 'auto',
* 'disposition' => 'inline',
* ]
- * @param bool $returnResponse Defaults to false, returns a Response object instead of directly outputting to the browser
- * @return Response | void
+ * @param bool $returnResponse Defaults to `false`, returns a Response object instead of directly outputting to the
+ * browser
+ * @return \Illuminate\Http\Response|void
*/
public function outputThumb($width, $height, $options = [], $returnResponse = false)
{
@@ -320,17 +337,17 @@ public function outputThumb($width, $height, $options = [], $returnResponse = fa
$response = response($contents)->withHeaders([
'Content-type' => $this->getContentType(),
'Content-Disposition' => $disposition . '; filename="' . basename($thumbFile) . '"',
- 'Cache-Control' => 'private, no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
+ 'Cache-Control' => 'private, no-store, no-cache, must-revalidate, max-age=0',
'Accept-Ranges' => 'bytes',
'Content-Length' => mb_strlen($contents, '8bit'),
]);
if ($returnResponse) {
return $response;
- } else {
- $response->sendHeaders();
- $response->sendContent();
}
+
+ $response->sendHeaders();
+ $response->sendContent();
}
//
@@ -340,7 +357,7 @@ public function outputThumb($width, $height, $options = [], $returnResponse = fa
/**
* Returns the cache key used for the hasFile method
*
- * @param string $path The path to get the cache key for
+ * @param string|null $path The path to get the cache key for
* @return string
*/
public function getCacheKey($path = null)
@@ -354,6 +371,8 @@ public function getCacheKey($path = null)
/**
* Returns the file name without path
+ *
+ * @return string
*/
public function getFilename()
{
@@ -362,6 +381,8 @@ public function getFilename()
/**
* Returns the file extension.
+ *
+ * @return string
*/
public function getExtension()
{
@@ -370,6 +391,8 @@ public function getExtension()
/**
* Returns the last modification date as a UNIX timestamp.
+ *
+ * @param string|null $fileName
* @return int
*/
public function getLastModified($fileName = null)
@@ -379,6 +402,10 @@ public function getLastModified($fileName = null)
/**
* Returns the file content type.
+ *
+ * Returns `null` if the file content type cannot be determined.
+ *
+ * @return string|null
*/
public function getContentType()
{
@@ -396,6 +423,9 @@ public function getContentType()
/**
* Get file contents from storage device.
+ *
+ * @param string|null $fileName
+ * @return string
*/
public function getContents($fileName = null)
{
@@ -404,6 +434,9 @@ public function getContents($fileName = null)
/**
* Returns the public address to access the file.
+ *
+ * @param string|null $fileName
+ * @return string
*/
public function getPath($fileName = null)
{
@@ -416,6 +449,8 @@ public function getPath($fileName = null)
/**
* Returns a local path to this file. If the file is stored remotely,
* it will be downloaded to a temporary directory.
+ *
+ * @return string
*/
public function getLocalPath()
{
@@ -436,6 +471,8 @@ public function getLocalPath()
/**
* Returns the path to the file, relative to the storage disk.
+ *
+ * @param string|null $fileName
* @return string
*/
public function getDiskPath($fileName = null)
@@ -448,15 +485,17 @@ public function getDiskPath($fileName = null)
/**
* Determines if the file is flagged "public" or not.
+ *
+ * @return bool
*/
public function isPublic()
{
if (array_key_exists('is_public', $this->attributes)) {
- return $this->attributes['is_public'];
+ return (bool) $this->attributes['is_public'];
}
if (isset($this->is_public)) {
- return $this->is_public;
+ return (bool) $this->is_public;
}
return true;
@@ -464,7 +503,8 @@ public function isPublic()
/**
* Returns the file size as string.
- * @return string Returns the size as string.
+ *
+ * @return string
*/
public function sizeToString()
{
@@ -478,6 +518,8 @@ public function sizeToString()
/**
* Before the model is saved
* - check if new file data has been supplied, eg: $model->data = Input::file('something');
+ *
+ * @return void
*/
public function beforeSave()
{
@@ -487,8 +529,7 @@ public function beforeSave()
if ($this->data !== null) {
if ($this->data instanceof UploadedFile) {
$this->fromPost($this->data);
- }
- else {
+ } else {
$this->fromFile($this->data);
}
@@ -499,14 +540,15 @@ public function beforeSave()
/**
* After model is deleted
* - clean up it's thumbnails
+ *
+ * @return void
*/
public function afterDelete()
{
try {
$this->deleteThumbs();
$this->deleteFile();
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
}
}
@@ -516,6 +558,8 @@ public function afterDelete()
/**
* Checks if the file extension is an image and returns true or false.
+ *
+ * @return bool
*/
public function isImage()
{
@@ -524,7 +568,8 @@ public function isImage()
/**
* Get image dimensions
- * @return array|bool
+ *
+ * @return array|false
*/
protected function getImageDimensions()
{
@@ -564,8 +609,7 @@ public function getThumb($width, $height, $options = [])
if (!$this->hasFile($thumbFile)) {
if ($this->isLocalStorage()) {
$this->makeThumbLocal($thumbFile, $thumbPath, $width, $height, $options);
- }
- else {
+ } else {
$this->makeThumbStorage($thumbFile, $thumbPath, $width, $height, $options);
}
}
@@ -575,16 +619,37 @@ public function getThumb($width, $height, $options = [])
/**
* Generates a thumbnail filename.
- * @return string
+ *
+ * @param integer $width
+ * @param integer $height
+ * @param array $options [
+ * 'mode' => 'auto',
+ * 'offset' => [0, 0],
+ * 'quality' => 90,
+ * 'sharpen' => 0,
+ * 'interlace' => false,
+ * 'extension' => 'auto',
+ * ]
+ * @return string The filename of the thumbnail
*/
- public function getThumbFilename($width, $height, $options)
+ public function getThumbFilename($width, $height, $options = [])
{
$options = $this->getDefaultThumbOptions($options);
- return 'thumb_' . $this->id . '_' . $width . '_' . $height . '_' . $options['offset'][0] . '_' . $options['offset'][1] . '_' . $options['mode'] . '.' . $options['extension'];
+ return implode('_', [
+ 'thumb',
+ (string) $this->id,
+ (string) $width,
+ (string) $height,
+ (string) $options['offset'][0],
+ (string) $options['offset'][1],
+ (string) $options['mode'] . '.' . (string) $options['extension'],
+ ]);
}
/**
* Returns the default thumbnail options.
+ *
+ * @param array $overrideOptions Overridden options
* @return array
*/
protected function getDefaultThumbOptions($overrideOptions = [])
@@ -614,9 +679,17 @@ protected function getDefaultThumbOptions($overrideOptions = [])
}
/**
- * Generate the thumbnail based on the local file system. This step is necessary
- * to simplify things and ensure the correct file permissions are given
+ * Generate the thumbnail based on the local file system.
+ *
+ * This step is necessary to simplify things and ensure the correct file permissions are given
* to the local files.
+ *
+ * @param string $thumbFile
+ * @param string $thumbPath
+ * @param int $width
+ * @param int $height
+ * @param array $options
+ * @return void
*/
protected function makeThumbLocal($thumbFile, $thumbPath, $width, $height, $options)
{
@@ -629,18 +702,16 @@ protected function makeThumbLocal($thumbFile, $thumbPath, $width, $height, $opti
*/
if (!$this->hasFile($this->disk_name)) {
BrokenImage::copyTo($thumbPath);
- }
- /*
- * Generate thumbnail
- */
- else {
+ } else {
+ /*
+ * Generate thumbnail
+ */
try {
Resizer::open($filePath)
->resize($width, $height, $options)
->save($thumbPath)
;
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
Log::error($ex);
BrokenImage::copyTo($thumbPath);
}
@@ -651,6 +722,13 @@ protected function makeThumbLocal($thumbFile, $thumbPath, $width, $height, $opti
/**
* Generate the thumbnail based on a remote storage engine.
+ *
+ * @param string $thumbFile
+ * @param string $thumbPath
+ * @param int $width
+ * @param int $height
+ * @param array $options
+ * @return void
*/
protected function makeThumbStorage($thumbFile, $thumbPath, $width, $height, $options)
{
@@ -662,11 +740,10 @@ protected function makeThumbStorage($thumbFile, $thumbPath, $width, $height, $op
*/
if (!$this->hasFile($this->disk_name)) {
BrokenImage::copyTo($tempThumb);
- }
- /*
- * Generate thumbnail
- */
- else {
+ } else {
+ /*
+ * Generate thumbnail
+ */
$this->copyStorageToLocal($this->getDiskPath(), $tempFile);
try {
@@ -674,8 +751,7 @@ protected function makeThumbStorage($thumbFile, $thumbPath, $width, $height, $op
->resize($width, $height, $options)
->save($tempThumb)
;
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
Log::error($ex);
BrokenImage::copyTo($tempThumb);
}
@@ -690,8 +766,10 @@ protected function makeThumbStorage($thumbFile, $thumbPath, $width, $height, $op
FileHelper::delete($tempThumb);
}
- /*
+ /**
* Delete all thumbnails for this file.
+ *
+ * @return void
*/
public function deleteThumbs()
{
@@ -712,8 +790,7 @@ public function deleteThumbs()
if (!empty($collection)) {
if ($this->isLocalStorage()) {
FileHelper::delete($collection);
- }
- else {
+ } else {
$this->getDisk()->delete($collection);
}
}
@@ -725,6 +802,8 @@ public function deleteThumbs()
/**
* Generates a disk name from the supplied file name.
+ *
+ * @return string
*/
protected function getDiskName()
{
@@ -739,13 +818,16 @@ protected function getDiskName()
$ext = $this->data->guessExtension();
}
- $name = str_replace('.', '', uniqid(null, true));
+ $name = str_replace('.', '', uniqid('', true));
return $this->disk_name = !empty($ext) ? $name.'.'.$ext : $name;
}
/**
* Returns a temporary local path to work from.
+ *
+ * @param string|null $path Optional path to append to the temp path
+ * @return string
*/
protected function getLocalTempPath($path = null)
{
@@ -758,8 +840,10 @@ protected function getLocalTempPath($path = null)
/**
* Saves a file
+ *
* @param string $sourcePath An absolute local path to a file name to read from.
- * @param string $destinationFileName A storage file name to save to.
+ * @param string|null $destinationFileName A storage file name to save to.
+ * @return bool
*/
protected function putFile($sourcePath, $destinationFileName = null)
{
@@ -786,10 +870,9 @@ protected function putFile($sourcePath, $destinationFileName = null)
*/
if (
!FileHelper::isDirectory($destinationPath) &&
- !FileHelper::makeDirectory($destinationPath, 0777, true, true) &&
- !FileHelper::isDirectory($destinationPath)
+ !FileHelper::makeDirectory($destinationPath, 0777, true, true)
) {
- trigger_error(error_get_last(), E_USER_WARNING);
+ trigger_error(error_get_last()['message'], E_USER_WARNING);
}
return FileHelper::copy($sourcePath, $destinationPath . $destinationFileName);
@@ -797,6 +880,8 @@ protected function putFile($sourcePath, $destinationFileName = null)
/**
* Delete file contents from storage device.
+ *
+ * @param string|null $fileName
* @return void
*/
protected function deleteFile($fileName = null)
@@ -818,7 +903,9 @@ protected function deleteFile($fileName = null)
/**
* Check file exists on storage device.
- * @return void
+ *
+ * @param string|null $fileName
+ * @return bool
*/
protected function hasFile($fileName = null)
{
@@ -837,8 +924,9 @@ protected function hasFile($fileName = null)
}
/**
- * Checks if directory is empty then deletes it,
- * three levels up to match the partition directory.
+ * Checks if directory is empty then deletes it, three levels up to match the partition directory.
+ *
+ * @param string|null $dir Directory to check and delete if empty.
* @return void
*/
protected function deleteEmptyDirectory($dir = null)
@@ -866,14 +954,12 @@ protected function deleteEmptyDirectory($dir = null)
/**
* Returns true if a directory contains no files.
- * @return void
+ *
+ * @param string|null $dir Directory to check.
+ * @return bool
*/
- protected function isDirectoryEmpty($dir)
+ protected function isDirectoryEmpty($dir = null)
{
- if (!$dir) {
- return null;
- }
-
return count($this->storageCmd('allFiles', $dir)) === 0;
}
@@ -883,10 +969,10 @@ protected function isDirectoryEmpty($dir)
/**
* Calls a method against File or Storage depending on local storage.
- * This allows local storage outside the storage/app folder and is
- * also good for performance. For local storage, *every* argument
- * is prefixed with the local root path. Props to Laravel for
- * the unified interface.
+ *
+ * This allows local storage outside the storage/app folder and is also good for performance. For local storage,
+ * *every* argument is prefixed with the local root path. Props to Laravel for the unified interface.
+ *
* @return mixed
*/
protected function storageCmd()
@@ -903,8 +989,7 @@ protected function storageCmd()
}, $args);
$result = forward_static_call_array([$interface, $command], $args);
- }
- else {
+ } else {
$result = call_user_func_array([$this->getDisk(), $command], $args);
}
@@ -913,6 +998,10 @@ protected function storageCmd()
/**
* Copy the Storage to local file
+ *
+ * @param string $storagePath
+ * @param string $localPath
+ * @return int The filesize of the copied file.
*/
protected function copyStorageToLocal($storagePath, $localPath)
{
@@ -921,6 +1010,10 @@ protected function copyStorageToLocal($storagePath, $localPath)
/**
* Copy the local file to Storage
+ *
+ * @param string $storagePath
+ * @param string $localPath
+ * @return string|bool
*/
protected function copyLocalToStorage($localPath, $storagePath)
{
@@ -932,8 +1025,9 @@ protected function copyLocalToStorage($localPath, $storagePath)
//
/**
- * Returns the maximum size of an uploaded file as configured in php.ini
- * @return int The maximum size of an uploaded file in kilobytes
+ * Returns the maximum size of an uploaded file as configured in php.ini in kilobytes (rounded)
+ *
+ * @return float
*/
public static function getMaxFilesize()
{
@@ -942,6 +1036,8 @@ public static function getMaxFilesize()
/**
* Define the internal storage path, override this method to define.
+ *
+ * @return string
*/
public function getStorageDirectory()
{
@@ -954,6 +1050,8 @@ public function getStorageDirectory()
/**
* Define the public address for the storage path.
+ *
+ * @return string
*/
public function getPublicPath()
{
@@ -966,6 +1064,8 @@ public function getPublicPath()
/**
* Define the internal working path, override this method to define.
+ *
+ * @return string
*/
public function getTempPath()
{
@@ -980,7 +1080,8 @@ public function getTempPath()
/**
* Returns the storage disk the file is stored on
- * @return FilesystemAdapter
+ *
+ * @return Filesystem
*/
public function getDisk()
{
@@ -989,6 +1090,7 @@ public function getDisk()
/**
* Returns true if the storage engine is local.
+ *
* @return bool
*/
protected function isLocalStorage()
@@ -997,12 +1099,12 @@ protected function isLocalStorage()
}
/**
- * Generates a partition for the file.
- * return /ABC/DE1/234 for an name of ABCDE1234.
- * @param Attachment $attachment
- * @param string $styleName
- * @return mixed
- */
+ * Generates a partition for the file.
+ *
+ * For example, returns `/ABC/DE1/234` for an name of `ABCDE1234`.
+ *
+ * @return string
+ */
protected function getPartitionDirectory()
{
return implode('/', array_slice(str_split($this->disk_name, 3), 0, 3)) . '/';
@@ -1010,10 +1112,11 @@ protected function getPartitionDirectory()
/**
* If working with local storage, determine the absolute local path.
+ *
* @return string
*/
protected function getLocalRootPath()
{
- return storage_path().'/app';
+ return storage_path() . '/app';
}
}
diff --git a/src/Database/Attach/Resizer.php b/src/Database/Attach/Resizer.php
index 777d4b972..f0f199496 100644
--- a/src/Database/Attach/Resizer.php
+++ b/src/Database/Attach/Resizer.php
@@ -1,7 +1,8 @@
retainImageTransparency($img);
break;
default:
- throw new Exception(sprintf('Invalid mime type: %s. Accepted types: image/jpeg, image/gif, image/png, image/webp.', $this->mime));
- break;
+ throw new Exception(
+ sprintf(
+ 'Invalid mime type: %s. Accepted types: image/jpeg, image/gif, image/png, image/webp.',
+ $this->mime
+ )
+ );
}
return $img;
diff --git a/src/Database/Behaviors/Purgeable.php b/src/Database/Behaviors/Purgeable.php
index cd37d787c..dd1c84d68 100644
--- a/src/Database/Behaviors/Purgeable.php
+++ b/src/Database/Behaviors/Purgeable.php
@@ -3,11 +3,10 @@
class Purgeable extends \Winter\Storm\Extension\ExtensionBase
{
/**
- * @var array List of attribute names which should not be saved to the database.
+ * Model to purge.
*
- * public $purgeable = [];
+ * @var \Winter\Storm\Database\Model
*/
-
protected $model;
public function __construct($parent)
@@ -47,7 +46,7 @@ public function bootPurgeable()
/**
* Adds an attribute to the purgeable attributes list
* @param array|string|null $attributes
- * @return $this
+ * @return \Winter\Storm\Database\Model
*/
public function addPurgeable($attributes = null)
{
@@ -60,7 +59,7 @@ public function addPurgeable($attributes = null)
/**
* Removes purged attributes from the dataset, used before saving.
- * @param $attributes mixed Attribute(s) to purge, if unspecified, $purgable property is used
+ * @param string|array|null $attributesToPurge Attribute(s) to purge. If unspecified, $purgable property is used
* @return array Current attribute set
*/
public function purgeAttributes($attributesToPurge = null)
@@ -76,12 +75,7 @@ public function purgeAttributes($attributesToPurge = null)
$cleanAttributes = array_diff_key($attributes, array_flip($purgeable));
$originalAttributes = array_diff_key($attributes, $cleanAttributes);
- if (is_array($this->originalPurgeableValues)) {
- $this->originalPurgeableValues = array_merge($this->originalPurgeableValues, $originalAttributes);
- }
- else {
- $this->originalPurgeableValues = $originalAttributes;
- }
+ $this->originalPurgeableValues = array_merge($this->originalPurgeableValues, $originalAttributes);
return $this->model->attributes = $cleanAttributes;
}
@@ -112,6 +106,8 @@ public function getOriginalPurgeValue($attribute)
/**
* Restores the original values of any purged attributes.
+ *
+ * @return \Winter\Storm\Database\Model
*/
public function restorePurgedValues()
{
diff --git a/src/Database/Builder.php b/src/Database/Builder.php
index c39264053..2d760194d 100644
--- a/src/Database/Builder.php
+++ b/src/Database/Builder.php
@@ -10,9 +10,24 @@
* Extends Eloquent builder class.
*
* @author Alexey Bobkov, Samuel Georges
+ * @mixin \Winter\Storm\Database\QueryBuilder
*/
class Builder extends BuilderModel
{
+ /**
+ * The base query builder instance.
+ *
+ * @var \Winter\Storm\Database\QueryBuilder
+ */
+ protected $query;
+
+ /**
+ * The model being queried.
+ *
+ * @var \Winter\Storm\Database\Model
+ */
+ protected $model;
+
/**
* Get an array with the values of a given column.
*
@@ -103,10 +118,14 @@ protected function searchWhereInternal($term, $columns, $mode, $boolean)
/**
* Paginate the given query.
*
- * @param int $perPage
- * @param int $currentPage
- * @param array $columns
- * @param string $pageName
+ * This method also accepts the Laravel signature:
+ *
+ * `paginate(int|null $perPage, array $columns, string $pageName, int|null $page)`
+ *
+ * @param int|null $perPage
+ * @param array|int|null $currentPage
+ * @param array|string $columns
+ * @param string|int|null $pageName
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = null, $currentPage = null, $columns = ['*'], $pageName = 'page')
@@ -146,9 +165,14 @@ public function paginate($perPage = null, $currentPage = null, $columns = ['*'],
/**
* Paginate the given query into a simple paginator.
*
- * @param int $perPage
- * @param int $currentPage
- * @param array $columns
+ * This method also accepts the Laravel signature:
+ *
+ * `simplePaginate(int|null $perPage, array $columns, string $pageName, int|null $page)`
+ *
+ * @param int|null $perPage
+ * @param array|int|null $currentPage
+ * @param array|string $columns
+ * @param string|int|null $pageName
* @return \Illuminate\Contracts\Pagination\Paginator
*/
public function simplePaginate($perPage = null, $currentPage = null, $columns = ['*'], $pageName = 'page')
diff --git a/src/Database/Concerns/HasRelationships.php b/src/Database/Concerns/HasRelationships.php
index acc5e1412..ecf581b9c 100644
--- a/src/Database/Concerns/HasRelationships.php
+++ b/src/Database/Concerns/HasRelationships.php
@@ -151,13 +151,15 @@ public function hasRelation($name)
/**
* Returns relationship details from a supplied name.
* @param string $name Relation name
- * @return array
+ * @return array|null
*/
public function getRelationDefinition($name)
{
if (($type = $this->getRelationType($name)) !== null) {
return (array) $this->getRelationTypeDefinition($type, $name) + $this->getRelationDefaults($type);
}
+
+ return null;
}
/**
@@ -178,7 +180,7 @@ public function getRelationTypeDefinitions($type)
* Returns the given relation definition.
* @param string $type Relation type
* @param string $name Relation name
- * @return array
+ * @return string|null
*/
public function getRelationTypeDefinition($type, $name)
{
@@ -187,6 +189,8 @@ public function getRelationTypeDefinition($type, $name)
if (isset($definitions[$name])) {
return $definitions[$name];
}
+
+ return null;
}
/**
@@ -216,7 +220,7 @@ public function getRelationDefinitions()
/**
* Returns a relationship type based on a supplied name.
* @param string $name Relation name
- * @return string
+ * @return string|null
*/
public function getRelationType($name)
{
@@ -225,12 +229,14 @@ public function getRelationType($name)
return $type;
}
}
+
+ return null;
}
/**
* Returns a relation class object
* @param string $name Relation name
- * @return string
+ * @return \Winter\Storm\Database\Relations\Relation|null
*/
public function makeRelation($name)
{
@@ -336,6 +342,11 @@ protected function handleRelation($relationName)
case 'morphToMany':
$relation = $this->validateRelationArgs($relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps'], ['name']);
$relationObj = $this->$relationType($relation[0], $relation['name'], $relation['table'], $relation['key'], $relation['otherKey'], $relation['parentKey'], $relation['relatedKey'], false, $relationName);
+
+ if (isset($relation['pivotModel'])) {
+ $relationObj->using($relation['pivotModel']);
+ }
+
break;
case 'morphedByMany':
@@ -465,7 +476,7 @@ public function belongsTo($related, $foreignKey = null, $parentKey = null, $rela
/**
* Define an polymorphic, inverse one-to-one or many relationship.
* Overridden from {@link Eloquent\Model} to allow the usage of the intermediary methods to handle the relation.
- * @return \Winter\Storm\Database\Relations\BelongsTo
+ * @return \Winter\Storm\Database\Relations\MorphTo
*/
public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
{
@@ -487,7 +498,7 @@ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null
* @param string $type
* @param string $id
* @param string $ownerKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+ * @return \Winter\Storm\Database\Relations\MorphTo
*/
protected function morphEagerTo($name, $type, $id, $ownerKey)
{
@@ -508,10 +519,10 @@ protected function morphEagerTo($name, $type, $id, $ownerKey)
* @param string $name
* @param string $type
* @param string $id
- * @param string $ownerKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+ * @param string|null $ownerKey
+ * @return \Winter\Storm\Database\Relations\MorphTo
*/
- protected function morphInstanceTo($target, $name, $type, $id, $ownerKey)
+ protected function morphInstanceTo($target, $name, $type, $id, $ownerKey = null)
{
$instance = $this->newRelatedInstance(
static::getActualClassNameForMorph($target)
@@ -718,7 +729,7 @@ public function morphedByMany($related, $name, $table = null, $primaryKey = null
/**
* Define an attachment one-to-one relationship.
* This code is a duplicate of Eloquent but uses a Storm relation class.
- * @return \Winter\Storm\Database\Relations\MorphOne
+ * @return \Winter\Storm\Database\Relations\AttachOne
*/
public function attachOne($related, $isPublic = true, $localKey = null, $relationName = null)
{
@@ -740,7 +751,7 @@ public function attachOne($related, $isPublic = true, $localKey = null, $relatio
/**
* Define an attachment one-to-many relationship.
* This code is a duplicate of Eloquent but uses a Storm relation class.
- * @return \Winter\Storm\Database\Relations\MorphMany
+ * @return \Winter\Storm\Database\Relations\AttachMany
*/
public function attachMany($related, $isPublic = null, $localKey = null, $relationName = null)
{
@@ -764,7 +775,7 @@ public function attachMany($related, $isPublic = null, $localKey = null, $relati
*/
protected function getRelationCaller()
{
- $backtrace = debug_backtrace(false);
+ $backtrace = debug_backtrace(0);
$caller = ($backtrace[2]['function'] == 'handleRelation') ? $backtrace[4] : $backtrace[2];
return $caller['function'];
}
@@ -808,8 +819,7 @@ protected function addRelation(string $type, string $name, array $config): void
sprintf(
'Cannot add the "%s" relation to %s, it conflicts with an existing relation, attribute, or property.',
$name,
- get_class($this),
- $name
+ get_class($this)
)
);
}
@@ -946,4 +956,17 @@ public function addHasManyThroughRelation(string $name, array $config): void
{
$this->addRelation('HasManyThrough', $name, $config);
}
+
+ /**
+ * Get the polymorphic relationship columns.
+ *
+ * @param string $name
+ * @param string|null $type
+ * @param string|null $id
+ * @return array
+ */
+ protected function getMorphs($name, $type = null, $id = null)
+ {
+ return [$type ?: $name.'_type', $id ?: $name.'_id'];
+ }
}
diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php
index dcd804cb7..062c6205c 100644
--- a/src/Database/Connections/Connection.php
+++ b/src/Database/Connections/Connection.php
@@ -39,9 +39,7 @@ public static function flushDuplicateCache()
*/
public function logQuery($query, $bindings, $time = null)
{
- if (isset($this->events)) {
- $this->events->fire('illuminate.query', [$query, $bindings, $time, $this->getName()]);
- }
+ $this->fireEvent('illuminate.query', [$query, $bindings, $time, $this->getName()]);
parent::logQuery($query, $bindings, $time);
}
@@ -50,14 +48,27 @@ public function logQuery($query, $bindings, $time = null)
* Fire an event for this connection.
*
* @param string $event
- * @return void
+ * @return array|null
*/
protected function fireConnectionEvent($event)
{
- if (isset($this->events)) {
- $this->events->fire('connection.'.$this->getName().'.'.$event, $this);
- }
+ $this->fireEvent('connection.'.$this->getName().'.'.$event, $this);
parent::fireConnectionEvent($event);
}
+
+ /**
+ * Fire the given event if possible.
+ */
+ protected function fireEvent(string $event, array|object $attributes = []): void
+ {
+ /** @var \Winter\Storm\Events\Dispatcher|null */
+ $eventManager = $this->events;
+
+ if (!isset($eventManager)) {
+ return;
+ }
+
+ $eventManager->fire($event, $attributes);
+ }
}
diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php
index 8082c7399..2458a9ee6 100644
--- a/src/Database/Connections/MySqlConnection.php
+++ b/src/Database/Connections/MySqlConnection.php
@@ -1,18 +1,21 @@
schemaGrammar)) {
+ if (!isset($this->schemaGrammar)) {
$this->useDefaultSchemaGrammar();
}
@@ -36,7 +39,7 @@ public function getSchemaBuilder()
/**
* Get the default schema grammar instance.
*
- * @return \Illuminate\Database\Schema\Grammars\MySqlGrammar
+ * @return \Illuminate\Database\Grammar
*/
protected function getDefaultSchemaGrammar()
{
@@ -56,11 +59,11 @@ protected function getDefaultPostProcessor()
/**
* Get the Doctrine DBAL driver.
*
- * @return \Doctrine\DBAL\Driver\PDOMySql\Driver
+ * @return \Illuminate\Database\PDO\MySqlDriver
*/
protected function getDoctrineDriver()
{
- return new DoctrineDriver;
+ return new MySqlDriver;
}
/**
diff --git a/src/Database/Connections/PostgresConnection.php b/src/Database/Connections/PostgresConnection.php
index e012a260b..d768d69e4 100644
--- a/src/Database/Connections/PostgresConnection.php
+++ b/src/Database/Connections/PostgresConnection.php
@@ -1,17 +1,20 @@
schemaGrammar)) {
+ if (!isset($this->schemaGrammar)) {
$this->useDefaultSchemaGrammar();
}
@@ -35,7 +38,7 @@ public function getSchemaBuilder()
/**
* Get the default schema grammar instance.
*
- * @return \Illuminate\Database\Schema\Grammars\PostgresGrammar
+ * @return \Illuminate\Database\Grammar
*/
protected function getDefaultSchemaGrammar()
{
@@ -55,10 +58,10 @@ protected function getDefaultPostProcessor()
/**
* Get the Doctrine DBAL driver.
*
- * @return \Doctrine\DBAL\Driver\PDOPgSql\Driver
+ * @return \Illuminate\Database\PDO\PostgresDriver
*/
protected function getDoctrineDriver()
{
- return new DoctrineDriver;
+ return new PostgresDriver;
}
}
diff --git a/src/Database/Connections/SQLiteConnection.php b/src/Database/Connections/SQLiteConnection.php
index eaf4e49e2..1d6e3db1e 100644
--- a/src/Database/Connections/SQLiteConnection.php
+++ b/src/Database/Connections/SQLiteConnection.php
@@ -2,16 +2,19 @@
use Illuminate\Database\Schema\SQLiteBuilder;
use Illuminate\Database\Query\Processors\SQLiteProcessor;
-use Doctrine\DBAL\Driver\PDOSqlite\Driver as DoctrineDriver;
+use Illuminate\Database\PDO\SQLiteDriver;
use Winter\Storm\Database\Query\Grammars\SQLiteGrammar as QueryGrammar;
use Illuminate\Database\Schema\Grammars\SQLiteGrammar as SchemaGrammar;
+/**
+ * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar
+ */
class SQLiteConnection extends Connection
{
/**
* Get the default query grammar instance.
*
- * @return \Winter\Storm\Database\Query\Grammars\SQLiteGrammar
+ * @return \Illuminate\Database\Grammar
*/
protected function getDefaultQueryGrammar()
{
@@ -25,7 +28,7 @@ protected function getDefaultQueryGrammar()
*/
public function getSchemaBuilder()
{
- if (is_null($this->schemaGrammar)) {
+ if (!isset($this->schemaGrammar)) {
$this->useDefaultSchemaGrammar();
}
@@ -35,7 +38,7 @@ public function getSchemaBuilder()
/**
* Get the default schema grammar instance.
*
- * @return \Winter\Storm\Database\Query\Grammars\SQLiteGrammar
+ * @return \Illuminate\Database\Grammar
*/
protected function getDefaultSchemaGrammar()
{
@@ -55,10 +58,10 @@ protected function getDefaultPostProcessor()
/**
* Get the Doctrine DBAL driver.
*
- * @return \Doctrine\DBAL\Driver\PDOSqlite\Driver
+ * @return \Illuminate\Database\PDO\SQLiteDriver
*/
protected function getDoctrineDriver()
{
- return new DoctrineDriver;
+ return new SQLiteDriver;
}
}
diff --git a/src/Database/Connections/SqlServerConnection.php b/src/Database/Connections/SqlServerConnection.php
index e48c45167..07a6af21a 100644
--- a/src/Database/Connections/SqlServerConnection.php
+++ b/src/Database/Connections/SqlServerConnection.php
@@ -4,11 +4,14 @@
use Exception;
use Throwable;
use Illuminate\Database\Schema\SqlServerBuilder;
-use Doctrine\DBAL\Driver\PDOSqlsrv\Driver as DoctrineDriver;
+use Illuminate\Database\PDO\SqlServerDriver;
use Illuminate\Database\Query\Processors\SqlServerProcessor;
-use Winter\Storm\Database\Query\Grammars\SqlServerGrammar as QueryGrammar;
use Illuminate\Database\Schema\Grammars\SqlServerGrammar as SchemaGrammar;
+use Winter\Storm\Database\Query\Grammars\SqlServerGrammar as QueryGrammar;
+/**
+ * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar
+ */
class SqlServerConnection extends Connection
{
/**
@@ -57,7 +60,7 @@ public function transaction(Closure $callback, $attempts = 1)
/**
* Get the default query grammar instance.
*
- * @return \Illuminate\Database\Query\Grammars\SqlServerGrammar
+ * @return \Illuminate\Database\Grammar
*/
protected function getDefaultQueryGrammar()
{
@@ -71,7 +74,7 @@ protected function getDefaultQueryGrammar()
*/
public function getSchemaBuilder()
{
- if (is_null($this->schemaGrammar)) {
+ if (!isset($this->schemaGrammar)) {
$this->useDefaultSchemaGrammar();
}
@@ -81,7 +84,7 @@ public function getSchemaBuilder()
/**
* Get the default schema grammar instance.
*
- * @return \Illuminate\Database\Schema\Grammars\SqlServerGrammar
+ * @return \Illuminate\Database\Grammar
*/
protected function getDefaultSchemaGrammar()
{
@@ -101,10 +104,10 @@ protected function getDefaultPostProcessor()
/**
* Get the Doctrine DBAL driver.
*
- * @return \Doctrine\DBAL\Driver\PDOSqlsrv\Driver
+ * @return \Illuminate\Database\PDO\SqlServerDriver
*/
protected function getDoctrineDriver()
{
- return new DoctrineDriver;
+ return new SqlServerDriver;
}
}
diff --git a/src/Database/Connectors/ConnectionFactory.php b/src/Database/Connectors/ConnectionFactory.php
index 0e7f04a5e..629630037 100644
--- a/src/Database/Connectors/ConnectionFactory.php
+++ b/src/Database/Connectors/ConnectionFactory.php
@@ -32,7 +32,9 @@ protected function createPdoResolverWithHosts(array $config)
}
}
- throw $e;
+ if (isset($e)) {
+ throw $e;
+ }
};
}
diff --git a/src/Database/DataFeed.php b/src/Database/DataFeed.php
index 7d12f0ace..8d51a3f07 100644
--- a/src/Database/DataFeed.php
+++ b/src/Database/DataFeed.php
@@ -1,10 +1,9 @@
processCollection();
$bindings = $query->bindings;
$records = sprintf("(%s) as records", $query->toSql());
- $result = Db::table(Db::raw($records))->selectRaw("COUNT(*) as total");
+ $result = DB::table(DB::raw($records))->selectRaw("COUNT(*) as total");
// Set the bindings, if present
foreach ($bindings as $type => $params) {
@@ -134,8 +133,8 @@ public function get()
*/
$mixedArray = [];
foreach ($records as $record) {
- $tagName = $record->{$this->tagVar};
- $mixedArray[$tagName][] = $record->id;
+ $tagName = $record->getAttribute($this->tagVar);
+ $mixedArray[$tagName][] = $record->getKey();
}
/*
@@ -155,8 +154,8 @@ public function get()
foreach ($records as $record) {
$tagName = $record->{$this->tagVar};
- $obj = $collectionArray[$tagName]->find($record->id);
- $obj->{$this->tagVar} = $tagName;
+ $obj = $collectionArray[$tagName]->find($record->getKey());
+ $obj->setAttribute($this->tagVar, $tagName);
$dataArray[] = $obj;
}
diff --git a/src/Database/Model.php b/src/Database/Model.php
index 4e5315fad..e708566f7 100644
--- a/src/Database/Model.php
+++ b/src/Database/Model.php
@@ -1,13 +1,13 @@
save(null, $sessionKey);
+ $model->save([], $sessionKey);
return $model;
}
@@ -477,7 +479,7 @@ public static function fetched($callback)
/**
* Checks if an attribute is jsonable or not.
*
- * @return array
+ * @return bool
*/
public function isJsonable($key)
{
@@ -530,7 +532,7 @@ public function getObservableEvents()
/**
* Get a fresh timestamp for the model.
*
- * @return \Winter\Storm\Argon\Argon
+ * @return \Illuminate\Support\Carbon
*/
public function freshTimestamp()
{
@@ -605,10 +607,10 @@ protected function asDateTime($value)
/**
* Convert a DateTime to a storable string.
*
- * @param \DateTime|int $value
- * @return string
+ * @param \DateTime|int|null $value
+ * @return string|null
*/
- public function fromDateTime($value)
+ public function fromDateTime($value = null)
{
if (is_null($value)) {
return $value;
@@ -621,7 +623,7 @@ public function fromDateTime($value)
* Create a new Eloquent query builder for the model.
*
* @param \Winter\Storm\Database\QueryBuilder $query
- * @return \Winter\Storm\Database\Builder|static
+ * @return \Winter\Storm\Database\Builder
*/
public function newEloquentBuilder($query)
{
@@ -670,7 +672,7 @@ public function __get($name)
public function __set($name, $value)
{
- return $this->extendableSet($name, $value);
+ $this->extendableSet($name, $value);
}
public function __call($name, $params)
@@ -741,7 +743,7 @@ public function newPivot(EloquentModel $parent, array $attributes, $table, $exis
* @param array $attributes
* @param string $table
* @param bool $exists
- * @return \Winter\Storm\Database\Pivot
+ * @return \Winter\Storm\Database\Pivot|null
*/
public function newRelationPivot($relationName, $parent, $attributes, $table, $exists)
{
@@ -762,7 +764,7 @@ public function newRelationPivot($relationName, $parent, $attributes, $table, $e
* @param array $options
* @return bool
*/
- protected function saveInternal($options = [])
+ protected function saveInternal(array $options = [])
{
/**
* @event model.saveInternal
@@ -803,15 +805,6 @@ protected function saveInternal($options = [])
return $result;
}
- /*
- * If there is nothing to update, Eloquent will not fire afterSave(),
- * events should still fire for consistency.
- */
- if ($result === null) {
- $this->fireModelEvent('updated', false);
- $this->fireModelEvent('saved', false);
- }
-
// Apply post deferred bindings
if ($this->sessionKey !== null) {
$this->commitDeferredAfter($this->sessionKey);
@@ -823,10 +816,10 @@ protected function saveInternal($options = [])
/**
* Save the model to the database.
* @param array $options
- * @param null $sessionKey
+ * @param string|null $sessionKey
* @return bool
*/
- public function save(array $options = null, $sessionKey = null)
+ public function save(?array $options = [], $sessionKey = null)
{
$this->sessionKey = $sessionKey;
return $this->saveInternal(['force' => false] + (array) $options);
@@ -834,15 +827,16 @@ public function save(array $options = null, $sessionKey = null)
/**
* Save the model and all of its relationships.
+ *
* @param array $options
- * @param null $sessionKey
+ * @param string|null $sessionKey
* @return bool
*/
- public function push($options = null, $sessionKey = null)
+ public function push(?array $options = [], $sessionKey = null)
{
$always = Arr::get($options, 'always', false);
- if (!$this->save(null, $sessionKey) && !$always) {
+ if (!$this->save([], $sessionKey) && !$always) {
return false;
}
@@ -874,11 +868,12 @@ public function push($options = null, $sessionKey = null)
/**
* Pushes the first level of relations even if the parent
* model has no changes.
+ *
* @param array $options
- * @param string $sessionKey
+ * @param string|null $sessionKey
* @return bool
*/
- public function alwaysPush($options, $sessionKey)
+ public function alwaysPush(?array $options = [], $sessionKey = null)
{
return $this->push(['always' => true] + (array) $options, $sessionKey);
}
@@ -1201,7 +1196,7 @@ public function attributesToArray()
* Set a given attribute on the model.
* @param string $key
* @param mixed $value
- * @return void
+ * @return mixed|null
*/
public function setAttribute($key, $value)
{
@@ -1216,7 +1211,8 @@ public function setAttribute($key, $value)
* Handle direct relation setting
*/
if ($this->hasRelation($key) && !$this->hasSetMutator($key)) {
- return $this->setRelationValue($key, $value);
+ $this->setRelationValue($key, $value);
+ return;
}
/**
diff --git a/src/Database/ModelInterface.php b/src/Database/ModelInterface.php
new file mode 100644
index 000000000..3bad3e2e3
--- /dev/null
+++ b/src/Database/ModelInterface.php
@@ -0,0 +1,20 @@
+cast == 'date' && !is_null($value)) {
+ if ($this->getAttribute('cast') === 'date' && !is_null($value)) {
return $this->asDateTime($value);
}
@@ -34,7 +33,7 @@ public function getNewValueAttribute($value)
*/
public function getOldValueAttribute($value)
{
- if ($this->cast == 'date' && !is_null($value)) {
+ if ($this->getAttribute('cast') === 'date' && !is_null($value)) {
return $this->asDateTime($value);
}
diff --git a/src/Database/MorphPivot.php b/src/Database/MorphPivot.php
new file mode 100644
index 000000000..de1b324e0
--- /dev/null
+++ b/src/Database/MorphPivot.php
@@ -0,0 +1,190 @@
+where($this->morphType, $this->morphClass);
+
+ return parent::setKeysForSaveQuery($query);
+ }
+
+ /**
+ * Set the keys for a select query.
+ *
+ * @param \Illuminate\Database\Eloquent\Builder $query
+ * @return \Illuminate\Database\Eloquent\Builder
+ */
+ protected function setKeysForSelectQuery($query)
+ {
+ $query->where($this->morphType, $this->morphClass);
+
+ return parent::setKeysForSelectQuery($query);
+ }
+
+ /**
+ * Delete the pivot model record from the database.
+ *
+ * @return int
+ */
+ public function delete()
+ {
+ if (isset($this->attributes[$this->getKeyName()])) {
+ return (int) parent::delete();
+ }
+
+ if ($this->fireModelEvent('deleting') === false) {
+ return 0;
+ }
+
+ $query = $this->getDeleteQuery();
+
+ $query->where($this->morphType, $this->morphClass);
+
+ return tap($query->delete(), function () {
+ $this->fireModelEvent('deleted', false);
+ });
+ }
+
+ /**
+ * Get the morph type for the pivot.
+ *
+ * @return string
+ */
+ public function getMorphType()
+ {
+ return $this->morphType;
+ }
+
+
+ /**
+ * Set the morph type for the pivot.
+ *
+ * @param string $morphType
+ * @return $this
+ */
+ public function setMorphType($morphType)
+ {
+ $this->morphType = $morphType;
+
+ return $this;
+ }
+
+ /**
+ * Set the morph class for the pivot.
+ *
+ * @param string $morphClass
+ * @return \Winter\Storm\Database\MorphPivot
+ */
+ public function setMorphClass($morphClass)
+ {
+ $this->morphClass = $morphClass;
+
+ return $this;
+ }
+
+
+ /**
+ * Get the queueable identity for the entity.
+ *
+ * @return mixed
+ */
+ public function getQueueableId()
+ {
+ if (isset($this->attributes[$this->getKeyName()])) {
+ return $this->getKey();
+ }
+
+ return sprintf(
+ '%s:%s:%s:%s:%s:%s',
+ $this->foreignKey,
+ $this->getAttribute($this->foreignKey),
+ $this->relatedKey,
+ $this->getAttribute($this->relatedKey),
+ $this->morphType,
+ $this->morphClass
+ );
+ }
+
+ /**
+ * Get a new query to restore one or more models by their queueable IDs.
+ *
+ * @param array|int $ids
+ * @return \Illuminate\Database\Eloquent\Builder
+ */
+ public function newQueryForRestoration($ids)
+ {
+ if (is_array($ids)) {
+ return $this->newQueryForCollectionRestoration($ids);
+ }
+
+ if (!str_contains($ids, ':')) {
+ return parent::newQueryForRestoration($ids);
+ }
+
+ $segments = explode(':', $ids);
+
+ return $this->newQueryWithoutScopes()
+ ->where($segments[0], $segments[1])
+ ->where($segments[2], $segments[3])
+ ->where($segments[4], $segments[5]);
+ }
+
+ /**
+ * Get a new query to restore multiple models by their queueable IDs.
+ *
+ * @param array $ids
+ * @return \Illuminate\Database\Eloquent\Builder
+ */
+ protected function newQueryForCollectionRestoration(array $ids)
+ {
+ $ids = array_values($ids);
+
+ if (!str_contains($ids[0], ':')) {
+ return parent::newQueryForRestoration($ids);
+ }
+
+ $query = $this->newQueryWithoutScopes();
+
+ foreach ($ids as $id) {
+ $segments = explode(':', $id);
+
+ $query->orWhere(function ($query) use ($segments) {
+ return $query->where($segments[0], $segments[1])
+ ->where($segments[2], $segments[3])
+ ->where($segments[4], $segments[5]);
+ });
+ }
+
+ return $query;
+ }
+}
diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php
index 8f9ee21b2..9126576ad 100644
--- a/src/Database/QueryBuilder.php
+++ b/src/Database/QueryBuilder.php
@@ -1,7 +1,8 @@
columns)) {
- $this->columns = $columns;
- }
-
$cache = MemoryCache::instance();
if ($cache->has($this)) {
@@ -140,14 +137,10 @@ protected function getDuplicateCached($columns = ['*'])
* Execute the query as a cached "select" statement.
*
* @param array $columns
- * @return array
+ * @return BaseCollection
*/
public function getCached($columns = ['*'])
{
- if (is_null($this->columns)) {
- $this->columns = $columns;
- }
-
// If the query is requested to be cached, we will cache it using a unique key
// for this database connection and query statement, including the bindings
// that are used on this query, providing great convenience when caching.
@@ -388,10 +381,8 @@ public function flushDuplicateCache()
/**
* Enable the memory cache on the query.
- *
- * @return \Illuminate\Database\Query\Builder|static
*/
- public function enableDuplicateCache()
+ public function enableDuplicateCache(): static
{
$this->cachingDuplicateQueries = true;
@@ -400,10 +391,8 @@ public function enableDuplicateCache()
/**
* Disable the memory cache on the query.
- *
- * @return \Illuminate\Database\Query\Builder|static
*/
- public function disableDuplicateCache()
+ public function disableDuplicateCache(): static
{
$this->cachingDuplicateQueries = false;
@@ -467,7 +456,7 @@ protected function runPaginationCountQuery($columns = ['*'])
if ($this->groups || $this->havings) {
$clone = $this->cloneForPaginationCount();
- if (is_null($clone->columns) && !empty($this->joins)) {
+ if (empty($clone->columns) && !empty($this->joins)) {
$clone->select($this->from . '.*');
}
diff --git a/src/Database/Relations/AttachMany.php b/src/Database/Relations/AttachMany.php
index 856978f6f..06e0005b1 100644
--- a/src/Database/Relations/AttachMany.php
+++ b/src/Database/Relations/AttachMany.php
@@ -1,25 +1,27 @@
getPath();
}
}
diff --git a/src/Database/Relations/AttachOne.php b/src/Database/Relations/AttachOne.php
index c8687cc2f..33a676b72 100644
--- a/src/Database/Relations/AttachOne.php
+++ b/src/Database/Relations/AttachOne.php
@@ -5,21 +5,23 @@
use Illuminate\Database\Eloquent\Relations\MorphOne as MorphOneBase;
use Winter\Storm\Database\Attach\File as FileModel;
+/**
+ * @phpstan-property \Winter\Storm\Database\Model $parent
+ */
class AttachOne extends MorphOneBase
{
- use AttachOneOrMany;
- use DefinedConstraints;
+ use Concerns\AttachOneOrMany;
+ use Concerns\DefinedConstraints;
/**
* Create a new has many relationship instance.
* @param Builder $query
* @param Model $parent
- * @param $type
- * @param $id
- * @param $isPublic
- * @param $localKey
+ * @param string $type
+ * @param string $id
+ * @param bool $isPublic
+ * @param string $localKey
* @param null|string $relationName
- * @param null|string $keyType
*/
public function __construct(Builder $query, Model $parent, $type, $id, $isPublic, $localKey, $relationName = null)
{
diff --git a/src/Database/Relations/BelongsTo.php b/src/Database/Relations/BelongsTo.php
index 370102ee7..798906eac 100644
--- a/src/Database/Relations/BelongsTo.php
+++ b/src/Database/Relations/BelongsTo.php
@@ -4,10 +4,13 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo as BelongsToBase;
+/**
+ * @phpstan-property \Winter\Storm\Database\Model $child
+ */
class BelongsTo extends BelongsToBase
{
- use DeferOneOrMany;
- use DefinedConstraints;
+ use Concerns\DeferOneOrMany;
+ use Concerns\DefinedConstraints;
/**
* @var string The "name" of the relationship.
diff --git a/src/Database/Relations/BelongsToMany.php b/src/Database/Relations/BelongsToMany.php
index f498c3641..ec140847c 100644
--- a/src/Database/Relations/BelongsToMany.php
+++ b/src/Database/Relations/BelongsToMany.php
@@ -1,427 +1,10 @@
addDefinedConstraints();
- }
-
- /**
- * Get the select columns for the relation query.
- *
- * @param array $columns
- * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
- */
- protected function shouldSelect(array $columns = ['*'])
- {
- if ($this->countMode) {
- return $this->table.'.'.$this->foreignPivotKey.' as pivot_'.$this->foreignPivotKey;
- }
-
- if ($columns == ['*']) {
- $columns = [$this->related->getTable().'.*'];
- }
-
- if ($this->orphanMode) {
- return $columns;
- }
-
- return array_merge($columns, $this->aliasedPivotColumns());
- }
-
- /**
- * Save the supplied related model with deferred binding support.
- */
- public function save(Model $model, array $pivotData = [], $sessionKey = null)
- {
- $model->save();
- $this->add($model, $sessionKey, $pivotData);
- return $model;
- }
-
- /**
- * Override sync() method of BelongToMany relation in order to flush the query cache.
- * @param array $ids
- * @param bool $detaching
- * @return array
- */
- public function sync($ids, $detaching = true)
- {
- $changed = parent::sync($ids, $detaching);
-
- $this->flushDuplicateCache();
-
- return $changed;
- }
-
- /**
- * Create a new instance of this related model with deferred binding support.
- */
- public function create(array $attributes = [], array $pivotData = [], $sessionKey = null)
- {
- $model = $this->related->create($attributes);
-
- $this->add($model, $sessionKey, $pivotData);
-
- return $model;
- }
-
- /**
- * Override attach() method of BelongToMany relation.
- * This is necessary in order to fire 'model.relation.beforeAttach', 'model.relation.afterAttach' events
- * @param mixed $id
- * @param array $attributes
- * @param bool $touch
- */
- public function attach($id, array $attributes = [], $touch = true)
- {
- $insertData = $this->formatAttachRecords($this->parseIds($id), $attributes);
- $attachedIdList = array_pluck($insertData, $this->relatedPivotKey);
-
- /**
- * @event model.relation.beforeAttach
- * Called before creating a new relation between models (only for BelongsToMany relation)
- *
- * Example usage:
- *
- * $model->bindEvent('model.relation.beforeAttach', function (string $relationName, array $attachedIdList, array $insertData) use (\Winter\Storm\Database\Model $model) {
- * if (!$model->isRelationValid($attachedIdList)) {
- * throw new \Exception("Invalid relation!");
- * return false;
- * }
- * });
- *
- */
- if ($this->parent->fireEvent('model.relation.beforeAttach', [$this->relationName, $attachedIdList, $insertData], true) === false) {
- return;
- }
-
- // Here we will insert the attachment records into the pivot table. Once we have
- // inserted the records, we will touch the relationships if necessary and the
- // function will return. We can parse the IDs before inserting the records.
- $this->newPivotStatement()->insert($insertData);
-
- if ($touch) {
- $this->touchIfTouching();
- }
-
- /**
- * @event model.relation.afterAttach
- * Called after creating a new relation between models (only for BelongsToMany relation)
- *
- * Example usage:
- *
- * $model->bindEvent('model.relation.afterAttach', function (string $relationName, array $attachedIdList, array $insertData) use (\Winter\Storm\Database\Model $model) {
- * traceLog("New relation {$relationName} was created", $attachedIdList);
- * });
- *
- */
- $this->parent->fireEvent('model.relation.afterAttach', [$this->relationName, $attachedIdList, $insertData]);
- }
-
- /**
- * Override detach() method of BelongToMany relation.
- * This is necessary in order to fire 'model.relation.beforeDetach', 'model.relation.afterDetach' events
- * @param null $ids
- * @param bool $touch
- * @return int|void
- */
- public function detach($ids = null, $touch = true)
- {
- $attachedIdList = $this->parseIds($ids);
- if (empty($attachedIdList)) {
- $attachedIdList = $this->newPivotQuery()->lists($this->relatedPivotKey);
- }
-
- /**
- * @event model.relation.beforeDetach
- * Called before removing a relation between models (only for BelongsToMany relation)
- *
- * Example usage:
- *
- * $model->bindEvent('model.relation.beforeDetach', function (string $relationName, array $attachedIdList) use (\Winter\Storm\Database\Model $model) {
- * if (!$model->isRelationValid($attachedIdList)) {
- * throw new \Exception("Invalid relation!");
- * return false;
- * }
- * });
- *
- */
- if ($this->parent->fireEvent('model.relation.beforeDetach', [$this->relationName, $attachedIdList], true) === false) {
- return;
- }
-
- /**
- * @see Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable
- */
- parent::detach($attachedIdList, $touch);
-
- /**
- * @event model.relation.afterDetach
- * Called after removing a relation between models (only for BelongsToMany relation)
- *
- * Example usage:
- *
- * $model->bindEvent('model.relation.afterDetach', function (string $relationName, array $attachedIdList) use (\Winter\Storm\Database\Model $model) {
- * traceLog("Relation {$relationName} was removed", $attachedIdList);
- * });
- *
- */
- $this->parent->fireEvent('model.relation.afterDetach', [$this->relationName, $attachedIdList]);
- }
-
- /**
- * Adds a model to this relationship type.
- */
- public function add(Model $model, $sessionKey = null, $pivotData = [])
- {
- if (is_array($sessionKey)) {
- $pivotData = $sessionKey;
- $sessionKey = null;
- }
-
- if ($sessionKey === null || $sessionKey === false) {
- $this->attach($model->getKey(), $pivotData);
- $this->parent->reloadRelations($this->relationName);
- }
- else {
- $this->parent->bindDeferred($this->relationName, $model, $sessionKey, $pivotData);
- }
- }
-
- /**
- * Removes a model from this relationship type.
- */
- public function remove(Model $model, $sessionKey = null)
- {
- if ($sessionKey === null) {
- $this->detach($model->getKey());
- $this->parent->reloadRelations($this->relationName);
- }
- else {
- $this->parent->unbindDeferred($this->relationName, $model, $sessionKey);
- }
- }
-
- /**
- * Get a paginator for the "select" statement. Complies with Winter Storm.
- *
- * @param int $perPage
- * @param int $currentPage
- * @param array $columns
- * @param string $pageName
- * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
- */
- public function paginate($perPage = 15, $currentPage = null, $columns = ['*'], $pageName = 'page')
- {
- $this->query->addSelect($this->shouldSelect($columns));
-
- $paginator = $this->query->paginate($perPage, $currentPage, $columns);
-
- $this->hydratePivotRelation($paginator->items());
-
- return $paginator;
- }
-
- /**
- * Create a new pivot model instance.
- *
- * @param array $attributes
- * @param bool $exists
- * @return \Illuminate\Database\Eloquent\Relations\Pivot
- */
- public function newPivot(array $attributes = [], $exists = false)
- {
- /*
- * Winter looks to the relationship parent
- */
- $pivot = $this->parent->newRelationPivot($this->relationName, $this->parent, $attributes, $this->table, $exists);
-
- /*
- * Laravel looks to the related model
- */
- if (empty($pivot)) {
- $pivot = $this->related->newPivot($this->parent, $attributes, $this->table, $exists);
- }
-
- return $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey);
- }
-
- /**
- * Helper for setting this relationship using various expected
- * values. For example, $model->relation = $value;
- */
- public function setSimpleValue($value)
- {
- $relationModel = $this->getRelated();
-
- /*
- * Nulling the relationship
- */
- if (!$value) {
- // Disassociate in memory immediately
- $this->parent->setRelation($this->relationName, $relationModel->newCollection());
-
- // Perform sync when the model is saved
- $this->parent->bindEventOnce('model.afterSave', function () use ($value) {
- $this->detach();
- });
- return;
- }
-
- /*
- * Convert models to keys
- */
- if ($value instanceof Model) {
- $value = $value->getKey();
- }
- elseif (is_array($value)) {
- foreach ($value as $_key => $_value) {
- if ($_value instanceof Model) {
- $value[$_key] = $_value->getKey();
- }
- }
- }
-
- /*
- * Convert scalar to array
- */
- if (!is_array($value) && !$value instanceof CollectionBase) {
- $value = [$value];
- }
-
- /*
- * Setting the relationship
- */
- $relationCollection = $value instanceof CollectionBase
- ? $value
- : $relationModel->whereIn($relationModel->getKeyName(), $value)->get();
-
- // Associate in memory immediately
- $this->parent->setRelation($this->relationName, $relationCollection);
-
- // Perform sync when the model is saved
- $this->parent->bindEventOnce('model.afterSave', function () use ($value) {
- $this->sync($value);
- });
- }
-
- /**
- * Helper for getting this relationship simple value,
- * generally useful with form values.
- */
- public function getSimpleValue()
- {
- $value = [];
-
- $relationName = $this->relationName;
-
- $sessionKey = $this->parent->sessionKey;
-
- if ($this->parent->relationLoaded($relationName)) {
- $related = $this->getRelated();
-
- $value = $this->parent->getRelation($relationName)->pluck($related->getKeyName())->all();
- }
- else {
- $value = $this->allRelatedIds($sessionKey)->all();
- }
-
- return $value;
- }
-
- /**
- * Get all of the IDs for the related models, with deferred binding support
- *
- * @param string $sessionKey
- * @return \Winter\Storm\Support\Collection
- */
- public function allRelatedIds($sessionKey = null)
- {
- $related = $this->getRelated();
-
- $fullKey = $related->getQualifiedKeyName();
-
- $query = $sessionKey ? $this->withDeferred($sessionKey) : $this;
-
- return $query->getQuery()->select($fullKey)->pluck($related->getKeyName());
- }
-
- /**
- * Get the fully qualified foreign key for the relation.
- *
- * @return string
- */
- public function getForeignKey()
- {
- return $this->table.'.'.$this->foreignPivotKey;
- }
-
- /**
- * Get the fully qualified "other key" for the relation.
- *
- * @return string
- */
- public function getOtherKey()
- {
- return $this->table.'.'.$this->relatedPivotKey;
- }
-
- /**
- * @deprecated Use allRelatedIds instead. Remove if year >= 2018.
- */
- public function getRelatedIds($sessionKey = null)
- {
- traceLog('Method BelongsToMany::getRelatedIds has been deprecated, use BelongsToMany::allRelatedIds instead.');
- return $this->allRelatedIds($sessionKey)->all();
- }
+ use Concerns\BelongsOrMorphsToMany;
+ use Concerns\DeferOneOrMany;
+ use Concerns\DefinedConstraints;
}
diff --git a/src/Database/Relations/AttachOneOrMany.php b/src/Database/Relations/Concerns/AttachOneOrMany.php
similarity index 95%
rename from src/Database/Relations/AttachOneOrMany.php
rename to src/Database/Relations/Concerns/AttachOneOrMany.php
index 0dec41d28..441e6b218 100644
--- a/src/Database/Relations/AttachOneOrMany.php
+++ b/src/Database/Relations/Concerns/AttachOneOrMany.php
@@ -1,10 +1,11 @@
-public) && $this->public !== null) {
+ if (isset($this->public)) {
return $this->public;
}
@@ -117,7 +118,7 @@ public function save(Model $model, $sessionKey = null)
$this->delete();
}
- if (!array_key_exists('is_public', $model->attributes)) {
+ if (!array_key_exists('is_public', $model->getAttributes())) {
$model->setAttribute('is_public', $this->isPublic());
}
@@ -161,8 +162,8 @@ public function create(array $attributes = [], $sessionKey = null)
*/
public function add(Model $model, $sessionKey = null)
{
- if (!array_key_exists('is_public', $model->attributes)) {
- $model->is_public = $this->isPublic();
+ if (!array_key_exists('is_public', $model->getAttributes())) {
+ $model->setAttribute('is_public', $this->isPublic());
}
if ($sessionKey === null) {
@@ -270,7 +271,6 @@ public function makeValidationFile($value)
$value->getLocalPath(),
$value->file_name,
$value->content_type,
- $value->file_size,
null,
true
);
diff --git a/src/Database/Relations/Concerns/BelongsOrMorphsToMany.php b/src/Database/Relations/Concerns/BelongsOrMorphsToMany.php
new file mode 100644
index 000000000..7e346d3d6
--- /dev/null
+++ b/src/Database/Relations/Concerns/BelongsOrMorphsToMany.php
@@ -0,0 +1,422 @@
+addDefinedConstraints();
+ }
+
+ /**
+ * Get the select columns for the relation query.
+ *
+ * @param array $columns
+ * @return array|string
+ */
+ protected function shouldSelect(array $columns = ['*'])
+ {
+ if ($this->countMode) {
+ return $this->table.'.'.$this->foreignPivotKey.' as pivot_'.$this->foreignPivotKey;
+ }
+
+ if ($columns == ['*']) {
+ $columns = [$this->related->getTable().'.*'];
+ }
+
+ if ($this->orphanMode) {
+ return $columns;
+ }
+
+ return array_merge($columns, $this->aliasedPivotColumns());
+ }
+
+ /**
+ * Save the supplied related model with deferred binding support.
+ */
+ public function save(Model $model, array $pivotData = [], $sessionKey = null)
+ {
+ $model->save();
+ $this->add($model, $sessionKey, $pivotData);
+ return $model;
+ }
+
+ /**
+ * Override sync() method of BelongToMany relation in order to flush the query cache.
+ * @param array $ids
+ * @param bool $detaching
+ * @return array
+ */
+ public function sync($ids, $detaching = true)
+ {
+ $changed = parent::sync($ids, $detaching);
+
+ $this->flushDuplicateCache();
+
+ return $changed;
+ }
+
+ /**
+ * Create a new instance of this related model with deferred binding support.
+ */
+ public function create(array $attributes = [], array $pivotData = [], $sessionKey = null)
+ {
+ $model = $this->related->create($attributes);
+
+ $this->add($model, $sessionKey, $pivotData);
+
+ return $model;
+ }
+
+ /**
+ * Override attach() method of BelongToMany relation.
+ * This is necessary in order to fire 'model.relation.beforeAttach', 'model.relation.afterAttach' events
+ * @param mixed $id
+ * @param array $attributes
+ * @param bool $touch
+ */
+ public function attach($id, array $attributes = [], $touch = true)
+ {
+ $insertData = $this->formatAttachRecords($this->parseIds($id), $attributes);
+ $attachedIdList = array_pluck($insertData, $this->relatedPivotKey);
+
+ /**
+ * @event model.relation.beforeAttach
+ * Called before creating a new relation between models (only for BelongsToMany relation)
+ *
+ * Example usage:
+ *
+ * $model->bindEvent('model.relation.beforeAttach', function (string $relationName, array $attachedIdList, array $insertData) use (\Winter\Storm\Database\Model $model) {
+ * if (!$model->isRelationValid($attachedIdList)) {
+ * throw new \Exception("Invalid relation!");
+ * return false;
+ * }
+ * });
+ *
+ */
+ if ($this->parent->fireEvent('model.relation.beforeAttach', [$this->relationName, $attachedIdList, $insertData], true) === false) {
+ return;
+ }
+
+ // Here we will insert the attachment records into the pivot table. Once we have
+ // inserted the records, we will touch the relationships if necessary and the
+ // function will return. We can parse the IDs before inserting the records.
+ $this->newPivotStatement()->insert($insertData);
+
+ if ($touch) {
+ $this->touchIfTouching();
+ }
+
+ /**
+ * @event model.relation.afterAttach
+ * Called after creating a new relation between models (only for BelongsToMany relation)
+ *
+ * Example usage:
+ *
+ * $model->bindEvent('model.relation.afterAttach', function (string $relationName, array $attachedIdList, array $insertData) use (\Winter\Storm\Database\Model $model) {
+ * traceLog("New relation {$relationName} was created", $attachedIdList);
+ * });
+ *
+ */
+ $this->parent->fireEvent('model.relation.afterAttach', [$this->relationName, $attachedIdList, $insertData]);
+ }
+
+ /**
+ * Override detach() method of BelongToMany relation.
+ * This is necessary in order to fire 'model.relation.beforeDetach', 'model.relation.afterDetach' events
+ * @param Collection|Model|array|null $ids
+ * @param bool $touch
+ * @return int|void
+ */
+ public function detach($ids = null, $touch = true)
+ {
+ $attachedIdList = $this->parseIds($ids);
+ if (empty($attachedIdList)) {
+ $attachedIdList = $this->newPivotQuery()->lists($this->relatedPivotKey);
+ }
+
+ /**
+ * @event model.relation.beforeDetach
+ * Called before removing a relation between models (only for BelongsToMany relation)
+ *
+ * Example usage:
+ *
+ * $model->bindEvent('model.relation.beforeDetach', function (string $relationName, array $attachedIdList) use (\Winter\Storm\Database\Model $model) {
+ * if (!$model->isRelationValid($attachedIdList)) {
+ * throw new \Exception("Invalid relation!");
+ * return false;
+ * }
+ * });
+ *
+ */
+ if ($this->parent->fireEvent('model.relation.beforeDetach', [$this->relationName, $attachedIdList], true) === false) {
+ return;
+ }
+
+ /**
+ * @see Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable
+ */
+ parent::detach($attachedIdList, $touch);
+
+ /**
+ * @event model.relation.afterDetach
+ * Called after removing a relation between models (only for BelongsToMany relation)
+ *
+ * Example usage:
+ *
+ * $model->bindEvent('model.relation.afterDetach', function (string $relationName, array $attachedIdList) use (\Winter\Storm\Database\Model $model) {
+ * traceLog("Relation {$relationName} was removed", $attachedIdList);
+ * });
+ *
+ */
+ $this->parent->fireEvent('model.relation.afterDetach', [$this->relationName, $attachedIdList]);
+ }
+
+ /**
+ * Adds a model to this relationship type.
+ */
+ public function add(Model $model, $sessionKey = null, $pivotData = [])
+ {
+ if (is_array($sessionKey)) {
+ $pivotData = $sessionKey;
+ $sessionKey = null;
+ }
+
+ if ($sessionKey === null || $sessionKey === false) {
+ $this->attach($model->getKey(), $pivotData);
+ $this->parent->reloadRelations($this->relationName);
+ }
+ else {
+ $this->parent->bindDeferred($this->relationName, $model, $sessionKey, $pivotData);
+ }
+ }
+
+ /**
+ * Removes a model from this relationship type.
+ */
+ public function remove(Model $model, $sessionKey = null)
+ {
+ if ($sessionKey === null) {
+ $this->detach($model->getKey());
+ $this->parent->reloadRelations($this->relationName);
+ }
+ else {
+ $this->parent->unbindDeferred($this->relationName, $model, $sessionKey);
+ }
+ }
+
+ /**
+ * Get a paginator for the "select" statement. Complies with Winter Storm.
+ *
+ * @param int $perPage
+ * @param int $currentPage
+ * @param array $columns
+ * @param string $pageName
+ * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+ */
+ public function paginate($perPage = 15, $currentPage = null, $columns = ['*'], $pageName = 'page')
+ {
+ $this->query->addSelect($this->shouldSelect($columns));
+
+ $paginator = $this->query->paginate($perPage, $currentPage, $columns);
+
+ $this->hydratePivotRelation($paginator->items());
+
+ return $paginator;
+ }
+
+ /**
+ * Create a new pivot model instance.
+ *
+ * @param array $attributes
+ * @param bool $exists
+ * @return \Illuminate\Database\Eloquent\Relations\Pivot
+ */
+ public function newPivot(array $attributes = [], $exists = false)
+ {
+ /*
+ * Winter looks to the relationship parent
+ */
+ $pivot = $this->parent->newRelationPivot($this->relationName, $this->parent, $attributes, $this->table, $exists);
+
+ /*
+ * Laravel looks to the related model
+ */
+ if (empty($pivot)) {
+ $pivot = $this->related->newPivot($this->parent, $attributes, $this->table, $exists);
+ }
+
+ return $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey);
+ }
+
+ /**
+ * Helper for setting this relationship using various expected
+ * values. For example, $model->relation = $value;
+ */
+ public function setSimpleValue($value)
+ {
+ $relationModel = $this->getRelated();
+
+ /*
+ * Nulling the relationship
+ */
+ if (!$value) {
+ // Disassociate in memory immediately
+ $this->parent->setRelation($this->relationName, $relationModel->newCollection());
+
+ // Perform sync when the model is saved
+ $this->parent->bindEventOnce('model.afterSave', function () {
+ $this->detach();
+ });
+ return;
+ }
+
+ /*
+ * Convert models to keys
+ */
+ if ($value instanceof Model) {
+ $value = $value->getKey();
+ }
+ elseif (is_array($value)) {
+ foreach ($value as $_key => $_value) {
+ if ($_value instanceof Model) {
+ $value[$_key] = $_value->getKey();
+ }
+ }
+ }
+
+ /*
+ * Convert scalar to array
+ */
+ if (!is_array($value) && !$value instanceof Collection) {
+ $value = [$value];
+ }
+
+ /*
+ * Setting the relationship
+ */
+ $relationCollection = $value instanceof Collection
+ ? $value
+ : $relationModel->whereIn($relationModel->getKeyName(), $value)->get();
+
+ // Associate in memory immediately
+ $this->parent->setRelation($this->relationName, $relationCollection);
+
+ // Perform sync when the model is saved
+ $this->parent->bindEventOnce('model.afterSave', function () use ($value) {
+ $this->sync($value);
+ });
+ }
+
+ /**
+ * Helper for getting this relationship simple value,
+ * generally useful with form values.
+ */
+ public function getSimpleValue()
+ {
+ $value = [];
+
+ $relationName = $this->relationName;
+
+ $sessionKey = $this->parent->sessionKey;
+
+ if ($this->parent->relationLoaded($relationName)) {
+ $related = $this->getRelated();
+
+ $value = $this->parent->getRelation($relationName)->pluck($related->getKeyName())->all();
+ }
+ else {
+ $value = $this->allRelatedIds($sessionKey)->all();
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get all of the IDs for the related models, with deferred binding support
+ *
+ * @param string $sessionKey
+ * @return \Illuminate\Support\Collection
+ */
+ public function allRelatedIds($sessionKey = null)
+ {
+ $related = $this->getRelated();
+
+ $fullKey = $related->getQualifiedKeyName();
+
+ $query = $sessionKey ? $this->withDeferred($sessionKey) : $this;
+
+ return $query->getQuery()->select($fullKey)->pluck($related->getKeyName());
+ }
+
+ /**
+ * Get the fully qualified foreign key for the relation.
+ *
+ * @return string
+ */
+ public function getForeignKey()
+ {
+ return $this->table.'.'.$this->foreignPivotKey;
+ }
+
+ /**
+ * Get the fully qualified "other key" for the relation.
+ *
+ * @return string
+ */
+ public function getOtherKey()
+ {
+ return $this->table.'.'.$this->relatedPivotKey;
+ }
+
+ /**
+ * @deprecated Use allRelatedIds instead. Remove if year >= 2018.
+ */
+ public function getRelatedIds($sessionKey = null)
+ {
+ traceLog('Method BelongsToMany::getRelatedIds has been deprecated, use BelongsToMany::allRelatedIds instead.');
+ return $this->allRelatedIds($sessionKey)->all();
+ }
+}
diff --git a/src/Database/Relations/DeferOneOrMany.php b/src/Database/Relations/Concerns/DeferOneOrMany.php
similarity index 83%
rename from src/Database/Relations/DeferOneOrMany.php
rename to src/Database/Relations/Concerns/DeferOneOrMany.php
index 2ff605b7f..519c9d8ba 100644
--- a/src/Database/Relations/DeferOneOrMany.php
+++ b/src/Database/Relations/Concerns/DeferOneOrMany.php
@@ -1,13 +1,14 @@
-orphanMode = true;
}
$newQuery->where(function ($query) use ($sessionKey) {
if ($this->parent->exists) {
+ /** @phpstan-ignore-next-line */
if ($this instanceof MorphToMany) {
/*
* Custom query for MorphToMany since a "join" cannot be used
@@ -35,24 +38,27 @@ public function withDeferred($sessionKey)
$query
->select($this->parent->getConnection()->raw(1))
->from($this->table)
- ->where($this->getOtherKey(), DbDongle::raw(DbDongle::getTablePrefix().$this->related->getQualifiedKeyName()))
+ ->where($this->getOtherKey(), DbDongle::raw(
+ DbDongle::getTablePrefix() . $this->related->getQualifiedKeyName()
+ ))
->where($this->getForeignKey(), $this->parent->getKey())
->where($this->getMorphType(), $this->getMorphClass());
});
- }
- elseif ($this instanceof BelongsToManyBase) {
+ /** @phpstan-ignore-next-line */
+ } elseif ($this instanceof BelongsToMany) {
/*
- * Custom query for BelongsToManyBase since a "join" cannot be used
+ * Custom query for BelongsToMany since a "join" cannot be used
*/
$query->whereExists(function ($query) {
$query
->select($this->parent->getConnection()->raw(1))
->from($this->table)
- ->where($this->getOtherKey(), DbDongle::raw(DbDongle::getTablePrefix().$this->related->getQualifiedKeyName()))
+ ->where($this->getOtherKey(), DbDongle::raw(
+ DbDongle::getTablePrefix() . $this->related->getQualifiedKeyName()
+ ))
->where($this->getForeignKey(), $this->parent->getKey());
});
- }
- else {
+ } else {
/*
* Trick the relation to add constraints to this nested query
*/
diff --git a/src/Database/Relations/DefinedConstraints.php b/src/Database/Relations/Concerns/DefinedConstraints.php
similarity index 86%
rename from src/Database/Relations/DefinedConstraints.php
rename to src/Database/Relations/Concerns/DefinedConstraints.php
index 21b1bf7fc..3df5fd83b 100644
--- a/src/Database/Relations/DefinedConstraints.php
+++ b/src/Database/Relations/Concerns/DefinedConstraints.php
@@ -1,4 +1,4 @@
-parent->getRelationDefinition($this->relationName);
@@ -76,10 +76,10 @@ public function addDefinedConstraintsToRelation($relation, $args = null)
/**
* Add query based constraints.
*
- * @param Winter\Storm\Database\QueryBuilder $query
- * @param array $args
+ * @param \Illuminate\Database\Eloquent\Relations\Relation|\Winter\Storm\Database\QueryBuilder $query
+ * @param array|null $args
*/
- public function addDefinedConstraintsToQuery($query, $args = null)
+ public function addDefinedConstraintsToQuery($query, ?array $args = null)
{
if ($args === null) {
$args = $this->parent->getRelationDefinition($this->relationName);
diff --git a/src/Database/Relations/HasOneOrMany.php b/src/Database/Relations/Concerns/HasOneOrMany.php
similarity index 97%
rename from src/Database/Relations/HasOneOrMany.php
rename to src/Database/Relations/Concerns/HasOneOrMany.php
index 82ff74446..24cb271e8 100644
--- a/src/Database/Relations/HasOneOrMany.php
+++ b/src/Database/Relations/Concerns/HasOneOrMany.php
@@ -1,6 +1,7 @@
-addDefinedConstraints();
}
- /**
- * Get the results of the relationship.
- * @return mixed
- */
- public function getResults()
- {
- // New models have no possibility of having a relationship here
- // so prevent the first orphaned relation from being used.
- if (!$this->parent->exists) {
- return null;
- }
-
- return parent::getResults();
- }
-
/**
* Helper for setting this relationship using various expected
* values. For example, $model->relation = $value;
diff --git a/src/Database/Relations/HasOneThrough.php b/src/Database/Relations/HasOneThrough.php
index 8a4497a59..efcfcfab4 100644
--- a/src/Database/Relations/HasOneThrough.php
+++ b/src/Database/Relations/HasOneThrough.php
@@ -4,9 +4,13 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasOneThrough as HasOneThroughBase;
+/**
+ * @phpstan-property \Winter\Storm\Database\Model $farParent
+ * @phpstan-property \Winter\Storm\Database\Model $parent
+ */
class HasOneThrough extends HasOneThroughBase
{
- use DefinedConstraints;
+ use Concerns\DefinedConstraints;
/**
* @var string The "name" of the relationship.
diff --git a/src/Database/Relations/MorphMany.php b/src/Database/Relations/MorphMany.php
index 10d4df8df..dca6564d9 100644
--- a/src/Database/Relations/MorphMany.php
+++ b/src/Database/Relations/MorphMany.php
@@ -6,10 +6,13 @@
use Illuminate\Database\Eloquent\Collection as CollectionBase;
use Illuminate\Database\Eloquent\Relations\MorphMany as MorphManyBase;
+/**
+ * @phpstan-property \Winter\Storm\Database\Model $parent
+ */
class MorphMany extends MorphManyBase
{
- use MorphOneOrMany;
- use DefinedConstraints;
+ use Concerns\MorphOneOrMany;
+ use Concerns\DefinedConstraints;
/**
* Create a new has many relationship instance.
diff --git a/src/Database/Relations/MorphOne.php b/src/Database/Relations/MorphOne.php
index 77afefe7d..42bbabfaf 100644
--- a/src/Database/Relations/MorphOne.php
+++ b/src/Database/Relations/MorphOne.php
@@ -4,10 +4,13 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphOne as MorphOneBase;
+/**
+ * @phpstan-property \Winter\Storm\Database\Model $parent
+ */
class MorphOne extends MorphOneBase
{
- use MorphOneOrMany;
- use DefinedConstraints;
+ use Concerns\MorphOneOrMany;
+ use Concerns\DefinedConstraints;
/**
* Create a new has many relationship instance.
diff --git a/src/Database/Relations/MorphTo.php b/src/Database/Relations/MorphTo.php
index 92e567eae..661cdc337 100644
--- a/src/Database/Relations/MorphTo.php
+++ b/src/Database/Relations/MorphTo.php
@@ -4,9 +4,13 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo as MorphToBase;
+/**
+ * @phpstan-property \Winter\Storm\Database\Model $parent
+ */
class MorphTo extends MorphToBase
{
- use DefinedConstraints;
+ use Concerns\DeferOneOrMany;
+ use Concerns\DefinedConstraints;
/**
* @var string The "name" of the relationship.
diff --git a/src/Database/Relations/MorphToMany.php b/src/Database/Relations/MorphToMany.php
index b2771ca08..e8eb53b91 100644
--- a/src/Database/Relations/MorphToMany.php
+++ b/src/Database/Relations/MorphToMany.php
@@ -1,42 +1,24 @@
addDefinedConstraints();
}
- /**
- * Set the where clause for the relation query.
- *
- * @return $this
- */
- protected function addWhereConstraints()
- {
- parent::addWhereConstraints();
-
- $this->query->where($this->table.'.'.$this->morphType, $this->morphClass);
-
- return $this;
- }
-
- /**
- * Set the constraints for an eager load of the relation.
- *
- * @param array $models
- * @return void
- */
- public function addEagerConstraints(array $models)
- {
- parent::addEagerConstraints($models);
-
- $this->query->where($this->table.'.'.$this->morphType, $this->morphClass);
- }
-
- /**
- * Create a new pivot attachment record.
- *
- * @param int $id
- * @param bool $timed
- * @return array
- */
- protected function baseAttachRecord($id, $timed)
- {
- return Arr::add(
- parent::baseAttachRecord($id, $timed),
- $this->morphType,
- $this->morphClass
- );
- }
-
- /**
- * Add the constraints for a relationship count query.
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param \Illuminate\Database\Eloquent\Builder $parentQuery
- * @param array|mixed $columns
- * @return \Illuminate\Database\Eloquent\Builder
- */
- public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
- {
- return parent::getRelationExistenceQuery($query, $parentQuery, $columns)->where(
- $this->table.'.'.$this->morphType,
- $this->morphClass
- );
- }
-
/**
* Create a new query builder for the pivot table.
*
@@ -172,24 +96,4 @@ public function newPivot(array $attributes = [], $exists = false)
return $pivot;
}
-
- /**
- * Get the foreign key "type" name.
- *
- * @return string
- */
- public function getMorphType()
- {
- return $this->morphType;
- }
-
- /**
- * Get the class name of the parent model.
- *
- * @return string
- */
- public function getMorphClass()
- {
- return $this->morphClass;
- }
}
diff --git a/src/Database/Traits/Encryptable.php b/src/Database/Traits/Encryptable.php
index 7e1844dc4..762eece0d 100644
--- a/src/Database/Traits/Encryptable.php
+++ b/src/Database/Traits/Encryptable.php
@@ -1,7 +1,7 @@
getPurgeableAttributes();
}
@@ -70,10 +69,9 @@ public function purgeAttributes($attributesToPurge = null)
$cleanAttributes = array_diff_key($attributes, array_flip($purgeable));
$originalAttributes = array_diff_key($attributes, $cleanAttributes);
- if (is_array($this->originalPurgeableValues)) {
+ if (count($this->originalPurgeableValues)) {
$this->originalPurgeableValues = array_merge($this->originalPurgeableValues, $originalAttributes);
- }
- else {
+ } else {
$this->originalPurgeableValues = $originalAttributes;
}
diff --git a/src/Database/Traits/Revisionable.php b/src/Database/Traits/Revisionable.php
index 58a724095..9dcdc459f 100644
--- a/src/Database/Traits/Revisionable.php
+++ b/src/Database/Traits/Revisionable.php
@@ -1,9 +1,9 @@
getTable())->insert($toSave);
+ DB::table($revisionModel->getTable())->insert($toSave);
$this->revisionableCleanUp();
}
diff --git a/src/Database/Traits/Sortable.php b/src/Database/Traits/Sortable.php
index 96499c99f..03ca6030d 100644
--- a/src/Database/Traits/Sortable.php
+++ b/src/Database/Traits/Sortable.php
@@ -75,6 +75,6 @@ public function setSortableOrder($itemIds, $itemOrders = null)
*/
public function getSortOrderColumn()
{
- return defined('static::SORT_ORDER') ? static::SORT_ORDER : 'sort_order';
+ return defined('static::SORT_ORDER') ? constant('static::SORT_ORDER') : 'sort_order';
}
}
diff --git a/src/Database/Traits/Validation.php b/src/Database/Traits/Validation.php
index a58d99e1a..94dca33d1 100644
--- a/src/Database/Traits/Validation.php
+++ b/src/Database/Traits/Validation.php
@@ -1,13 +1,13 @@
isValidScript($object, $file);
- Eloquent::unguard();
+ Model::unguard();
- if ($object instanceof Updates\Migration) {
+ if ($object instanceof Updates\Migration && method_exists($object, 'up')) {
$object->up();
}
- elseif ($object instanceof Updates\Seeder) {
+ elseif ($object instanceof Updates\Seeder && method_exists($object, 'run')) {
$object->run();
}
- Eloquent::reguard();
+ Model::reguard();
return true;
}
@@ -54,13 +53,13 @@ public function packDown($file)
$this->isValidScript($object, $file);
- Eloquent::unguard();
+ Model::unguard();
- if ($object instanceof Updates\Migration) {
+ if ($object instanceof Updates\Migration && method_exists($object, 'down')) {
$object->down();
}
- Eloquent::reguard();
+ Model::reguard();
return true;
}
@@ -68,12 +67,12 @@ public function packDown($file)
/**
* Resolve a migration instance from a file.
* @param string $file
- * @return object
+ * @return object|null
*/
public function resolve($file)
{
if (!File::isFile($file)) {
- return;
+ return null;
}
$instance = require_once $file;
@@ -107,7 +106,7 @@ protected function isValidScript($object, $file)
/**
* Extracts the namespace and class name from a file.
* @param string $file
- * @return string
+ * @return string|false
*/
public function getClassFromFile($file)
{
diff --git a/src/Events/Dispatcher.php b/src/Events/Dispatcher.php
index 5939fda3b..788606829 100644
--- a/src/Events/Dispatcher.php
+++ b/src/Events/Dispatcher.php
@@ -1,7 +1,6 @@
listen($this->firstClosureParameterType($events), $events, $priority);
+ $this->listen($this->firstClosureParameterType($events), $events, $priority);
+ return;
} elseif ($events instanceof QueuedClosure) {
- return $this->listen($this->firstClosureParameterType($events->closure), $events->resolve(), $priority);
+ $this->listen($this->firstClosureParameterType($events->closure), $events->resolve(), $priority);
+ return;
} elseif ($listener instanceof QueuedClosure) {
$listener = $listener->resolve();
}
@@ -102,7 +103,7 @@ public function until($event, $payload = [])
* @param string|object $event
* @param mixed $payload
* @param bool $halt
- * @return array|null
+ * @return array|mixed|null
*/
public function fire($event, $payload = [], $halt = false)
{
@@ -193,8 +194,8 @@ public function getListeners($eventName)
/**
* Sort the listeners for a given event by priority.
*
- * @param string $eventName
- * @return array
+ * @param string $eventName
+ * @return void
*/
protected function sortListeners($eventName)
{
diff --git a/src/Exception/ErrorHandler.php b/src/Exception/ErrorHandler.php
index 84e83c0ed..b429cf99e 100644
--- a/src/Exception/ErrorHandler.php
+++ b/src/Exception/ErrorHandler.php
@@ -1,10 +1,9 @@
setMask($proposedException);
}
@@ -149,9 +148,9 @@ public function handleCustomError()
/**
* Displays the detailed system exception page.
- * @return View Object containing the error page.
+ * @return \Illuminate\View\View|string Object containing the error page.
*/
- public function handleDetailedError($exception)
+ public function handleDetailedError(Throwable $exception)
{
return 'Error: ' . $exception->getMessage();
}
diff --git a/src/Exception/ExceptionBase.php b/src/Exception/ExceptionBase.php
index c48b0c522..157d1ba07 100644
--- a/src/Exception/ExceptionBase.php
+++ b/src/Exception/ExceptionBase.php
@@ -1,8 +1,8 @@
formatStackArguments($event['args'], false);
+ $args = $this->formatStackArguments($event['args']);
}
$result[] = (object)[
diff --git a/src/Extension/ExtendableTrait.php b/src/Extension/ExtendableTrait.php
index 8c73be7d0..d8dbf998f 100644
--- a/src/Extension/ExtendableTrait.php
+++ b/src/Extension/ExtendableTrait.php
@@ -1,12 +1,12 @@
extensionNormalizeClassName($extensionName);
@@ -364,8 +369,8 @@ protected function extendableIsAccessible($class, $propertyName)
/**
* Magic method for `__get()`
- * @param string $name
- * @return string
+ * @param string $name
+ * @return mixed|null
*/
public function extendableGet($name)
{
@@ -382,13 +387,15 @@ public function extendableGet($name)
if ($parent !== false && method_exists($parent, '__get')) {
return parent::__get($name);
}
+
+ return null;
}
/**
* Magic method for `__set()`
* @param string $name
- * @param string $value
- * @return string
+ * @param mixed $value
+ * @return void
*/
public function extendableSet($name, $value)
{
diff --git a/src/Filesystem/Definitions.php b/src/Filesystem/Definitions.php
index a11a73c07..934bb7f72 100644
--- a/src/Filesystem/Definitions.php
+++ b/src/Filesystem/Definitions.php
@@ -1,6 +1,6 @@
getDefinitions($type);
+ return (new static)->getDefinitions($type);
}
/**
* Returns a definition set from config or from the default sets.
- * @param $type string
- * @return array
+ *
+ * @throws Exception If the provided definition type does not exist.
*/
- public function getDefinitions($type)
+ public function getDefinitions(string $type): array
{
if (!method_exists($this, $type)) {
throw new Exception(sprintf('No such definition set exists for "%s"', $type));
@@ -37,13 +42,13 @@ public function getDefinitions($type)
}
/**
- * Determines if a path should be ignored, sourced from the ignoreFiles
- * and ignorePatterns definitions.
+ * Determines if a path should be ignored based on the ignoreFiles and ignorePatterns definitions.
+ *
+ * Returns `true` if the path is ignored, `false` otherwise.
+ *
* @todo Efficiency of this method can be improved.
- * @param string $path Specifies a path to check.
- * @return boolean Returns TRUE if the path is visible.
*/
- public static function isPathIgnored($path)
+ public static function isPathIgnored(string $path): bool
{
$ignoreNames = self::get('ignoreFiles');
$ignorePatterns = self::get('ignorePatterns');
@@ -63,10 +68,12 @@ public static function isPathIgnored($path)
/**
* Files that can be safely ignored.
- * This list can be customized with config:
- * - cms.fileDefinitions.ignoreFiles
+ *
+ * This list can be customized with the config:
+ *
+ * `cms.fileDefinitions.ignoreFiles`
*/
- protected function ignoreFiles()
+ protected function ignoreFiles(): array
{
return [
'.svn',
@@ -78,10 +85,12 @@ protected function ignoreFiles()
/**
* File patterns that can be safely ignored.
- * This list can be customized with config:
- * - cms.fileDefinitions.ignorePatterns
+ *
+ * This list can be customized with the config:
+ *
+ * `cms.fileDefinitions.ignorePatterns`
*/
- protected function ignorePatterns()
+ protected function ignorePatterns(): array
{
return [
'^\..*'
@@ -90,10 +99,12 @@ protected function ignorePatterns()
/**
* Extensions that are particularly benign.
+ *
* This list can be customized with config:
- * - cms.fileDefinitions.defaultExtensions
+ *
+ * `cms.fileDefinitions.defaultExtensions`
*/
- protected function defaultExtensions()
+ protected function defaultExtensions(): array
{
return [
'jpg',
@@ -142,10 +153,12 @@ protected function defaultExtensions()
/**
* Extensions seen as public assets.
+ *
* This list can be customized with config:
- * - cms.fileDefinitions.assetExtensions
+ *
+ * `cms.fileDefinitions.assetExtensions`
*/
- protected function assetExtensions()
+ protected function assetExtensions(): array
{
return [
'jpg',
@@ -171,10 +184,12 @@ protected function assetExtensions()
/**
* Extensions typically used as images.
+ *
* This list can be customized with config:
- * - cms.fileDefinitions.imageExtensions
+ *
+ * `cms.fileDefinitions.imageExtensions`
*/
- protected function imageExtensions()
+ protected function imageExtensions(): array
{
return [
'jpg',
@@ -188,10 +203,12 @@ protected function imageExtensions()
/**
* Extensions typically used as video files.
+ *
* This list can be customized with config:
- * - cms.fileDefinitions.videoExtensions
+ *
+ * `cms.fileDefinitions.videoExtensions`
*/
- protected function videoExtensions()
+ protected function videoExtensions(): array
{
return [
'mp4',
@@ -206,10 +223,12 @@ protected function videoExtensions()
/**
* Extensions typically used as audio files.
+ *
* This list can be customized with config:
- * - cms.fileDefinitions.audioExtensions
+ *
+ * `cms.fileDefinitions.audioExtensions`
*/
- protected function audioExtensions()
+ protected function audioExtensions(): array
{
return [
'mp3',
diff --git a/src/Filesystem/Filesystem.php b/src/Filesystem/Filesystem.php
index 640d73ca3..ba8c3df9c 100644
--- a/src/Filesystem/Filesystem.php
+++ b/src/Filesystem/Filesystem.php
@@ -1,9 +1,9 @@
= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
@@ -87,12 +86,13 @@ public function sizeToString($bytes)
}
/**
- * Returns a public file path from an absolute one
- * eg: /home/mysite/public_html/welcome -> /welcome
- * @param string $path Absolute path
- * @return string
+ * Returns a public file path from an absolute path.
+ *
+ * Eg: `/home/mysite/public_html/welcome` -> `/welcome`
+ *
+ * Returns `null` if the path cannot be converted.
*/
- public function localToPublic($path)
+ public function localToPublic(string $path): ?string
{
$result = null;
$publicPath = public_path();
@@ -124,12 +124,15 @@ public function localToPublic($path)
}
/**
- * Returns true if the specified path is within the path of the application
- * @param string $path The path to
- * @param boolean $realpath Default true, uses realpath() to resolve the provided path before checking location. Set to false if you need to check if a potentially non-existent path would be within the application path
- * @return boolean
+ * Determines if the given path is a local path.
+ *
+ * Returns `true` if the path is local, `false` otherwise.
+ *
+ * @param string $path The path to check
+ * @param boolean $realpath If `true` (default), the `realpath()` method will be used to resolve symlinks before checking if
+ * the path is local. Set to `false` if you are looking up non-existent paths.
*/
- public function isLocalPath($path, $realpath = true)
+ public function isLocalPath(string $path, bool $realpath = true): bool
{
$base = base_path();
@@ -141,34 +144,30 @@ public function isLocalPath($path, $realpath = true)
}
/**
- * Returns true if the provided disk is using the "local" driver
- *
- * @param Illuminate\Filesystem\FilesystemAdapter $disk
- * @return boolean
+ * Determines if the given disk is using the "local" driver.
*/
- public function isLocalDisk($disk)
+ public function isLocalDisk(\Illuminate\Filesystem\FilesystemAdapter $disk): bool
{
return ($disk->getAdapter() instanceof \League\Flysystem\Local\LocalFilesystemAdapter);
}
/**
- * Finds the path to a class
- * @param mixed $className Class name or object
- * @return string The file path
+ * Finds the path of a given class.
+ *
+ * Returns `false` if the path cannot be determined.
+ *
+ * @param string|object $className Class name or object
*/
- public function fromClass($className)
+ public function fromClass(string|object $className): string|false
{
$reflector = new ReflectionClass($className);
return $reflector->getFileName();
}
/**
- * Determine if a file exists with case insensitivity
- * supported for the file only.
- * @param string $path
- * @return mixed Sensitive path or false
+ * Determines if a file exists (ignoring the case for the filename only).
*/
- public function existsInsensitive($path)
+ public function existsInsensitive(string $path): string|false
{
if ($this->exists($path)) {
return $path;
@@ -191,52 +190,48 @@ public function existsInsensitive($path)
}
/**
- * Normalizes the directory separator, often used by Win systems.
- * @param string $path Path name
- * @return string Normalized path
+ * Normalizes the directory separator, often used by Windows systems.
*/
- public function normalizePath($path)
+ public function normalizePath(string $path): string
{
return str_replace('\\', '/', $path);
}
/**
- * Converts a path using path symbol. Returns the original path if
- * no symbol is used and no default is specified.
- * @param string $path
- * @param mixed $default
- * @return string
+ * Converts a path using path symbol.
+ *
+ * Returns the original path if no symbol is used, and no default is specified.
*/
- public function symbolizePath($path, $default = false)
+ public function symbolizePath(string $path, string|bool|null $default = null): string
{
- if (!$firstChar = $this->isPathSymbol($path)) {
- return $default === false ? $path : $default;
+ if (!$this->isPathSymbol($path)) {
+ return (is_null($default)) ? $path : $default;
}
+ $firstChar = substr($path, 0, 1);
$_path = substr($path, 1);
return $this->pathSymbols[$firstChar] . $_path;
}
/**
- * Returns true if the path uses a symbol.
- * @param string $path
- * @return boolean
+ * Determines if the given path is using a path symbol.
*/
- public function isPathSymbol($path)
+ public function isPathSymbol(string $path): bool
{
- $firstChar = substr($path, 0, 1);
- if (isset($this->pathSymbols[$firstChar])) {
- return $firstChar;
- }
-
- return false;
+ return array_key_exists(substr($path, 0, 1), $this->pathSymbols);
}
/**
* Write the contents of a file.
- * @param string $path
- * @param string $contents
- * @return int
+ *
+ * This method will also set the permissions based on the given chmod() mask in use.
+ *
+ * Returns the number of bytes written to the file, or `false` on failure.
+ *
+ * @param string $path
+ * @param string $contents
+ * @param bool|int $lock
+ * @return bool|int
*/
public function put($path, $contents, $lock = false)
{
@@ -247,8 +242,13 @@ public function put($path, $contents, $lock = false)
/**
* Copy a file to a new location.
- * @param string $path
- * @param string $target
+ *
+ * This method will also set the permissions based on the given chmod() mask in use.
+ *
+ * Returns `true` if successful, or `false` on failure.
+ *
+ * @param string $path
+ * @param string $target
* @return bool
*/
public function copy($path, $target)
@@ -260,26 +260,28 @@ public function copy($path, $target)
/**
* Create a directory.
- * @param string $path
- * @param int $mode
- * @param bool $recursive
- * @param bool $force
+ *
+ * @param string $path
+ * @param int $mode
+ * @param bool $recursive
+ * @param bool $force
* @return bool
*/
public function makeDirectory($path, $mode = 0777, $recursive = false, $force = false)
{
- if ($mask = $this->getFolderPermissions()) {
+ $mask = $this->getFolderPermissions();
+ if (!is_null($mask)) {
$mode = $mask;
}
/*
* Find the green leaves
*/
- if ($recursive && $mask) {
+ if ($recursive === true && !is_null($mask)) {
$chmodPath = $path;
while (true) {
$basePath = dirname($chmodPath);
- if ($chmodPath == $basePath) {
+ if ($chmodPath === $basePath) {
break;
}
if ($this->isDirectory($basePath)) {
@@ -287,8 +289,7 @@ public function makeDirectory($path, $mode = 0777, $recursive = false, $force =
}
$chmodPath = $basePath;
}
- }
- else {
+ } else {
$chmodPath = $path;
}
@@ -312,10 +313,11 @@ public function makeDirectory($path, $mode = 0777, $recursive = false, $force =
}
/**
- * Modify file/folder permissions
- * @param string $path
- * @param octal $mask
- * @return void
+ * Modify file/folder permissions.
+ *
+ * @param string $path
+ * @param int|float|null $mask
+ * @return bool
*/
public function chmod($path, $mask = null)
{
@@ -326,20 +328,16 @@ public function chmod($path, $mask = null)
}
if (!$mask) {
- return;
+ return false;
}
return @chmod($path, $mask);
}
/**
- * Modify file/folder permissions recursively
- * @param string $path
- * @param octal $fileMask
- * @param octal $directoryMask
- * @return void
+ * Modify file/folder permissions recursively in a given path.
*/
- public function chmodRecursive($path, $fileMask = null, $directoryMask = null)
+ public function chmodRecursive(string $path, int|float|null $fileMask = null, int|float|null $directoryMask = null): void
{
if (!$fileMask) {
$fileMask = $this->getFilePermissions();
@@ -354,7 +352,8 @@ public function chmodRecursive($path, $fileMask = null, $directoryMask = null)
}
if (!$this->isDirectory($path)) {
- return $this->chmod($path, $fileMask);
+ $this->chmod($path, $fileMask);
+ return;
}
$items = new FilesystemIterator($path, FilesystemIterator::SKIP_DOTS);
@@ -372,9 +371,8 @@ public function chmodRecursive($path, $fileMask = null, $directoryMask = null)
/**
* Returns the default file permission mask to use.
- * @return string Permission mask as octal (0777) or null
*/
- public function getFilePermissions()
+ public function getFilePermissions(): int|float|null
{
return $this->filePermissions
? octdec($this->filePermissions)
@@ -383,9 +381,8 @@ public function getFilePermissions()
/**
* Returns the default folder permission mask to use.
- * @return string Permission mask as octal (0777) or null
*/
- public function getFolderPermissions()
+ public function getFolderPermissions(): int|float|null
{
return $this->folderPermissions
? octdec($this->folderPermissions)
@@ -394,11 +391,8 @@ public function getFolderPermissions()
/**
* Match filename against a pattern.
- * @param string|array $fileName
- * @param string $pattern
- * @return bool
*/
- public function fileNameMatch($fileName, $pattern)
+ public function fileNameMatch(string $fileName, string $pattern): bool
{
if ($pattern === $fileName) {
return true;
@@ -410,11 +404,9 @@ public function fileNameMatch($fileName, $pattern)
}
/**
- * Finds symlinks within the base path and provides a source => target array of symlinks.
- *
- * @return void
+ * Finds symlinks within the base path and populates the local symlinks property with an array of source => target symlinks.
*/
- protected function findSymlinks()
+ protected function findSymlinks(): void
{
$restrictBaseDir = Config::get('cms.restrictBaseDir', true);
$deep = Config::get('develop.allowDeepSymlinks', false);
diff --git a/src/Filesystem/FilesystemManager.php b/src/Filesystem/FilesystemManager.php
index 330ac14e2..6a7118a86 100644
--- a/src/Filesystem/FilesystemManager.php
+++ b/src/Filesystem/FilesystemManager.php
@@ -1,6 +1,5 @@
prefixer->prefixPath('');
});
FilesystemAdapter::macro('setPathPrefix', function (string $prefix) {
+ /** @phpstan-ignore-next-line */
$this->prefixer = new PathPrefixer($prefix, $this->config['directory_separator'] ?? DIRECTORY_SEPARATOR);
});
}
diff --git a/src/Filesystem/PathResolver.php b/src/Filesystem/PathResolver.php
index 6d5d036b6..31377d26e 100644
--- a/src/Filesystem/PathResolver.php
+++ b/src/Filesystem/PathResolver.php
@@ -19,11 +19,8 @@ class PathResolver
* and directories.
*
* Returns canonical path if it can be resolved, otherwise `false`.
- *
- * @param string $path The path to resolve
- * @return string|bool
*/
- public static function resolve($path)
+ public static function resolve(string $path): string|bool
{
// Check if path is within any "open_basedir" restrictions
if (!static::withinOpenBaseDir($path)) {
@@ -100,12 +97,8 @@ public static function resolve($path)
/**
* Determines if the path is within the given directory.
- *
- * @param string $path
- * @param string $directory
- * @return bool
*/
- public static function within($path, $directory)
+ public static function within(string $path, string $directory): bool
{
$directory = static::resolve($directory);
$path = static::resolve($path);
@@ -115,12 +108,8 @@ public static function within($path, $directory)
/**
* Join two paths, making sure they use the correct directory separators.
- *
- * @param string $prefix
- * @param string $path The path to add to the prefix.
- * @return string
*/
- public static function join($prefix, $path = '')
+ public static function join(string $prefix, string $path = ''): string
{
$fullPath = rtrim(static::normalize($prefix, false) . '/' . static::normalize($path, false), '/');
@@ -133,11 +122,9 @@ public static function join($prefix, $path = '')
* Converts any type of path (Unix or Windows) into a Unix-style path, so that we have a consistent format to work
* with internally. All paths will be returned with no trailing path separator.
*
- * @param string $path
- * @param bool $applyCwd If true, the current working directory will be appended if the path is relative.
- * @return string
+ * If `$applyCwd` is true, the current working directory will be prepended if the path is relative.
*/
- protected static function normalize($path, $applyCwd = true)
+ protected static function normalize(string $path, bool $applyCwd = true): string
{
// Change directory separators to Unix-based
$path = rtrim(str_replace('\\', '/', $path), '/');
@@ -159,11 +146,8 @@ protected static function normalize($path, $applyCwd = true)
/**
* Standardizes the path separators of a path back to the expected separator for the operating system.
- *
- * @param string $path
- * @return string
*/
- public static function standardize($path)
+ public static function standardize(string $path): string
{
return str_replace('/', DIRECTORY_SEPARATOR, static::normalize($path, false));
}
@@ -171,10 +155,9 @@ public static function standardize($path)
/**
* Resolves a symlink target.
*
- * @param mixed $path The symlink source's path.
- * @return string|bool
+ * Returns the resolved symlink path, or `false` if it cannot be resolved.
*/
- protected static function resolveSymlink($symlink)
+ protected static function resolveSymlink($symlink): string|bool
{
// Check that the symlink is valid and the target exists
$stat = linkinfo($symlink);
@@ -205,11 +188,8 @@ protected static function resolveSymlink($symlink)
/**
* Checks if a given path is within "open_basedir" restrictions.
- *
- * @param string $path
- * @return bool
*/
- protected static function withinOpenBaseDir($path)
+ protected static function withinOpenBaseDir(string $path): bool
{
$baseDirs = ini_get('open_basedir');
diff --git a/src/Filesystem/Zip.php b/src/Filesystem/Zip.php
index ef9938550..d8ca7a62c 100644
--- a/src/Filesystem/Zip.php
+++ b/src/Filesystem/Zip.php
@@ -50,22 +50,28 @@
class Zip extends ZipArchive
{
/**
- * @var string Folder prefix
+ * Folder prefix
*/
- protected $folderPrefix = '';
+ protected string $folderPrefix = '';
/**
- * Extract an existing zip file.
- * @param string $source Path for the existing zip
- * @param string $destination Path to extract the zip files
- * @param array $options
- * @return bool
+ * Lock down the constructor for this class.
*/
- public static function extract($source, $destination, $options = [])
+ final public function __construct()
{
- extract(array_merge([
- 'mask' => 0777
- ], $options));
+ }
+
+ /**
+ * Extracts an existing ZIP file.
+ *
+ * @param string $source Path to the ZIP file.
+ * @param string $destination Path to the destination directory.
+ * @param array $options Optional. An array of options. Only one option is currently supported:
+ * `mask`, which defines the permission mask to use when creating the destination folder.
+ */
+ public static function extract(string $source, string $destination, array $options = []): bool
+ {
+ $mask = $options['mask'] ?? 0777;
if (file_exists($destination) || mkdir($destination, $mask, true)) {
$zip = new ZipArchive;
@@ -80,24 +86,25 @@ public static function extract($source, $destination, $options = [])
}
/**
- * Creates a new empty zip file.
- * @param string $destination Path for the new zip
- * @param mixed $source
- * @param array $options
- * @return self
+ * Creates a new empty Zip file, optionally populating it with given source files.
+ *
+ * Source can be a single path, an array of paths or a callback which allows you to manipulate
+ * the Zip file.
+ *
+ * @param string $destination Path to the destination ZIP file.
+ * @param string|callable|array|null $source Optional. Path to the source file(s) or a callback.
+ * @param array $options Optional. An array of options. Uses the same options as `Zip::add()`.
*/
- public static function make($destination, $source, $options = [])
+ public static function make(string $destination, string|callable|array|null $source = null, array $options = []): static
{
- $zip = new self;
+ $zip = new static;
$zip->open($destination, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE);
if (is_string($source)) {
$zip->add($source, $options);
- }
- elseif (is_callable($source)) {
+ } elseif (is_callable($source)) {
$source($zip);
- }
- elseif (is_array($source)) {
+ } elseif (is_array($source)) {
foreach ($source as $_source) {
$zip->add($_source, $options);
}
@@ -108,13 +115,22 @@ public static function make($destination, $source, $options = [])
}
/**
- * Includes a source to the Zip
- * @param mixed $source
- * @param array $options
- * @return self
+ * Adds a source file or directory to a Zip file.
+ *
+ * @param string $source Path to the source file or directory.
+ * @param array $options Optional. An array of options. Supports the following options:
+ * - `recursive`, which determines whether to add subdirectories and files recursively.
+ * Defaults to `true`.
+ * - `includeHidden`, which determines whether to add hidden files and directories.
+ * Defaults to `false`.
+ * - `baseDir`, which determines the base directory to use when adding files.
+ * - `baseglob`, which defines a glob pattern to match files and directories to add.
*/
- public function add($source, $options = [])
+ public function add(string $source, array $options = []): self
{
+ $recursive = (bool) ($options['recursive'] ?? true);
+ $includeHidden = isset($options['includeHidden']) && $options['includeHidden'] === true;
+
/*
* A directory has been supplied, convert it to a useful glob
*
@@ -124,23 +140,18 @@ public function add($source, $options = [])
* - starts with '..' but has at least one character after it
*/
if (is_dir($source)) {
- $includeHidden = isset($options['includeHidden']) && $options['includeHidden'];
$wildcard = $includeHidden ? '{*,.[!.]*,..?*}' : '*';
$source = implode('/', [dirname($source), basename($source), $wildcard]);
}
- extract(array_merge([
- 'recursive' => true,
- 'includeHidden' => false,
- 'basedir' => dirname($source),
- 'baseglob' => basename($source)
- ], $options));
+ $basedir = $options['basedir'] ?? dirname($source);
+ $baseglob = $options['baseglob'] ?? basename($source);
if (is_file($source)) {
$files = [$source];
+ $folders = [];
$recursive = false;
- }
- else {
+ } else {
$files = glob($source, GLOB_BRACE);
$folders = glob(dirname($source) . '/*', GLOB_ONLYDIR);
}
@@ -173,12 +184,11 @@ public function add($source, $options = [])
}
/**
- * Creates a new folder inside the Zip and adds source files (optional)
- * @param string $name Folder name
- * @param mixed $source
- * @return self
+ * Creates a new folder inside the Zip file, and optionally adds the given source files/folders to this folder.
+ *
+ * Source can be a single path, an array of paths or a callback which allows you to manipulate the Zip file.
*/
- public function folder($name, $source = null)
+ public function folder(string $name, string|callable|array|null $source = null): self
{
$prefix = $this->folderPrefix;
$this->addEmptyDir($prefix . $name);
@@ -190,11 +200,9 @@ public function folder($name, $source = null)
if (is_string($source)) {
$this->add($source);
- }
- elseif (is_callable($source)) {
+ } elseif (is_callable($source)) {
$source($this);
- }
- elseif (is_array($source)) {
+ } elseif (is_array($source)) {
foreach ($source as $_source) {
$this->add($_source);
}
@@ -205,12 +213,11 @@ public function folder($name, $source = null)
}
/**
- * Removes a file or folder from the zip collection.
+ * Removes file(s) or folder(s) from the Zip file.
+ *
* Does not support wildcards.
- * @param string $source
- * @return self
*/
- public function remove($source)
+ public function remove(array|string $source): self
{
if (is_array($source)) {
foreach ($source as $_source) {
@@ -237,12 +244,9 @@ public function remove($source)
}
/**
- * Removes a prefix from a path.
- * @param string $prefix /var/sites/
- * @param string $path /var/sites/moo/cow/
- * @return string moo/cow/
+ * Removes a prefix from a given path.
*/
- protected function removePathPrefix($prefix, $path)
+ protected function removePathPrefix(string $prefix, string $path): string
{
return (strpos($path, $prefix) === 0)
? substr($path, strlen($prefix))
diff --git a/src/Flash/FlashBag.php b/src/Flash/FlashBag.php
index 71bf17ef2..61abb1f39 100644
--- a/src/Flash/FlashBag.php
+++ b/src/Flash/FlashBag.php
@@ -1,7 +1,7 @@
instance('path.temp', $this->tempPath());
$this->instance('path.uploads', $this->uploadsPath());
$this->instance('path.media', $this->mediaPath());
+ $this->instance('path.lang', $this->langPath());
}
/**
@@ -224,7 +225,7 @@ public function tempPath()
/**
* Set the temp path for the application.
*
- * @return string
+ * @return static
*/
public function setTempPath($path)
{
@@ -247,7 +248,7 @@ public function uploadsPath()
/**
* Set the uploads path for the application.
*
- * @return string
+ * @return static
*/
public function setUploadsPath($path)
{
@@ -270,7 +271,7 @@ public function mediaPath()
/**
* Set the media path for the application.
*
- * @return string
+ * @return static
*/
public function setMediaPath($path)
{
@@ -311,7 +312,7 @@ public function make($abstract, array $parameters = [])
*/
public function before($callback)
{
- return $this['router']->before($callback);
+ $this['router']->before($callback);
}
/**
@@ -322,7 +323,7 @@ public function before($callback)
*/
public function after($callback)
{
- return $this['router']->after($callback);
+ $this['router']->after($callback);
}
/**
@@ -390,12 +391,12 @@ public function setLocale($locale)
/**
* Register all of the configured providers.
*
- * @var bool $isRetry If true, this is a second attempt without the cached packages.
+ * @param bool $isRetry If true, this is a second attempt without the cached packages.
* @return void
*/
public function registerConfiguredProviders($isRetry = false)
{
- $providers = Collection::make($this->config['app.providers'])
+ $providers = Collection::make($this->get('config')['app.providers'])
->partition(function ($provider) {
return Str::startsWith($provider, 'Illuminate\\');
});
diff --git a/src/Foundation/Bootstrap/LoadConfiguration.php b/src/Foundation/Bootstrap/LoadConfiguration.php
index ae384fa15..3c26852f3 100644
--- a/src/Foundation/Bootstrap/LoadConfiguration.php
+++ b/src/Foundation/Bootstrap/LoadConfiguration.php
@@ -1,24 +1,22 @@
detectEnvironment(function () use ($app) {
- return $this->getEnvironmentFromHost($app);
+ $app->detectEnvironment(function () {
+ return $this->getEnvironmentFromHost();
});
$app->instance('config', $config = new Repository($fileLoader, $app['env']));
@@ -28,15 +26,13 @@ public function bootstrap(Application $app)
mb_internal_encoding('UTF-8');
// Fix for XDebug aborting threads > 100 nested
- ini_set('xdebug.max_nesting_level', 1000);
+ ini_set('xdebug.max_nesting_level', '1000');
}
/**
* Returns the environment based on hostname.
- * @param array $config
- * @return void
*/
- protected function getEnvironmentFromHost(Application $app)
+ protected function getEnvironmentFromHost(): string
{
$config = $this->getEnvironmentConfiguration();
@@ -51,15 +47,14 @@ protected function getEnvironmentFromHost(Application $app)
/**
* Load the environment configuration.
- * @return array
*/
- protected function getEnvironmentConfiguration()
+ protected function getEnvironmentConfiguration(): array
{
$config = [];
$environment = env('APP_ENV');
- if ($environment && file_exists($configPath = base_path().'/config/'.$environment.'/environment.php')) {
+ if ($environment && file_exists($configPath = base_path() . '/config/' . $environment . '/environment.php')) {
try {
$config = require $configPath;
}
@@ -67,7 +62,7 @@ protected function getEnvironmentConfiguration()
//
}
}
- elseif (file_exists($configPath = base_path().'/config/environment.php')) {
+ elseif (file_exists($configPath = base_path() . '/config/environment.php')) {
try {
$config = require $configPath;
}
diff --git a/src/Foundation/Bootstrap/RegisterClassLoader.php b/src/Foundation/Bootstrap/RegisterClassLoader.php
index 4df803f6b..21dd8c734 100644
--- a/src/Foundation/Bootstrap/RegisterClassLoader.php
+++ b/src/Foundation/Bootstrap/RegisterClassLoader.php
@@ -2,17 +2,14 @@
use Winter\Storm\Support\ClassLoader;
use Winter\Storm\Filesystem\Filesystem;
-use Illuminate\Contracts\Foundation\Application;
+use Winter\Storm\Foundation\Application;
class RegisterClassLoader
{
/**
- * Register The Winter Auto Loader
- *
- * @param \Illuminate\Contracts\Foundation\Application $app
- * @return void
+ * Register the Winter class loader service.
*/
- public function bootstrap(Application $app)
+ public function bootstrap(Application $app): void
{
$loader = new ClassLoader(
new Filesystem,
diff --git a/src/Foundation/Bootstrap/RegisterWinter.php b/src/Foundation/Bootstrap/RegisterWinter.php
index 6f66b9515..c9ff12699 100644
--- a/src/Foundation/Bootstrap/RegisterWinter.php
+++ b/src/Foundation/Bootstrap/RegisterWinter.php
@@ -1,16 +1,16 @@
>
*/
protected $dontReport = [
\Winter\Storm\Exception\AjaxException::class,
@@ -84,7 +84,7 @@ public function report(Throwable $throwable)
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $throwable
- * @return \Illuminate\Http\Response
+ * @return \Symfony\Component\HttpFoundation\Response
*/
public function render($request, Throwable $throwable)
{
@@ -157,7 +157,7 @@ public function error(Closure $callback)
*
* @param \Throwable $throwable
* @param bool $fromConsole
- * @return void
+ * @return mixed|null
*/
protected function callCustomHandlers($throwable, $fromConsole = false)
{
@@ -176,14 +176,14 @@ protected function callCustomHandlers($throwable, $fromConsole = false)
// at least some errors, and avoid errors with no data or not log writes.
try {
$response = $handler($throwable, $code, $fromConsole);
+ } catch (Throwable $t) {
+ $response = $this->convertExceptionToResponse($t);
}
- catch (Throwable $t) {
- $response = $this->convertThrowableToResponse($t);
- }
+
// If this handler returns a "non-null" response, we will return it so it will
// get sent back to the browsers. Once the handler returns a valid response
// we will cease iterating through them and calling these other handlers.
- if (isset($response) && ! is_null($response)) {
+ if (isset($response)) {
return $response;
}
}
@@ -214,11 +214,24 @@ protected function hints(ReflectionFunction $reflection, $throwable)
$parameters = $reflection->getParameters();
$expected = $parameters[0];
- try {
- return (new ReflectionClass($expected->getType()->getName()))
- ->isInstance($throwable);
- } catch (\Throwable $t) {
- return false;
+ if ($expected->getType() instanceof \ReflectionNamedType) {
+ try {
+ return (new ReflectionClass($expected->getType()->getName()))
+ ->isInstance($throwable);
+ } catch (\Throwable $t) {
+ return false;
+ }
+ } elseif ($expected->getType() instanceof \ReflectionUnionType) {
+ foreach ($expected->getType()->getTypes() as $type) {
+ try {
+ return (new ReflectionClass($type->getName()))
+ ->isInstance($throwable);
+ } catch (\Throwable $t) {
+ return false;
+ }
+ }
}
+
+ return false;
}
}
diff --git a/src/Foundation/Http/Kernel.php b/src/Foundation/Http/Kernel.php
index c915c13e5..3d3e5bd24 100644
--- a/src/Foundation/Http/Kernel.php
+++ b/src/Foundation/Http/Kernel.php
@@ -7,7 +7,7 @@ class Kernel extends HttpKernel
/**
* The bootstrap classes for the application.
*
- * @var array
+ * @var string[]
*/
protected $bootstrappers = [
\Winter\Storm\Foundation\Bootstrap\RegisterClassLoader::class,
@@ -71,7 +71,7 @@ class Kernel extends HttpKernel
*
* Forces the listed middleware to always be in the given order.
*
- * @var array
+ * @var string[]
*/
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
diff --git a/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php b/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php
index 851b21cc9..6a82797bf 100644
--- a/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php
+++ b/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php
@@ -2,11 +2,11 @@
namespace Winter\Storm\Foundation\Http\Middleware;
-use Lang;
-use View;
use Closure;
-use Response;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
+use Illuminate\Support\Facades\Lang;
+use Illuminate\Support\Facades\View;
+use Illuminate\Support\Facades\Response;
class CheckForMaintenanceMode extends Middleware
{
diff --git a/src/Foundation/Http/Middleware/CheckForTrustedHost.php b/src/Foundation/Http/Middleware/CheckForTrustedHost.php
index be181df88..128264104 100644
--- a/src/Foundation/Http/Middleware/CheckForTrustedHost.php
+++ b/src/Foundation/Http/Middleware/CheckForTrustedHost.php
@@ -1,6 +1,6 @@
allowProxies($request, [
+ $this->allowProxies($request, [
$request->server->get('REMOTE_ADDR')
]);
+ return;
}
// Support comma-separated strings as well as arrays
@@ -104,7 +105,7 @@ protected function setTrustedProxies(Request $request)
: $proxies;
if (is_array($proxies)) {
- return $this->allowProxies($request, $proxies);
+ $this->allowProxies($request, $proxies);
}
}
diff --git a/src/Foundation/Maker.php b/src/Foundation/Maker.php
index 648ca1371..9253a16b2 100644
--- a/src/Foundation/Maker.php
+++ b/src/Foundation/Maker.php
@@ -5,6 +5,8 @@
use Illuminate\Contracts\Container\BindingResolutionException;
use ReflectionClass;
use ReflectionParameter;
+use ReflectionNamedType;
+use ReflectionUnionType;
class Maker
{
@@ -29,13 +31,7 @@ public function __construct(Application $app)
$this->app = $app;
}
- /**
- * @param $abstract
- * @param array $parameters
- *
- * @return mixed
- */
- public function make($abstract, $parameters = [])
+ public function make($abstract, array $parameters = [])
{
return $this->build(
$this->getBinding($abstract),
@@ -43,12 +39,6 @@ public function make($abstract, $parameters = [])
);
}
- /**
- * @param $abstract
- * @param $concrete
- *
- * @return void
- */
public function bind($abstract, Closure $concrete)
{
$this->bindings[$abstract] = $concrete;
@@ -125,11 +115,9 @@ protected function getDependencies(array $parameters, array $primitives = [])
if (array_key_exists($parameter->name, $primitives)) {
$dependencies[] = $primitives[$parameter->name];
- }
- elseif (is_null($dependency)) {
+ } elseif (is_null($dependency)) {
$dependencies[] = $this->resolvePrimitive($parameter);
- }
- else {
+ } elseif ($dependency instanceof ReflectionUnionType === false) {
$dependencies[] = $this->resolveClass($parameter);
}
}
@@ -145,10 +133,12 @@ protected function getDependencies(array $parameters, array $primitives = [])
*/
protected function resolveClass(ReflectionParameter $parameter)
{
+ /** @var ReflectionNamedType */
+ $type = $parameter->getType();
+
try {
- return $this->getFromContainer($parameter->getType()->getName());
- }
- catch (BindingResolutionException $e) {
+ return $this->getFromContainer($type->getName());
+ } catch (BindingResolutionException $e) {
if ($parameter->isOptional()) {
return $parameter->getDefaultValue();
}
@@ -156,19 +146,12 @@ protected function resolveClass(ReflectionParameter $parameter)
}
}
- /**
- * @param $abstract
- *
- * @return mixed
- */
protected function getBinding($abstract)
{
return $this->isBound($abstract) ? $this->bindings[$abstract] : $abstract;
}
/**
- * @param $abstract
- *
* @return bool
*/
protected function isBound($abstract)
diff --git a/src/Foundation/Providers/ArtisanServiceProvider.php b/src/Foundation/Providers/ArtisanServiceProvider.php
index 7d23dc970..3b6d348b4 100644
--- a/src/Foundation/Providers/ArtisanServiceProvider.php
+++ b/src/Foundation/Providers/ArtisanServiceProvider.php
@@ -141,9 +141,7 @@ public function register()
*/
protected function registerKeyGenerateCommand()
{
- $this->app->singleton(KeyGenerateCommand::class, function ($app) {
- return new KeyGenerateCommand($app['files']);
- });
+ $this->app->singleton(KeyGenerateCommand::class);
}
/**
diff --git a/src/Foundation/Providers/ExecutionContextProvider.php b/src/Foundation/Providers/ExecutionContextProvider.php
index 2bc2282d4..16178f0f3 100644
--- a/src/Foundation/Providers/ExecutionContextProvider.php
+++ b/src/Foundation/Providers/ExecutionContextProvider.php
@@ -1,6 +1,7 @@
validateFileName();
@@ -360,10 +362,10 @@ public function insert(array $values)
/**
* Update a record in the datasource.
*
- * @param array $values
- * @return int
+ * @param array $values The values to store in the model.
+ * @return int The filesize of the created model file.
*/
- public function update(array $values)
+ public function update(array $values = []): int
{
$this->validateFileName();
@@ -392,7 +394,7 @@ public function update(array $values)
/**
* Delete a record from the database.
*
- * @return int
+ * @return bool
*/
public function delete()
{
@@ -528,7 +530,7 @@ protected function validateFileNameExtension($fileName, $allowedExtensions)
* Template directory and file names can contain only alphanumeric symbols, dashes and dots.
* @param string $filePath Specifies a path to validate
* @param integer $maxNesting Specifies the maximum allowed nesting level
- * @return void
+ * @return bool
*/
protected function validateFileNamePath($filePath, $maxNesting = 2)
{
@@ -697,7 +699,7 @@ protected function isCacheBusted($result)
/**
* Get the cache object with tags assigned, if applicable.
*
- * @return \Illuminate\Cache\CacheManager
+ * @return \Illuminate\Contracts\Cache\Repository
*/
protected function getCache()
{
@@ -737,7 +739,7 @@ public function generateCacheKey()
/**
* Get the Closure callback used when caching queries.
*
- * @param string $fileName
+ * @param string|array $columns
* @return \Closure
*/
protected function getCacheCallback($columns)
@@ -749,8 +751,8 @@ protected function getCacheCallback($columns)
/**
* Initialize the cache data of each record.
- * @param array $data
- * @return array
+ * @param \Winter\Storm\Halcyon\Collection|array $data
+ * @return \Winter\Storm\Halcyon\Collection|array
*/
protected function processInitCacheData($data)
{
diff --git a/src/Halcyon/Datasource/Datasource.php b/src/Halcyon/Datasource/Datasource.php
index 9f2323974..42bc1640b 100644
--- a/src/Halcyon/Datasource/Datasource.php
+++ b/src/Halcyon/Datasource/Datasource.php
@@ -1,56 +1,91 @@
postProcessor;
}
/**
- * Force the deletion of a record against the datasource
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return void
+ * @inheritDoc
+ */
+ abstract public function selectOne(string $dirName, string $fileName, string $extension): ?array;
+
+ /**
+ * @inheritDoc
+ */
+ abstract public function select(string $dirName, array $options = []): array;
+
+ /**
+ * @inheritDoc
+ */
+ abstract public function insert(string $dirName, string $fileName, string $extension, string $content): int;
+
+ /**
+ * @inheritDoc
+ */
+ abstract public function update(string $dirName, string $fileName, string $extension, string $content, ?string $oldFileName = null, ?string $oldExtension = null): int;
+
+ /**
+ * @inheritDoc
*/
- public function forceDelete(string $dirName, string $fileName, string $extension)
+ abstract public function delete(string $dirName, string $fileName, string $extension): bool;
+
+ /**
+ * @inheritDoc
+ */
+ public function forceDelete(string $dirName, string $fileName, string $extension): bool
{
$this->forceDeleting = true;
- $this->delete($dirName, $fileName, $extension);
+ $success = $this->delete($dirName, $fileName, $extension);
$this->forceDeleting = false;
+
+ return $success;
}
/**
- * Generate a cache key unique to this datasource.
+ * @inheritDoc
+ */
+ abstract public function lastModified(string $dirName, string $fileName, string $extension): ?int;
+
+ /**
+ * @inheritDoc
*/
- public function makeCacheKey($name = '')
+ public function makeCacheKey(string $name = ''): string
{
- return crc32($name);
+ return hash('crc32b', $name);
}
+
+ /**
+ * @inheritDoc
+ */
+ abstract public function getPathsCacheKey(): string;
+
+ /**
+ * @inheritDoc
+ */
+ abstract public function getAvailablePaths(): array;
}
diff --git a/src/Halcyon/Datasource/DatasourceInterface.php b/src/Halcyon/Datasource/DatasourceInterface.php
index 1f328df0b..1c1fc25a9 100644
--- a/src/Halcyon/Datasource/DatasourceInterface.php
+++ b/src/Halcyon/Datasource/DatasourceInterface.php
@@ -2,99 +2,135 @@
interface DatasourceInterface
{
+ /**
+ * Get the query post processor used by the connection.
+ */
+ public function getPostProcessor(): \Winter\Storm\Halcyon\Processors\Processor;
/**
- * Returns a single template.
+ * Returns a single Halcyon model (template).
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return mixed
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @return array|null An array of template data (`fileName`, `mtime` and `content`), or `null` if the model does
+ * not exist.
*/
- public function selectOne(string $dirName, string $fileName, string $extension);
+ public function selectOne(string $dirName, string $fileName, string $extension): ?array;
/**
- * Returns all templates.
+ * Returns all Halcyon models (templates) within a given directory.
*
- * @param string $dirName
- * @param array $options
- * @return array
+ * You can provide multiple options with the `$options` property, in order to filter the retrieved records:
+ * - `columns`: Only retrieve certain columns. Must be an array with any combination of `fileName`, `mtime` and
+ * `content`.
+ * - `extensions`: Defines the accepted extensions as an array. Eg: `['htm', 'md', 'twig']`
+ * - `fileMatch`: Defines a glob string to match filenames against. Eg: `'*gr[ae]y'`
+ * - `orders`: Not implemented
+ * - `limit`: Not implemented
+ * - `offset`: Not implemented
+ *
+ * @todo Implement support for `orders`, `limit` and `offset` options.
+ * @param string $dirName The directory in which the model is stored.
+ * @param array $options Defines the options for this query.
+ * @return array An array of models found, with the columns defined as per the `columns` parameter for `$options`.
*/
- public function select(string $dirName, array $options = []);
+ public function select(string $dirName, array $options = []): array;
/**
- * Creates a new template.
+ * Creates a new Halcyon model (template).
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @param array $content
- * @return bool
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @param string $content The content to store for the model.
+ * @return int The filesize of the created model.
*/
public function insert(string $dirName, string $fileName, string $extension, string $content);
/**
- * Updates an existing template.
+ * Updates an existing Halcyon model (template).
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @param array $content
- * @param string $oldFileName Defaults to null
- * @param string $oldExtension Defaults to null
- * @return int
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @param string $content The content to store for the model.
+ * @param string|null $oldFileName Used for renaming templates. If specified, this will delete the "old" path.
+ * @param string|null $oldExtension Used for renaming templates. If specified, this will delete the "old" path.
+ * @return int The filesize of the updated model.
*/
- public function update(string $dirName, string $fileName, string $extension, string $content, $oldFileName = null, $oldExtension = null);
+ public function update(
+ string $dirName,
+ string $fileName,
+ string $extension,
+ string $content,
+ ?string $oldFileName = null,
+ ?string $oldExtension = null
+ ): int;
/**
- * Run a delete statement against the datasource.
+ * Runs a delete statement against the datasource.
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return bool
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @return bool If the delete operation completed successfully.
*/
- public function delete(string $dirName, string $fileName, string $extension);
+ public function delete(string $dirName, string $fileName, string $extension): bool;
/**
- * Run a delete statement against the datasource, forcing the complete removal of the template
+ * Runs a delete statement against the datasource, forcing the complete removal of the model (template).
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return bool
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @return bool If the delete operation completed successfully.
*/
- public function forceDelete(string $dirName, string $fileName, string $extension);
+ public function forceDelete(string $dirName, string $fileName, string $extension): bool;
/**
- * Return the last modified date of an object
+ * Returns the last modified date of a model (template).
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return int
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @return int|null The last modified time as a timestamp, or `null` if the object doesn't exist.
*/
- public function lastModified(string $dirName, string $fileName, string $extension);
+ public function lastModified(string $dirName, string $fileName, string $extension): ?int;
/**
* Generate a cache key unique to this datasource.
*
- * @param string $name
- * @return string
+ * @param string $name The name of the key.
+ * @return string The hashed key.
*/
- public function makeCacheKey($name = '');
+ public function makeCacheKey(string $name = ''): string;
/**
- * Generate a paths cache key unique to this datasource
+ * Gets the prefix of the cache keys.
+ *
+ * This is based off a prefix including the base path for the model.
*
- * @return string
+ * @return string The cache key prefix.
*/
- public function getPathsCacheKey();
+ public function getPathsCacheKey(): string;
/**
- * Get all available paths within this datastore
+ * Get all available paths within this datasource.
+ *
+ * This method returns an array, with all available paths as the key, and a boolean that represents whether the path
+ * can be handled or modified.
+ *
+ * Example:
+ *
+ * ```php
+ * [
+ * 'path/to/file.md' => true, // (this path is available, and can be handled)
+ * 'path/to/file2.md' => false // (this path is available, but cannot be handled)
+ * ]
+ * ```
*
- * @return array $paths ['path/to/file1.md' => true (path can be handled and exists), 'path/to/file2.md' => false (path can be handled but doesn't exist)]
+ * @return array An array of available paths alongside whether they can be handled.
*/
- public function getAvailablePaths();
+ public function getAvailablePaths(): array;
}
diff --git a/src/Halcyon/Datasource/DbDatasource.php b/src/Halcyon/Datasource/DbDatasource.php
index e4bc633b1..a91de342f 100644
--- a/src/Halcyon/Datasource/DbDatasource.php
+++ b/src/Halcyon/Datasource/DbDatasource.php
@@ -1,12 +1,12 @@
source = $source;
-
$this->table = $table;
-
$this->postProcessor = new Processor;
}
/**
- * Get the base QueryBuilder object
+ * Get the base QueryBuilder object.
*/
- public function getBaseQuery()
+ public function getBaseQuery(): \Winter\Storm\Database\QueryBuilder
{
- return Db::table($this->table)->enableDuplicateCache();
+ return DB::table($this->table)->enableDuplicateCache();
}
/**
- * Get the QueryBuilder object
+ * Get the QueryBuilder object.
*
- * @param bool $ignoreDeleted Flag to ignore deleted records, defaults to true
- * @return QueryBuilder
+ * @param bool $ignoreDeleted Ignore deleted records. Defaults to `true`.
*/
- public function getQuery($ignoreDeleted = true)
+ public function getQuery(bool $ignoreDeleted = true): \Winter\Storm\Database\QueryBuilder
{
$query = $this->getBaseQuery();
@@ -90,27 +86,22 @@ public function getQuery($ignoreDeleted = true)
}
/**
- * Helper to make file path.
+ * Helper method to combine the provided directory, filename and extension into a single path.
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return string
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @return string The combined path.
*/
- protected function makeFilePath(string $dirName, string $fileName, string $extension)
+ protected function makeFilePath(string $dirName, string $fileName, string $extension): string
{
return $dirName . '/' . $fileName . '.' . $extension;
}
/**
- * Returns a single template.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return mixed
+ * @inheritDoc
*/
- public function selectOne(string $dirName, string $fileName, string $extension)
+ public function selectOne(string $dirName, string $fileName, string $extension): ?array
{
$result = $this->getQuery()->where('path', $this->makeFilePath($dirName, $fileName, $extension))->first();
@@ -127,35 +118,28 @@ public function selectOne(string $dirName, string $fileName, string $extension)
}
/**
- * Returns all templates.
- *
- * @param string $dirName
- * @param array $options Array of options, [
- * 'columns' => ['fileName', 'mtime', 'content'], // Only return specific columns
- * 'extensions' => ['htm', 'md', 'twig'], // Extensions to search for
- * 'fileMatch' => '*gr[ae]y', // Shell matching pattern to match the filename against using the fnmatch function
- * 'orders' => false // Not implemented
- * 'limit' => false // Not implemented
- * 'offset' => false // Not implemented
- * ];
- * @return array
+ * @inheritDoc
*/
- public function select(string $dirName, array $options = [])
+ public function select(string $dirName, array $options = []): array
{
// Initialize result set
$result = [];
// Prepare query options
- extract(array_merge([
+ $queryOptions = array_merge([
'columns' => null, // Only return specific columns (fileName, mtime, content)
'extensions' => null, // Match specified extensions
'fileMatch' => null, // Match the file name using fnmatch()
'orders' => null, // @todo
'limit' => null, // @todo
'offset' => null // @todo
- ], $options));
+ ], $options);
+ extract($queryOptions);
- if ($columns === ['*'] || !is_array($columns)) {
+ if (
+ isset($columns)
+ && ($columns === ['*'] || !is_array($columns))
+ ) {
$columns = null;
}
@@ -163,7 +147,7 @@ public function select(string $dirName, array $options = [])
$query = $this->getQuery()->where('path', 'like', $dirName . '%');
// Apply the extensions filter
- if (is_array($extensions) && !empty($extensions)) {
+ if (!empty($extensions) && is_array($extensions)) {
$query->where(function ($query) use ($extensions) {
// Get the first extension to query for
$query->where('path', 'like', '%' . '.' . array_pop($extensions));
@@ -189,7 +173,7 @@ public function select(string $dirName, array $options = [])
}
// Apply the columns filter on the data returned
- if (is_null($columns)) {
+ if (!isset($columns)) {
$resultItem = [
'fileName' => $fileName,
'content' => $item->content,
@@ -221,15 +205,9 @@ public function select(string $dirName, array $options = [])
}
/**
- * Creates a new template.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @param string $content
- * @return bool
+ * @inheritDoc
*/
- public function insert(string $dirName, string $fileName, string $extension, string $content)
+ public function insert(string $dirName, string $fileName, string $extension, string $content): int
{
$path = $this->makeFilePath($dirName, $fileName, $extension);
@@ -278,17 +256,9 @@ public function insert(string $dirName, string $fileName, string $extension, str
}
/**
- * Updates an existing template.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @param string $content
- * @param string $oldFileName Defaults to null
- * @param string $oldExtension Defaults to null
- * @return int
+ * @inheritDoc
*/
- public function update(string $dirName, string $fileName, string $extension, string $content, $oldFileName = null, $oldExtension = null)
+ public function update(string $dirName, string $fileName, string $extension, string $content, ?string $oldFileName = null, ?string $oldExtension = null): int
{
$path = $this->makeFilePath($dirName, $fileName, $extension);
@@ -338,14 +308,9 @@ public function update(string $dirName, string $fileName, string $extension, str
}
/**
- * Run a delete statement against the datasource.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return bool
+ * @inheritDoc
*/
- public function delete(string $dirName, string $fileName, string $extension)
+ public function delete(string $dirName, string $fileName, string $extension): bool
{
try {
// Get the existing record
@@ -372,52 +337,31 @@ public function delete(string $dirName, string $fileName, string $extension)
}
/**
- * Return the last modified date of an object
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return int
+ * @inheritDoc
*/
- public function lastModified(string $dirName, string $fileName, string $extension)
+ public function lastModified(string $dirName, string $fileName, string $extension): ?int
{
try {
return Carbon::parse($this->getQuery()
->where('path', $this->makeFilePath($dirName, $fileName, $extension))
->first()->updated_at)->timestamp;
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
return null;
}
}
/**
- * Generate a cache key unique to this datasource.
- *
- * @param string $name
- * @return string
+ * @inheritDoc
*/
- public function makeCacheKey($name = '')
- {
- return crc32($this->source . $name);
- }
-
- /**
- * Generate a paths cache key unique to this datasource
- *
- * @return string
- */
- public function getPathsCacheKey()
+ public function getPathsCacheKey(): string
{
return 'halcyon-datastore-db-' . $this->table . '-' . $this->source;
}
/**
- * Get all available paths within this datastore
- *
- * @return array $paths ['path/to/file1.md' => true (path can be handled and exists), 'path/to/file2.md' => false (path can be handled but doesn't exist)]
- */
- public function getAvailablePaths()
+ * @inheritDoc
+ **/
+ public function getAvailablePaths(): array
{
/**
* @event halcyon.datasource.db.beforeGetAvailablePaths
diff --git a/src/Halcyon/Datasource/FileDatasource.php b/src/Halcyon/Datasource/FileDatasource.php
index a6fabbdf6..b8cd59278 100644
--- a/src/Halcyon/Datasource/FileDatasource.php
+++ b/src/Halcyon/Datasource/FileDatasource.php
@@ -1,5 +1,8 @@
basePath = $basePath;
-
$this->files = $files;
-
$this->postProcessor = new Processor;
}
/**
- * Returns a single template.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return mixed
+ * @inheritDoc
*/
- public function selectOne(string $dirName, string $fileName, string $extension)
+ public function selectOne(string $dirName, string $fileName, string $extension): ?array
{
try {
$path = $this->makeFilePath($dirName, $fileName, $extension);
@@ -72,36 +61,26 @@ public function selectOne(string $dirName, string $fileName, string $extension)
'content' => $this->files->get($path),
'mtime' => $this->files->lastModified($path)
];
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
return null;
}
}
/**
- * Returns all templates.
- *
- * @param string $dirName
- * @param array $options Array of options, [
- * 'columns' => ['fileName', 'mtime', 'content'], // Only return specific columns
- * 'extensions' => ['htm', 'md', 'twig'], // Extensions to search for
- * 'fileMatch' => '*gr[ae]y', // Shell matching pattern to match the filename against using the fnmatch function
- * 'orders' => false // Not implemented
- * 'limit' => false // Not implemented
- * 'offset' => false // Not implemented
- * ];
- * @return array
+ * @inheritDoc
*/
- public function select(string $dirName, array $options = [])
+ public function select(string $dirName, array $options = []): array
{
- extract(array_merge([
+ // Prepare query options
+ $queryOptions = array_merge([
'columns' => null, // Only return specific columns (fileName, mtime, content)
'extensions' => null, // Match specified extensions
'fileMatch' => null, // Match the file name using fnmatch()
'orders' => null, // @todo
'limit' => null, // @todo
'offset' => null // @todo
- ], $options));
+ ], $options);
+ extract($queryOptions);
$result = [];
$dirPath = $this->makeDirectoryPath($dirName);
@@ -110,11 +89,12 @@ public function select(string $dirName, array $options = [])
return $result;
}
- if ($columns === ['*'] || !is_array($columns)) {
- $columns = null;
- }
- else {
- $columns = array_flip($columns);
+ if (isset($columns)) {
+ if ($columns === ['*'] || !is_array($columns)) {
+ $columns = null;
+ } else {
+ $columns = array_flip($columns);
+ }
}
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirPath));
@@ -131,7 +111,7 @@ public function select(string $dirName, array $options = [])
* Filter by extension
*/
$fileExt = $it->getExtension();
- if ($extensions !== null && !in_array($fileExt, $extensions)) {
+ if (isset($extensions) && !in_array($fileExt, $extensions)) {
$it->next();
continue;
}
@@ -144,7 +124,7 @@ public function select(string $dirName, array $options = [])
/*
* Filter by file name match
*/
- if ($fileMatch !== null && !fnmatch($fileMatch, $fileName)) {
+ if (isset($fileMatch) && !fnmatch($fileMatch, $fileName)) {
$it->next();
continue;
}
@@ -155,11 +135,11 @@ public function select(string $dirName, array $options = [])
$item['fileName'] = $fileName;
- if (!$columns || array_key_exists('content', $columns)) {
+ if (!isset($columns) || array_key_exists('content', $columns)) {
$item['content'] = $this->files->get($path);
}
- if (!$columns || array_key_exists('mtime', $columns)) {
+ if (!isset($columns) || array_key_exists('mtime', $columns)) {
$item['mtime'] = $this->files->lastModified($path);
}
@@ -172,15 +152,9 @@ public function select(string $dirName, array $options = [])
}
/**
- * Creates a new template.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @param string $content
- * @return bool
+ * @inheritDoc
*/
- public function insert(string $dirName, string $fileName, string $extension, string $content)
+ public function insert(string $dirName, string $fileName, string $extension, string $content): int
{
$this->validateDirectoryForSave($dirName, $fileName, $extension);
@@ -192,24 +166,15 @@ public function insert(string $dirName, string $fileName, string $extension, str
try {
return $this->files->put($path, $content);
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
throw (new CreateFileException)->setInvalidPath($path);
}
}
/**
- * Updates an existing template.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @param string $content
- * @param string $oldFileName Defaults to null
- * @param string $oldExtension Defaults to null
- * @return int
+ * @inheritDoc
*/
- public function update(string $dirName, string $fileName, string $extension, string $content, $oldFileName = null, $oldExtension = null)
+ public function update(string $dirName, string $fileName, string $extension, string $content, ?string $oldFileName = null, ?string $oldExtension = null): int
{
$this->validateDirectoryForSave($dirName, $fileName, $extension);
@@ -238,48 +203,35 @@ public function update(string $dirName, string $fileName, string $extension, str
try {
return $this->files->put($path, $content);
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
throw (new CreateFileException)->setInvalidPath($path);
}
}
/**
- * Run a delete statement against the datasource.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return bool
+ * @inheritDoc
*/
- public function delete(string $dirName, string $fileName, string $extension)
+ public function delete(string $dirName, string $fileName, string $extension): bool
{
$path = $this->makeFilePath($dirName, $fileName, $extension);
try {
return $this->files->delete($path);
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
throw (new DeleteFileException)->setInvalidPath($path);
}
}
/**
- * Run a delete statement against the datasource.
- *
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return int
+ * @inheritDoc
*/
- public function lastModified(string $dirName, string $fileName, string $extension)
+ public function lastModified(string $dirName, string $fileName, string $extension): ?int
{
try {
$path = $this->makeFilePath($dirName, $fileName, $extension);
return $this->files->lastModified($path);
- }
- catch (Exception $ex) {
+ } catch (Exception $ex) {
return null;
}
}
@@ -287,12 +239,11 @@ public function lastModified(string $dirName, string $fileName, string $extensio
/**
* Ensure the requested file can be created in the requested directory.
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return void
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
*/
- protected function validateDirectoryForSave(string $dirName, string $fileName, string $extension)
+ protected function validateDirectoryForSave(string $dirName, string $fileName, string $extension): void
{
$path = $this->makeFilePath($dirName, $fileName, $extension);
$dirPath = $this->makeDirectoryPath($dirName);
@@ -330,7 +281,7 @@ protected function validateDirectoryForSave(string $dirName, string $fileName, s
* @throws InvalidFileNameException If the path is outside of the basePath of the datasource
* @return string
*/
- protected function makeDirectoryPath($dirName, $relativePath = '')
+ protected function makeDirectoryPath(string $dirName, string $relativePath = ''): string
{
$base = $this->basePath . '/' . $dirName;
$path = !empty($relativePath) ? $base . '/' . $relativePath : $base;
@@ -348,54 +299,38 @@ protected function makeDirectoryPath($dirName, $relativePath = '')
}
/**
- * Helper to make file path.
+ * Helper method to make the full file path to the model.
*
- * @param string $dirName
- * @param string $fileName
- * @param string $extension
- * @return string
+ * @param string $dirName The directory in which the model is stored.
+ * @param string $fileName The filename of the model.
+ * @param string $extension The file extension of the model.
+ * @return string The full file path.
*/
- protected function makeFilePath(string $dirName, string $fileName, string $extension)
+ protected function makeFilePath(string $dirName, string $fileName, string $extension): string
{
return $this->makeDirectoryPath($dirName, $fileName . '.' . $extension);
}
- /**
- * Generate a cache key unique to this datasource.
- *
- * @param string $name
- * @return string
- */
- public function makeCacheKey($name = '')
- {
- return crc32($this->basePath . $name);
- }
-
/**
* Returns the base path for this datasource.
- * @return string
*/
- public function getBasePath()
+ public function getBasePath(): string
{
return $this->basePath;
}
/**
- * Generate a paths cache key unique to this datasource
- *
- * @return string
+ * @inheritDoc
*/
- public function getPathsCacheKey()
+ public function getPathsCacheKey(): string
{
return 'halcyon-datastore-file-' . $this->basePath;
}
/**
- * Get all available paths within this datastore
- *
- * @return array $paths ['path/to/file1.md' => true (path can be handled and exists), 'path/to/file2.md' => false (path can be handled but doesn't exist)]
+ * @inheritDoc
*/
- public function getAvailablePaths()
+ public function getAvailablePaths(): array
{
$pathsCache = [];
$it = (is_dir($this->basePath))
diff --git a/src/Halcyon/Datasource/Resolver.php b/src/Halcyon/Datasource/Resolver.php
index 2d4dd11ed..8ce7d1f6b 100644
--- a/src/Halcyon/Datasource/Resolver.php
+++ b/src/Halcyon/Datasource/Resolver.php
@@ -1,5 +1,7 @@
getDefaultDatasource();
}
+ if (!array_key_exists($name, $this->datasources)) {
+ throw new MissingDatasourceException(
+ sprintf('The Halcyon datasource "%s" does not exist.', $name)
+ );
+ }
return $this->datasources[$name];
}
/**
- * Add a datasource to the resolver.
- *
- * @param string $name
- * @param \Winter\Storm\Halcyon\Datasource\DatasourceInterface $datasource
- * @return void
+ * @inheritDoc
*/
- public function addDatasource($name, DatasourceInterface $datasource)
+ public function addDatasource(string $name, DatasourceInterface $datasource): void
{
$this->datasources[$name] = $datasource;
}
/**
- * Check if a datasource has been registered.
- *
- * @param string $name
- * @return bool
+ * @inheritDoc
*/
- public function hasDatasource($name)
+ public function hasDatasource(string $name): bool
{
- return isset($this->datasources[$name]);
+ return array_key_exists($name, $this->datasources);
}
/**
- * Get the default datasource name.
- *
- * @return string
+ * @inheritDoc
*/
- public function getDefaultDatasource()
+ public function getDefaultDatasource(): ?string
{
- return $this->default;
+ return $this->default ?? null;
}
/**
- * Set the default datasource name.
- *
- * @param string $name
- * @return void
+ * @inheritDoc
*/
- public function setDefaultDatasource($name)
+ public function setDefaultDatasource(string $name): void
{
$this->default = $name;
}
diff --git a/src/Halcyon/Datasource/ResolverInterface.php b/src/Halcyon/Datasource/ResolverInterface.php
index 0f5a03730..2753599b1 100644
--- a/src/Halcyon/Datasource/ResolverInterface.php
+++ b/src/Halcyon/Datasource/ResolverInterface.php
@@ -1,28 +1,48 @@
putInMemoryCache($key, $value);
- parent::forever($key, $value);
+ return parent::forever($key, $value);
}
/**
@@ -127,8 +127,8 @@ public function flush()
* Retrieve an item from the internal memory cache without trying the external driver.
* Used in testing
*
- * @param $key
- * @return mixed
+ * @param string $key
+ * @return mixed|null
*/
public function getFromMemoryCache($key)
{
@@ -139,8 +139,8 @@ public function getFromMemoryCache($key)
* Puts an item in the memory cache, but not in the external cache.
* Used in testing
*
- * @param $key
- * @param $value
+ * @param string $key
+ * @param mixed $value
*/
public function putInMemoryCache($key, $value)
{
diff --git a/src/Halcyon/Model.php b/src/Halcyon/Model.php
index 289e20c73..427607cdd 100644
--- a/src/Halcyon/Model.php
+++ b/src/Halcyon/Model.php
@@ -15,19 +15,23 @@
/**
* This is a base template object. Equivalent to a Model in ORM.
*
+ * @property string|null $fileName Halcyon models generally provide a filename of the model being manipulated.
+ * @property int|null $mtime Halcyon models generally provide a timestamp of last modification.
+ * @method \Illuminate\Support\MessageBag|null errors() If the Validation trait is attached to the model, this method will provide the validation errors.
+ *
* @author Alexey Bobkov, Samuel Georges
*/
-class Model extends Extendable implements ArrayAccess, Arrayable, Jsonable, JsonSerializable
+class Model extends Extendable implements ModelInterface, ArrayAccess, Arrayable, Jsonable, JsonSerializable
{
use \Winter\Storm\Support\Traits\Emitter;
/**
- * @var string The data source for the model, a directory path.
+ * @var string|null The data source for the model, a directory path.
*/
protected $datasource;
/**
- * @var string The container name associated with the model, eg: pages.
+ * @var string|null The container name associated with the model, eg: pages.
*/
protected $dirName;
@@ -105,21 +109,21 @@ class Model extends Extendable implements ArrayAccess, Arrayable, Jsonable, Json
/**
* The cache manager instance.
*
- * @var \Illuminate\Cache\CacheManager
+ * @var \Illuminate\Cache\CacheManager|null
*/
protected static $cache;
/**
* The datasource resolver instance.
*
- * @var \Winter\Storm\Halcyon\Datasource\ResolverInterface
+ * @var \Winter\Storm\Halcyon\Datasource\ResolverInterface|null
*/
protected static $resolver;
/**
* The event dispatcher instance.
*
- * @var \Illuminate\Contracts\Events\Dispatcher
+ * @var \Winter\Storm\Events\Dispatcher|null
*/
protected static $dispatcher;
@@ -143,10 +147,7 @@ class Model extends Extendable implements ArrayAccess, Arrayable, Jsonable, Json
protected static $booted = [];
/**
- * Create a new Halcyon model instance.
- *
- * @param array $attributes
- * @return void
+ * @inheritDoc
*/
public function __construct(array $attributes = [])
{
@@ -406,7 +407,7 @@ public function isLoadedFromCache()
/**
* Returns true if the object was loaded from the cache.
- * @return boolean
+ * @return void
*/
public function setLoadedFromCache($value)
{
@@ -556,7 +557,7 @@ public static function on($datasource = null)
/**
* Get all of the models from the datasource.
*
- * @return \Winter\Storm\Halcyon\Collection|static[]
+ * @return \Winter\Storm\Halcyon\Collection
*/
public static function all()
{
@@ -679,7 +680,8 @@ public function getAttribute($key)
/**
* @see Winter\Storm\Database\Model::getAttributeValue
*/
- if (($attr = $this->fireEvent('model.beforeGetAttribute', [$key], true)) !== null) {
+ $attr = $this->fireEvent('model.beforeGetAttribute', [$key], true);
+ if (!is_null($attr)) {
return $attr;
}
@@ -695,7 +697,8 @@ public function getAttribute($key)
/**
* @see Winter\Storm\Database\Model::getAttributeValue
*/
- if (($_attr = $this->fireEvent('model.getAttribute', [$key, $value], true)) !== null) {
+ $_attr = $this->fireEvent('model.getAttribute', [$key, $value], true);
+ if (!is_null($_attr)) {
return $_attr;
}
@@ -978,12 +981,12 @@ public function delete()
*/
protected function performDeleteOnModel()
{
- $this->newQuery()->delete($this->fileName);
+ $this->newQuery()->delete();
}
/**
* Create a new native event for handling beforeFetch().
- * @param Closure|string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function fetching($callback)
@@ -993,7 +996,7 @@ public static function fetching($callback)
/**
* Create a new native event for handling afterFetch().
- * @param Closure|string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function fetched($callback)
@@ -1211,7 +1214,7 @@ public function update(array $attributes = [])
* @param array $options
* @return bool
*/
- public function save(array $options = null)
+ public function save(?array $options = [])
{
return $this->saveInternal(['force' => false] + (array) $options);
}
@@ -1241,14 +1244,14 @@ public function saveInternal(array $options = [])
}
if ($this->exists) {
- $saved = $this->performUpdate($query, $options);
+ $saved = $this->performUpdate($query);
}
else {
- $saved = $this->performInsert($query, $options);
+ $saved = $this->performInsert($query);
}
if ($saved) {
- $this->finishSave($options);
+ $this->finishSave();
}
return $saved;
@@ -1257,10 +1260,9 @@ public function saveInternal(array $options = [])
/**
* Finish processing on a successful save operation.
*
- * @param array $options
* @return void
*/
- protected function finishSave(array $options)
+ protected function finishSave()
{
$this->fireModelEvent('saved', false);
@@ -1272,11 +1274,10 @@ protected function finishSave(array $options)
/**
* Perform a model update operation.
*
- * @param Winter\Storm\Halcyon\Builder $query
- * @param array $options
+ * @param \Winter\Storm\Halcyon\Builder $query
* @return bool
*/
- protected function performUpdate(Builder $query, array $options = [])
+ protected function performUpdate(Builder $query)
{
$dirty = $this->getDirty();
@@ -1305,11 +1306,10 @@ protected function performUpdate(Builder $query, array $options = [])
/**
* Perform a model insert operation.
*
- * @param Winter\Storm\Halcyon\Builder $query
- * @param array $options
+ * @param \Winter\Storm\Halcyon\Builder $query
* @return bool
*/
- protected function performInsert(Builder $query, array $options = [])
+ protected function performInsert(Builder $query)
{
if ($this->fireModelEvent('creating') === false) {
return false;
@@ -1402,7 +1402,7 @@ public function getFileNameParts($fileName = null)
/**
* Get the datasource for the model.
*
- * @return \Winter\Storm\Halcyon\Datasource
+ * @return \Winter\Storm\Halcyon\Datasource\DatasourceInterface
*/
public function getDatasource()
{
@@ -1436,7 +1436,7 @@ public function setDatasource($name)
* Resolve a datasource instance.
*
* @param string|null $datasource
- * @return \Winter\Storm\Halcyon\Datasource
+ * @return \Winter\Storm\Halcyon\Datasource\DatasourceInterface
*/
public static function resolveDatasource($datasource = null)
{
@@ -1446,7 +1446,7 @@ public static function resolveDatasource($datasource = null)
/**
* Get the datasource resolver instance.
*
- * @return \Winter\Storm\Halcyon\DatasourceResolverInterface
+ * @return \Winter\Storm\Halcyon\Datasource\ResolverInterface
*/
public static function getDatasourceResolver()
{
@@ -1477,7 +1477,7 @@ public static function unsetDatasourceResolver()
/**
* Get the event dispatcher instance.
*
- * @return \Illuminate\Contracts\Events\Dispatcher
+ * @return \Winter\Storm\Events\Dispatcher
*/
public static function getEventDispatcher()
{
@@ -1487,7 +1487,7 @@ public static function getEventDispatcher()
/**
* Set the event dispatcher instance.
*
- * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
+ * @param \Winter\Storm\Events\Dispatcher $dispatcher
* @return void
*/
public static function setEventDispatcher(Dispatcher $dispatcher)
@@ -1508,7 +1508,7 @@ public static function unsetEventDispatcher()
/**
* Get the cache manager instance.
*
- * @return \Illuminate\Cache\CacheManager
+ * @return \Illuminate\Cache\CacheManager|null
*/
public static function getCacheManager()
{
@@ -1539,7 +1539,8 @@ public static function unsetCacheManager()
/**
* Initializes the object properties from the cached data. The extra data
* set here becomes available as attributes set on the model after fetch.
- * @param array $cached The cached data array.
+ *
+ * @param mixed $item
*/
public static function initCacheItem(&$item)
{
@@ -1551,9 +1552,13 @@ public static function initCacheItem(&$item)
*/
public static function flushDuplicateCache()
{
- if (MemoryCacheManager::isEnabled() && self::getCacheManager() !== null) {
- self::getCacheManager()->driver()->flushInternalCache();
+ if (!MemoryCacheManager::isEnabled() || is_null(self::getCacheManager())) {
+ return;
}
+
+ /** @var \Winter\Storm\Halcyon\MemoryRepository */
+ $cacheDriver = self::getCacheManager()->driver();
+ $cacheDriver->flushInternalCache();
}
/**
diff --git a/src/Halcyon/ModelInterface.php b/src/Halcyon/ModelInterface.php
new file mode 100644
index 000000000..4da27073e
--- /dev/null
+++ b/src/Halcyon/ModelInterface.php
@@ -0,0 +1,20 @@
+ true,
'isCompoundObject' => true,
- ], $options));
+ ], $options);
+ extract($sectionOptions);
- if (!$isCompoundObject) {
+ if (!isset($isCompoundObject) || $isCompoundObject === false) {
return array_get($data, 'content', '');
}
@@ -93,7 +94,7 @@ public static function render(array $data, array $options = []): string
// Prepare code section for saving
$code = trim(array_get($data, 'code', '') ?? '');
if ($code) {
- if ($wrapCodeInPhpTags) {
+ if (isset($wrapCodeInPhpTags) && $wrapCodeInPhpTags === true) {
$code = preg_replace('/^\<\?php/', '', $code);
$code = preg_replace('/^\<\?/', '', $code);
$code = preg_replace('/\?>$/', '', $code);
@@ -189,9 +190,10 @@ public static function render(array $data, array $options = []): string
*/
public static function parse(string $content, array $options = []): array
{
- extract(array_merge([
- 'isCompoundObject' => true,
- ], $options));
+ $sectionOptions = array_merge([
+ 'isCompoundObject' => true
+ ], $options);
+ extract($sectionOptions);
$result = [
'settings' => [],
@@ -199,7 +201,7 @@ public static function parse(string $content, array $options = []): array
'markup' => null,
];
- if (!$isCompoundObject || !strlen($content)) {
+ if (!isset($isCompoundObject) || $isCompoundObject === false || !strlen($content)) {
return $result;
}
@@ -222,7 +224,7 @@ public static function parse(string $content, array $options = []): array
$result['markup'] = $sections[2];
} elseif ($count == 2) {
- $result['settings'] = @$iniParser->parse($sections[0], true)
+ $result['settings'] = @$iniParser->parse($sections[0])
?: [self::ERROR_INI => $sections[0]];
$result['markup'] = $sections[1];
@@ -267,9 +269,12 @@ public static function parseOffset(string $content): array
}
/**
- * Returns the line number of a found instance of a section separator (==).
+ * Returns the line number of a found instance of CMS object section separator (==).
+ * @param string $content Object content
+ * @param int $instance Which instance to look for
+ * @return int|null The line number the instance was found.
*/
- private static function calculateLinePosition(string $content, int $instance = 1): int
+ protected static function calculateLinePosition(string $content, int $instance = 1): ?int
{
$count = 0;
$lines = explode(PHP_EOL, $content);
@@ -278,7 +283,7 @@ private static function calculateLinePosition(string $content, int $instance = 1
$count++;
}
- if ($count == $instance) {
+ if ($count === $instance) {
return static::adjustLinePosition($content, $number);
}
}
@@ -291,7 +296,7 @@ private static function calculateLinePosition(string $content, int $instance = 1
* after the separator (==). There can be an opening tag or white space in between
* where the section really begins.
*/
- private static function adjustLinePosition(string $content, int $startLine = -1): int
+ protected static function adjustLinePosition(string $content, int $startLine = -1): int
{
// Account for the separator itself.
$startLine++;
diff --git a/src/Html/BlockBuilder.php b/src/Html/BlockBuilder.php
index d072bd25e..575071ea7 100644
--- a/src/Html/BlockBuilder.php
+++ b/src/Html/BlockBuilder.php
@@ -9,42 +9,44 @@
*/
class BlockBuilder
{
- protected $blockStack = [];
- protected $blocks = [];
+ /**
+ * The block stack.
+ */
+ protected array $blockStack = [];
/**
- * Helper for startBlock
- *
- * @param string $name Specifies the block name.
- * @return void
+ * Registered block contents, keyed by block name.
*/
- public function put($name)
+ protected array $blocks = [];
+
+ /**
+ * Helper method for the "startBlock" templating function.
+ */
+ public function put(string $name): void
{
$this->startBlock($name);
}
/**
- * Begins the layout block.
+ * Begins the layout block for a given block name.
*
* This method enables output buffering, so all output will be captured as a part of this block.
- *
- * @param string $name Specifies the block name.
- * @return void
*/
- public function startBlock($name)
+ public function startBlock(string $name): void
{
array_push($this->blockStack, $name);
ob_start();
}
/**
- * Helper for endBlock and also clears the output buffer.
+ * Helper method for the "endBlock" templating function.
+ *
+ * If `$append` is `true`, the new content should be appended to an existing block, as opposed to overwriting any
+ * previous content.
*
- * @param boolean $append Indicates that the new content should be appended to the existing block content.
- * @return void
* @throws \Exception if there are no items in the block stack
*/
- public function endPut($append = false)
+ public function endPut(bool $append = false): void
{
$this->endBlock($append);
}
@@ -54,11 +56,9 @@ public function endPut($append = false)
*
* This captures all buffered output as the block's content, and ends output buffering.
*
- * @param boolean $append Indicates that the new content should be appended to the existing block content.
- * @return void
* @throws \Exception if there are no items in the block stack
*/
- public function endBlock($append = false)
+ public function endBlock(bool $append = false): void
{
if (!count($this->blockStack)) {
throw new Exception('Invalid block nesting');
@@ -75,30 +75,21 @@ public function endBlock($append = false)
}
/**
- * Sets a content of the layout block.
+ * Sets a content of the layout block, overwriting any previous content for that block.
*
* Output buffering is not used for this method.
- *
- * @param string $name Specifies the block name.
- * @param string $content Specifies the block content.
- * @return void
- * @throws \Exception if there are no items in the block stack
*/
- public function set($name, $content)
+ public function set(string $name, string $content): void
{
$this->blocks[$name] = $content;
}
/**
- * Appends a content of the layout block.
+ * Appends content to a layout block.
*
* Output buffering is not used for this method.
- *
- * @param string $name Specifies the block name.
- * @param string $content Specifies the block content.
- * @return void
*/
- public function append($name, $content)
+ public function append(string $name, string $content): void
{
if (!isset($this->blocks[$name])) {
$this->blocks[$name] = '';
@@ -108,13 +99,11 @@ public function append($name, $content)
}
/**
- * Returns the layout block contents and deletes the block from memory.
+ * Returns the layout block contents of a given block name and deletes the block from memory.
*
- * @param string $name Specifies the block name.
- * @param string $default Specifies a default block value to use if the block requested is not exists.
- * @return string
+ * If the block does not exist, then the `$default` content will be returned instead.
*/
- public function placeholder($name, $default = null)
+ public function placeholder(string $name, string $default = ''): string
{
$result = $this->get($name, $default);
unset($this->blocks[$name]);
@@ -127,16 +116,14 @@ public function placeholder($name, $default = null)
}
/**
- * Returns the layout block contents but not deletes the block from memory.
+ * Returns the layout block contents of a given name, but does not delete it from memory.
*
- * @param string $name Specifies the block name.
- * @param string $default Specifies a default block value to use if the block requested is not exists.
- * @return string
+ * If the block does not exist, then the `$default` content will be returned instead.
*/
- public function get($name, $default = null)
+ public function get(string $name, string $default = ''): string
{
if (!isset($this->blocks[$name])) {
- return $default;
+ return $default;
}
return $this->blocks[$name];
@@ -144,10 +131,8 @@ public function get($name, $default = null)
/**
* Clears all the registered blocks.
- *
- * @return void
*/
- public function reset()
+ public function reset(): void
{
$this->blockStack = [];
$this->blocks = [];
@@ -155,10 +140,8 @@ public function reset()
/**
* Gets the block stack at this point.
- *
- * @return array
*/
- public function getBlockStack()
+ public function getBlockStack(): array
{
return $this->blockStack;
}
diff --git a/src/Html/FormBuilder.php b/src/Html/FormBuilder.php
index c42816d07..a671a558a 100644
--- a/src/Html/FormBuilder.php
+++ b/src/Html/FormBuilder.php
@@ -14,88 +14,92 @@ class FormBuilder
/**
* The HTML builder instance.
- *
- * @var \Winter\Storm\Html\HtmlBuilder
*/
- protected $html;
+ protected \Winter\Storm\Html\HtmlBuilder $html;
/**
* The URL generator instance.
- *
- * @var \Illuminate\Routing\UrlGenerator $url
*/
- protected $url;
+ protected \Illuminate\Routing\UrlGenerator $url;
/**
* The CSRF token used by the form builder.
- *
- * @var string
*/
- protected $csrfToken;
+ protected ?string $csrfToken = null;
/**
* The session store implementation.
- *
- * @var \Illuminate\Session\Store
*/
- protected $session;
+ protected ?\Illuminate\Session\Store $session;
/**
* The current model instance for the form.
- *
- * @var mixed
*/
- protected $model;
+ protected object|array|null $model = null;
/**
* An array of label names we've created.
- *
- * @var array
*/
- protected $labels = [];
+ protected array $labels = [];
/**
* The reserved form open attributes.
- * @var array
*/
- protected $reserved = ['method', 'url', 'route', 'action', 'files', 'request', 'model', 'sessionKey'];
-
- /**
- * The reserved form open attributes.
- * @var array
- */
- protected $reservedAjax = ['request', 'success', 'error', 'complete', 'confirm', 'redirect', 'update', 'data', 'validate', 'flash'];
+ protected array $reserved = [
+ 'method',
+ 'url',
+ 'route',
+ 'action',
+ 'files',
+ 'request',
+ 'model',
+ 'sessionKey'
+ ];
+
+ /**
+ * The reserved form AJAX attributes.
+ */
+ protected array $reservedAjax = [
+ 'request',
+ 'success',
+ 'error',
+ 'complete',
+ 'confirm',
+ 'redirect',
+ 'update',
+ 'data',
+ 'validate',
+ 'flash'
+ ];
/**
* The form methods that should be spoofed, in uppercase.
- *
- * @var array
*/
- protected $spoofedMethods = ['DELETE', 'PATCH', 'PUT'];
+ protected array $spoofedMethods = [
+ 'DELETE',
+ 'PATCH',
+ 'PUT'
+ ];
/**
* The types of inputs to not fill values on by default.
- *
- * @var array
*/
- protected $skipValueTypes = ['file', 'password', 'checkbox', 'radio'];
+ protected array $skipValueTypes = [
+ 'file',
+ 'password',
+ 'checkbox',
+ 'radio'
+ ];
/**
* The session key used by the form builder.
- * @var string
*/
- protected $sessionKey;
+ protected ?string $sessionKey = null;
/**
* Create a new form builder instance.
- *
- * @param \Winter\Storm\Html\HtmlBuilder $html
- * @param \Illuminate\Routing\UrlGenerator $url
- * @param string $csrfToken
- * @param string $sessionKey
- * @return void
*/
- public function __construct(HtmlBuilder $html, UrlGeneratorBase $url, $csrfToken, $sessionKey)
+ public function __construct(HtmlBuilder $html, UrlGeneratorBase $url, ?string $csrfToken = null, ?string $sessionKey = null)
{
$this->url = $url;
$this->html = $html;
@@ -105,10 +109,8 @@ public function __construct(HtmlBuilder $html, UrlGeneratorBase $url, $csrfToken
/**
* Open up a new HTML form and includes a session key.
- * @param array $options
- * @return string
*/
- public function open(array $options = [])
+ public function open(array $options = []): string
{
$method = strtoupper(array_get($options, 'method', 'post'));
$request = array_get($options, 'request');
@@ -162,11 +164,8 @@ public function open(array $options = [])
/**
* Helper for opening a form used for an AJAX call.
- * @param string $handler Request handler name, eg: onUpdate
- * @param array $options
- * @return string
*/
- public function ajax($handler, array $options = [])
+ public function ajax(string|array $handler, array $options = []): string
{
if (is_array($handler)) {
$handler = implode('::', $handler);
@@ -194,12 +193,8 @@ public function ajax($handler, array $options = [])
/**
* Create a new model based form builder.
- *
- * @param mixed $model
- * @param array $options
- * @return string
*/
- public function model($model, array $options = [])
+ public function model(object|array $model, array $options = []): string
{
$this->model = $model;
@@ -208,21 +203,16 @@ public function model($model, array $options = [])
/**
* Set the model instance on the form builder.
- *
- * @param mixed $model
- * @return void
*/
- public function setModel($model)
+ public function setModel(object|array|null $model): void
{
$this->model = $model;
}
/**
* Close the current form.
- *
- * @return string
*/
- public function close()
+ public function close(): string
{
$this->labels = [];
@@ -233,10 +223,8 @@ public function close()
/**
* Generate a hidden field with the current CSRF token.
- *
- * @return string
*/
- public function token()
+ public function token(): string
{
$token = !empty($this->csrfToken)
? $this->csrfToken
@@ -247,13 +235,8 @@ public function token()
/**
* Create a form label element.
- *
- * @param string $name
- * @param string $value
- * @param array $options
- * @return string
*/
- public function label($name, $value = null, $options = [])
+ public function label(string $name, string $value = '', array $options = []): string
{
$this->labels[] = $name;
@@ -266,12 +249,8 @@ public function label($name, $value = null, $options = [])
/**
* Format the label value.
- *
- * @param string $name
- * @param string|null $value
- * @return string
*/
- protected function formatLabel($name, $value)
+ protected function formatLabel(string $name, string $value = ''): string
{
return $value ?: ucwords(str_replace('_', ' ', $name));
}
@@ -279,109 +258,85 @@ protected function formatLabel($name, $value)
/**
* Create a form input field.
*
- * @param string $type
- * @param string $name
- * @param string $value
- * @param array $options
+ * @param string $type
+ * @param string|null $name
+ * @param string|null $value
+ * @param array $options
* @return string
*/
- public function input($type, $name, $value = null, $options = [])
+ public function input(string $type, ?string $name = null, ?string $value = null, array $options = []): string
{
if (!isset($options['name'])) {
$options['name'] = $name;
}
- // We will get the appropriate value for the given field. We will look for the
- // value in the session for the value in the old input data then we'll look
- // in the model instance if one is set. Otherwise we will just use empty.
- $id = $this->getIdAttribute($name, $options);
+ if (!empty($name)) {
+ // We will get the appropriate value for the given field. We will look for the
+ // value in the session for the value in the old input data then we'll look
+ // in the model instance if one is set. Otherwise we will just use empty.
+ $id = $this->getIdAttribute($name, $options);
- if (!in_array($type, $this->skipValueTypes)) {
- $value = $this->getValueAttribute($name, $value);
- }
+ if (!in_array($type, $this->skipValueTypes)) {
+ $value = $this->getValueAttribute($name, $value);
+ }
- // Once we have the type, value, and ID we can merge them into the rest of the
- // attributes array so we can convert them into their HTML attribute format
- // when creating the HTML element. Then, we will return the entire input.
- $merge = compact('type', 'value', 'id');
+ // Once we have the type, value, and ID we can merge them into the rest of the
+ // attributes array so we can convert them into their HTML attribute format
+ // when creating the HTML element. Then, we will return the entire input.
+ $merge = compact('type', 'value', 'id');
- $options = array_merge($options, $merge);
+ $options = array_filter(array_merge($options, $merge), function ($item) {
+ return !is_null($item);
+ });
+ }
- return 'html->attributes($options).'>';
+ return 'html->attributes($options) . '>';
}
/**
* Create a text input field.
- *
- * @param string $name
- * @param string $value
- * @param array $options
- * @return string
*/
- public function text($name, $value = null, $options = [])
+ public function text(string $name, ?string $value = null, array $options = []): string
{
return $this->input('text', $name, $value, $options);
}
/**
* Create a password input field.
- *
- * @param string $name
- * @param array $options
- * @return string
*/
- public function password($name, $options = [])
+ public function password(string $name, array $options = []): string
{
return $this->input('password', $name, '', $options);
}
/**
* Create a hidden input field.
- *
- * @param string $name
- * @param string $value
- * @param array $options
- * @return string
*/
- public function hidden($name, $value = null, $options = [])
+ public function hidden(string $name, ?string $value = null, array $options = []): string
{
return $this->input('hidden', $name, $value, $options);
}
/**
- * Create an e-mail input field.
- *
- * @param string $name
- * @param string $value
- * @param array $options
- * @return string
+ * Create an email input field.
*/
- public function email($name, $value = null, $options = [])
+ public function email(string $name, ?string $value = null, array $options = []): string
{
return $this->input('email', $name, $value, $options);
}
/**
- * Create a url input field.
- *
- * @param string $name
- * @param string $value
- * @param array $options
- * @return string
+ * Create a URL input field.
*/
- public function url($name, $value = null, $options = [])
+ public function url(string $name, ?string $value = null, array $options = []): string
{
return $this->input('url', $name, $value, $options);
}
/**
* Create a file input field.
- *
- * @param string $name
- * @param array $options
- * @return string
*/
- public function file($name, $options = [])
+ public function file(string $name, array $options = []): string
{
return $this->input('file', $name, null, $options);
}
@@ -392,13 +347,8 @@ public function file($name, $options = [])
/**
* Create a textarea input field.
- *
- * @param string $name
- * @param string $value
- * @param array $options
- * @return string
*/
- public function textarea($name, $value = null, $options = [])
+ public function textarea(string $name, ?string $value = null, array $options = []): string
{
if (!isset($options['name'])) {
$options['name'] = $name;
@@ -425,11 +375,8 @@ public function textarea($name, $value = null, $options = [])
/**
* Set the text area size on the attributes.
- *
- * @param array $options
- * @return array
*/
- protected function setTextAreaSize($options)
+ protected function setTextAreaSize(array $options): array
{
if (isset($options['size'])) {
return $this->setQuickTextAreaSize($options);
@@ -447,11 +394,8 @@ protected function setTextAreaSize($options)
/**
* Set the text area size using the quick "size" attribute.
- *
- * @param array $options
- * @return array
*/
- protected function setQuickTextAreaSize($options)
+ protected function setQuickTextAreaSize(array $options): array
{
$segments = explode('x', $options['size']);
@@ -464,13 +408,8 @@ protected function setQuickTextAreaSize($options)
/**
* Create a select box field with empty option support.
- * @param string $name
- * @param array $list
- * @param string $selected
- * @param array $options
- * @return string
*/
- public function select($name, $list = [], $selected = null, $options = [])
+ public function select(string $name, array $list = [], string|array|null $selected = null, array $options = []): string
{
if (array_key_exists('emptyOption', $options)) {
$list = ['' => $options['emptyOption']] + $list;
@@ -508,15 +447,8 @@ public function select($name, $list = [], $selected = null, $options = [])
/**
* Create a select range field.
- *
- * @param string $name
- * @param string $begin
- * @param string $end
- * @param string $selected
- * @param array $options
- * @return string
*/
- public function selectRange($name, $begin, $end, $selected = null, $options = [])
+ public function selectRange(string $name, string|int|float $begin, string|int|float $end, string|array|null $selected = null, array $options = []): string
{
$range = array_combine($range = range($begin, $end), $range);
@@ -525,29 +457,19 @@ public function selectRange($name, $begin, $end, $selected = null, $options = []
/**
* Create a select year field.
- *
- * @param string $name
- * @param string $begin
- * @param string $end
- * @param string $selected
- * @param array $options
- * @return string
*/
- public function selectYear()
+ public function selectYear(string $name, int $begin = 1900, ?int $end = null, string|array|null $selected = null, array $options = []): string
{
- return call_user_func_array([$this, 'selectRange'], func_get_args());
+ if (is_null($end)) {
+ $end = (int) date('Y');
+ }
+ return $this->selectRange($name, $begin, $end, $selected, $options);
}
/**
* Create a select month field.
- *
- * @param string $name
- * @param string $selected
- * @param array $options
- * @param string $format
- * @return string
*/
- public function selectMonth($name, $selected = null, $options = [], $format = '%B')
+ public function selectMonth(string $name, string|array|null $selected = null, array $options = [], $format = '%B'): string
{
$months = [];
@@ -560,13 +482,8 @@ public function selectMonth($name, $selected = null, $options = [], $format = '%
/**
* Get the select option for the given value.
- *
- * @param string $display
- * @param string $value
- * @param string $selected
- * @return string
*/
- public function getSelectOption($display, $value, $selected)
+ public function getSelectOption(string|array $display, string $value, string|array|null $selected = null): string
{
if (is_array($display)) {
return $this->optionGroup($display, $value, $selected);
@@ -577,13 +494,8 @@ public function getSelectOption($display, $value, $selected)
/**
* Create an option group form element.
- *
- * @param array $list
- * @param string $label
- * @param string $selected
- * @return string
*/
- protected function optionGroup($list, $label, $selected)
+ protected function optionGroup(array $list, string $label, string|array|null $selected = null): string
{
$html = [];
@@ -591,40 +503,38 @@ protected function optionGroup($list, $label, $selected)
$html[] = $this->option($display, $value, $selected);
}
- return '';
+ return '';
}
/**
* Create a select element option.
- *
- * @param string $display
- * @param string $value
- * @param string $selected
- * @return string
*/
- protected function option($display, $value, $selected)
+ protected function option(string $display, string $value, string|array|null $selected = null): string
{
- $selected = $this->getSelectedValue($value, $selected);
+ $selectedAttr = $this->getSelectedValue($value, $selected);
- $options = ['value' => e($value), 'selected' => $selected];
+ $options = [
+ 'value' => e($value),
+ 'selected' => $selectedAttr
+ ];
- return '';
+ return '';
}
/**
* Determine if the value is selected.
- *
- * @param string $value
- * @param string $selected
- * @return string
*/
- protected function getSelectedValue($value, $selected)
+ protected function getSelectedValue(string $value, string|array|null $selected): string|null
{
+ if (is_null($selected)) {
+ return null;
+ }
+
if (is_array($selected)) {
return in_array($value, $selected) ? 'selected' : null;
}
- return ((string) $value == (string) $selected) ? 'selected' : null;
+ return ((string) $value === (string) $selected) ? 'selected' : null;
}
//
@@ -633,28 +543,16 @@ protected function getSelectedValue($value, $selected)
/**
* Create a checkbox input field.
- *
- * @param string $name
- * @param mixed $value
- * @param bool $checked
- * @param array $options
- * @return string
*/
- public function checkbox($name, $value = 1, $checked = null, $options = [])
+ public function checkbox(string $name, string $value = '1', bool $checked = false, array $options = []): string
{
return $this->checkable('checkbox', $name, $value, $checked, $options);
}
/**
* Create a radio button input field.
- *
- * @param string $name
- * @param mixed $value
- * @param bool $checked
- * @param array $options
- * @return string
*/
- public function radio($name, $value = null, $checked = null, $options = [])
+ public function radio(string $name, ?string $value = null, bool $checked = false, array $options = []): string
{
if (is_null($value)) {
$value = $name;
@@ -665,15 +563,8 @@ public function radio($name, $value = null, $checked = null, $options = [])
/**
* Create a checkable input field.
- *
- * @param string $type
- * @param string $name
- * @param mixed $value
- * @param bool $checked
- * @param array $options
- * @return string
*/
- protected function checkable($type, $name, $value, $checked, $options)
+ protected function checkable(string $type, string $name, string $value, bool $checked = false, array $options = []): string
{
$checked = $this->getCheckedState($type, $name, $value, $checked);
@@ -947,18 +838,20 @@ public function getIdAttribute($name, $attributes)
if (in_array($name, $this->labels)) {
return $name;
}
+
+ return '';
}
/**
* Get the value that should be assigned to the field.
*
* @param string $name
- * @param string $value
- * @return string
+ * @param string|array $value
+ * @return string|array|null
*/
public function getValueAttribute($name, $value = null)
{
- if (is_null($name)) {
+ if (empty($name)) {
return $value;
}
@@ -979,7 +872,7 @@ public function getValueAttribute($name, $value = null)
* Get the model value that should be assigned to the field.
*
* @param string $name
- * @return string
+ * @return string|array|null
*/
protected function getModelValueAttribute($name)
{
@@ -995,13 +888,15 @@ protected function getModelValueAttribute($name)
* Get a value from the session's old input.
*
* @param string $name
- * @return string
+ * @return string|array|null
*/
public function old($name)
{
if (isset($this->session)) {
return $this->session->getOldInput($this->transformKey($name));
}
+
+ return null;
}
/**
@@ -1057,7 +952,7 @@ public function setSessionStore(Session $session)
*/
public function value($name, $value = null)
{
- if (is_null($name)) {
+ if (empty($name)) {
return $value;
}
@@ -1104,7 +999,7 @@ public function sessionKey($sessionKey = null)
/**
* Returns the active session key, used fr deferred bindings.
- * @return string
+ * @return string|null
*/
public function getSessionKey()
{
diff --git a/src/Html/Helper.php b/src/Html/Helper.php
index 8df245c34..b521b9b15 100644
--- a/src/Html/Helper.php
+++ b/src/Html/Helper.php
@@ -11,7 +11,7 @@ class Helper
* Converts a HTML array string to an identifier string.
* HTML: user[location][city]
* Result: user-location-city
- * @param $string String to process
+ * @param string $string String to process
* @return string
*/
public static function nameToId($string)
@@ -23,7 +23,7 @@ public static function nameToId($string)
* Converts a HTML named array string to a PHP array. Empty values are removed.
* HTML: user[location][city]
* PHP: ['user', 'location', 'city']
- * @param $string String to process
+ * @param string $string String to process
* @return array
*/
public static function nameToArray($string)
diff --git a/src/Html/HtmlBuilder.php b/src/Html/HtmlBuilder.php
index 0f93f8808..6e5ef3307 100644
--- a/src/Html/HtmlBuilder.php
+++ b/src/Html/HtmlBuilder.php
@@ -108,7 +108,7 @@ public function image($url, $alt = null, $attributes = [], $secure = null)
* Generate a HTML link.
*
* @param string $url
- * @param string $title
+ * @param string|false|null $title
* @param array $attributes
* @param bool $secure
* @return string
@@ -281,7 +281,7 @@ protected function listing($type, $list, $attributes = [])
*
* @param mixed $key
* @param string $type
- * @param string $value
+ * @param string|array $value
* @return string
*/
protected function listingElement($key, $type, $value)
@@ -338,17 +338,17 @@ public function attributes($attributes)
* Build a single attribute element.
*
* @param string $key
- * @param string $value
- * @return string|void
+ * @param string|array|null $value
+ * @return string|null
*/
- protected function attributeElement($key, $value)
+ protected function attributeElement($key, $value = null)
{
if (is_numeric($key)) {
$key = $value;
}
if (is_null($value)) {
- return;
+ return null;
}
if (is_array($value)) {
@@ -395,7 +395,7 @@ public function obfuscate($value)
/**
* Removes HTML from a string
- * @param $string String to strip HTML from
+ * @param string $string String to strip HTML from
* @return string
*/
public static function strip($string)
@@ -412,15 +412,11 @@ public static function strip($string)
*/
public static function limit($html, $maxLength = 100, $end = '...')
{
- $isUtf8 = true;
$printedLength = 0;
$position = 0;
$tags = [];
- $regex = $isUtf8
- ? '{?([a-z]+)[^>]*>|?[a-zA-Z0-9]+;|[\x80-\xFF][\x80-\xBF]*}'
- : '{?([a-z]+)[^>]*>|?[a-zA-Z0-9]+;}';
-
+ $regex = '{?([a-z]+)[^>]*>|?[a-zA-Z0-9]+;|[\x80-\xFF][\x80-\xBF]*}';
$result = '';
while ($printedLength < $maxLength && preg_match($regex, $html, $match, PREG_OFFSET_CAPTURE, $position)) {
@@ -447,7 +443,7 @@ public static function limit($html, $maxLength = 100, $end = '...')
else {
$tagName = $match[1][0];
if ($tag[1] == '/') {
- $openingTag = array_pop($tags);
+ array_pop($tags);
$result .= $tag;
}
elseif ($tag[strlen($tag) - 2] == '/') {
diff --git a/src/Mail/MailManager.php b/src/Mail/MailManager.php
index 7b29ceef9..ade48d425 100644
--- a/src/Mail/MailManager.php
+++ b/src/Mail/MailManager.php
@@ -36,6 +36,7 @@ public function mailer($name = null)
*/
protected function resolve($name)
{
+ /** @var array|null */
$config = $this->getConfig($name);
if (is_null($config)) {
diff --git a/src/Mail/Mailable.php b/src/Mail/Mailable.php
index 69cb77c15..78722102e 100644
--- a/src/Mail/Mailable.php
+++ b/src/Mail/Mailable.php
@@ -1,6 +1,6 @@
fireEvent('mailer.beforeSend', [$view, $data, $callback], true) === false) ||
(Event::fire('mailer.beforeSend', [$view, $data, $callback], true) === false)
) {
- return;
+ return null;
}
if ($view instanceof MailableContract) {
@@ -142,7 +142,7 @@ public function send($view, array $data = [], $callback = null)
($this->fireEvent('mailer.prepareSend', [$view, $message, $data], true) === false) ||
(Event::fire('mailer.prepareSend', [$this, $view, $message, $data], true) === false)
) {
- return;
+ return null;
}
@@ -195,13 +195,13 @@ public function send($view, array $data = [], $callback = null)
* - Support for the Winter MailParser
*
* @param \Illuminate\Mail\Message $message
- * @param string $view
- * @param string $plain
- * @param string $raw
- * @param array $data
+ * @param string|null $view
+ * @param string|null $plain
+ * @param string|null $raw
+ * @param array|null $data
* @return void
*/
- protected function addContent($message, $view, $plain, $raw, $data)
+ protected function addContent($message, $view = null, $plain = null, $raw = null, $data = null)
{
/**
* @event mailer.beforeAddContent
@@ -286,11 +286,11 @@ protected function addContent($message, $view, $plain, $raw, $data)
* Add the raw content to the provided message.
*
* @param \Illuminate\Mail\Message $message
- * @param string $html
- * @param string $text
+ * @param string|null $html
+ * @param string|null $text
* @return void
*/
- protected function addContentRaw($message, $html, $text)
+ protected function addContentRaw($message, $html = null, $text = null)
{
if (isset($html)) {
$message->html($html);
@@ -304,7 +304,7 @@ protected function addContentRaw($message, $html, $text)
/**
* Queue a new e-mail message for sending.
*
- * @param string|array $view
+ * @param MailableContract|string|array $view
* @param array $data
* @param \Closure|string $callback
* @param string|null $queue
@@ -341,7 +341,7 @@ public function queueOn($queue, $view, $data = null, $callback = null)
* Queue a new e-mail message for sending after (n) seconds.
*
* @param int $delay
- * @param string|array $view
+ * @param MailableContract|string|array $view
* @param array $data
* @param \Closure|string $callback
* @param string|null $queue
@@ -401,7 +401,7 @@ protected function buildQueueMailable($view, $data, $callback, $queueName = null
/**
* Helper for raw() method, send a new message when only a raw text part.
* @param array $recipients
- * @param string $view
+ * @param array|string $view
* @param mixed $callback
* @param array $options
* @return \Illuminate\Mail\SentMessage|null
@@ -438,10 +438,8 @@ public function sendTo($recipients, $view, array $data = [], $callback = null, $
$queue = $options;
$bcc = false;
} else {
- extract(array_merge([
- 'queue' => false,
- 'bcc' => false
- ], $options));
+ $queue = (bool) ($options['queue'] ?? false);
+ $bcc = (bool) ($options['bcc'] ?? false);
}
$method = $queue === true ? 'queue' : 'send';
diff --git a/src/Network/Http.php b/src/Network/Http.php
index e5cb3a373..a58bd9d27 100644
--- a/src/Network/Http.php
+++ b/src/Network/Http.php
@@ -94,7 +94,7 @@ class Http
public $rawBody = '';
/**
- * @var array The last returned HTTP code.
+ * @var int The last returned HTTP code.
*/
public $code;
@@ -331,6 +331,18 @@ public function send()
stream_filter_append($stream, $this->streamFilter, STREAM_FILTER_WRITE);
}
+ if ($headerStream === false) {
+ throw new ApplicationException('Unable to create a temporary header stream');
+ }
+ if ($stream === false) {
+ throw new ApplicationException(
+ sprintf(
+ 'Unable to stream file contents from HTTP response to "%s". Please check your permissions.',
+ $this->streamFile
+ )
+ );
+ }
+
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_WRITEHEADER, $headerStream);
curl_setopt($curl, CURLOPT_FILE, $stream);
@@ -356,7 +368,7 @@ public function send()
*/
curl_close($curl);
- if ($this->streamFile) {
+ if ($this->streamFile && !empty($stream) && !empty($headerStream)) {
rewind($headerStream);
$this->headers = $this->headerToArray(stream_get_contents($headerStream));
fclose($headerStream);
@@ -556,9 +568,13 @@ public function toFile($path, $filter = null)
}
/**
- * Add a single option to the request.
- * @param string $option
- * @param string $value
+ * Add single or multiple CURL options to this request.
+ *
+ * You must either provide a constant or string that represents a CURL_* constant as the $option,
+ * and a $value to set a single option, or you may provide an array of CURL_* constants and values instead.
+ *
+ * @param array|string|int $option
+ * @param mixed $value
* @return self
*/
public function setOption($option, $value = null)
diff --git a/src/Parse/Assetic/Cache/FilesystemCache.php b/src/Parse/Assetic/Cache/FilesystemCache.php
index ea6a85a08..e0d5bd3ee 100644
--- a/src/Parse/Assetic/Cache/FilesystemCache.php
+++ b/src/Parse/Assetic/Cache/FilesystemCache.php
@@ -1,8 +1,8 @@
scriptPath = dirname($asset->getSourceRoot() . '/' . $asset->getSourcePath());
@@ -49,13 +52,13 @@ public function filterDump(AssetInterface $asset)
}
/**
- * Process JS imports inside a string of javascript
- * @param $content string JS code to process.
+ * Process JS imports inside a string of JavaScript
+ *
+ * @param string $content JS code to process.
* @return string Processed JS.
*/
protected function parse($content)
{
- $macros = [];
$imported = '';
// Look for: /* comments */
diff --git a/src/Parse/Assetic/Filter/ScssCompiler.php b/src/Parse/Assetic/Filter/ScssCompiler.php
index fdd563197..59b0d5ae5 100644
--- a/src/Parse/Assetic/Filter/ScssCompiler.php
+++ b/src/Parse/Assetic/Filter/ScssCompiler.php
@@ -1,11 +1,11 @@
[]
];
- public function __construct($options = [])
+ final public function __construct($options = [])
{
$this->setOptions($options);
}
@@ -34,7 +34,7 @@ public function setOptions($options = [])
* @param string $template
* @param array $vars
* @param array $options
- * @return self
+ * @return string
*/
public static function parse($template, $vars = [], $options = [])
{
@@ -43,15 +43,16 @@ public static function parse($template, $vars = [], $options = [])
}
/**
- * Parse a string against data
+ * Parse a string against data.
+ *
* @param string $string
* @param array $data
* @return string
*/
public function parseString($string, $data)
{
- if (!is_string($string) || !strlen(trim($string))) {
- return false;
+ if (!strlen(trim($string))) {
+ return '';
}
foreach ($data as $key => $value) {
diff --git a/src/Parse/EnvFile.php b/src/Parse/EnvFile.php
index 6276885dc..dd590d625 100644
--- a/src/Parse/EnvFile.php
+++ b/src/Parse/EnvFile.php
@@ -168,7 +168,12 @@ protected function escapeValue($value): string
*/
protected function parse(string $filePath): array
{
- if (!file_exists($filePath) || !($contents = file($filePath)) || !count($contents)) {
+ if (!is_file($filePath)) {
+ return [[], []];
+ }
+
+ $contents = file($filePath);
+ if (empty($contents)) {
return [[], []];
}
diff --git a/src/Parse/Ini.php b/src/Parse/Ini.php
index 60e15bba9..82e112de7 100644
--- a/src/Parse/Ini.php
+++ b/src/Parse/Ini.php
@@ -110,11 +110,11 @@ protected function parsePostProcess($array)
* Expands a single array property from traditional INI syntax.
* If no key is given to the method, the entire array will be replaced.
* @param array $array
- * @param string $key
+ * @param string|null $key
* @param mixed $value
* @return array
*/
- public function expandProperty(&$array, $key, $value)
+ public function expandProperty(array &$array, $key = null, $value = null)
{
if (is_null($key)) {
return $array = $value;
diff --git a/src/Parse/Markdown.php b/src/Parse/Markdown.php
index 7e31f5e14..5146257d1 100644
--- a/src/Parse/Markdown.php
+++ b/src/Parse/Markdown.php
@@ -1,6 +1,6 @@
parserClass;
+ }
+
+ /**
+ * Sets the Markdown parser.
+ *
+ * @param string|object $parserClass
+ * @return void
+ */
+ public function setParser(string|object $parserClass)
+ {
+ if (is_object($parserClass)) {
+ $this->parserClass = get_class($parserClass);
+ } else {
+ $this->parserClass = $parserClass;
+ }
+ }
/**
* Parse text using Markdown and Markdown-Extra
- * @param string $text Markdown text to parse
- * @return string Resulting HTML
+ * @param string $text Markdown text to parse
+ * @return string Resulting HTML
*/
public function parse($text)
{
@@ -52,13 +79,9 @@ public function parse($text)
*/
public function parseClean($text)
{
- $this->getParser()->setSafeMode(true);
-
- $result = $this->parse($text);
-
- $this->parser = null;
+ $parser = $this->getParser()->setSafeMode(true);
- return $result;
+ return $this->parseInternal($text, 'text', $parser);
}
/**
@@ -68,13 +91,9 @@ public function parseClean($text)
*/
public function parseSafe($text)
{
- $this->getParser()->setUnmarkedBlockTypes([]);
+ $parser = $this->getParser()->setUnmarkedBlockTypes([]);
- $result = $this->parse($text);
-
- $this->parser = null;
-
- return $result;
+ return $this->parseInternal($text, 'text', $parser);
}
/**
@@ -90,16 +109,19 @@ public function parseLine($text)
/**
* Internal method for parsing
*/
- protected function parseInternal($text, $method = 'text')
+ protected function parseInternal($text, $method = 'text', Parsedown $parser = null)
{
+ if (is_null($parser)) {
+ $parser = $this->getParser();
+ }
$data = new MarkdownData($text);
- $this->fireEvent('beforeParse', $data, false);
- Event::fire('markdown.beforeParse', $data, false);
+ $this->fireEvent('beforeParse', [$data], false);
+ Event::fire('markdown.beforeParse', [$data], false);
$result = $data->text;
- $result = $this->getParser()->$method($result);
+ $result = $parser->$method($result);
$data->text = $result;
@@ -110,13 +132,4 @@ protected function parseInternal($text, $method = 'text')
return $data->text;
}
-
- protected function getParser()
- {
- if ($this->parser === null) {
- $this->parser = new Parsedown;
- }
-
- return $this->parser;
- }
}
diff --git a/src/Parse/PHP/ArrayFile.php b/src/Parse/PHP/ArrayFile.php
index fe18024aa..c0b2e7a18 100644
--- a/src/Parse/PHP/ArrayFile.php
+++ b/src/Parse/PHP/ArrayFile.php
@@ -309,7 +309,7 @@ protected function getAstReturnIndex(array $ast): ?int
* If the path cannot be found completely, return the nearest parent and the remainder of the path
*
* @param array $path
- * @param $pointer
+ * @param mixed $pointer
* @param int $depth
* @throws SystemException if trying to set a position that is already occupied by a value
*/
diff --git a/src/Parse/PHP/ArrayPrinter.php b/src/Parse/PHP/ArrayPrinter.php
index 04bf38937..81f967daf 100644
--- a/src/Parse/PHP/ArrayPrinter.php
+++ b/src/Parse/PHP/ArrayPrinter.php
@@ -81,7 +81,7 @@ protected function pMaybeMultiline(array $nodes, bool $trailingComma = false)
*
* The result includes a leading newline and one level of indentation (same as pStmts).
*
- * @param Node[] $nodes Array of Nodes to be printed
+ * @param array $nodes Array of Nodes to be printed
* @param bool $trailingComma Whether to use a trailing comma
*
* @return string Comma separated pretty printed nodes in multiline style
diff --git a/src/Parse/Syntax/FieldParser.php b/src/Parse/Syntax/FieldParser.php
index 9b389c812..383628575 100644
--- a/src/Parse/Syntax/FieldParser.php
+++ b/src/Parse/Syntax/FieldParser.php
@@ -59,21 +59,23 @@ class FieldParser
];
/**
- * Constructor
+ * Constructor.
+ *
* @param string $template Template to parse.
+ * @param array $options
*/
- public function __construct($template = null, $options = [])
+ final public function __construct($template, $options = [])
{
- if ($template) {
- $this->tagPrefix = array_get($options, 'tagPrefix', '');
- $this->template = $template;
- $this->processTemplate($template);
- }
+ $this->tagPrefix = array_get($options, 'tagPrefix', '');
+ $this->template = $template;
+ $this->processTemplate($template);
}
/**
* Processes repeating tags first, then registered tags and assigns
* the results to local object properties.
+ *
+ * @param string $template
* @return void
*/
protected function processTemplate($template)
@@ -103,7 +105,7 @@ protected function processTemplate($template)
* Static helper for new instances of this class.
* @param string $template
* @param array $options
- * @return FieldParser
+ * @return static
*/
public static function parse($template, $options = [])
{
@@ -181,7 +183,7 @@ public function getDefaultParams($fields = null)
* Processes all repeating tags against a template, this will strip
* any repeaters from the template for further processing.
* @param string $template
- * @return void
+ * @return array
*/
protected function processRepeaterTags($template)
{
@@ -213,8 +215,8 @@ protected function processRepeaterTags($template)
/**
* Processes all registered tags against a template.
* @param string $template
- * @param bool $usingTags
- * @return void
+ * @param array $usingTags
+ * @return array
*/
protected function processTags($template, $usingTags = null)
{
@@ -375,7 +377,7 @@ protected function processParamsRegex($string)
* 2 - The default text inside the tag (optional), eg: Foobar
*
* @param string $string
- * @param string $tags
+ * @param array $tags
* @return array
*/
protected function processTagsRegex($string, $tags)
diff --git a/src/Parse/Syntax/Parser.php b/src/Parse/Syntax/Parser.php
index ab842f17b..61de67454 100644
--- a/src/Parse/Syntax/Parser.php
+++ b/src/Parse/Syntax/Parser.php
@@ -10,6 +10,11 @@ class Parser
const CHAR_OPEN = '{';
const CHAR_CLOSE = '}';
+ /**
+ * @var string The template content to parse.
+ */
+ protected $template = '';
+
/**
* @var \Winter\Storm\Parse\Syntax\FieldParser Field parser instance.
*/
@@ -28,33 +33,33 @@ class Parser
/**
* Constructor.
+ *
* Available options:
* - varPrefix: Prefix to add to every top level parameter.
* - tagPrefix: Prefix to add to all tags, in addition to tags without a prefix.
- * @param array $options
+ *
* @param string $template Template to parse.
+ * @param array $options
*/
- public function __construct($template = null, $options = [])
+ final public function __construct($template, $options = [])
{
- if ($template) {
- $this->template = $template;
- $this->varPrefix = array_get($options, 'varPrefix', '');
- $this->fieldParser = new FieldParser($template, $options);
+ $this->template = $template;
+ $this->varPrefix = array_get($options, 'varPrefix', '');
+ $this->fieldParser = new FieldParser($template, $options);
- $textFilters = [
- 'md' => ['Markdown', 'parse'],
- 'media' => ['System\Classes\MediaLibrary', 'url']
- ];
+ $textFilters = [
+ 'md' => ['Winter\Storm\Parse\Markdown', 'parse'],
+ 'media' => ['System\Classes\MediaLibrary', 'url']
+ ];
- $this->textParser = new TextParser(['filters' => $textFilters]);
- }
+ $this->textParser = new TextParser(['filters' => $textFilters]);
}
/**
* Static helper for new instances of this class.
* @param string $template
* @param array $options
- * @return self
+ * @return static
*/
public static function parse($template, $options = [])
{
diff --git a/src/Parse/Syntax/SyntaxModelTrait.php b/src/Parse/Syntax/SyntaxModelTrait.php
index c49a25038..75b02f56b 100644
--- a/src/Parse/Syntax/SyntaxModelTrait.php
+++ b/src/Parse/Syntax/SyntaxModelTrait.php
@@ -1,6 +1,6 @@
20,
- 'exceptionOnInvalidType' => false,
- 'objectSupport' => true,
- ], $options));
+ $inline = (int) ($options['inline'] ?? 20);
+ $exceptionOnInvalidType = (bool) ($options['exceptionOnInvalidType'] ?? false);
+ $objectSupport = (bool) ($options['objectSupport'] ?? true);
$flags = null;
- if ($exceptionOnInvalidType) {
+ if ($exceptionOnInvalidType === true) {
$flags |= YamlComponent::DUMP_EXCEPTION_ON_INVALID_TYPE;
}
- if ($objectSupport) {
+ if ($objectSupport === true) {
$flags |= YamlComponent::DUMP_OBJECT;
}
diff --git a/src/Router/CoreRouter.php b/src/Router/CoreRouter.php
index 59a402cf7..bbc905a7b 100644
--- a/src/Router/CoreRouter.php
+++ b/src/Router/CoreRouter.php
@@ -9,7 +9,7 @@ class CoreRouter extends RouterBase
* Dispatch the request to the application.
*
* @param \Illuminate\Http\Request $request
- * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
+ * @return \Symfony\Component\HttpFoundation\Response
*/
public function dispatch(Request $request)
{
diff --git a/src/Router/Helper.php b/src/Router/Helper.php
index 2b636bce7..a610657cd 100644
--- a/src/Router/Helper.php
+++ b/src/Router/Helper.php
@@ -72,7 +72,7 @@ public static function rebuildUrl(array $urlArray)
/**
* Replaces :column_name with it's object value. Example: /some/link/:id/:name -> /some/link/1/Joe
*
- * @param stdObject $object Object containing the data
+ * @param object|array $object Object containing the data
* @param array $columns Expected key names to parse
* @param string $string URL template
* @return string Built string
@@ -102,7 +102,7 @@ public static function parseValues($object, array $columns, $string)
/**
* Replaces :column_name with object value without requiring a list of names. Example: /some/link/:id/:name -> /some/link/1/Joe
*
- * @param stdObject $object Object containing the data
+ * @param object|array $object Object containing the data
* @param string $string URL template
* @return string Built string
*/
@@ -144,11 +144,7 @@ public static function segmentIsOptional($segment)
return true;
}
- if ($optMarkerPos !== false && $regexMarkerPos !== false) {
- return $optMarkerPos < $regexMarkerPos;
- }
-
- return false;
+ return $optMarkerPos < $regexMarkerPos;
}
/**
@@ -194,7 +190,7 @@ public static function getParameterName($segment)
/**
* Extracts the regular expression from a URL pattern segment definition.
* @param string $segment The segment definition.
- * @return string Returns the regular expression string or false if the expression is not defined.
+ * @return string|false Returns the regular expression string or false if the expression is not defined.
*/
public static function getSegmentRegExp($segment)
{
@@ -213,7 +209,7 @@ public static function getSegmentRegExp($segment)
/**
* Extracts the default parameter value from a URL pattern segment definition.
* @param string $segment The segment definition.
- * @return string Returns the default value if it is provided. Returns false otherwise.
+ * @return string|false Returns the default value if it is provided. Returns false otherwise.
*/
public static function getSegmentDefaultValue($segment)
{
diff --git a/src/Router/Router.php b/src/Router/Router.php
index b3fd5fd06..de4b9e313 100644
--- a/src/Router/Router.php
+++ b/src/Router/Router.php
@@ -20,7 +20,7 @@ class Router
protected $routeMap = [];
/**
- * @var \Winter\Storm\Router\Rule A referred to the matched router rule
+ * @var \Winter\Storm\Router\Rule|null A referred to the matched router rule
*/
protected $matchedRouteRule;
@@ -41,7 +41,7 @@ public function route($name, $route)
* Match given URL string
*
* @param string $url Request URL to match for
- * @return array $parameters A reference to a PHP array variable to return the parameter list fetched from URL.
+ * @return bool
*/
public function match($url)
{
@@ -73,9 +73,9 @@ public function match($url)
}
// Success
- if ($this->matchedRouteRule) {
+ if (!is_null($this->matchedRouteRule)) {
// If this route has a match callback, run it
- $matchCallback = $routeRule->afterMatch();
+ $matchCallback = $this->matchedRouteRule->afterMatch();
if ($matchCallback !== null) {
$parameters = call_user_func($matchCallback, $parameters, $url);
}
@@ -83,7 +83,7 @@ public function match($url)
$this->parameters = $parameters;
- return $this->matchedRouteRule ? true : false;
+ return !is_null($this->matchedRouteRule);
}
/**
@@ -91,7 +91,7 @@ public function match($url)
*
* @param string $name Name of the route previously defined.
* @param array $parameters Parameter name => value items to fill in for given route.
- * @return string Full matched URL as string with given values put in place of named parameters
+ * @return string|null Full matched URL as string with given values put in place of named parameters. Returns `null` if no route map is specified.
*/
public function url($name, $parameters = [])
{
@@ -224,7 +224,8 @@ public function getParameters()
/**
* Returns the matched route rule name.
- * @return \Winter\Storm\Router\Rule The matched rule object.
+ *
+ * @return \Winter\Storm\Router\Rule|false The matched rule object. If no rule was matched, returns `false`.
*/
public function matchedRoute()
{
diff --git a/src/Router/Rule.php b/src/Router/Rule.php
index 0c46681ad..395198499 100644
--- a/src/Router/Rule.php
+++ b/src/Router/Rule.php
@@ -20,12 +20,12 @@ class Rule
protected $rulePattern;
/**
- * @var function Custom condition used when matching this rule.
+ * @var callable Custom condition used when matching this rule.
*/
protected $conditionCallback;
/**
- * @var function Called when this rule is matched.
+ * @var callable Called when this rule is matched.
*/
protected $afterMatchCallback;
@@ -197,7 +197,7 @@ public function resolveUrl($url, &$parameters)
/*
* Determine if wildcard and add stored parameters as a suffix
*/
- if (Helper::segmentIsWildcard($patternSegment) && count($wildSegments)) {
+ if (Helper::segmentIsWildcard($patternSegment) && isset($wildSegments) && count($wildSegments)) {
$parameters[$paramName] .= Helper::rebuildUrl($wildSegments);
}
}
@@ -248,8 +248,10 @@ protected function captureWildcardSegments(&$urlSegments)
/**
* Unique route name
*
+ * This is a getter and setter method.
+ *
* @param string $name Unique name for the router object
- * @return object Self
+ * @return object|string
*/
public function name($name = null)
{
@@ -265,8 +267,10 @@ public function name($name = null)
/**
* Route match pattern
*
+ * This is a getter and setter method.
+ *
* @param string $pattern Pattern used to match this rule
- * @return object Self
+ * @return object|string
*/
public function pattern($pattern = null)
{
@@ -282,9 +286,9 @@ public function pattern($pattern = null)
/**
* Condition callback
*
- * @param callback $callback Callback function to be used when providing custom route match conditions
+ * @param callable $callback Callback function to be used when providing custom route match conditions
* @throws InvalidArgumentException When supplied argument is not a valid callback
- * @return callback
+ * @return callable
*/
public function condition($callback = null)
{
@@ -307,9 +311,9 @@ public function condition($callback = null)
/**
* After match callback
*
- * @param callback $callback Callback function to be used to modify params after a successful match
+ * @param callable $callback Callback function to be used to modify params after a successful match
* @throws InvalidArgumentException When supplied argument is not a valid callback
- * @return callback
+ * @return callable
*/
public function afterMatch($callback = null)
{
diff --git a/src/Router/UrlGenerator.php b/src/Router/UrlGenerator.php
index 11a56504f..74791ef8a 100644
--- a/src/Router/UrlGenerator.php
+++ b/src/Router/UrlGenerator.php
@@ -80,14 +80,15 @@ public static function buildUrl($url, $replace = [], $flags = HTTP_URL_REPLACE,
foreach ($url as $key => &$value) {
// Remove invalid segments
if (
- (!in_array($key, $urlSegments) || !isset($value)) ||
- (is_array($value) && empty($value))
+ !in_array($key, $urlSegments)
+ || !isset($value)
+ || (is_array($value) && !count($value))
) {
unset($url[$key]);
continue;
}
- // Trim strings and remove empty strings
+ // Trim strings
if (!is_array($value)) {
$value = trim((string) $value);
}
@@ -162,10 +163,13 @@ public static function buildUrl($url, $replace = [], $flags = HTTP_URL_REPLACE,
$rQuery = str_replace(array('[', '%5B'), '{{{', $rQuery);
$rQuery = str_replace(array(']', '%5D'), '}}}', $rQuery);
- parse_str($uQuery, $uQuery);
- parse_str($rQuery, $rQuery);
+ $parsedUQuery = [];
+ $parsedRQuery = [];
- $query = static::buildStr(array_merge($uQuery, $rQuery));
+ parse_str($uQuery, $parsedUQuery);
+ parse_str($rQuery, $parsedRQuery);
+
+ $query = static::buildStr(array_merge($parsedUQuery, $parsedRQuery));
$query = str_replace(array('{{{', '%7B%7B%7B'), '%5B', $query);
$query = str_replace(array('}}}', '%7D%7D%7D'), '%5D', $query);
@@ -270,9 +274,10 @@ public static function buildUrl($url, $replace = [], $flags = HTTP_URL_REPLACE,
// Populate the query section
if (isset($url['query']) && $url['query'] !== '') {
+ $queryParams = [];
+
if (is_string($url['query'])) {
- $queryParams = [];
- $pairs = explode(ini_get('arg_separator.output') ?? '&', $url['query']);
+ $pairs = explode(ini_get('arg_separator.output') ?: '&', $url['query']);
foreach ($pairs as $pair) {
$key = Str::before($pair, '=');
$value = Str::after($pair, '=');
@@ -320,7 +325,7 @@ public static function buildUrl($url, $replace = [], $flags = HTTP_URL_REPLACE,
public static function buildStr(array $query, string $prefix = '', $argSeparator = null): string
{
if (is_null($argSeparator)) {
- $argSeparator = ini_get('arg_separator.output') ?? '&';
+ $argSeparator = ini_get('arg_separator.output') ?: '&';
}
$result = [];
diff --git a/src/Scaffold/GeneratorCommand.php b/src/Scaffold/GeneratorCommand.php
index b7e1c190f..37f2b3136 100644
--- a/src/Scaffold/GeneratorCommand.php
+++ b/src/Scaffold/GeneratorCommand.php
@@ -1,12 +1,18 @@
files->isDirectory(dirname($path))) {
+ if (!$this->files->isDirectory(dirname($path))) {
$this->files->makeDirectory(dirname($path), 0777, true, true);
}
}
diff --git a/src/Support/ClassLoader.php b/src/Support/ClassLoader.php
index da80d04f0..f9ffd932d 100644
--- a/src/Support/ClassLoader.php
+++ b/src/Support/ClassLoader.php
@@ -1,8 +1,8 @@
registered) {
+ if (!is_null($this->registered)) {
return;
}
$this->ensureManifestIsLoaded();
- $this->registered = spl_autoload_register([$this, 'load']);
+ $this->registered = function ($class) {
+ $this->load($class);
+ };
+ spl_autoload_register($this->registered);
}
/**
@@ -218,12 +221,12 @@ public function register()
*/
public function unregister()
{
- if (!$this->registered) {
+ if (is_null($this->registered)) {
return;
}
- spl_autoload_unregister([$this, 'load']);
- $this->registered = false;
+ spl_autoload_unregister($this->registered);
+ $this->registered = null;
}
/**
@@ -309,7 +312,7 @@ public function addAliases(array $aliases)
* Aliases are first-come, first-served. If a real class already exists with the same name as an alias, the real
* class is used over the alias.
*
- * @param array $aliases
+ * @param array $namespaceAliases
* @return void
*/
public function addNamespaceAliases(array $namespaceAliases)
@@ -409,7 +412,7 @@ protected static function normalizeClass($class)
* Get the possible paths for a class.
*
* @param string $class
- * @return string
+ * @return array
*/
protected static function getPathsForClass($class)
{
diff --git a/src/Support/Facade.php b/src/Support/Facade.php
index 84b05071a..ec68bd149 100644
--- a/src/Support/Facade.php
+++ b/src/Support/Facade.php
@@ -10,31 +10,4 @@
*/
class Facade extends FacadeParent
{
-
- /**
- * @inheritDoc
- */
- protected static function resolveFacadeInstance($name)
- {
- if (
- !is_object($name) &&
- !is_null(static::$app) &&
- !static::$app->bound($name) &&
- ($instance = static::getFacadeInstance()) !== null
- ) {
- static::$app->instance($name, $instance);
- }
-
- return parent::resolveFacadeInstance($name);
- }
-
- /**
- * If the accessor is not found via getFacadeAccessor, use this instance as a fallback.
- *
- * @return mixed
- */
- protected static function getFacadeInstance()
- {
- return null;
- }
}
diff --git a/src/Support/Facades/DB.php b/src/Support/Facades/DB.php
new file mode 100644
index 000000000..f5738678c
--- /dev/null
+++ b/src/Support/Facades/DB.php
@@ -0,0 +1,50 @@
+input($key, $default);
}
+ /**
+ * Gets all input data items.
+ *
+ * This method is used for all request verbs (GET, POST, PUT, and DELETE)
+ *
+ * @return array|null
+ */
+ public static function all()
+ {
+ return static::$app['request']->input();
+ }
+
/**
* Get the registered name of the component.
*
diff --git a/src/Support/Facades/Schema.php b/src/Support/Facades/Schema.php
index 6a4de871a..b3c38ce20 100644
--- a/src/Support/Facades/Schema.php
+++ b/src/Support/Facades/Schema.php
@@ -45,7 +45,7 @@ public static function connection($name)
/**
* Get a schema builder instance for the default connection.
*
- * @return \Illuminate\Database\Schema\Builder
+ * @return string
*/
protected static function getFacadeAccessor()
{
diff --git a/src/Support/ModuleServiceProvider.php b/src/Support/ModuleServiceProvider.php
index f7c36a0b8..1a7620e0d 100644
--- a/src/Support/ModuleServiceProvider.php
+++ b/src/Support/ModuleServiceProvider.php
@@ -51,8 +51,8 @@ public function getModule($args)
/**
* Registers a new console (artisan) command
- * @param $key The command name
- * @param $class The command class
+ * @param string $key The command name
+ * @param string $class The command class
* @return void
*/
public function registerConsoleCommand($key, $class)
diff --git a/src/Support/Serialization.php b/src/Support/Serialization.php
index 45422571f..54587bc71 100644
--- a/src/Support/Serialization.php
+++ b/src/Support/Serialization.php
@@ -12,11 +12,11 @@ class Serialization
* Wraps a closure in a SerializableClosure, returns the provided object if it's not a closure.
*
* @param Closure|mixed $callable provided callable to be wrapped if it's a closure
- * @return mixed|SerializableClosure
+ * @return SerializableClosure|mixed
*/
public static function wrapClosure($callable)
{
- if ($callable instanceof Closure && !($callable instanceof SerializableClosure)) {
+ if ($callable instanceof Closure) {
$callable = new SerializableClosure($callable);
}
return $callable;
diff --git a/src/Support/Singleton.php b/src/Support/Singleton.php
index f4452c71d..5dc5b9ace 100644
--- a/src/Support/Singleton.php
+++ b/src/Support/Singleton.php
@@ -1,6 +1,6 @@
buildMailable($view, $data, $callback, true);
}
- return parent::queue($view, $data = null, $callback = null, $queue = null);
+ return parent::queue($view, $queue = null);
}
/**
diff --git a/src/Support/Traits/Emitter.php b/src/Support/Traits/Emitter.php
index 49bbc5044..d52e14b93 100644
--- a/src/Support/Traits/Emitter.php
+++ b/src/Support/Traits/Emitter.php
@@ -60,7 +60,7 @@ public function bindEvent($event, $callback = null, $priority = 0)
/**
* Create a new event binding that fires once only
* @param string|Closure|QueuedClosure $event
- * @param Closure|null $callback When a Closure or QueuedClosure is provided as the first parameter
+ * @param QueuedClosure|Closure|null $callback When a Closure or QueuedClosure is provided as the first parameter
* this parameter can be omitted
* @return self
*/
@@ -81,7 +81,7 @@ public function bindEventOnce($event, $callback = null)
* Sort the listeners for a given event by priority.
*
* @param string $eventName
- * @return array
+ * @return void
*/
protected function emitterEventSortEvents($eventName)
{
@@ -96,7 +96,7 @@ protected function emitterEventSortEvents($eventName)
/**
* Destroys an event binding.
- * @param string $event Event to destroy
+ * @param string|array|object $event Event to destroy
* @return self
*/
public function unbindEvent($event = null)
@@ -108,7 +108,7 @@ public function unbindEvent($event = null)
foreach ($event as $_event) {
$this->unbindEvent($_event);
}
- return;
+ return $this;
}
if (is_object($event)) {
@@ -140,7 +140,8 @@ public function unbindEvent($event = null)
* @param string $event Event name
* @param array $params Event parameters
* @param boolean $halt Halt after first non-null result
- * @return array Collection of event results / Or single result (if halted)
+ * @return array|mixed|null If halted, the first non-null result. If not halted, an array of event results. Returns
+ * null if no listeners returned a result.
*/
public function fireEvent($event, $params = [], $halt = false)
{
diff --git a/src/Support/aliases.php b/src/Support/aliases.php
index c29f443da..a3debf09e 100644
--- a/src/Support/aliases.php
+++ b/src/Support/aliases.php
@@ -99,7 +99,6 @@ class_alias(\Winter\Storm\Config\Repository::class, \October\Rain\Config\Reposit
/**
* Alias October\Rain\Cookie
*/
-class_alias(\Winter\Storm\Cookie\CookieValuePrefix::class, \October\Rain\Cookie\CookieValuePrefix::class);
class_alias(\Winter\Storm\Cookie\Middleware\EncryptCookies::class, \October\Rain\Cookie\Middleware\EncryptCookies::class);
/**
@@ -141,19 +140,19 @@ class_alias(\Winter\Storm\Database\Query\Grammars\SqlServerGrammar::class, \Octo
class_alias(\Winter\Storm\Database\QueryBuilder::class, \October\Rain\Database\QueryBuilder::class);
class_alias(\Winter\Storm\Database\Relations\AttachMany::class, \October\Rain\Database\Relations\AttachMany::class);
class_alias(\Winter\Storm\Database\Relations\AttachOne::class, \October\Rain\Database\Relations\AttachOne::class);
-class_alias(\Winter\Storm\Database\Relations\AttachOneOrMany::class, \October\Rain\Database\Relations\AttachOneOrMany::class);
+class_alias(\Winter\Storm\Database\Relations\Concerns\AttachOneOrMany::class, \October\Rain\Database\Relations\AttachOneOrMany::class);
class_alias(\Winter\Storm\Database\Relations\BelongsTo::class, \October\Rain\Database\Relations\BelongsTo::class);
class_alias(\Winter\Storm\Database\Relations\BelongsToMany::class, \October\Rain\Database\Relations\BelongsToMany::class);
-class_alias(\Winter\Storm\Database\Relations\DeferOneOrMany::class, \October\Rain\Database\Relations\DeferOneOrMany::class);
-class_alias(\Winter\Storm\Database\Relations\DefinedConstraints::class, \October\Rain\Database\Relations\DefinedConstraints::class);
+class_alias(\Winter\Storm\Database\Relations\Concerns\DeferOneOrMany::class, \October\Rain\Database\Relations\DeferOneOrMany::class);
+class_alias(\Winter\Storm\Database\Relations\Concerns\DefinedConstraints::class, \October\Rain\Database\Relations\DefinedConstraints::class);
class_alias(\Winter\Storm\Database\Relations\HasMany::class, \October\Rain\Database\Relations\HasMany::class);
class_alias(\Winter\Storm\Database\Relations\HasManyThrough::class, \October\Rain\Database\Relations\HasManyThrough::class);
class_alias(\Winter\Storm\Database\Relations\HasOne::class, \October\Rain\Database\Relations\HasOne::class);
-class_alias(\Winter\Storm\Database\Relations\HasOneOrMany::class, \October\Rain\Database\Relations\HasOneOrMany::class);
+class_alias(\Winter\Storm\Database\Relations\Concerns\HasOneOrMany::class, \October\Rain\Database\Relations\HasOneOrMany::class);
class_alias(\Winter\Storm\Database\Relations\HasOneThrough::class, \October\Rain\Database\Relations\HasOneThrough::class);
class_alias(\Winter\Storm\Database\Relations\MorphMany::class, \October\Rain\Database\Relations\MorphMany::class);
class_alias(\Winter\Storm\Database\Relations\MorphOne::class, \October\Rain\Database\Relations\MorphOne::class);
-class_alias(\Winter\Storm\Database\Relations\MorphOneOrMany::class, \October\Rain\Database\Relations\MorphOneOrMany::class);
+class_alias(\Winter\Storm\Database\Relations\Concerns\MorphOneOrMany::class, \October\Rain\Database\Relations\MorphOneOrMany::class);
class_alias(\Winter\Storm\Database\Relations\MorphTo::class, \October\Rain\Database\Relations\MorphTo::class);
class_alias(\Winter\Storm\Database\Relations\MorphToMany::class, \October\Rain\Database\Relations\MorphToMany::class);
class_alias(\Winter\Storm\Database\Relations\Relation::class, \October\Rain\Database\Relations\Relation::class);
diff --git a/src/Support/helpers-array.php b/src/Support/helpers-array.php
index 78efc93d0..e07c42f03 100644
--- a/src/Support/helpers-array.php
+++ b/src/Support/helpers-array.php
@@ -152,7 +152,7 @@ function array_last($array, callable $callback = null, $default = null)
* Flatten a multi-dimensional array into a single level.
*
* @param array $array
- * @param int $depth
+ * @param float|int $depth
* @return array
*/
function array_flatten($array, $depth = INF)
diff --git a/src/Support/helpers.php b/src/Support/helpers.php
index e9b5d1392..c8dd611b6 100644
--- a/src/Support/helpers.php
+++ b/src/Support/helpers.php
@@ -115,7 +115,7 @@ function post($name = null, $default = null)
function input($name = null, $default = null)
{
if ($name === null) {
- return Input::all();
+ return \Winter\Storm\Support\Facades\Input::all();
}
/*
@@ -125,21 +125,18 @@ function input($name = null, $default = null)
$name = implode('.', Winter\Storm\Html\Helper::nameToArray($name));
}
- return Input::get($name, $default);
+ return \Winter\Storm\Support\Facades\Input::get($name, $default);
}
}
if (!function_exists('trace_log')) {
/**
* Writes a trace message to a log file.
- * @param mixed $message Specifies a message to log. The message can be an object, array or string.
- * @param string $level Specifies a level to use. If this parameter is omitted, the default listener will be used (info).
+ * @param Exception|array|object|string... $messages
* @return void
*/
- function trace_log()
+ function trace_log(...$messages)
{
- $messages = func_get_args();
-
foreach ($messages as $message) {
$level = 'info';
@@ -158,11 +155,12 @@ function trace_log()
if (!function_exists('traceLog')) {
/**
* Alias for trace_log()
+ * @param Exception|array|object|string... $messages
* @return void
*/
- function traceLog()
+ function traceLog(...$messages)
{
- call_user_func_array('trace_log', func_get_args());
+ call_user_func_array('trace_log', $messages);
}
}
diff --git a/src/Translation/TranslationServiceProvider.php b/src/Translation/TranslationServiceProvider.php
index 05fc9bd4e..0cbfc4abf 100644
--- a/src/Translation/TranslationServiceProvider.php
+++ b/src/Translation/TranslationServiceProvider.php
@@ -35,7 +35,7 @@ public function register()
protected function registerLoader()
{
$this->app->singleton('translation.loader', function ($app) {
- return new FileLoader($app['files'], $app['path'].'/lang');
+ return new FileLoader($app['files'], $app['path.lang']);
});
}
diff --git a/src/Translation/Translator.php b/src/Translation/Translator.php
index 02a2c9622..aef6454f1 100644
--- a/src/Translation/Translator.php
+++ b/src/Translation/Translator.php
@@ -16,7 +16,7 @@ class Translator extends TranslatorBase
/**
* The event dispatcher instance.
*
- * @var \Illuminate\Contracts\Events\Dispatcher|\Winter\Storm\Events\Dispatcher
+ * @var \Illuminate\Contracts\Events\Dispatcher|\Winter\Storm\Events\Dispatcher|null
*/
protected $events;
@@ -106,7 +106,7 @@ public function transChoice($key, $number, array $replace = [], $locale = null)
* @param string $key
* @param array $replace
* @param string $locale
- * @return string
+ * @return string|null
*/
protected function getValidationSpecific($key, $replace, $locale)
{
@@ -132,7 +132,7 @@ protected function localeForChoice($locale)
{
$locale = parent::localeForChoice($locale);
- if (!is_null($locale) && str_contains($locale, '-')) {
+ if (str_contains($locale, '-')) {
$localeParts = explode('-', $locale, 2);
$locale = $localeParts[0] . '_' . strtoupper($localeParts[1]);
}
@@ -167,9 +167,13 @@ public function parseKey($key)
*/
protected function localeArray($locale)
{
- $locales = array_values(array_filter([$locale ?: $this->locale, $this->fallback, static::CORE_LOCALE]));
+ $locales = array_values(parent::localeArray($locale));
+
+ if (!in_array(static::CORE_LOCALE, $locales)) {
+ $locales[] = static::CORE_LOCALE;
+ }
- return call_user_func($this->determineLocalesUsing ?: fn () => $locales, $locales);
+ return $locales;
}
/**
diff --git a/src/Validation/Concerns/ValidatesEmail.php b/src/Validation/Concerns/ValidatesEmail.php
index 8487c9a6e..1f4c5ffc8 100644
--- a/src/Validation/Concerns/ValidatesEmail.php
+++ b/src/Validation/Concerns/ValidatesEmail.php
@@ -5,7 +5,7 @@
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
use Egulias\EmailValidator\Validation\RFCValidation;
-use Egulias\EmailValidator\Validation\SpoofCheckValidation;
+use Egulias\EmailValidator\Validation\Extra\SpoofCheckValidation;
use Illuminate\Validation\Concerns\FilterEmailValidation;
trait ValidatesEmail
diff --git a/src/Validation/Validator.php b/src/Validation/Validator.php
index b26b0eee9..c6d175a92 100644
--- a/src/Validation/Validator.php
+++ b/src/Validation/Validator.php
@@ -13,7 +13,7 @@
*/
class Validator extends BaseValidator implements ValidatorContract
{
- use \Winter\Storm\Validation\Concerns\ValidatesEmail;
+ use Concerns\ValidatesEmail;
use Concerns\FormatsMessages;
/**
diff --git a/tests/Database/Fixtures/CustomMorphPivot.php b/tests/Database/Fixtures/CustomMorphPivot.php
new file mode 100644
index 000000000..a3ff91ca9
--- /dev/null
+++ b/tests/Database/Fixtures/CustomMorphPivot.php
@@ -0,0 +1,9 @@
+createTables();
+ }
+
+ public function testCreateMorphyToManyRelationAndCheckForMorphPivot()
+ {
+ // Create a couple of tags
+ $cool = Tag::create([
+ 'name' => 'Cool',
+ ]);
+ $awesome = Tag::create([
+ 'name' => 'Awesome',
+ ]);
+
+ // Create a post
+ $post = Post::create([
+ 'title' => 'Check this out',
+ 'body' => 'It is pretty cool and pretty awesome too',
+ ]);
+
+ // Attach tags
+ $post->tags()->attach($cool);
+ $post->tags()->attach($awesome);
+
+ // Get first tag and get a pivot instance
+ $pivot = $post->tags()->first()->pivot;
+
+ $this->assertInstanceOf(MorphPivot::class, $pivot);
+ $this->assertEquals('0', $pivot->hidden);
+ }
+
+ public function testCreateMorphyToManyRelationAndCheckForCustomMorphPivot()
+ {
+ // Create a couple of tags
+ $cool = Tag::create([
+ 'name' => 'Cool',
+ ]);
+ $awesome = Tag::create([
+ 'name' => 'Awesome',
+ ]);
+
+ // Create a post
+ $post = CustomPost::create([
+ 'title' => 'Check this out',
+ 'body' => 'It is pretty cool and pretty awesome too',
+ ]);
+
+ // Attach tags
+ $post->tags()->attach($cool);
+ $post->tags()->attach($awesome);
+
+ // Get first tag and get a pivot instance
+ $pivot = $post->tags()->first()->pivot;
+
+ $this->assertInstanceOf(CustomMorphPivot::class, $pivot);
+ }
+
+ protected function createTables()
+ {
+ $this->getBuilder()->create('posts', function ($table) {
+ $table->increments('id');
+ $table->string('title');
+ $table->text('body')->nullable();
+ $table->timestamps();
+ });
+
+ $this->getBuilder()->create('tags', function ($table) {
+ $table->increments('id');
+ $table->string('name');
+ $table->timestamps();
+ });
+
+ $this->getBuilder()->create('taggings', function ($table) {
+ $table->increments('id');
+ $table->integer('tag_id')->unsigned();
+ $table->morphs('taggable');
+ $table->boolean('hidden')->default(0);
+ $table->timestamps();
+ });
+ }
+}
+
+class Post extends Model
+{
+ public $table = 'posts';
+
+ public $fillable = [
+ 'title',
+ 'body',
+ ];
+
+ public $morphToMany = [
+ 'tags' => [
+ Tag::class,
+ 'table' => 'taggings',
+ 'name' => 'taggable',
+ 'pivot' => ['hidden'],
+ ],
+ ];
+}
+
+class CustomPost extends Post
+{
+ public $morphToMany = [
+ 'tags' => [
+ Tag::class,
+ 'table' => 'taggings',
+ 'name' => 'taggable',
+ 'pivot' => ['hidden'],
+ 'pivotModel' => CustomMorphPivot::class,
+ ],
+ ];
+}
+
+class Tagging extends Model
+{
+ public $table = 'taggings';
+
+ protected $casts = [
+ 'hidden' => 'boolean',
+ ];
+}
+
+class Tag extends Model
+{
+ public $table = 'tags';
+
+ public $fillable = [
+ 'name',
+ ];
+}
diff --git a/tests/Database/QueryBuilderTest.php b/tests/Database/QueryBuilderTest.php
index 7c8aa7118..0d2387ce9 100644
--- a/tests/Database/QueryBuilderTest.php
+++ b/tests/Database/QueryBuilderTest.php
@@ -124,11 +124,13 @@ protected function getConnection($connection = null, $table = null)
'rollBack',
'transactionLevel',
'pretend',
- 'getDatabaseName'
+ 'getDatabaseName',
+ 'getConfig',
])
->getMock();
$connection->method('getDatabaseName')->willReturn('database');
+ $connection->method('getConfig')->with('use_upsert_alias')->willReturn(false);
return $connection;
}
diff --git a/tests/Database/SortableTest.php b/tests/Database/SortableTest.php
index fc6f0d75d..8b16990fc 100644
--- a/tests/Database/SortableTest.php
+++ b/tests/Database/SortableTest.php
@@ -10,6 +10,14 @@ public function testOrderByIsAutomaticallyAdded()
$this->assertEquals('select * from "test" order by "sort_order" asc', $query);
}
+ public function testCustomSortOrderByIsAutomaticallyAdded()
+ {
+ $model = new TestCustomSortableModel();
+ $query = $model->newQuery()->toSql();
+
+ $this->assertEquals('select * from "test" order by "rank" asc', $query);
+ }
+
public function testOrderByCanBeOverridden()
{
$model = new TestSortableModel();
@@ -18,6 +26,13 @@ public function testOrderByCanBeOverridden()
$this->assertEquals('select * from "test" order by "name" asc, "email" desc', $query1);
$this->assertEquals('select * from "test" order by "sort_order" asc, "name" asc', $query2);
+
+ $model = new TestCustomSortableModel();
+ $query1 = $model->newQuery()->orderBy('name')->orderBy('email', 'desc')->toSql();
+ $query2 = $model->newQuery()->orderBy('sort_order')->orderBy('name')->toSql();
+
+ $this->assertEquals('select * from "test" order by "name" asc, "email" desc', $query1);
+ $this->assertEquals('select * from "test" order by "sort_order" asc, "name" asc', $query2);
}
}
@@ -27,3 +42,12 @@ class TestSortableModel extends \Winter\Storm\Database\Model
protected $table = 'test';
}
+
+class TestCustomSortableModel extends \Winter\Storm\Database\Model
+{
+ use \Winter\Storm\Database\Traits\Sortable;
+
+ const SORT_ORDER = 'rank';
+
+ protected $table = 'test';
+}
diff --git a/tests/Html/BlockBuilderTest.php b/tests/Html/BlockBuilderTest.php
index 51e82a096..eede6e329 100644
--- a/tests/Html/BlockBuilderTest.php
+++ b/tests/Html/BlockBuilderTest.php
@@ -121,7 +121,7 @@ public function testPlaceholderBlock()
. '',
$this->Block->placeholder('test')
);
- $this->assertNull($this->Block->get('test'));
+ $this->assertEquals('', $this->Block->get('test'));
}
public function testResetBlocks()
@@ -137,7 +137,7 @@ public function testResetBlocks()
$this->Block->reset();
- $this->assertNull($this->Block->get('test'));
+ $this->assertEquals('', $this->Block->get('test'));
}
public function testNestedBlocks()
diff --git a/tests/Parse/SyntaxFieldParserTest.php b/tests/Parse/SyntaxFieldParserTest.php
index 4f5915d29..a61808f96 100644
--- a/tests/Parse/SyntaxFieldParserTest.php
+++ b/tests/Parse/SyntaxFieldParserTest.php
@@ -256,13 +256,13 @@ public function testParseRepeater()
public function testProcessTag()
{
- $parser = new FieldParser;
$content = '';
$content .= '{text name="websiteName" label="Website Name" size="large"}{/text}'.PHP_EOL;
$content .= '{text name="blogName" label="Blog Name" color="re\"d"}WinterCMS{/text}'.PHP_EOL;
$content .= '{text name="storeName" label="Store Name" shape="circle"}{/text}';
$content .= '{text label="Unnamed" distance="400m"}Foobar{/text}';
$content .= '{foobar name="nullName" label="Valid tag, not searched by this test"}{/foobar}';
+ $parser = new FieldParser($content);
list($tags, $fields) = self::callProtectedMethod($parser, 'processTags', [$content]);
$unnamedTag = md5('{text label="Unnamed" distance="400m"}Foobar{/text}');
@@ -328,11 +328,11 @@ public function testProcessTag()
public function testProcessTagsRegex()
{
- $parser = new FieldParser;
$content = '';
$content .= '{text name="websiteName" label="Website Name"}{/text}'.PHP_EOL;
$content .= '{text name="blogName" label="Blog Name"}WinterCMS{/text}'.PHP_EOL;
$content .= '{text name="storeName" label="Store Name"}{/text}';
+ $parser = new FieldParser($content);
$result = self::callProtectedMethod($parser, 'processTagsRegex', [$content, ['text']]);
$this->assertArrayHasKey(0, $result[2]);
@@ -346,8 +346,8 @@ public function testProcessTagsRegex()
public function testProcessParamsRegex()
{
- $parser = new FieldParser;
$content = 'name="test" comment="This is a test"';
+ $parser = new FieldParser($content);
$result = self::callProtectedMethod($parser, 'processParamsRegex', [$content]);
$this->assertArrayHasKey(0, $result[1]);
diff --git a/tests/Support/ClassLoaderTest.php b/tests/Support/ClassLoaderTest.php
index 827c5f709..9f1ffb23b 100644
--- a/tests/Support/ClassLoaderTest.php
+++ b/tests/Support/ClassLoaderTest.php
@@ -44,7 +44,7 @@ public function testAliases()
]);
// Check that class identifies as both original and alias
- $newInstance = new Winter\Plugin\Classes\TestClass;
+ $newInstance = new \Winter\Plugin\Classes\TestClass;
$this->assertTrue($newInstance instanceof Winter\Plugin\Classes\TestClass);
$this->assertTrue($newInstance instanceof OldOrg\Plugin\Classes\TestClass);
diff --git a/tests/Translation/TranslatorTest.php b/tests/Translation/TranslatorTest.php
index 1ab7b0fc1..29b6f8cd5 100644
--- a/tests/Translation/TranslatorTest.php
+++ b/tests/Translation/TranslatorTest.php
@@ -5,7 +5,6 @@
use Illuminate\Support\Collection;
use Illuminate\Translation\MessageSelector;
use Mockery as m;
-use Winter\Storm\Events\Dispatcher;
use Winter\Storm\Translation\FileLoader;
use Winter\Storm\Translation\Translator;
@@ -323,7 +322,7 @@ public function testDetermineLocalesUsingMethod()
{
$t = new Translator($this->getLoader(), 'en');
$t->determineLocalesUsing(function ($locales) {
- $this->assertSame(['en', 'en'], $locales);
+ $this->assertSame(['en'], $locales);
return ['en', 'lz'];
});