Как вернуть логическое значение, если строка содержит одну из массива подстрок?

Я борюсь с простой функцией, которая перебирает массив и возвращает true, только если находит заданную подстроку в одном из элементов массива.

По какой-то причине я ВСЕГДА получаю false... даже если параметр $email содержит один из допустимых доменов. Пример: [email protected].

function check_email($email) {
    $whitelist_domains = array(
        '@domain1.com',
        '@domain2.com',
        '@domain3.com'
    );
    $output = FALSE;
    foreach ($whitelist_domains as $domain) {   
        $pos = strpos( $email, $domain ); 
        if ( $pos ) {
            $output = TRUE;
        }
    }
    return $output;
}

person emersonthis    schedule 15.03.2012    source источник
comment
strpos может вернуть 0, что == false; вместо этого используйте if($pos !== false)   -  person miki    schedule 16.03.2012
comment
Вам нужно выполнить строгое сравнение $pos, потому что, если он найдет его в начале строки, ваш тест завершится ошибкой, потому что $pos = 0. Вместо этого сделайте if($pos === false)   -  person jprofitt    schedule 16.03.2012


Ответы (6)


вы не прерываете цикл, если найдете домен, поэтому то, что вы получаете, на самом деле является результатом только проверенной ПОСЛЕДНЕЙ строки. просто добавьте break; после $output = TRUE;

person Not_a_Golfer    schedule 15.03.2012
comment
Кстати, есть более эффективные способы сделать это, проверьте preg_match. вы можете сделать это без цикла. - person Not_a_Golfer; 16.03.2012
comment
Бум! Вот оно. Когда я впервые написал функцию, я использовал return TRUE внутри цикла (я думаю, что return выходит из цикла, верно?). Я собирался использовать preg_match, но от синтаксиса регулярных выражений у меня всегда немного болит голова. Для короткого массива это сильно влияет на производительность? - person emersonthis; 16.03.2012
comment
Я обычно не увлекаюсь такими вещами, но я провел небольшое исследование, основанное на вашем предложении (используйте preg_match), и я был удивлен, обнаружив эту статью... lzone.de/articles/php-string-search.htm ...утверждая, что strpos() на самом деле быстрее, чем preg_match() . - person emersonthis; 16.03.2012
comment
Это зависит от варианта использования. strpos быстрее, но циклы PHP медленнее. Напишите сравнительный тест и опубликуйте результаты, если вы это сделаете! - person Not_a_Golfer; 16.03.2012
comment
Мне интересна ваша идея, но я не понимаю, как избежать петли. Вы предполагаете, что есть способ preg_match всего массива? - person emersonthis; 16.03.2012
comment
если количество доменов разумно, вы можете сделать что-то вроде: preg_match("/(domain1\\.com|domain2\\.com|...)$/", $email);. но на самом деле самым быстрым подходом было бы сохранить словарь, такой как array('domain1.com' => true, 'domain2.com' => true, ...);, который имеет хэш-таблицу O (1), а затем просто отделить домен электронной почты и проверить if ($domains[$email_domain] == true) { ... }. Это должно быть СУПЕР быстро по сравнению с чем-либо еще. - person Not_a_Golfer; 17.03.2012
comment
Ах. Сейчас мы говорим. Спасибо! - person emersonthis; 18.03.2012

Из официального документа strpos:

Предупреждение

Эта функция может возвращать логическое значение FALSE, но также может возвращать нелогическое значение, которое оценивается как FALSE. Пожалуйста, прочтите раздел о логических значениях для получения дополнительной информации. Используйте оператор === для проверки возвращаемого значения этой функции.

И не забудьте добавить разрыв после того, как вы установили $output в true.

person anubhava    schedule 15.03.2012

Это хорошая функция для использования оператора ===, поскольку она гарантирует, что значение и тип равны (1==true, но 1!==true).

if (strpos( $email, $domain )!==false) {
    $output = TRUE;
}
person Tim Withers    schedule 15.03.2012
comment
если strpos возвращает 7, будет ли (strpos === true) истинным? - person dqhendricks; 16.03.2012

Изменять

if ( $pos ) {

to

if ( $pos !== false) {

это связано с тем, что is strpos возвращает 0, что будет равнозначно false, даже если строка была найдена.

person dqhendricks    schedule 15.03.2012

Вот два прямых/распространенных метода, которые имеют разные преимущества:

Способ 1: подход без регулярных выражений

function check_email1($email){
    $whitelist_domains=['@domain1.com','@domain2.com','@domain3.com'];
    foreach($whitelist_domains as $domain){
        if(strpos($email,$domain)!==false){
            return true;  // allow quick return (exit loop & function asap)
        }
    }
    return false;  // default response
}

Способ 2: регулярное выражение

function check_email2($email){
    $whitelist_pattern='/@(?:domain1\.com|domain2\.com|domain3\.com)$/';  // condense if possible /@domain[123]\.com$/
    return (bool)preg_match($whitelist_pattern,$email);  // convert 0 or 1 output to boolean (false/true)
}

Ссылка на демонстрацию

Ввод/вызов функции:

$emails=['[email protected]','[email protected]'];
foreach($emails as $email){
    echo "$email\n";
    var_export(check_email1($email));
    echo "\n";
    var_export(check_email2($email));
    echo "\n\n";
}

Вывод:

[email protected]
true
true

[email protected]
false
false

Преимущества/недостатки:

  • strpos() в большинстве случаев превосходит функции регулярных выражений. Ваш метод по умолчанию должен заключаться в использовании строковых функций и переходе на регулярное выражение только тогда, когда строковые функции менее эффективны или слишком запутаны для кода. Связанная страница: Что более эффективно, PHP строковые функции или регулярное выражение в PHP?

  • Зацикливание $whitelist_domains в № 1 делает блок кода более неуклюжим по сравнению с № 2 (который можно сжать до однострочного, если вы запишете шаблон непосредственно в preg_match()).

  • Простые/распространенные ошибки иногда случаются при работе с strpos(). Эти ошибки могут включать:

    • not checking for false in the if condition
    • написание haystack и needle в неправильном порядке
  • # 2 требует некоторых знаний о регулярных выражениях (экранирование, классы символов, альтернативы и т. д.), что может быть сдерживающим фактором для неопытных кодеров. В зависимости от того, как вы напишете свой шаблон регулярного выражения и сколько доменов будет занесено в белый список, № 2, вероятно, будет сложнее поддерживать, чем № 1.

  • #2 имеет дополнительное преимущество, заключающееся в возможности проверки появления подстроки domain.com в конце слова с помощью метасимвола $. По этой причине регулярное выражение предлагает более сильную проверку.

person mickmackusa    schedule 03.09.2017

Вы должны изменить свой код в этом:

function check_email($email) {
    $whitelist_domains = array(
        '@domain1.com',
        '@domain2.com',
        '@domain3.com'
    );

    foreach ($whitelist_domains as $domain) {   
        if ( strpos( $email, $domain ) !== false ) {
            return true;
        }
    }
    return false;
}

Документация strpos

Цитата из руководства (http://php.net/manual/en/function.strpos.php):

Также можно использовать оператор !==. Использование != не будет работать должным образом, поскольку позиция 'a' равна 0. Оператор (0 != false) оценивается как false.

Пример кода

<?php
$mystring = 'abc';
$findme   = 'a';
$pos = strpos($mystring, $findme);

// The !== operator can also be used.  Using != would not work as expected
// because the position of 'a' is 0. The statement (0 != false) evaluates 
// to false.
if ($pos !== false) {
     echo "The string '$findme' was found in the string '$mystring'";
         echo " and exists at position $pos";
} else {
     echo "The string '$findme' was not found in the string '$mystring'";
}
?>
person Domenico Ruggiano    schedule 03.09.2017