Home > Back-end >  Regex conditional statement or negation
Regex conditional statement or negation

Time:06-30

I'm trying to detect mobile with the user agent string. According MDN there is always the pattern "mobile" on mobiles user agent strings and not on PC, tablet or whatever; but with one exception the iPad's user agent string :(

Basicly I need this for (in)activate a JS function :

examples of user agent strings:
$str = 'Mozilla/5.0 (Linux; Android 10; Redmi Note 9S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36';
$str = 'Mozilla/5.0 (iPad; CPU OS 15_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/95.0.4638.50 Mobile/15E148 Safari/604.1';
$str = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0';

basic working code in php:
if(preg_match('#Mobile#i', $str)){  
   if(preg_match('#iPad#i', $str)){    $mobile = false; // (ipad)  
   }   else    {   $mobile = true; //(mobile);
       }
} else  {   $mobile = false;    } //others

But:

  1. this is inelegant :D
  2. I want to improve my knowledge in Regexp, which at the moment I'm using in a simple way without the lookaround or conditionnal stuff.

So since yesterday I try to learn about lookaround and conditionnal, read a lot of tutorials, try a bunch of patterns but I still not find a solution or really understand the lookahead ou lookbehind patterns, neither forcing the match attempt to Fail with (*PRUNE)(*F)

$reg = '/(?(?<=iPad)|Mobile)/i';
$reg = '/(?(?<=Mobile)|(iPad))/i';
$reg='^(?(?=.*Mobile)(?:iPad|)).*$';
$reg='(?(?=Mobile)(?(?=iPad)(*F)|Mobile)|(*F))';
$reg='(?(?=Mobile)(?(?!iPad)(Mobile)|(*F))|(*F))'; //and variations lookbehind /lookahead (positive or negative) 

As there is not conditional patterns within the JS regex engine , all I need is a boolean: false for "mobile" without the ipad, or true for the rest. Why $reg='(?(?=Mobile)(?(?=iPad)(*F)|Mobile)|(*F))'; return 'mobile' and not fail ? Is there an elegant solution ? Please help me ... I'm going nuts :D

Cheers.

CodePudding user response:

The first thing I see is the variable name from hell:
$notMobile = true that hurts the brain => edit your code in a way to have:
$mobile = false.

What is elegant? As suggested by user3783243, that:

$mobile = stripos($ua, 'mobile') && !stripos($ua, 'ipad');

(Substrings you are looking for are never at the start of the string, you don't have to care with cases where strpos returns 0).

To do it with a single regex:

$mobile = (bool) preg_match('~^(?=.*mobile)(?!.*ipad)~i', $ua);

Two lookahead assertions tested from the position 0 of the string. You don't need to use conditional statements at all (most of the time, this feature is totally useless and can be replaced with something more simple).

Or just with the negative lookahead:

$mobile = (bool) preg_match('~^(?!.*ipad).*mobile~i', $ua);

Obviously you can do something similar with Javascript. (If you choose the regex version, use RegExp.prototype.test() method)

  • Related