Пишу, по большей части, про историю, свою жизнь и немного про программирование.

PHP, UTF-8, всё ещё строки: упрощаем задачу, часть I

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

Выделив типичные случаи использования, частые и простые можно заменить автоматом, редкие и сложные — просто руками. Первым делом было бы неплохо узнать каково общее количество строк, с которыми мне придётся работать:

bolk@dev:~/daproject$ find \( -name '*.php' -or -name '*.inc' -or -name '*.tpl' \) -exec \
egrep '\$\{?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*[\[\{]' -- {} \; |
sort -u | tee out-vars-1.log | wc -l

3670

Около 3,5 тысяч. Я временами дьявольски усидчив, но рассматривать каждую строку из нескольких тысяч, это для меня перебор. Что же делать?

Для начала мне хочется упростить своё регулярное выражение. Конечно, по синтаксису PHP имена переменных можно писать, например, по-русски (в UTF-8), но в реальности, у меня имён, которые содержат не только цифры и латинские буквы, только шестнадцать (в этом я убедился, сделав «egrep -v ’\$\{?\w+’ out-vars.log | wc -l») и все эти строки содержат ерунду — совпадение внутри строк в апострофах, поэтому я с чистым сердцем выбрасываю все такие строки и упрощаю себе жизнь.

Безусловно, моё регулярное выражение захватило очень много лишнего, надо эти ситуации обработать. Например, можно исключить добавление элемента в массив, это легко узнаваемая конструкция ($arr[] = $elm):

bolk@wiki:~/daproject$ IFS='\n'
for line in $(cat out-vars-1.log); do
(echo $line | sed -r 's/\$\w+\[\]//g' | egrep -q '\$\{?\w+[\[\{]') && echo $line
done | sed -r 's/^\s+//' | awk '!t[$0]++' | tee out-vars-2.log | wc -l

3357

Ситуация мало изменилась, давайте попробуем улучшить её значительно. Например, удалить все строки, где происходит только явное обращение к массиву: $arr[’index’] (но не $arr[’index’][0]):

bolk@dev:~/daproject$ IFS='\n'
for line in $(cat out-vars-2.log); do
(echo $line | sed -r 's/\$\w+[\[\{](["'\''])[^\1]*\1(\]|\})([^\[\{])/\2/g' | egrep -q '\$\{?\w+[\[\{]') && echo $line
done | tee out-vars-3.log | wc -l

1353

Ощутимая разница. Открываем получившийся файл и смотрим что бы мы ещё могли выкинуть…

Продолжение следует, я сегодня рано встал, пойду домой.