2

I want to split strings produced by an older version of phpstan we are constrained to use (v0.9).

Each error string is separated by :, but there are sometimes static calls marked with :: which I want to ignore.

My code:

$error = '/path/to/file/namespace/filename:line_number:error message Namespace\ClassName::method().'
$output = preg_split('/:/', $error);

A var_dump of $output gives this:

Array
(
    [0] => /path/to/file/namespace/filename
    [1] => line_number
    [2] => error message Namespace\ClassName
    [3] => 
    [4] => method().
)

The result I want is this:

Array
(
    [0] => /path/to/file/namespace/filename
    [1] => line_number
    [2] => error message Namespace\ClassName::method().
)

I was hoping this could be solved with regex.

I have been reading similar questions and have tried variations of regex, none of which worked.

Leon Segal
  • 679
  • 7
  • 28
  • 1
    You could split on `::(*SKIP)(*F)|:` https://regex101.com/r/KliiMs/1 `$output = preg_split('/::(*SKIP)(*F)|:/', $error);` – The fourth bird Mar 07 '20 at 16:42
  • You can use a different error formatter that's much better suited for parsing. Like JSON or XML. – Ondřej Mirtes Mar 08 '20 at 13:13
  • Sorry @OndřejMirtes, you are right - I should have explained in my question that we are constrained to phpstan v0.9 which only has `raw`, `checklist` and `table` as the output. – Leon Segal Mar 09 '20 at 18:07
  • @LeonSegal That's an ancient version! You should really upgrade to PHPStan 0.12 and I don't know about any blocker why no one should be able to. If you don't have time to fix all the new reported errors, you can take advantage of the baseline feature: https://medium.com/@ondrejmirtes/phpstans-baseline-feature-lets-you-hold-new-code-to-a-higher-standard-e77d815a5dff – Ondřej Mirtes Mar 14 '20 at 09:00
  • 1
    If you "update" to PHPStan 0.9.3, there's a helpful message printed at the beginning of the analysis that tells you the same :) – Ondřej Mirtes Mar 14 '20 at 09:01
  • @OndřejMirtes it is because we are using another package (weebly/phpstan-laravel) which is now deprecated. It relies/relied on phpstanv0.9. The reason we are using it is that we are stuck on a very old version of Laravel, until we can update it. – Leon Segal Mar 14 '20 at 16:07

3 Answers3

2

You can use lookahead and lookbehind for your split:

$error = '/path/to/file/namespace/filename:line_number:error message Namespace\ClassName::method().';
$arr = preg_split('/(?<!:):(?!:)/', $error, -1, PREG_SPLIT_NO_EMPTY);
print_r($arr);
Array
(
    [0] => /path/to/file/namespace/filename
    [1] => line_number
    [2] => error message Namespace\ClassName::method().
)

RegEx Demo

RegEx Details:

  • (?<!:): Negative lookbehind to fail the match if there is a : behind
  • :: Match a :
  • (?!:): Negative lookahead to fail the match if there is a : ahead
Community
  • 1
  • 1
anubhava
  • 761,203
  • 64
  • 569
  • 643
1

Another option is to match 2 or more occurrences of : and use (*SKIP)(*F). Then match a single : to split on.

:{2,}(*SKIP)(*F)|:

Explanation

  • :{2,}(*SKIP)(*F) Match 2 or more occurrences of :, then skip all currently matched chars
  • | Or
  • : Match a single :

Regex demo | Php demo

$error = '/path/to/file/namespace/filename:line_number:error message Namespace\ClassName::method().';
$output = preg_split('/:{2,}(*SKIP)(*F)|:/', $error);
print_r($output);

Output

Array
(
    [0] => /path/to/file/namespace/filename
    [1] => line_number
    [2] => error message Namespace\ClassName::method().
)
The fourth bird
  • 154,723
  • 16
  • 55
  • 70
0

Using preg_match_all (sometimes more simple to split):

preg_match_all('~[^:]+(?>::[^:]*)*~', $error, $matches);

print_r($matches[0]);
Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125