Skip to content

Modernise code#156

Open
mrliptontea wants to merge 56 commits intomasterfrom
PLT-951-modernise-code
Open

Modernise code#156
mrliptontea wants to merge 56 commits intomasterfrom
PLT-951-modernise-code

Conversation

@mrliptontea
Copy link
Copy Markdown
Member

@mrliptontea mrliptontea commented Feb 27, 2026

  • Precursor to PLT-951
    • Modernise codebase with PHPStan, PHP CS Fixer, and Rector
    • Drop PHP 7.3
    • Use PHP 7.4 features
    • Strict types where possible
    • Resolve todo: use ObjectId->getTimestamp()
Used rector.php
<?php

declare(strict_types=1);

use Rector\CodeQuality\Rector\Empty_\SimplifyEmptyCheckOnEmptyArrayRector;
use Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector;
use Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector;
use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector;
use Rector\CodingStyle\Rector\PostInc\PostIncDecToPreIncDecRector;
use Rector\Config\RectorConfig;
use Rector\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector;
use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector;
use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector;
use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector;
use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector;
use Rector\Privatization\Rector\Class_\FinalizeTestCaseClassRector;
use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector;
use Rector\TypeDeclarationDocblocks\Rector\Class_\AddReturnDocblockDataProviderRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromDimFetchAccessRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockReturnArrayFromDirectArrayInstanceRector;

return RectorConfig::configure()
    ->withPaths([
        __DIR__ . '/scripts',
        __DIR__ . '/src',
        __DIR__ . '/test',
    ])
    ->withBootstrapFiles([
        __DIR__ . '/src/tripod.inc.php',
    ])
    ->withPhpSets(php74: true)
    ->withPreparedSets(
        deadCode: true,
        codeQuality: true,
        codingStyle: true,
        typeDeclarations: true,
        typeDeclarationDocblocks: true,
        privatization: true,
        naming: true,
        instanceOf: true,
        earlyReturn: true,
        rectorPreset: true,
        phpunitCodeQuality: true,
    )
    ->withSkip([
        AddReturnDocblockDataProviderRector::class,
        CatchExceptionNameMatchingTypeRector::class,
        DisallowedEmptyRuleFixerRector::class,
        FinalizeTestCaseClassRector::class,
        MakeInheritedMethodVisibilitySameAsParentRector::class,
        ParamTypeByMethodCallTypeRector::class,
        PostIncDecToPreIncDecRector::class,
        RenameForeachValueVariableToMatchExprVariableRector::class,
        RenameParamToMatchTypeRector::class,
        RenamePropertyToMatchTypeRector::class,
        RenameVariableToMatchMethodCallReturnTypeRector::class,
        RenameVariableToMatchNewTypeRector::class,
        SimplifyEmptyCheckOnEmptyArrayRector::class,
        FlipTypeControlToUseExclusiveTypeRector::class,
        AddParamArrayDocblockFromDimFetchAccessRector::class,
        DocblockReturnArrayFromDirectArrayInstanceRector::class,
    ])
;

@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch 9 times, most recently from a1fa945 to 6a0ca80 Compare March 5, 2026 23:25
@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch 3 times, most recently from 30f20fc to 7d9faf7 Compare March 9, 2026 14:43
@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch from 7d9faf7 to 3cca01b Compare March 10, 2026 12:03
@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch from a98ede3 to 18afec6 Compare March 17, 2026 09:49
@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch from c912d90 to 2c80ef9 Compare March 17, 2026 13:06
@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch from 2c80ef9 to edac916 Compare March 17, 2026 13:09
@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch 2 times, most recently from ba3d8fe to 143e27b Compare March 18, 2026 11:20
@mrliptontea mrliptontea marked this pull request as ready for review March 18, 2026 11:30
@mrliptontea mrliptontea force-pushed the PLT-951-modernise-code branch from b76f569 to 2adc875 Compare March 19, 2026 13:25
$a[$rdf] = $parser->getSimpleIndex(0);
} elseif (
is_array($a[$rdf])
and isset($a[$rdf][0])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd long since forgotten and existed in PHP...!

* @param ObjectType $type
*/
private function get_subjects_where($p, $o, $type)
private function get_subjects_where(string $p, $o, string $type): array
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why does the docblock type it as a TriplePredicate but in the function it types it as a string ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Types like TriplePredicate, ObjectValue are PHPStan type aliases, defined here:

* @phpstan-type ObjectResource string
* @phpstan-type ObjectLiteral string|int|float|bool
* @phpstan-type ObjectType 'bnode'|'uri'|'literal'
* @phpstan-type ObjectValue ObjectResource|ObjectLiteral
* @phpstan-type TripleSubject string
* @phpstan-type TriplePredicate string

(though language servers in PHPStorm, Intelephense in VSCode, etc. also interpret them and provide completions based on them)

I'm using TriplePredicate for consistency, even though it translates to just string. This helps make a better sense of what's inside the object, which is especially visible here:

* @phpstan-type TripleGraph array<TripleSubject, array<TriplePredicate, TripleObject[]>>

I think this enhances the reading experience as you can quickly find where predicates are referenced, such as here:

* @return TriplePredicate[] list of property URIs
*/
public function get_subject_properties(string $s, bool $distinct = true): array

So you know it's not just an array being returned, it's an array of predicates.

* @param 'literal'|'resource' $type
*/
private function add_to_sequence($s, $o, $type = 'resource')
private function add_to_sequence(string $s, $o, string $type = 'resource'): void
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same question as above, but TripleSubject vs. string

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See above

@@ -0,0 +1,17 @@
parameters:
level: 5
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Woop woop

Copy link
Copy Markdown
Member

@astilla astilla left a comment

Choose a reason for hiding this comment

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

Well...it's massive... 🤣
Looks like some good automated updates.
I've left a couple of questions I wasn't sure about a difference in typing between docblock and function param definition.
Other than than it looks sane but being so big I've somewhat skimmed it.

Copy link
Copy Markdown

@mike-dean-talis mike-dean-talis left a comment

Choose a reason for hiding this comment

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

I reached the end. Holy moly. That's 2 hours I won't get back.

This generally looks good to me although I have a few questions and observations along the way.


if (empty($options) || isset($options['h']) || isset($options['help'])
if (
$options === [] || $options === false || isset($options['h']) || isset($options['help'])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

comment: I am surprised that $options === [] works as intended to be honest. empty does check for false-y values, so I am surprised this is necessary.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is on Rector: https://getrector.com/rule-detail/disallowed-empty-rule-fixer-rector.

Since PHP's getopt can return array|false that's what's it checking for.

From type safety standpoint this make more sense, since the definition of what "empty" means in PHP is quite loose...

$ php -r 'var_dump(empty("0"));'
bool(true)

} else {
$id = null;
}
$id = isset($options['i']) || isset($options['id']) ? $options['i'] ?? $options['id'] : null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

comment: I wonder if we can make it simpler than this:

$id = $options['i'] ?? $options['id'] ?? null; 

We seem to do similar elsewhere, so I won't go through them all (I'll be here forever) but you can get my point.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

True, I missed this. Automagic tools can only take us so far I guess...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See bfc8602

Config::getInstance()->setMongoCursorTimeout(-1);

echo "Generating {$tableId}";
echo 'Generating ' . $tableId;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question: I am not entirely sure what was "bad" about the previous syntax? We do a lot of interleaved strings like this in JS, I just wonder what makes it "modern" to go backwards? Anyway... enough of my opinionated waffle.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Again, Rector at play: https://getrector.com/rule-detail/encapsed-strings-to-sprintf-rector

I believe it's about consistency:

  • In PHP, unlike JS, {} is limited, and only works with variables:

    echo "{$var}";
    echo "{$obj->prop}";
    echo "{$obj->method()}";
    echo "{$obj::$staticProp}";
    echo "{$obj::static()}";
    # But you can't e.g.:
    echo "{func()}";
    echo "{CONSTANT}";
    echo "{SomeClass::static()}";
  • There is also cursed, deprecated, "${var}" syntax.

$json = json_encode(['namespaces' => $config['namespaces']]);

echo indent($json);
echo json_encode(['namespaces' => $config['namespaces']], JSON_PRETTY_PRINT);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

praise: This one made me laugh out loud. Why have 50 lines when you can have one? 👍

$subject = trim($parts[0], '><');

if (empty($currentSubject)) { // set for first iteration
if ($currentSubject === '' || $currentSubject === '0') { // set for first iteration
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

comment: This PR makes me wonder if there is something inherently wrong with empty? As it seems to be avoided in favour of direct comparisons that I wouldn't usually use.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See my comment above; but yes, Rector seem to prefer to avoid empty since it's a catch-all for null, false, '', '0', 0, 0.0, []. Converting to an explicit comparison forces you to think about types and possible values.

That said: 119a88f

Comment on lines +355 to +365
if (
preg_match('~^[a-zA-Z][a-zA-Z0-9\-]+$~', $parts[$i])
&& !isset($this->_ns[$parts[$i]])
&& $parts[$i] != 'schema'
&& $parts[$i] != 'ontology'
&& $parts[$i] != 'vocab'
&& $parts[$i] != 'terms'
&& $parts[$i] != 'ns'
&& $parts[$i] != 'core'
&& strlen($parts[$i]) > 3
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

comment: This is my favourite if statement. I have no idea what it's really doing or why.

No change necessary just... man. I despair.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

* @param array $index
*/
public function getSerializedIndex($index, $context)
public function getSerializedIndex($index, ?string $context): string
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question: Can we not type $index as an array? Maybe I have missed something obvious.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's not you, it'm me (and Rector) who missed this.

Yes, we can type it: 4a8479e

Comment on lines +500 to +504
$milliseconds = (int) $lastUpdatedDate->__toString();
$seconds = intdiv($milliseconds, 1000);
$fraction = ($milliseconds % 1000) / 1000;

return sprintf('%.8f %d', $fraction, $seconds);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

praise: I think this is doing the same work. I'm hoping tests would catch it if not. This makes the process more readable at least.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

$this->assertMatchesRegularExpression('/^0.[0-9]{8} [0-9]{10}/', $tripod->getETag($_id['r']));

Though why microtime format was chosen in the first place evades me...

$this->assertInstanceOf(Tripod\Mongo\Config::class, $instance);
$this->assertInstanceOf(ITripodConfigSerializer::class, $instance);
$this->assertEquals(
$this->assertSame(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

praise: Bless your soul. assertSame.

Comment on lines +57 to +62
public function mandatoryArgDataProvider(): iterable
{
return [
['tripodConfig', 'tripodConfig or tripodConfigGenerator'],
['storeName'],
['reindex'],
['background'],
];
yield ['tripodConfig', 'tripodConfig or tripodConfigGenerator'];
yield ['storeName'];
yield ['reindex'];
yield ['background'];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

praise: Another thing I didn't know PHP had... iterators. Well, I hope to add that one to my repertoire.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Generators are pretty neat.

But for writing tests with many cases wait for PHP attributes!
https://github.com/talis/depot/blob/b2d73c12/server/tests/unit/VaultTest.php#L19-L25

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants