Skip to content

Clarifications Needed for Ambiguities in PER CS 2.0 Specification #107

Closed
@azjezz

Description

@azjezz

Hi there,

I'm currently working on Mago, a PHP toolchain that happens to include a formatter (crates/formatter). I'm interested in making Mago conform to PER CS 2.0 by default, but I’ve encountered some ambiguities in the specification that I'd like to clarify. Here are a few questions I have:

1. Attributes (Section 12.3)

When multiple attributes exist in a single attribute block (between #[ and ]), and breaking those attributes doesn’t result in lines shorter than 120 characters, should the block itself break? For example:

<?php

#[SomeAttributeWithASuperLongName, SomeOtherAttribute, SomeOtherAttribute, SomeOtherAttribute, SomeOtherAttribute, Foo, Bar, Baz]
class Foo
{
}

Would it be formatted as:

<?php

#[
    SomeAttributeWithASuperLongName,
    SomeOtherAttribute,
    SomeOtherAttribute,
    SomeOtherAttribute,
    SomeOtherAttribute,
    Foo,
    Bar,
    Baz,
]
class Foo
{
}

This would make it consistent with arguments, parameters, etc.

ref: https://github.com/carthage-software/mago/blob/89bfe68a778f75b8b99273a8e531ceef81c886b3/crates/formatter/src/format/mod.rs#L1392-L1467


2. Arrow Functions (Section 7.1)

The spec says that an arrow function can break with the arrow on the next line, indented:

$fn = fn($x) =>
    $x + 1;

When the expression in the arrow function also wraps, how should it format? For example:

if ($expr) {
    $fn = fn($x) => ['foo' => 'bar', 'baz' => 'qux', 'quux' => 'quuz', 'corge' => 'grault', 'garply' => 'waldo', 'fred' => $x];
}

Should it wrap the arrow function first or the expression? For instance:

if ($expr) {
    $fn = fn($x) 
        => ['foo' => 'bar', 'baz' => 'qux', 'quux' => 'quuz', 'corge' => 'grault', 'garply' => 'waldo', 'fred' => $x];
}

// or

if ($expr) {
    $fn = fn($x) => [
        'foo' => 'bar',
        'baz' => 'qux',
        'quux' => 'quuz',
        'corge' => 'grault',
        'garply' => 'waldo',
        'fred' => $x,
    ];
}

And if it's the second, when the expression wraps, does the arrow function need to also wrap again if it still exceeds 120 characters?

ref: https://github.com/carthage-software/mago/blob/89bfe68a778f75b8b99273a8e531ceef81c886b3/crates/formatter/src/format/expression.rs#L592-L627


3. Ternary Operators (Section 6.3)

The specification mentions spaces around ? and :, but what happens when the ternary wraps? For example:

$foo = $bar
    ? 'baz'
    : 'qux';
    
// or

$foo = $bar ?
    'baz' :
    'qux';

What should wrap first—the ternary operator or the expressions inside it?

$foo = $foo 
    ? $foo
    : ['foo' => 'bar', 'baz' => 'qux', 'quux' => 'quuz', 'corge' => 'grault', 'garply' => 'waldo', 'fred' => $x];

// or
$foo = $foo ? $foo : [
    'foo' => 'bar',
    'baz' => 'qux',
    'quux' => 'quuz',
    'corge' => 'grault',
    'garply' => 'waldo',
    'fred' => $x,
];

ref: https://github.com/carthage-software/mago/blob/89bfe68a778f75b8b99273a8e531ceef81c886b3/crates/formatter/src/format/expression.rs#L838-L866


4. Try/Catch/Finally (Section 5.6)

In this section, the example provided is:

<?php

try {
    // try body
} catch (FirstThrowableType $e) {
    // catch body
} catch (OtherThrowableType | AnotherThrowableType $e) {
    // catch body
} finally {
    // finally body
}

Regarding OtherThrowableType | AnotherThrowableType type hints, should it be consistent with other type hints, or should there always be a space between the | operator?

ref: https://github.com/carthage-software/mago/blob/89bfe68a778f75b8b99273a8e531ceef81c886b3/crates/formatter/src/format/mod.rs#L1226-L1373


5. Foreach (Section 5.5)

The specification does not mention how to format the foreach statement when it wraps. Should it be formatted as follows?

foreach (
    $array as $key => $value
) {
    // body
}

// or

foreach (
    $array as
        $key => $value
) {
    // body
}

What about when the target of the foreach wraps?

foreach ($array as [
    $a,
    $b,
    $c,
    $d,
]) {
    // body
}

ref: https://github.com/carthage-software/mago/blob/89bfe68a778f75b8b99273a8e531ceef81c886b3/crates/formatter/src/format/control_structure.rs#L390-L438


6. Method Chaining (Section 4.7)

The spec mentions method chaining can be put on separate lines, e.g.:

$someInstance
    ->create()
    ->prepare()
    ->run();

When should method chaining break? Should there be a threshold for the number of methods called or the character count exceeding 120? Should it break before or after arguments of methods?

$someInstance->create()->prepare()->run($foo, $bar, $baz);

// Break as:
$someInstance
    ->create()
    ->prepare()
    ->run($foo, $bar, $baz);

// Or:
$someInstance->create()->prepare()->run(
    $foo,
    $bar,
    $baz
);

Does this chain include property access, null-safe method calls, or method closure creation?

$foo->property?->method()?->property?->method()->closure(...);

ref: https://github.com/carthage-software/mago/blob/89bfe68a778f75b8b99273a8e531ceef81c886b3/crates/formatter/src/format/call.rs#L17-L94


7. Line Width and Blank Lines (Section 2.3)

The spec says:

There MUST NOT be a hard limit on line length.

The soft limit on line length MUST be 120 characters.

Lines SHOULD NOT be longer than 80 characters; lines longer than that SHOULD be split into multiple subsequent lines of no more than 80 characters each.

This is confusing. Is the soft limit 120 or 80? If it’s 120, why split lines into 80-character chunks?

Additionally, the spec mentions:

Blank lines MAY be added to improve readability and to indicate related blocks of code except where explicitly forbidden.

What about multiple blank lines in a row? Should they be allowed, or should they be collapsed into one?


Thanks for taking the time to clarify these ambiguities! These answers will help me make Mago more consistent with the intent of the specification.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions