9

I want to check if an if statement is on one line or the next line without a brace like so:

if (blah === blah)
    do something

or:

if (foo === foo) do something

The regex i have currently is

/\)(?!.*\{)/

But doesnt work. Anyone have any ideas?

To elaborate the only If statement that would not be pulled by this regex is the following:

if (foo === bar)
{
Cjmarkham
  • 9,484
  • 5
  • 48
  • 81
  • 1
    Both `\r` and `\n` have their uses. – hjpotter92 Aug 24 '12 at 14:55
  • [How to replace different newline styles in PHP the smartest way?](http://stackoverflow.com/questions/7836632/how-to-replace-different-newline-styles-in-php-the-smartest-way) and [PCRE and newlines](http://nikic.github.com/2011/12/10/PCRE-and-newlines.html) - You might be looking for some modifier so that `.` matches newlines as well: [`PCRE_DOTALL` (s)](http://php.net/manual/reference.pcre.pattern.modifiers.php). – hakre Aug 24 '12 at 14:57
  • Ive tried the following but it doesnt seem to work: /(if[()\w=]+)^(\r|\n|\r\n)/ – Cjmarkham Aug 24 '12 at 15:06
  • @CarlMarkham: Just use the `s` modifier, see: http://stackoverflow.com/a/12111990/367456 – hakre Aug 24 '12 at 15:11
  • I used the "s" modifier aswell but it was getting normal If statements too as in "ifs" with a newline then the curly brace – Cjmarkham Aug 24 '12 at 15:13
  • @CarlMarkham: Sure, PCRE is not a lexer. You might want to use the PHP tokenizer instead, it will work like PHP: http://php.net/manual/en/function.token-get-all.php - with some little tricks you could even combine that with regular expressions. But reading your comment makes clear that you can not use `preg_match` here to cover your bases. The problem you face is too complex so that you could create a simple regex to match your needs. You *might* want to normalize whitespace first. – hakre Aug 24 '12 at 15:27

5 Answers5

8

simple \r \n (carriage return and new line)

/[\r\n]/
Mihai Iorga
  • 39,330
  • 16
  • 106
  • 107
8

New lines may be \n (UNIX), \r\n (Windows), \r (for good measure). The expression to broadly match new lines is:

/\r\n|\r|\n/

To check if an expression is on one line, you can do this:

if( preg_match( '/\r\n|\r|\n/', $string ) ) {
    // on more than one line
    ...
} else {
   // on one line
   ...
}

To further develop this to apply to your specific example of an if ... do statement, you could do this:

if( preg_match( '/if *\([^\)]+\) *(\r\n|\r|\n) *do /', $string ) ) {
    // on more than one line
    ...
} elseif( preg_match( '/if *\([^\)]+\) *do /', $string ) ) {
   // on one line
   ...
}
kingjeffrey
  • 14,894
  • 6
  • 42
  • 47
  • You could learn a bit about [atomic grouping](http://www.regular-expressions.info/atomic.html) to get some more understanding which part is potentially problematic with your suggested pattern. – hakre Aug 24 '12 at 15:10
  • 1
    @hakra, if there is an error in my logic, perhaps you could state precisely what it is. What i entered should work. – kingjeffrey Aug 24 '12 at 15:14
  • The characters and sequences you use - all on their own - are fine. The problem is when you have a combination of those. Hence the atomic grouping, it will take care that backtracking won't bite you. For the single newline this probably is not that much of an issue but if there is more than one line, it starts to become more problematic. – hakre Aug 24 '12 at 15:25
  • 1
    If I were trying to match an entire string, that could be true. But since my logic only looks for any single newline fragment (among the potential for many), your concern is not applicative. – kingjeffrey Aug 24 '12 at 15:27
  • Yes PCRE will consume the first matching subgroup pattern alternative and you've picked the largest first. However, backtracking still applies IIRC while it is not necessary - so even for the single match it can have some downside. Please see that I do not write that you pattern is wrong. – hakre Aug 24 '12 at 15:53
3

You need to make . match newlines, too:

/\)(?!.*\{)/s
            ^
    PCRE_DOTALL Modifier

This is done with the s (PCRE_DOTALL ) modifier (as written in comments):

s (PCRE_DOTALL)

If this modifier is set, a dot metacharacter in the pattern matches all characters, including newlines. Without it, newlines are excluded. This modifier is equivalent to Perl's /s modifier. A negative class such as [^a] always matches a newline character, independent of the setting of this modifier.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
0

As you want to check this specifically for an if, use this:

(if[ ()\w=]+)\r\n

returns true if the if has a newline and false if not.

sQVe
  • 1,977
  • 12
  • 16
0

You are looking for if statements that don't use curly braces, but your pattern requires curly braces.

Here is my suggestion: (Demo)

$strings = [
'if (blah === blah)
    do something',
'if (foo === foo) do something',
'if (bah === bah) {
    do something
}',
'if (bar === bar) {do something}'
];

foreach ($strings as $string) {
    var_export(preg_match('~if\s*\(.*?\)\s*(\{)?~', $string, $m) ? $m : '');
    echo "\nHas curly brace: " , isset($m[1]) ? 'Yes' : 'No';
    echo "\n---\n";
}

Output:

array (
  0 => 'if (blah === blah)
    ',
)
Has curly brace: No
---
array (
  0 => 'if (foo === foo) ',
)
Has curly brace: No
---
array (
  0 => 'if (bah === bah) {',
  1 => '{',
)
Has curly brace: Yes
---
array (
  0 => 'if (bar === bar) {',
  1 => '{',
)
Has curly brace: Yes
---

Basically, use \s* to signify where no space/newlines, a space/newline, multiple spaces/newlines may occur in the markup.

My pattern will not catch if statements with multi-line expressions. To accommodate those fringe cases, add the s pattern modifier to allow the . to match newlines.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136