Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] add static analysis tool #2664

Merged
merged 43 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9ac70cc
add PHPStan package (#1930)
Treggats Nov 2, 2023
9bc7245
#1930 add PHPStan config
Treggats Nov 2, 2023
2f182cf
#1930 add PHPStan to the coding-standards Github Workflow
Treggats Nov 2, 2023
314cd14
#1930 remove redundant config settings
Treggats Nov 2, 2023
e985e8e
#1930 continue job on error
Treggats Nov 2, 2023
1aad84a
#1930 add required return statements
Treggats Nov 2, 2023
4e7d4e0
#1930 rename arguments, keep in line with the parent name
Treggats Nov 2, 2023
0168dce
#1930 add missing parent calls
Treggats Nov 4, 2023
7349d4d
#1930 add parent call and simplify setting the properties
Treggats Nov 4, 2023
6b7d047
#1930 update return type
Treggats Nov 2, 2023
b19813a
#1930 update parameter type
Treggats Nov 2, 2023
f2ea91f
#1930 remove default config option values
Treggats Nov 2, 2023
d80979d
#1930 ignore two errors
Treggats Nov 2, 2023
7c4340a
#1930 add baseline for level 2
Treggats Nov 3, 2023
e3e5d2e
#1930 add PHPStan to the coding-standards Github Workflow
Treggats Nov 3, 2023
8b2b05e
#1930 add formatting to the coding-standards Github Workflow
Treggats Nov 3, 2023
829c410
#1930 rename argument names, in line with parent method
Treggats Nov 3, 2023
28436c1
#1930 convert to short closure
Treggats Nov 3, 2023
5885486
#1930 updated the baseline
Treggats Nov 3, 2023
51b0bb9
#1930 assert that a instance of the Mongo query builder is used
Treggats Nov 3, 2023
29101df
#1930 update phpunit config using `phpunit --generate-configuration`
Treggats Nov 4, 2023
aa05e9c
#1930 increase the php memory limit
Treggats Nov 15, 2023
b6c48c2
#1930 formatting/readability GH Action
Treggats Nov 15, 2023
ef81fd7
#1930 remove default value
Treggats Nov 15, 2023
d6e8a85
Apply suggestions from code review
Treggats Nov 16, 2023
e061452
continue workflow if `phpcbf` finds/fixes issues
Treggats Nov 16, 2023
b221fd2
apply phpcbf formatting
Treggats Nov 16, 2023
9148217
cs
Treggats Nov 16, 2023
306476c
use static call for static method
Treggats Nov 16, 2023
c4755b1
let early return return the expected data
Treggats Nov 16, 2023
6d0e49e
unused, removing
Treggats Nov 20, 2023
1cc60c2
update PHPStan baseline (level 2)
Treggats Nov 20, 2023
8bce1bf
replace assert with docblock
Treggats Nov 22, 2023
9b4de89
remove `@throw` docblock comment
Treggats Nov 22, 2023
a7b572e
fix PHPStan error; unknown method `pull()`
Treggats Nov 22, 2023
58d7c63
replace array_merge with array unpacking
Treggats Nov 22, 2023
6c8f41a
add return type to match the userland use case
Treggats Nov 22, 2023
02b7acb
ignore Eloquent magically passing arguments
Treggats Nov 22, 2023
48adaeb
replace with the suggested "new" method
Treggats Nov 22, 2023
395ed5c
Apply suggestions from code review
Treggats Nov 23, 2023
d73f9d9
fix null coalescing
Treggats Nov 23, 2023
0db7601
remove Collection check
Treggats Nov 23, 2023
6bfe719
remove Collection check
Treggats Nov 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
trim_trailing_whitespace = true

[*.yml]
indent_size = 2
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Match the current indenting.

14 changes: 3 additions & 11 deletions .github/workflows/build-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: CI

on:
push:
branches:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant, and not according to the Github Workflow schema

tags:
pull_request:

jobs:
Expand Down Expand Up @@ -55,7 +53,7 @@ jobs:
- name: Show Docker version
run: if [[ "$DEBUG" == "true" ]]; then docker version && env; fi
env:
DEBUG: ${{secrets.DEBUG}}
DEBUG: ${{ secrets.DEBUG }}
- name: Download Composer cache dependencies from cache
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
Expand All @@ -66,14 +64,8 @@ jobs:
key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ matrix.os }}-composer-
- name: Install dependencies
run: |
composer update --no-interaction $([[ "${{ matrix.mode }}" == low-deps ]] && echo ' --prefer-lowest --prefer-stable')
run: composer update --no-interaction $([[ "${{ matrix.mode }}" == low-deps ]] && echo ' --prefer-lowest --prefer-stable')
- name: Run tests
run: |
./vendor/bin/phpunit --coverage-clover coverage.xml
run: ./vendor/bin/phpunit --coverage-clover coverage.xml
env:
MONGODB_URI: 'mongodb://127.0.0.1/?replicaSet=rs'
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
68 changes: 66 additions & 2 deletions .github/workflows/coding-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: "Coding Standards"

on:
push:
branches:
tags:
pull_request:

env:
Expand All @@ -15,6 +13,11 @@ jobs:
name: "phpcs"
runs-on: "ubuntu-22.04"

permissions:
# Give the default GITHUB_TOKEN write permission to commit and push the
# added or changed files to the repository.
contents: write

steps:
- name: "Checkout"
uses: "actions/checkout@v4"
Expand Down Expand Up @@ -50,6 +53,67 @@ jobs:
with:
composer-options: "--no-suggest"

- name: "Format the code"
continue-on-error: true
run: |
mkdir .cache
./vendor/bin/phpcbf

# The -q option is required until phpcs v4 is released
- name: "Run PHP_CodeSniffer"
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"

- name: "Commit the changes"
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "apply phpcbf formatting"

analysis:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Borrowed from the changes that @divine made in #2471

runs-on: "ubuntu-22.04"
continue-on-error: true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will allow the job to continue even if PHPStan finds errors.

For now mostly a convenience to see the output of runs for both PHP 8.1 and 8.2

strategy:
matrix:
php:
- '8.1'
- '8.2'
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: curl, mbstring
tools: composer:v2
coverage: none

- name: Cache dependencies
id: composer-cache
uses: actions/cache@v3
with:
path: ./vendor
key: composer-${{ hashFiles('**/composer.lock') }}

- name: Install dependencies
run: composer install

- name: Restore cache PHPStan results
id: phpstan-cache-restore
uses: actions/cache/restore@v3
with:
path: .cache
key: "phpstan-result-cache-${{ github.run_id }}"
restore-keys: |
phpstan-result-cache-

- name: Run PHPStan
run: ./vendor/bin/phpstan analyse --no-interaction --no-progress --ansi

- name: Save cache PHPStan results
id: phpstan-cache-save
if: always()
uses: actions/cache/save@v3
with:
path: .cache
key: ${{ steps.phpstan-cache-restore.outputs.cache-primary-key }}
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*.sublime-workspace
.DS_Store
.idea/
.phpunit.cache/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I introduced the .cache directory for caching phpunit/phpcs/phpstan

.phpcs-cache
/vendor
composer.lock
composer.phar
phpunit.xml
phpstan.neon
/.cache/
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"orchestra/testbench": "^8.0",
"mockery/mockery": "^1.4.4",
"doctrine/coding-standard": "12.0.x-dev",
"spatie/laravel-query-builder": "^5.6"
"spatie/laravel-query-builder": "^5.6",
"phpstan/phpstan": "^1.10"
},
"replace": {
"jenssegers/mongodb": "self.version"
Expand Down
2 changes: 1 addition & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<arg name="basepath" value="." />
<arg name="extensions" value="php" />
<arg name="parallel" value="80" />
<arg name="cache" value=".phpcs-cache" />
<arg name="cache" value=".cache/phpcs" />
<arg name="colors" />

<!-- Ignore warnings (n), show progress of the run (p), and show sniff names (s) -->
Expand Down
31 changes: 31 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
parameters:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The baseline provides PHPStan with a list of errors to ignore, kind of saying

I know these issues exist, will fix it later

ignoreErrors:
-
message: "#^Variable \\$value on left side of \\?\\? always exists and is always null\\.$#"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we remove the null coalescence operator in Model::fromJson, tests still pass.

public function fromJson($value, $asObject = false)
{
if (! is_string($value)) {
$value = Json::encode($value ?? '');
}
return Json::decode($value ?? '', ! $asObject);
}

This was introduced in #2653, could you check this error @hans-thomas?

Copy link
Contributor

@hans-thomas hans-thomas Nov 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Treggats We can remove Null Coalescing Operator. If the $value was Null, the Json::encode will handle it.

UPDATE: To solve the second error, we can also remove the second Null Coalescing Operator.

The final method:

public function fromJson($value, $asObject = false) 
 { 
     if (! is_string($value)) { 
         $value = Json::encode($value); 
     } 
  
     return Json::decode($value, ! $asObject); 
 } 

count: 1
path: src/Eloquent/Model.php

-
message: "#^Variable \\$value on left side of \\?\\? always exists and is not nullable\\.$#"
count: 1
path: src/Eloquent/Model.php

-
message: "#^Cannot call method modelKeys\\(\\) on array\\|Illuminate\\\\Support\\\\Collection\\.$#"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there is an hidden bug if $ids is an array.

// See issue #256.
if ($current instanceof Collection) {
$current = $ids->modelKeys();
}

Copy link
Contributor

@hans-thomas hans-thomas Nov 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$current = $this->parent->{$this->relatedPivotKey} ?: [];
// See issue #256.
if ($current instanceof Collection) {
$current = $ids->modelKeys();
}

It's not even covered in the tests.
In what case, the $this->parent->{$this->relatedPivotKey} could return a Collection?
I think in the past, it was possible but not now. The pivot column always is an array.

UPDATE: removing this condition doesn't cause any error.

count: 1
path: src/Relations/BelongsToMany.php

-
message: "#^Method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:push\\(\\) invoked with 3 parameters, 0 required\\.$#"
count: 3
path: src/Relations/BelongsToMany.php

-
message: "#^Method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:push\\(\\) invoked with 3 parameters, 0 required\\.$#"
count: 6
path: src/Relations/MorphToMany.php

-
message: "#^Method Illuminate\\\\Database\\\\Schema\\\\Blueprint\\:\\:create\\(\\) invoked with 1 parameter, 0 required\\.$#"
count: 1
path: src/Schema/Builder.php
16 changes: 16 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
includes:
- ./phpstan-baseline.neon

parameters:
tmpDir: .cache/phpstan
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will speedup subsequent runs, in a Github Workflow we'd need to use the cache action to store this cache


paths:
- src

level: 2

editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%'

ignoreErrors:
- '#Unsafe usage of new static#'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These issues were the remaining errors from level 1, I wasn't sure how to fix them. So I chose to ignore them.

- '#Call to an undefined method [a-zA-Z0-9\\_\<\>]+::[a-zA-Z]+\(\)#'
23 changes: 13 additions & 10 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.4/phpunit.xsd"
backupGlobals="false"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed options that had the default value.

bootstrap="vendor/autoload.php"
colors="true"
processIsolation="false"
stopOnFailure="false"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false"
>
<coverage/>
cacheDirectory=".cache/phpunit"
executionOrder="depends,defects"
beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
failOnRisky="true"
failOnWarning="true">
<testsuites>
<testsuite name="Test Suite">
<directory>tests/</directory>
Expand All @@ -20,10 +18,15 @@
<env name="MONGODB_DATABASE" value="unittest"/>
<env name="SQLITE_DATABASE" value=":memory:"/>
<env name="QUEUE_CONNECTION" value="database"/>

<ini name="memory_limit" value="-1"/>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While running the tests locally in Docker I ran into the issue that there wasn't enough memory. Disabling the limit here, seems like the most logical place

</php>
<source>

<source restrictDeprecations="true"
restrictNotices="true"
restrictWarnings="true">
<include>
<directory suffix=".php">./src</directory>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the suffix was redundant so it could be removed

<directory>./src</directory>
</include>
</source>
</phpunit>
14 changes: 7 additions & 7 deletions src/Eloquent/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use function is_array;
use function iterator_to_array;

/** @method \MongoDB\Laravel\Query\Builder toBase() */
class Builder extends EloquentBuilder
{
use QueriesRelationships;
Expand Down Expand Up @@ -219,16 +220,15 @@ protected function ensureOrderForCursorPagination($shouldReverse = false)
}

if ($shouldReverse) {
$this->query->orders = collect($this->query->orders)->map(function ($direction) {
return $direction === 1 ? -1 : 1;
})->toArray();
$this->query->orders = collect($this->query->orders)
->map(static fn (int $direction) => $direction === 1 ? -1 : 1)
->toArray();
}

return collect($this->query->orders)->map(function ($direction, $column) {
return [
return collect($this->query->orders)
->map(static fn ($direction, $column) => [
'column' => $column,
'direction' => $direction === 1 ? 'asc' : 'desc',
];
})->values();
])->values();
}
}
35 changes: 8 additions & 27 deletions src/Relations/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
use function array_map;
use function array_merge;
use function array_values;
use function assert;
use function count;
use function is_array;
use function is_numeric;

class BelongsToMany extends EloquentBelongsToMany
Expand Down Expand Up @@ -82,11 +82,11 @@ protected function setWhere()
}

/** @inheritdoc */
public function save(Model $model, array $joining = [], $touch = true)
public function save(Model $model, array $pivotAttributes = [], $touch = true)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parent method argument name was changed, updated it to match it.

{
$model->save(['touch' => false]);

$this->attach($model, $joining, $touch);
$this->attach($model, $pivotAttributes, $touch);

return $model;
}
Expand Down Expand Up @@ -131,7 +131,7 @@ public function sync($ids, $detaching = true)
$current = $ids->modelKeys();
}

$records = $this->formatSyncList($ids);
$records = $this->formatRecordsList($ids);

$current = Arr::wrap($current);

Expand Down Expand Up @@ -171,6 +171,7 @@ public function sync($ids, $detaching = true)
public function updateExistingPivot($id, array $attributes, $touch = true)
{
// Do nothing, we have no pivot table.
return $this;
}

/** @inheritdoc */
Expand Down Expand Up @@ -229,6 +230,8 @@ public function detach($ids = [], $touch = true)
}

// Remove the relation to the parent.
assert($this->parent instanceof \MongoDB\Laravel\Eloquent\Model);
assert($query instanceof \MongoDB\Laravel\Eloquent\Builder);
$query->pull($this->foreignPivotKey, $this->parent->getKey());

if ($touch) {
Expand Down Expand Up @@ -266,7 +269,7 @@ public function newPivotQuery()
/**
* Create a new query builder for the related model.
*
* @return \Illuminate\Database\Query\Builder
* @return Builder|Model
*/
public function newRelatedQuery()
{
Expand Down Expand Up @@ -295,28 +298,6 @@ public function getQualifiedRelatedPivotKeyName()
return $this->relatedPivotKey;
}

/**
* Format the sync list so that it is keyed by ID. (Legacy Support)
* The original function has been renamed to formatRecordsList since Laravel 5.3.
*
* @deprecated
*
* @return array
*/
protected function formatSyncList(array $records)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing in favor of formatRecordList as mentioned in the comment

{
$results = [];
foreach ($records as $id => $attributes) {
if (! is_array($attributes)) {
[$id, $attributes] = [$attributes, []];
}

$results[$id] = $attributes;
}

return $results;
}

/**
* Get the name of the "where in" method for eager loading.
*
Expand Down
Loading