regex scan files for specific contents

You could make use of a branch reset group and match the 2 different formats using an alternation with the same named groups.

(?|(?:@can|hasPermissionTo|hasDirectPermission)\(\s*(?P<quote>['"])(?P<string>.*?)\1\)|\((?P<quote>['"])can:(?P<string>.*?)\1\)|\(\[[^][]*(?P<quote>['"])can:(?P<string>.*?)\1[^][]*]\))

The pattern in parts, using 2 alterations | for 3 different variations:

  • (?| Branch reset group
    • (?:@can|hasPermissionTo|hasDirectPermission) Match 1 of the alternatives
    • \(\s* Match ( and optional whitespace chars
    • (?P<quote>['"]) Match either ' or " in group quote
    • (?P<string>.*?)\1 Group string Match as least as possible chars till the same quote that was captured in group quote
    • \) Match )
    • | Or
    • \( Match (
    • (?P<quote>['"]) - Same as before
    • can: Match literally (Or use an alternation again for multiple words)
    • (?P<string>.*?)\1 - Same as before
    • \) Match )
    • | Or
    • \(\[ Match ([
    • [^][]* Match any char except [ and ]
    • (?P<quote>['"]) Same as before
    • can: Match literally
    • (?P<string>.*?)\1 Same as before
    • [^][]*]\) Match any char except [ ] using a negated characer class, then match ])
  • ) Close branch reset group

See a regex demo.

$re = '/(?|(?:@can|hasPermissionTo|hasDirectPermission)\(\s*(?P<quote>[\'"])(?P<string>.*?)\1\)|\((?P<quote>[\'"])can:(?P<string>.*?)\1\)|\(\[[^][]*(?P<quote>[\'"])can:(?P<string>.*?)\1[^][]*]\))/';
$str = <<<'STR'
@can('event-tools::menu.view')
$this->middleware('can:access registration check');
Route::prefix('administration')->middleware(['auth', 'verified', 'can:access admin area'])->group(static function () {
STR;

$result = preg_match_all($re, $str, $matches);
print_r($matches["string"]);

Output

Array
(
    [0] => event-tools::menu.view
    [1] => access registration check
    [2] => access admin area
)