Skip to content
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
2 changes: 2 additions & 0 deletions .github/workflows/test-coding-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ on:
push:
paths:
- '**.php'
- 'docs/**.md'
- 'spark'
- '.github/workflows/test-coding-standards.yml'
pull_request:
paths:
- '**.php'
- 'docs/**.md'
- 'spark'
- '.github/workflows/test-coding-standards.yml'

Expand Down
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
__FILE__,
__DIR__ . '/.php-cs-fixer.no-header.php',
__DIR__ . '/.php-cs-fixer.tests.php',
__DIR__ . '/.php-cs-fixer.user-guide.php',
__DIR__ . '/preload.php',
__DIR__ . '/rector.php',
__DIR__ . '/spark',
Expand Down
33 changes: 33 additions & 0 deletions .php-cs-fixer.user-guide.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

use PhpCsFixer\ConfigInterface;

/** @var ConfigInterface $config */
$config = require __DIR__ . '/.php-cs-fixer.dist.php';

$overrides = [
'echo_tag_syntax' => false,
'header_comment' => false,
'php_unit_internal_class' => false,
'no_unused_imports' => false,
'class_attributes_separation' => false,
'fully_qualified_strict_types' => [
'import_symbols' => false,
'leading_backslash_in_global_namespace' => true,
],
];

return $config
->setCacheFile('build/.php-cs-fixer.user-guide.cache')
->setRules(array_merge($config->getRules(), $overrides));
205 changes: 205 additions & 0 deletions admin/lint-docs-samples.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

/**
* Checks the code style of the PHP samples in the user guide.
*
* Every ```php fence that starts with an opening tag and parses is run through
* PHP CS Fixer with .php-cs-fixer.user-guide.php. A fence directly below a
* `<!-- lint-ignore -->` line is skipped. With --fix, the fences are
* rewritten in place. Otherwise, nothing is changed, and the script exits
* with 1 when a sample needs fixing.
*
* Usage: php admin/lint-docs-samples.php [--fix] [--docs=<directory>]
*/

/**
* @return list<string>
*/
function docs_pages(string $docs): array
{
$pages = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($docs, FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS),
);

/** @var SplFileInfo $file */
foreach ($iterator as $file) {
$path = $file->getPathname();

if ($file->getExtension() === 'md' && ! str_contains($path, '/overrides/')) {
$pages[] = $path;
}
}

sort($pages);

return $pages;
}

function parses(string $code): bool
{
try {
token_get_all($code, TOKEN_PARSE);
} catch (ParseError) {
return false;
}

return true;
}

/**
* @param list<string> $lines
*
* @return list<array{start: int, end: int, indent: string, code: string}>
*/
function find_samples(array $lines): array
{
$samples = [];
$count = count($lines);

for ($i = 0; $i < $count; $i++) {
if (preg_match('/^(\s*)```php\s*$/', $lines[$i], $opening) !== 1) {
continue;
}

$indent = $opening[1];
$skip = $i > 0 && trim($lines[$i - 1]) === '<!-- lint-ignore -->';

for ($end = $i + 1; $end < $count; $end++) {
if (rtrim($lines[$end]) === $indent . '```') {
break;
}
}

$body = array_map(
static fn (string $line): string => str_starts_with($line, $indent)
? substr($line, strlen($indent))
: ltrim($line),
array_slice($lines, $i + 1, $end - $i - 1),
);
$code = implode("\n", $body) . "\n";

if (! $skip && str_starts_with(ltrim($code), '<?php') && parses($code)) {
$samples[] = ['start' => $i + 1, 'end' => $end, 'indent' => $indent, 'code' => $code];
}

$i = $end;
}

return $samples;
}

function remove_directory(string $directory): void
{
foreach (glob($directory . '/*') ?: [] as $file) {
unlink($file);
}

rmdir($directory);
}

$options = getopt('', ['fix', 'docs::']);
$fix = isset($options['fix']);
$root = dirname(__DIR__);
$docs = rtrim(is_string($options['docs'] ?? null) ? $options['docs'] : $root . '/docs', '/');

$found = [];
$files = [];

foreach (docs_pages($docs) as $page) {
$lines = explode("\n", (string) file_get_contents($page));

foreach (find_samples($lines) as $sample) {
$found[] = ['page' => $page, 'lines' => $lines] + $sample;
}
}

$work = sys_get_temp_dir() . '/docs-samples-' . getmypid();
mkdir($work);

foreach ($found as $id => $sample) {
$files[$id] = sprintf('%s/%05d.php', $work, $id);
file_put_contents($files[$id], $sample['code']);
}

$command = sprintf(
'%s %s %s --verbose --ansi --using-cache=no --config=%s %s 2>&1',
escapeshellarg(PHP_BINARY),
escapeshellarg($root . '/utils/vendor/bin/php-cs-fixer'),
$fix ? 'fix' : 'check --diff',
escapeshellarg($root . '/.php-cs-fixer.user-guide.php'),
escapeshellarg($work),
);

exec($command, $output, $status);

if ($status !== 0 && ($fix || $status !== 8)) {
fwrite(STDERR, implode("\n", $output) . "\n");
remove_directory($work);

exit(2);
}

if (! $fix && $status === 8) {
$labels = [];

foreach ($found as $id => $sample) {
$labels[$files[$id]] = sprintf('%s:%d', substr($sample['page'], strlen($root) + 1), $sample['start'] + 1);
}

echo strtr(implode("\n", $output), $labels), "\n";
remove_directory($work);

exit(1);
}

$changed = [];

foreach ($found as $id => $sample) {
$fixed = (string) file_get_contents($files[$id]);

if ($fixed === $sample['code']) {
continue;
}

$changed[$sample['page']][] = $sample + ['fixed' => $fixed, 'file' => $files[$id]];
}

if ($changed === []) {
printf("\033[32m%s PHP samples checked, none need fixing.\033[0m\n", number_format(count($found)));
remove_directory($work);

exit(0);
}

$total = 0;

foreach ($changed as $page => $samples) {
$lines = $samples[0]['lines'];

foreach (array_reverse($samples) as $sample) {
$total++;
$body = array_map(
static fn (string $line): string => $line === '' ? '' : $sample['indent'] . $line,
explode("\n", rtrim($sample['fixed'], "\n")),
);
array_splice($lines, $sample['start'], $sample['end'] - $sample['start'], $body);
}

file_put_contents($page, implode("\n", $lines));
}

remove_directory($work);

printf("\033[32m%s of %s PHP samples fixed.\033[0m\n", number_format($total), number_format(count($found)));
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,15 @@
"Composer\\Config::disableProcessTimeout",
"utils/vendor/bin/php-cs-fixer check --ansi --verbose --diff --config=.php-cs-fixer.no-header.php",
"utils/vendor/bin/php-cs-fixer check --ansi --verbose --diff --config=.php-cs-fixer.tests.php",
"utils/vendor/bin/php-cs-fixer check --ansi --verbose --diff"
"utils/vendor/bin/php-cs-fixer check --ansi --verbose --diff",
"@php admin/lint-docs-samples.php"
],
"cs-fix": [
"Composer\\Config::disableProcessTimeout",
"utils/vendor/bin/php-cs-fixer fix --ansi --verbose --diff --config=.php-cs-fixer.no-header.php",
"utils/vendor/bin/php-cs-fixer fix --ansi --verbose --diff --config=.php-cs-fixer.tests.php",
"utils/vendor/bin/php-cs-fixer fix --ansi --verbose --diff"
"utils/vendor/bin/php-cs-fixer fix --ansi --verbose --diff",
"@php admin/lint-docs-samples.php --fix"
],
"phpstan:baseline": [
"phpstan analyse --ansi --generate-baseline=utils/phpstan-baseline/loader.neon",
Expand Down
8 changes: 6 additions & 2 deletions contributing/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,19 @@ Open the **app/Config/Filters.php** file and update the `$methods` property like

## Code Blocks

Fence a code block with three backticks and the name of its language.
PHP samples have no opening tag, and the highlighter expects that:
Fence a code block with three backticks and the name of its language:

````markdown
```php
<?php

$pager = service('pager');
```
````

A PHP sample that starts with `<?php` and parses is checked by `composer cs`, and `composer cs-fix` rewrites it in place.
To skip a sample, such as CodeIgniter 3 code, put `<!-- lint-ignore -->` on the line above its fence.

### CLI Command

Use the `console` language for a command that is run in a terminal:
Expand Down
1 change: 1 addition & 0 deletions docs/database/query_builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,7 @@ The behavior is the same as the `union()` method. However, all results will be r

Query grouping allows you to create groups of **WHERE** clauses by enclosing them in parentheses. This will allow you to create queries with complex **WHERE** clauses. Nested groups are supported. Example:

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

Path: **application/config/site.php**:

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ After that, you have to insert a `use` statement below the namespace definition

Path: **application/controllers**:

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_database.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

### CodeIgniter Version 3.x

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_file_upload.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ You have to change your file uploading code to match the new methods.

### CodeIgniter Version 3.x

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_images.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

### CodeIgniter Version 3.x

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ php spark migrate

Path: **application/migrations**:

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_models.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ You can find more information on those methods in [Using CodeIgniter's Model](..

Path: **application/models**:

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

### CodeIgniter Version 3.x

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_responses.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

### CodeIgniter Version 3.x

<!-- lint-ignore -->
```php
<?php

Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

Path: **application/config/routes.php**:

<!-- lint-ignore -->
```php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
Expand Down
1 change: 1 addition & 0 deletions docs/installation/upgrade_security.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

### CodeIgniter Version 3.x

<!-- lint-ignore -->
```php
<?php

Expand Down
Loading
Loading