You can match nested functions, example:
$pattern = '~(@(?<func>\w++)\((?<param>[^)]*+)\)(?<content>(?>[^@]++|(?-4))*)@end)~';
or without named captures:
$pattern = '~(@(\w++)\(([^)]*+)\)((?>[^@]++|(?-4))*)@end)~';
Note that you can have all the content of all nested functions, if you put the whole pattern in a lookahead (?=...)
pattern details:
~ # pattern delimiter
( # open the first capturing group
@(\w++) # function name in the second capturing group
\( # literal (
([^)]*+) # param in the third capturing group
\) # literal )
( # open the fourth capturing group
(?> # open an atomic group
[^@]++ # all characters but @ one or more times
| # OR
(?-4) # the first capturing group (the fourth on the left, from the current position)
)* # close the atomic group, repeat zero or more times
) # close the fourth capturing group
@end
)~ # close the first capturing group, end delimiter