Skip to content

PHPLIB-330: Implement checks for coding standard #668

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

Merged
merged 5 commits into from
Aug 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ vendor/
phpunit.phar
phpunit.xml
.phpunit.result.cache

# phpcs
.phpcs-cache
phpcs.xml
15 changes: 15 additions & 0 deletions .phpcs/autoload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

// Since doctrine/coding-standard requires PHP 7, we can't add it as a dependency
// yet. This autoload file adds more information to the phpcs error message,
// telling the user how they can fix the error presented to them by phpcs.
if (! file_exists(__DIR__ . '/../vendor/doctrine/coding-standard')) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there a particular reason we only pull doctrine/coding-standard in from the smoke test stage in .travis.yml and don't include it in our require-dev dependencies?

Copy link
Member Author

@alcaeus alcaeus Aug 22, 2019

Choose a reason for hiding this comment

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

Since doctrine/coding-standard requires PHP 7.1, including it in require-dev would implicitly bump our PHP requirement to 7.1 on CI systems, breaking tests for older PHP versions. That's why the check stage requires it manually.

To make the generic error message from phpcs more helpful I decided to use the autoload functionality of phpcs to include this error message if doctrine/coding-standard wasn't installed. Now that you mention it, it's probably best to also mention this in the contribution docs.

echo <<<ERRORMESSAGE
==============================================================================
ERROR: Doctrine coding standard is not installed. To rectify this, please run:
composer require --dev doctrine/coding-standard=^6.0
==============================================================================


ERRORMESSAGE;
}
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ jobs:

- stage: Smoke Testing
php: "7.3"
env:
- CHECKS=phpunit
- stage: Smoke Testing
php: "7.1"
Copy link
Member

Choose a reason for hiding this comment

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

Is there a particular reason to use 7.1?

Copy link
Member Author

Choose a reason for hiding this comment

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

Some of the sniffs we use have feature detection where they disable/enable functionality based on PHP version. For example, the sniff requiring type hints does not enforce an object type hint on methods unless running on PHP 7.2 or later. Thus, it's always a good idea to target the lowest supported version for the coding standard in CI.

before_install: []
before_script:
- pecl install -f mongodb-${DRIVER_VERSION}
- composer require --no-update doctrine/coding-standard=^6.0
- composer install --no-interaction --no-progress --no-suggest ${COMPOSER_OPTIONS}
script: vendor/bin/phpcs
after_script: []
after_failure: []
env:
- CHECKS=phpcs

# Test remaining supported PHP versions
- stage: Test
Expand Down
25 changes: 25 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@ test suite. In addition to various PHPUnit options, it defines required
this configuration by creating your own `phpunit.xml` file based on the
`phpunit.xml.dist` file we provide.

## Checking coding standards

The library's code is checked using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer),
which is installed as a development dependency by Composer. Due to the PHP
requirement, the base version of the coding standard is not installed and needs
to be added manually if you plan to contributing code:

```
$ composer require --dev doctrine/coding-standard=^6.0
```

Once the coding standard has been installed, you can check the code for style
errors:


```
$ vendor/bin/phpcs
```

To automatically fix all fixable errors, use the `phpcbf` binary:

```
$ vendor/bin/phpcbf
```

## Documentation

Documentation for the library lives in the `docs/` directory and is built with
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"require-dev": {
"phpunit/phpunit": "^5.7.27 || ^6.4 || ^8.3",
"sebastian/comparator": "^1.0 || ^2.0 || ^3.0",
"squizlabs/php_codesniffer": "^3.4",
"symfony/phpunit-bridge": "^4.4@dev"
},
"autoload": {
Expand Down
101 changes: 101 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?xml version="1.0"?>
<ruleset>
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
<arg name="cache" value=".phpcs-cache"/>
<arg name="colors" />

<!-- Ignore warnings, show progress of the run, and show sniff names -->
<arg value="nps"/>

<autoload>.phpcs/autoload.php</autoload>

<file>.phpcs</file>
<file>src</file>
<file>tests</file>

<rule ref="Doctrine">
Copy link
Member

Choose a reason for hiding this comment

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

Should we change this to "MongoDB" if it's only going to be derived from Doctrine and modified for our needs? Alternatively, is this just indicating that we're inheriting the Doctrine rule set and then applying our own modifications below?

Copy link
Member Author

Choose a reason for hiding this comment

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

This pulls in all rules from the Doctrine coding standard and allows us to exclude/modify some of them. Since we just configure phpcs.xml our ruleset doesn't have a name, that would only happen if we create a ruleset.xml in the standardised directory structure, which isn't necessary if we're not going to distribute the coding standard.

<!-- Exclude sniffs that require newer PHP versions -->
<!-- Available with PHP 7.0 -->
<exclude name="SlevomatCodingStandard.TypeHints.DeclareStrictTypes" />
<!-- In addition to requiring PHP 7.0, this sniff will cause a significant amount of BC breaks. Proceed with caution! -->
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration" />
<exclude name="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly" />
<exclude name="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator" />

<!-- Available with PHP 7.1 -->
<exclude name="SlevomatCodingStandard.Classes.ClassConstantVisibility" />
<exclude name="SlevomatCodingStandard.PHP.ShortList.LongListUsed" />
<exclude name="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue" />

<!-- No statement alignment so far -->
<exclude name="Generic.Formatting.MultipleStatementAlignment" />

<!-- Class naming sniffs are excluded to preserve BC -->
<exclude name="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming" />
<exclude name="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming" />
<exclude name="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming" />
<exclude name="SlevomatCodingStandard.Classes.SuperfluousTraitNaming" />

<!-- Forbid useless annotations - Git and LICENCE file provide more accurate information -->
<!-- Disable forbidden annotation sniff as excluding @api from the list doesn't work -->
<exclude name="SlevomatCodingStandard.Commenting.ForbiddenAnnotations.AnnotationForbidden" />

<!-- Keep long typehints (for now) -->
<exclude name="SlevomatCodingStandard.PHP.TypeCast.InvalidCastUsed" />
<exclude name="SlevomatCodingStandard.TypeHints.LongTypeHints" />

<!-- Don't require a full stop after @throws tags -->
<exclude name="Squiz.Commenting.FunctionComment.ThrowsNoFullStop" />

<!-- Disable some sniffs as they can cause functional changes. These will be enabled later -->
<exclude name="Generic.PHP.ForbiddenFunctions.FoundWithAlternative" />
<exclude name="SlevomatCodingStandard.Classes.UnusedPrivateElements" />
<exclude name="SlevomatCodingStandard.ControlStructures.DisallowYodaComparison" />
<exclude name="SlevomatCodingStandard.ControlStructures.EarlyExit" />
<exclude name="SlevomatCodingStandard.ControlStructures.UselessIfConditionWithReturn" />
<exclude name="SlevomatCodingStandard.Functions.StaticClosure" />
<exclude name="SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure" />
<exclude name="SlevomatCodingStandard.Operators.DisallowEqualOperators" />

<!-- These sniffs cause a large diff, so enable them in separate steps -->
<exclude name="SlevomatCodingStandard.Commenting.DocCommentSpacing.IncorrectAnnotationsGroup" />
<exclude name="SlevomatCodingStandard.Commenting.RequireOneLinePropertyDocComment" />
<exclude name="Squiz.Strings.DoubleQuoteUsage" />

<!-- Sniff currently breaks, see https://github.com/slevomat/coding-standard/issues/727 -->
<exclude name="SlevomatCodingStandard.Namespaces.NamespaceSpacing" />
</rule>

<!-- Change use statement sorting to be compatible with PSR-12 -->
<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses">
<properties>
<property name="psr12Compatible" value="true"/>
</properties>
</rule>

<!-- Forbid fully qualified names even for colliding names -->
<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly">
<properties>
<property name="allowFallbackGlobalConstants" value="false"/>
<property name="allowFallbackGlobalFunctions" value="false"/>
<property name="allowFullyQualifiedGlobalClasses" value="false"/>
<property name="allowFullyQualifiedGlobalConstants" value="false"/>
<property name="allowFullyQualifiedGlobalFunctions" value="false"/>
<property phpcs-only="true" name="allowFullyQualifiedNameForCollidingClasses" value="false"/>
<property phpcs-only="true" name="allowFullyQualifiedNameForCollidingConstants" value="false"/>
<property phpcs-only="true" name="allowFullyQualifiedNameForCollidingFunctions" value="false"/>
<property name="searchAnnotations" value="true"/>
</properties>
</rule>

<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
<exclude-pattern>/src/GridFS/StreamWrapper</exclude-pattern>
<exclude-pattern>/tests/DocumentationExamplesTest.php</exclude-pattern>
</rule>

<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>/tests/Compat/PolyfillAssertTrait.php</exclude-pattern>
</rule>
</ruleset>
2 changes: 0 additions & 2 deletions src/BulkWriteResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ class BulkWriteResult
private $isAcknowledged;

/**
* Constructor.
*
* @param WriteResult $writeResult
* @param mixed[] $insertedIds
*/
Expand Down
12 changes: 7 additions & 5 deletions src/ChangeStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@

namespace MongoDB;

use Iterator;
use MongoDB\Driver\CursorId;
use MongoDB\Driver\Exception\ConnectionException;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Driver\Exception\ServerException;
use MongoDB\Exception\ResumeTokenException;
use MongoDB\Model\ChangeStreamIterator;
use Iterator;
use function call_user_func;
use function in_array;

/**
* Iterator for a change stream.
Expand Down Expand Up @@ -57,8 +59,6 @@ class ChangeStream implements Iterator
private $hasAdvanced = false;

/**
* Constructor.
*
* @internal
* @param ChangeStreamIterator $iterator
* @param callable $resumeCallable
Expand Down Expand Up @@ -109,6 +109,7 @@ public function key()
if ($this->valid()) {
return $this->key;
}

return null;
}

Expand Down Expand Up @@ -167,7 +168,7 @@ private function isResumableError(RuntimeException $exception)
return true;
}

if ( ! $exception instanceof ServerException) {
if (! $exception instanceof ServerException) {
return false;
}

Expand Down Expand Up @@ -202,7 +203,7 @@ private function onIteration($incrementKey)

/* Return early if there is not a current result. Avoid any attempt to
* increment the iterator's key. */
if (!$this->valid()) {
if (! $this->valid()) {
Copy link
Member

Choose a reason for hiding this comment

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

Should this have spaces on both sides of the !? I see some later occurrences of ! were left as-is so I'm wondering if the coding standard permits both but only applies a trailing space when formatting incorrectly styled code.

Copy link
Member Author

Choose a reason for hiding this comment

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

There is only a sniff to enforce a space after the negation operator, but not to enforce it before it, hence the different styles. The space after the opening parenthesis in function calls is forbidden by PSR-2, which serves as the base of doctrine/coding-standard. The space before is left over from me excluding a sniff for now (see comment below about control structure spacing)

return;
}

Expand Down Expand Up @@ -236,6 +237,7 @@ private function resumeOrThrow(RuntimeException $exception)
{
if ($this->isResumableError($exception)) {
$this->resume();

return;
}

Expand Down
25 changes: 14 additions & 11 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,30 @@

namespace MongoDB;

use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\BSONArray;
use MongoDB\Model\BSONDocument;
use MongoDB\Model\DatabaseInfoIterator;
use MongoDB\Operation\DropDatabase;
use MongoDB\Operation\ListDatabases;
use MongoDB\Operation\Watch;
use function is_array;

class Client
{
private static $defaultTypeMap = [
'array' => \MongoDB\Model\BSONArray::class,
'document' => \MongoDB\Model\BSONDocument::class,
'root' => \MongoDB\Model\BSONDocument::class,
'array' => BSONArray::class,
'document' => BSONDocument::class,
'root' => BSONDocument::class,
];
private static $wireVersionForReadConcern = 4;
private static $wireVersionForWritableCommandWriteConcern = 5;
Expand Down Expand Up @@ -147,13 +150,13 @@ public function __toString()
*/
public function dropDatabase($databaseName, array $options = [])
{
if ( ! isset($options['typeMap'])) {
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$server = $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY));

if ( ! isset($options['writeConcern']) && \MongoDB\server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern)) {
if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern)) {
$options['writeConcern'] = $this->writeConcern;
}

Expand Down Expand Up @@ -269,7 +272,7 @@ public function selectDatabase($databaseName, array $options = [])
* Start a new client session.
*
* @see http://php.net/manual/en/mongodb-driver-manager.startsession.php
* @param array $options Session options
* @param array $options Session options
* @return Session
*/
public function startSession(array $options = [])
Expand All @@ -288,17 +291,17 @@ public function startSession(array $options = [])
*/
public function watch(array $pipeline = [], array $options = [])
{
if ( ! isset($options['readPreference'])) {
if (! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference;
}

$server = $this->manager->selectServer($options['readPreference']);

if ( ! isset($options['readConcern']) && \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcern)) {
if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern)) {
$options['readConcern'] = $this->readConcern;
}

if ( ! isset($options['typeMap'])) {
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

Expand Down
Loading