conduction/coding-standard

Conduction coding standards for the PHP CS Fixer. Extends nextcloud/coding-standard — adds rules, never overrides them.

Maintainers

Package info

github.com/ConductionNL/coding-standard

Documentation

pkg:composer/conduction/coding-standard

Transparency log

Statistics

Installs: 16 785

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-12 08:27 UTC

This package is auto-updated.

Last update: 2026-08-12 09:10:56 UTC


README

conduction/coding-standard

PHP CS Fixer configuration for Conduction's Nextcloud apps. It extends nextcloud/coding-standard and adds rules to it — never overrides them.

The rule this package enforces

Conduction code must pass Nextcloud's own coding standard unchanged. We may be stricter than Nextcloud. We may not be different from it.

That is enforced by construction, not by review. Conduction\CodingStandard\Config extends Nextcloud's and merges a private ADDITIONS array on top of parent::getRules(). tests/invariants.php fails the build if ADDITIONS shares a single key with the parent set, so a Nextcloud rule cannot be redefined even by accident.

The suite also asserts that no parent rule is dropped, that no parent rule's value changed, and that the indent is a literal tab — and each of those has a positive control proving the assertion can fail. A suite that cannot fail is indistinguishable from one that passes.

Why it exists

Measured 2026-08-12 against openregister's lib/: all 1,427 files failed nextcloud/coding-standard. curly_braces_position 100%, indentation_type 98.7%, phpdoc_align 85.6%, binary_operator_spaces 77.4%, cast_spaces 47.4%, concat_space 40.9%.

The fleet had been formatting PHP with a PEAR-derived PHP_CodeSniffer ruleset — four spaces, next-line braces, (int) $x, 'a'.'b' — under script names (cs:check / cs:fix) that belong to nextcloud/coding-standard, which 17 of 18 apps also carried in require-dev without ever invoking it. That was not a stricter standard; it was a different dialect wearing the right label.

Usage

// composer.json
"require-dev": {
    "conduction/coding-standard": "^1.0"
},
"scripts": {
    "cs:check": "php-cs-fixer fix --dry-run --diff",
    "cs:fix": "php-cs-fixer fix"
}
<?php
// .php-cs-fixer.dist.php
require_once __DIR__ . '/vendor/autoload.php';

$config = new Conduction\CodingStandard\Config();
$config->getFinder()->in(__DIR__ . '/lib');

return $config;

The require_once is not optional. .php-cs-fixer.dist.php is included by php-cs-fixer before your autoloader runs, so without it the file dies with Class "Conduction\CodingStandard\Config" not found — and in --format=json mode that fatal is reported as zero files needing changes, which reads exactly like a clean tree.

What ADDITIONS contains, and why it is empty

Empty is a result, not an omission.

Every rule this fleet wants beyond Nextcloud's is semantic, not typographic — named parameters on internal calls, an @spec tag on public API, no \OC::$server, no var_dump / die / error_log. php-cs-fixer cannot express any of them. They live in PHP_CodeSniffer, which the fleet now runs for semantics only, with every whitespace, brace and alignment sniff removed precisely so the two tools cannot contradict each other.

On formatting, we want exactly what Nextcloud wants.

Adding a key here is therefore a real decision. It must be a rule Nextcloud has no opinion on; the test refuses it otherwise.

Division of labour

Concern Tool Config
Formatting — whitespace, braces, imports, quotes, casts php-cs-fixer this package
Semantics — named parameters, @spec, banned functions, removed NC APIs, line length PHP_CodeSniffer quality-config/ in ConductionNL/.github
Types Psalm + PHPStan quality-config/

The PHPCS ruleset carries a test asserting it reports zero findings on php-cs-fixer-formatted fixtures. That is the automated form of the measurement that produced this split, and it is what stops the two tools drifting back into conflict.

Registry

Published on Packagist as conduction/coding-standard, so composer require --dev conduction/coding-standard needs no repositories block. That matters beyond convenience: a per-app repositories entry is a per-app file, and per-app files are what drift.

Tags reach Packagist through the push webhook Packagist installs on submission — https://packagist.org/api/github, push events. Nothing to configure by hand, but do check a real delivery after the first tag: a hook that exists and 403s looks identical to one that works, and a ping succeeding proves only that the endpoint answers a handshake.

Further reading