php перебор массива, и сравнение с частью переменной через regexp

Подскажите пожалуйста как перебрать массив и сравнить его значения с частью переменной?
Я сам не программист, сильно не пинайте, комплектую код из разных кусочков)

Этот код судя по всему не работает)

$cli = $result. " " .$client.  " " .$userAgent. " " .$rf;
$file = file("ip.txt");
foreach ($file as $value) {

if (preg_match("/($value)/", $cli)) {
    {$nume=3;}
}
  1. Сначала беру данные о посетителе. Ip, юзерагент, провайдер.
    Но этой части кода тут нет.

  2. Эти данные объединяю в

$cli = $result. " " .$client. " " .$userAgent. " " .$rf; 
  1. В файле ip.txt - есть список ip и провайдеров которые я хочу найти у посетителя. Беру в виде массива
$file = file("ip.txt"); 
  1. Далее массив перебираю в переменную $value:
foreach ($file as $value){ /** ... */ }
  1. А потом пытаюсь сравнить через regex, есть ли в данных пользователя $cli хотя бы одно значение из массива $file
if (preg_match("/($value)/", $cli)){ /** ... */ }
  1. Если значение есть то
$nume=3;

Ответы (1 шт):

Автор решения: Михаил Ребров

Далее я буду комментировать весь код построчно сверху вниз:

Неразрывный пробел

  - это спецсимвол, который называется "Неразрывный пробел"
Как следует из названия, данный символ запрещает разделять "слова", соединённые данным символом и используется он в HTML, для того чтобы устойчивые сочетания слов нельзя было разделить на несколько строк.

Использовать данный символ в других случаях не рекомендуется.
Данный символ не является заменой обычному пробелу (" ").
Более того, иногда могут возникать путаница и проблемы с поиском, заменой и разделением строк.

Поэтому если Вы, не намеревались добиться неразрывной строки в HTML,   в данном случае лучше заменить на обычный пробел:

$cli = $result. " " .$client.  " " .$userAgent. " " .$rf;

Это выглядит, читается и распознаётся лучше, чем оригинальный вариант.
Также можно избежать конкатенацию(соединение строк), просто поместив переменные внутрь строки(между кавычками)

$cli = "{$result} {$client} {$userAgent} {$rf}";

или

$cli = "$result $client $userAgent $rf";

Однако, стоит помнить, что замена переменных значениями внутри строк работает только с двойными кавычками. С одинарными замена работать не будет.

Относительный путь

$file = file("ip.txt");

Стоит отдавать себе отчёт в том, что данный код будет работать только тогда, когда файл ip.txt находится в одной папке с запущенным скриптом, потому что Вы передали относительный путь.

Во избежание ошибок, желательно передавать полный путь к файлу, начиная с корня файловой системы.Например:

// для Linux
$file = file("/var/www/test/ip.txt");
// для Windows
$file = file("C:\Users\Michael\www\test\ip.txt");

Иногда на хостинге Вы не имеете доступа ко всей файловой системе и даже не знаете точно в какой именно папке будет расположен скрипт, но Вы знаете относительное положение скрипта и требуемых ресурсов.

В этом случае может помочь:

  • константа __DIR__ , которая хранит в себе полный путь к директории, в которой находится запущенный скрипт.
  • символические ссылки на текущую . и родительскую .. директории
  • функция realpath(), которая, возвращает абсолютный канонизированный путь к файлу

Например:

$filename = realpath(__DIR__."/../../config/ip_list.txt");

Фигурные скобки

Рассмотрим код

if (/**condition */) {
    {$nume=3;}
}

Данные вложенные фигурные скобки ({$nume=3;}) не несут никакой пользы и не имеют никакого смысла.
Данный участок лучше переписать так:

if (/**condition */) {
    $nume = 3;
}

Проверка наличия подстроки в строке

Я иногда и сам грешу этим, тем не менее я не рекомендую использовать функцию preg_match() для проверки наличия определенной подстроки в строке.
preg_match() отвечает за то, соответствует ли строка регулярному выражению.
И это не совсем одно и тоже.

  • Во-первых: вы немного усложняете код составлением регулярного выражения.
  • Во-вторых: вы можете получить результаты, которые будут отличаться от ваших ожиданий из-за попадания символов, используемых в регулярных выражениях (например: точка может обозначать любой символ и т.д.)
  • В-третьих: проверка регулярного выражения будет требовать больших ресурсов, что может дать знать о себе на большом объеме.

Вместо этого для проверки наличия подстроки в строке обычно используют функцию strpos() или её аналог для мультибайтовых строк mb_strpos()

Данные функции возвращают позицию первого вхождения подстроки в строке или false при её отсутствии.

Например:

echo strpos("abcdefg", "d");
// output: 3

// обратите внимание на то, что отчёт начинается с нуля
// если мы будем искать букву "a", функция вернёт 0 и это будет ее верной позицией в строке
echo strpos("abcdefg", "a");
// output: 0

// пытаемся найти подстроку, которой нет 
echo strpos("abcdefg", "z");
// output: false

При отсутствии же функция вернёт false и таким образом можно будет проверять наличие подстроки в строке.

if (strpos($mystring, $findme) === false) {
    echo "Строка '$findme' не найдена в строке '$mystring'";
} else {
    echo "Строка '$findme' найдена в строке '$mystring'";
    echo " в позиции $pos";
}

Обратите внимание на то, что в данном случае для сравнения используется не ==, а ===.
=== - это строгое сравнение, которое сравнивает не только значение но и тип переменной.
php это динамично-типизируемый язык программирования и переменные могут автоматически приводиться от одного типа к другому.
И в частности: false может автоматически приводиться к 0 и наоборот.

Для того чтобы исключить путаницу и возможную ошибку лучше использовать строгое сравнение.

if ( 0 == false ){
    echo "0 равен false";
} else {
    echo "0 НЕ равен false";
}
// output: 0 равен false

if ( 0 === false ){
    echo "0 равен false";
} else {
    echo "0 НЕ равен false";
}
// output: 0 НЕ равен false

Соответственно, при проверке наличия подстроки "a" в строке "abcdefg", может возникнуть проблема, потому что strpos("abcdefg","a") вернёт 0, который может автоматически привестись к false.
Поэтому в данном случае важно использовать строгое сравнение с учетом типов переменных.

Также есть оператор сравнения !== (строго не равно).
И именно его стоит использовать для проверки наличия подстроки в строке следующим образом.

if(strpos("abcdefg","a") !== false){
    echo "Подстрока 'а' есть в строке 'abcdefg'";
}
// output: Подстрока 'а' есть в строке 'abcdefg'

Таким образом вашу проверку можно написать следующим образом:

if (strpos($cli, $value) !== false) {
    // ...
}

Замечание:
Не прошло и ста лет, как в языке php восьмой версии появилась функция str_contains(), которая проверяет наличия подстроки в строке, однако, я не рекомендую пока её использовать, потому что php версии 8, вы еще не скоро встретите на серверах хостингов и пока что эта функция имеется чуть более чем нигде
str_contains: https://www.php.net/manual/ru/function.str-contains.php

Результат работы функции file()

Скорее всего именно данный пункт был причиной ошибочной работы вашего скрипта.
Дело в том, что функция file() возвращает вам содержимое файла в виде массива строк.
Подвох состоит в том, что в конце каждой строки есть символ перехода строки(\n).
А это значит, что символ перехода строки также присутствует в условии поиска.
Т.е. по сути:
Вы думаете что ищите строку 192.168.1.1
Но на самом деле вы ищите строку 192.168.1.1\n
которой вы закономерно не находите.

Более того регулярные выражения по умолчанию выполняют поиск в строке, а не во всем тексте и для поиска соответствия регулярному выражению в многострочном тексте нужно указывать специальные ключи. Что в очередной раз говорит о том, что лучше данную функцию(preg_match) использовать только по прямому назначению.

Соответственно, перед тем как выполнять поиск Вам желательно очистить строку от символа конца строки. Сделать это можно с помощью функции trim(), передав в нее символ конца строки в качестве дополнительного параметра.

Символ конца строки можно указать как "\n" или с помощью константы PHP_EOL (EOL = End Of Line)

foreach ($file as $value) {
   $line = trim($value, PHP_EOL);
   // ...
}

В итоге:

$cli = "$result $client $userAgent $rf";
// при условии что ip.txt находится в той же папке
// в противном случае используйте ссылки на родительские директории 
// для позиционирования файла ip.txt относительно запущенного скрипта
$file = file(__DIR__."/ip.txt"); 
foreach ($file as $value) {
    $line = trim($value, PHP_EOL);
    if (strpos($cli, $line) !== false) {
        $nume = 3;
    }
    // ...
}

Проверка:

Я создал файл ip.txt со следующим содержимым:

192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.5
192.168.1.6
192.168.1.7
192.168.1.8
192.168.1.9

И выполнил данный скрипт для проверки:

$result = "192.168.1.3";
$client = "some client code";
$userAgent = "user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36";
$rf = "https://ru.stackoverflow.com";
$cli = "$result $client $userAgent $rf";
$file = file(__DIR__."/ip.txt");
echo $cli."\n";
foreach ($file as $value) {
    $line = trim($value, PHP_EOL);
    if (strpos($cli, $line) !== false) {
        echo "Данный клиент ($line)  имеется в списке\n";
    }
}

Вывод:

192.168.1.3 some client code Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36 https://ru.stackoverflow.com
Данный клиент (192.168.1.3)  имеется в списке

Что значит, что данный код работает и он находит IP адрес из списка в файле.

→ Ссылка