Передача аргументів у програму. Параметри функції main (argc, argv)

Htaccess – це додатковий конфігураційний файл Apache, який дозволяє настроювати роботу веб-сервера для кожної окремої директорії, не впливаючи на глобальні налаштування Apache. Локальна аналогія httpd.conf. Зазвичай він відповідає за редиректи та управління доступом до директорій.

Назва починається з точки. Можна сказати, що це файл без назви з розширенням htaccess.

Налаштування.htaccess діють на каталог, в якому він розташований, та на всі дочірні каталоги. Створіть файл і помістіть у потрібну вам директорію. Наприклад, у корінь проекту.

Тепер треба його наповнити. Подивимося, що взагалі умеет.htaccess, але спочатку вивчимо приклад найпростішого редиректа.

mod_rewrite та редиректи

Переконайтеся, що у конфігураційному файлі Apache httpd.confактивовано mod_rewrite. Тобто, розкоментований відповідний рядок:

LoadModule rewrite_module modules/mod_rewrite.so

Або, якщо не хочете відкривати в текстовому редакторіфайл, можна скористатися командою в терміналі:

Sudo a2enmod rewrite

mod_rewrite - це модуль Apache, призначений для перетворення URL. Розглянемо з прикладу, як і працює. Допустимо, користувач вводить наступну адресу:

За допомогою mod_rewrite можна відправити вміст з іншої URL, наприклад:

http://www.example.com/public/src/view/page.html

Навіщо нам це? Легко здогадатися, що писати повний шляхдо сторінки довго і просто незручно. Відвідувачам сайту не потрібно думати про внутрішньої структурисайту – їм важливо максимально швидко потрапити на потрібну сторінку.

У адресному рядкукористувач буде також бачити введене ним:

http://www.example.com/page.html

Це приклад найпростішого редиректу.

Відразу до практики

Розберемо конфігураційний файл, який використовується в одному з наших проектів. Так ми розумітимемо, який рядок правити у разі виникнення проблем.

Php_value short_open_tag 1 php_value upload_max_filesize 10M php_value post_max_size 10M RewriteEngine On RewriteBase / RewriteRule ^(application|modules|system) - RewriteCond ) !-d RewriteRule .* index.php/$0

  • php_valueвстановлення рядкових та числових значень
  • php_flagвстановлює логічні значення (так/ні)

Загальний синтаксис директив

Php_value/php_flag ім'я_директиви_php flag/value

Директива short_open_tagдозволяє використання короткого синтаксису для оформлення PHP-коду:

Php_value short_open_tag 1

upload_max_filesizeвизначає максимальний розмірфайлу, що завантажується.

Php_value upload_max_filesize 10M

А post_max_sizeвстановлює максимально допустимий розмір даних, що надсилаються методом POST.

Php_value post_max_size 10M

RewriteEngine

Включає/вимикає механізм mod_rewrite.

RewriteEngine On

RewriteRule

RewriteRule просто перетворює рядок відповідно до регулярних виразів.

Синтаксис: RewriteRule regular_expression

# На вході RewriteRule "index.php" RewriteRule ^index.php main.php [R] # На виході: "index.php" -> "main.php"

Ми перетворили index.php на main.php і виконали редирект.

Важливо: RewriteRule зазвичай приймає два аргументи: щопотрібно замінити та на щопотрібно замінити. Якщо нам не потрібно виконувати заміну, то можна записати у вигляді:

Символ «-» означає «не перетворювати»

RewriteBase

Після всіх RewriteRule, набуває чинності RewriteBase. Якщо запит, що вийшов після перетворення, є відносним і відрізняється від вихідного, RewriteBase відновить його, зробивши абсолютним. RewriteBase просто допише себе до запиту зліва. Тому що значення RewriteBase – шлях від кореня сайту до. htaccess. У нашому випадку. htaccess лежить прямо в корені, тому:

Синтаксис: RewriteBase URL-path-from-.htaccess-file-to-site-root

Наприклад:

# .htaccess знаходиться в /dir/ # Шлях від кореня сайту до.htaccess /dir/ RewriteBase /dir/ # Запит http://example.com/dir/logo.gif # На вхід RewriteRule потрапляє "logo.gif" RewriteRule ^ logo.gif$ logo-orange.gif # Після RewriteRule: "logo.gif" -> "logo-orange.gif" # Після RewriteBase: "logo-orange.gif" -> "/dir/logo-orange.gif"

Regular expressions

Регулярні вирази, які можуть зустрітися в.htaccess.

Символ Значення приклад
. Один будь-якийсимвол c.t це cat, cot, cut, і т.д.
+ Один чи кілька однаковихсимволів a+ це a, aa, aaa, і т.д.
* Нульабо кілька однаковихсимволів a* працює як і a+ але у разі a* умові задовольнить і порожня стрічка
? Збіг опціонально colou?r підійде як color, так і colour.
^ Символ, з якого починаєтьсярядок ^a відповідає рядок, який починається з a
$ Символ, яким закінчуєтьсярядок a$ відповідає рядок, який закінчується a .
() Знаходить та запам'ятовує відповідність груписимволів.

Також може бути використане для Back-Reference(дивись приклад)

(ab)+ задовольнить ababab

Back-Reference example:

RewriteRule ^/ (+) /(.*) $/home?page= $1 &id= $2

/album/123 → /home?page= album&id= 123

Один зможливих символів ct підійде cut, cot або cat.

Більше regular expressions

Прапори

Синтаксис: RewriteRule regular_expression [прапор1,прапор2,прапор3]

Прапор Опис
[F] Forbidden- Повертає помилку 403 Forbidden (заборонено).
[L] Last- Зупинити процес перетворення на цьому місці і не застосовувати більше жодних правил перетворень.
Query String Append- цей прапор вказує механізму перетворень на додавання, а не заміну, рядки запиту з URL до існуючої, у рядку підстановки.
PassThrough- зупиняє процес перетворення та передає отриману нове посиланнядалі по ланцюжку.
[R] Redirect- зупиняє процес перетворення та повертає результат браузеру клієнта як редирект на нову сторінку.
[S] Skip- пропускає наступне правилоякщо поточне правило спрацювало. Можна вказати кількість наступних правил, що ігноруються.

Буває, що дані до програми передаються з командного рядка під час її виклику. Такі дані називаються аргументами командного рядка. Виглядає це так, наприклад:

./a.out test.txt ls -lt /home/peter/

Тут викликаються програми a.out (з поточного каталогу) та ls (з одного каталогу, зазначеного у змінній оточенні PATH). Перша програма з командного рядка отримує одне слово - test.txt, друга - два: -lt та /home/peter/.

Якщо програма написана мовою C, то при її запуску керування відразу передається у функцію main() , отже, саме вона отримує аргументи командного рядка, які надаються її змінним параметрам.

До цього ми визначали функцію main() так, ніби вона не приймає жодних параметрів і нічого не повертає. Насправді в мові C будь-яка функція за умовчанням (якщо не визначено нічого іншого) повертає ціле число. У цьому можна переконатись. Якщо записати код таким чином:

main() ( printf ("Hi \n ");

return 0;

)

  1. То жодного попередження чи помилки при компіляції не виникне. Те саме буде, якщо записати int main() . Це доводить, що функція за замовчуванням повертає ціле число, а чи не ніщо (void). Хоча те, що функція повертає завжди можна "перевизначити", наприклад, voidmain() або float main() .При виклику програми з командного рядка до неї завжди передається пара даних:
  2. ціле число, що позначає кількість слів (елементів, розділених пробілами) у командному рядку під час виклику, покажчик на масив рядків, де кожен рядок - це

окреме слово

з командного рядка.

Перший аргумент програми має значення 4, а масив рядків визначається як ("./a.out", "12", "theme", "2").

Зверніть увагу на термінологію, є всього два аргументи програми (число та масив), але скільки завгодно аргументів командного рядка. Аргументи командного рядка "перетворюються" на аргументи програми (в аргументи функції main()).
Ці дані (число і покажчик) передаються в програму навіть тоді, коли вона просто викликається на ім'я без передачі в неї чого-небудь: ./a.out. У такому разі перший аргумент має значення 1, а другий вказує на масив, що складається всього з одного рядка ("/a.out").

Те, що в програму передаються дані, зовсім не означає, що функція main() повинна приймати їх. Якщо функція main() визначена без параметрів, отримати доступ до аргументів командного рядка неможливо. Хоча нічого вам не заважає їх передавати. Помилки не виникне.

Щоб отримати доступ до переданих у програму даних, їх необхідно присвоїти змінним. Оскільки аргументи відразу передаються в main() , її заголовок повинен виглядати так:
main (int n, char *arr)

У першій змінній (n) міститься кількість слів, а в другій – покажчик на масив рядків. Часто другий параметр записують як arr . Однак це те саме. Згадаймо, що сам масив рядків містить в якості своїх елементів покажчики на рядки. А на функцію ми передаємо покажчик на перший елемент масиву. Виходить, передаємо покажчик на покажчик, тобто. **arr.

Завдання
Напишіть таку програму:

#include int main(int argc, char ** argv) ( int i; printf ("%d \n ", argc);< argc; i++ ) puts (argv[ i] ) ; }

for (i = 0; i Вона виводить кількість слів у командному рядку під час її виклику та кожне слово знового рядка

. Викличте її без аргументів командного рядка та з аргументами.

У програмі ми використовували змінні параметри argc і argv. Прийнято використовувати саме такі імена, але вони можуть бути будь-якими. Краще дотримуватися цього стандарту, щоб ваші програми були більш зрозумілі не лише вам, а й іншим програмістам.

Практичне значення передачі в програму

Якщо у вас є досвід роботи в командному рядку GNU/Linux, ви знаєте, що більшість команд мають ключі та аргументи. Наприклад, під час перегляду вмісту каталогів, копіювання, переміщення як аргументи вказуються об'єкти файлової системи, над якими виконується команда. Особливості виконання визначаються за допомогою ключів. Наприклад, у команді

cp – це ім'я команди, -r – ключ, а../les_1 та../les_101 – аргументи команди.

Взагалі найчастіше програми при їх запуску передаються адреси файлів і "модифікатори" (це ключі) процесу виконання програми.

Напишемо програму, яка відкриває вказані користувачему командному рядку файли на запис або додавання та записує (додає) туди одну й ту саму інформацію, яку користувач вводить з клавіатури в процесі виконання програми:

#include #include main (int argc, char ** argv) ( int i, ch; FILE * f [ 5 ] ; if (argc< 3 || argc >7 ) ( puts ( "Неправильна кількість параметрів"); return 1;) if (strcmp (argv[1] , "-w") != 0 && strcmp (argv[1] , "-a") != 0 ) ( puts (< argc- 2 ; i++ ) { f[ i] = fopen (argv[ i+ 2 ] , argv[ 1 ] + 1 ) ; if (f[ i] == NULL) { printf ("Перший параметр може бути -w, або -a");< argc- 2 ; i++ ) putc (ch, f[ i] ) ; for (i= 0 ; i < argc- 2 ; i++ ) fclose (f[ i] ) ; return 0 ; }

return 2;

  1. ) for (i = 0; i
  2. "Файл %s не можна відкрити\n"
  3. , argv [i + 2]);
  4. У return 3;) ) while ((ch = getchar ()) ! = EOF) for (i = 0; i Пояснення до коду:Створюється масив із п'яти файлових покажчиків. Отже, можна одночасно відкрити не більше п'яти файлів. Файловий покажчик першого файлу зберігається в елементі масиву f, другого - f і т.д.
  5. Вираз argv+1 дозволяє "вирізати" з рядка "-w" (або "-a") підрядок "w" (або "a"), т.к. argv насправді покажчик на перший елемент рядка. Додаючи до покажчика одиницю, ми зміщуємо його до наступного елемента масиву.
  6. Якщо файл відкрити не вдається, то функція fopen() повертає NULL. У такому разі програма завершується.
  7. Кожен символ, введений користувачем із клавіатури, записується у всі відкриті файли.
  8. Наприкінці файли закриваються.

Ця стаття виросла з ідеї просунутого навчання наших співробітників технічної підтримкироботи з mod_rewrite. Практика показала, що після вивчення наявних у великій кількості підручників російською саппортам добре дається рішення шаблонних завдань, але самостійне складання правил відбувається методом проб і великої кількостіпомилок. Проблема полягає в тому, що для хорошого розуміння роботи mod_rewrite потрібно вивчення оригінальної англомовної документації, після чого – або додаткові роз'яснення, або годинник експериментів з RewriteLog.

У статті викладено механізм роботи mod_rewrite. Розуміння принципів його дозволяє чітко усвідомлювати дію кожної директиви і ясно уявляти, що відбувається у той чи інший момент усередині mod_rewrite під час обробки директив.

Я припускаю, що читач вже знайомий з тим, що таке mod_rewrite, і не описуватиму його основи, які легко знайти в інтернеті. Також слід зазначити, що у статті висвітлюється робота mod_rewrite під час використання його директив у файлі.htaccess. Відмінності під час роботи в контексті викладені у .

Отже, ви вивчили mod_rewrite, склали кілька RewriteRule і встигли зіткнутися з нескінченними перенаправленнями, з нагоди, коли правило чомусь не ловить ваш запит, і навіть з непередбачуваною роботою групи правил, коли наступне правило несподівано змінює запит, ретельно підготовлений попередніми правилами.

З чим працює RewriteRule

Першому RewriteRule передається шлях від місця, де находится.htaccess, до запитаного файла. Цей рядок ніколи не починається зі "/". Наступним RewriteRule передається результат попередніх перетворень.

Щоб докладно зрозуміти, як працює RewriteRule, необхідно спочатку визначити, з чим він працює. Розглянемо, як Apache отримує рядок, який спочатку передається обробку RewriteRule в.htaccess.

Коли тільки починаєш працювати з mod_rewrite, логічно припускаєш, що він працює із посиланнями. Однак у разі використання mod_rewrite в.htaccess це не так. Насправді RewriteRule передається не посилання, а шлях до запитаного файлу.

Через внутрішню архітектуру Apache в той момент, коли в дію вступає. htaccess, mod_rewrite може оперувати тільки шляхом до файлу, який повинен бути оброблений. Це пов'язано з тим, що до передачі в mod_rewrite запит могли змінити інші модулі (наприклад, mod_alias), і підсумковий шлях до файлу на сайті вже може не збігатися з вихідним посиланням. Якби mod_rewrite працював з вихідним посиланням, він порушував би дію модулів, які змінили запит до нього.

Тому в mod_rewrite передається абсолютний шлях до файлу, який має бути оброблений. Також mod_rewrite знає шлях до. htaccess, у якому розміщені правила RewriteRule. Щоб зробити з шляху до файлу щось схоже на посилання, з яким планує працювати розробник сайту, mod_rewrite відрізає частину до файлу.htaccess від абсолютного шляху.

Так ось, саме цей шлях, від якого відрізаний шлях до .htaccess, передається до першого RewriteRule. Наприклад:

Запит: http://example.com/templates/silver/images/logo.gif DocumentRoot: /var/www/example.com Шлях до файлу: /var/www/example.com/templates/silver/images/logo. gif .htaccess знаходиться в: /var/www/example.com/templates/.htaccess

У перший RewriteRule буде передано: silver/images/logo.gif Зверніть увагу: templates теж відрізалося. як працює RewriteRule Шлях до.htaccess відрізається разом зі слішем. З цього є наслідок: рядок, який спочатку передається на обробку RewriteRule, ніколи не починається зі "/".

Не робить RewriteRule. Вона не опрацьовує ім'я сайту, аргументи, які передані в скрипт, та й посилання обробляє не всю, якщо.htaccess розміщений не в корені сайту. Усім цим займається RewriteCond, якого коротко торкнемося трохи пізніше. Отже:

# працювати не буде - правило починається зі / RewriteRule ^/index.php$ /my-index.php # працювати не буде - назва сайту не аналізується RewriteRule RewriteRule ^example.com/.* http://www.example.com # працювати не буде - аргументи посилання не потрапляють у RewriteRule RewriteRule index.php\?newspage=(+) news.php?page=$1 # Буде працювати тільки якщо.htaccess знаходиться там же, де знаходиться папка templates, # наприклад, в корені сайту . Тобто якщо.htaccess знаходиться в templates/.htaccess , правило # працювати не буде, тому що mod_rewrite відріже шлях до.htaccess і на вхід RewriteRule # рядок потрапить вже без "templates/" RewriteRule ^templates/common/yandex-money. gif$ templates/shared/yad.gif

З чим працює RewriteRule, ми розібралися. Тепер побачимо, як він працює.

Як працює RewriteRule

RewriteRule просто перетворює рядок відповідно до регулярних виразів, і все. RewriteRule працює з рядком, а не з посиланням або шляхом до файлу.

Як ми з'ясували, на вхід RewriteRule потрапляє шлях від.htaccess до запитаного файлу. Найзручніше тепер абстрагуватися від шляхів та посилань та розглядати те, з чим працює RewriteRule, як звичайний рядок. Цей рядок передається від RewriteRule до RewriteRule, видозмінюючись, якщо якесь із RewriteRule спрацювало.

У загальному вигляді, якщо виключити складнощі з використанням прапорів (деякі з яких ми розглянемо нижче) і складнощі зі складанням регулярних виразів (яких ми майже не стосуватимемося в цій статті), RewriteRule працює ДУЖЕ просто. Взяли рядок. Порівняли з регулярним виразом у першому аргументі. Якщо є збіг – замінили весь рядок на значення другого аргументу. Передали наступний рядок RewriteRule. Ось загалом і все. Щоб наочно проілюструвати, що RewriteRule працює саме з рядком, розглянемо наступний фантастичний приклад:

# Запит: http://mysite.com/info.html # У перший RewriteRule потрапить "info.html" # Перетворюємо запит у довільний рядок. RewriteRule ^info.html$ "Я торкаюсь курчати в шпильці. І це було нескінченне рок-н-ролл. І це було сміливе. All in all, it was very funny doll." # "info.html" -> "I saw a turtle..." # Замінюємо цей рядок на зовнішнє посилання. RewriteRule turtle https://example.com/information/index.html # "I saw a turtle..." -> "https://example.com/information/index.html" # Замінюємо ім'я сайту! RewriteRule ^(.*)example.com(.*)$ $1example.org$2 # "https://example.com/information/index.html" -> "https://example.org/information/index. html" # Замінюємо протокол! RewriteRule ^https:(.*)$ ftp:$1 # "https://example.org/information/index.html" -> "ftp://example.org/information/index.html" # Замінюємо кінцеве посилання. RewriteRule ^(.*)/index.html$ $1/main.php # "ftp://example.org/information/index.html" -> "ftp://example.org/information/main.php"

Як бачите, RewriteRule все одно, з чим працювати - вона просто перетворює рядок відповідно до заданих аргументів. Якщо хочете, можете у рядку зберігати будь-які масиви даних, за бажання, наполегливості та хорошому знаннірегулярних виразів можете хоч хрестики-нуліки на RewriteRule написати.

Тут потрібно зауважити: хоч RewriteRule і працює з чистим рядком, вона все-таки орієнтована на роботу з посиланнями. Тому вона по-особливому реагуватиме на рядки, що починаються на

Https://

або аналоги (запам'ятає, що ми хотіли зробити зовнішній редирект) і символ "?" (Вважатиме наступні символи аргументами, які потрібно буде підставити до запиту). Однак зараз нас це не цікавить – важливо зрозуміти, що в RewriteRule немає ніякої магії – вона просто бере рядок і змінює його так, як ви їй сказали. Зовнішні редиректи та аргументи ми розглянемо пізніше у статті, там теж є про що поговорити.

Після того як всі перетворення зроблено і виконано останнє RewriteRule, набуває чинності RewriteBase.

Для чого потрібний RewriteBase

Якщо запит, що вийшов після перетворень, є відносним і відрізняється від вихідного, RewriteBase додасть себе до нього зліва. Потрібно обов'язково вказувати на RewriteBase в.htaccess. Його значення – шлях від кореня сайту до. htaccess. RewriteBase виконується лише після всіх RewriteRule, а не між ними.

Ми вже говорили вище про те, що mod_rewrite, що працює в.htaccess, потрапляє абсолютний шлях до запитаного файлу. Щоб передати його на RewriteRule, mod_rewrite відрізає шлях до.htaccess. Потім правила RewriteRule один за одним послідовно змінюють запит. І ось після того, як запит змінено, Apache повинен відновити абсолютний шлях до файлу, який він повинен обробити. RewriteBase фактично є хаком, який допомагає відновити вихідний шлях до файлу.

RewriteBase виконується після всіх перетворень. Це означає, що він не змінюватиме запит між RewriteRule, а набуде чинності тільки коли всі RewriteRule відпрацюють.

Після всіх перетворень RewriteBase дивиться, відносний вийшов у результаті шлях або абсолютний. У контексті Apache мають на увазі відносний або абсолютний шлях, відраховуючи від кореня сайту: images/logo.gif - відносний. /images/logo.gif - абсолютний (на початку сліш). http://example.com/images/logo.gif - найабсолютніший з усіх. Якщо абсолютний шлях, RewriteBase нічого не робить. А якщо відносний – RewriteBase дописує себе зліва. Це працює як для внутрішніх, так і для зовнішніх редиректів:

# .htaccess знаходиться в /images/ # RewriteBase вказано /images/ RewriteBase /images/ # Запит http://example.com/images/logo.gif # На вхід RewriteRule потрапляє "logo.gif" RewriteRule ^logo.gif$ logo -orange.gif # Після RewriteRule: "logo.gif" -> "logo-orange.gif" # Після RewriteBase: "logo-orange.gif" -> "/images/logo-orange.gif" # Запит http:/ /example.com/images/header.png # На вхід RewriteRule потрапляє "header.png" RewriteRule ^header.png$ /templates/rebranding/header.png # Після RewriteRule: "header.png" -> "/templates/rebranding /header.png" # Після RewriteBase: нічого не змінюється, тому підсумковий результат перетворень починається зі "/". # Запит http://example.com/images/director.tiff # На вхід RewriteRule потрапляє "director.tiff" # Використовуємо зовнішній відносний редирект RewriteRule ^director.tiff$ staff/manager/director.tiff # Після RewriteRule: "director. tiff" -> "staff/manager/director.tiff" # + mod_rewrite запам'ятав, що буде зовнішній редирект # Після RewriteBase: "staff/manager/director.tiff" -> "/images/staff/manager/director.tiff" # mod_rewrite згадав про зовнішній редирект: # "/images/staff/manager/director.tiff" -> http://example.com/images/staff/manager/director.tiff

Зазвичай після деякого знайомства з mod_rewrite складається така звичка:

    до кожного.htaccess додавати «RewriteBase /»

    всі перенаправлення починати зі сліша: "RewriteRule news.php /index.php?act=news". Це допомагає позбавитися артефактів роботи RewriteBase, але так робити неправильно. Тепер, коли нам відомо, що робить RewriteBase, можна сформулювати такі коректні правила:

RewriteBase повинен збігатися з від кореня сайту до.htaccess. Починати перенаправлення зі "/" потрібно лише тоді, коли необхідно вказати абсолютний шлях від кореня сайту до файлу.

Що буде, якщо не вказати RewriteBase? За умовчанням Apache робить його рівним абсолютному шляхуна файловій системідо.htaccess (наприклад, /var/www/example.com/templates/). Некоректність такого припущення Apache проявляється на зовнішніх відносних редиректах:

# Запит http://example.com/index.php # DocumentRoot: /var/www/example.com/ # .htaccess знаходиться в корені сайту, і в ньому НЕ УКАЗАН RewriteBase. # Тому за замовчуванням RewriteBase дорівнює абсолютному шляху до.htaccess: /var/www/example.com/ # На вході RewriteRule - "index.php" RewriteRule " -> "main.php" # mod_rewrite запам'ятав, що потрібен зовнішній редирект # Закінчилися RewriteRule # mod_rewrite все одно виконує RewriteBase, так як у нього є значення за замовчуванням. # Виходить: "main.php" -> "/var/www/example.com/main.php" # Тут mod_rewrite згадує, що був зовнішній редирект: # "/var/www/example.com/main.php" - > http://example.com/var/www/example.com/main.php # Вийшло зовсім не те, що мали на увазі.

Отже, запит пройшов через усі RewriteRule, після чого до нього, у разі потреби, додався RewriteBase. Чи повинен Apache віддати файл, на який показує результуючий шлях? Ні. Тепер запит, що вийшов, буде оброблятися ще раз.

Як працює mod_rewrite? Прапор [L]

mod_rewrite запускає обробку запиту знову і знову, доки він не перестане змінюватися. І прапор [L]неспроможна це зупинити.

При складанні більш-менш складних конфігурацій mod_rewrite важливо розуміти, що зміна запиту не закінчується на останньому RewriteRule. Після того, як спрацювало останнє правило RewriteRule і був доданий RewriteBase, mod_rewrite дивиться, змінився запит чи ні. Якщо запит змінився, його обробка починається знову з початку.htaccess.

Apache чинить так, тому що в процесі зміни запиту він міг бути перенаправлений в іншу директорію. У ній може бути власний.htaccess, який не брав участі у попередній обробці запиту. У цьому ж новом. Щоб коректно обробити цю ситуацію, Apache має запустити весь цикл обробки заново.

Стривайте, але є прапор [L], що зупиняє обробку запиту mod_rewrite"ом!

Не зовсім так. Прапор [L]зупиняє поточну ітерацію обробки запиту. Однак якщо запит був змінений тими RewriteRule, які встигли відпрацювати, Apache запустить цикл обробки запиту заново з першого RewriteRule.

# Запит: http://example.com/a.html RewriteBase / RewriteRule ^a.html$ b.html [L] RewriteRule ^b.html$ a.html [L]

Приклад вище призведе до нескінченного циклу перенаправлень та до «Internal Server Error" в підсумку. У цьому прикладі нескінченний цикл очевидний, однак у складніших конфігураціях може знадобитися покопатися в правилах, щоб визначити, які запити зациклюються між собою.

Щоб уникнути подібних ситуацій, рекомендується використовувати прапор [L] лише за необхідності. Необхідність може бути двох типів: Коли використовується зовнішній редирект - або . У разі зовнішнього редиректу подальша обробка запиту небажана (див. нижче про прапор [R]), і її краще зупинити. Коли в.htaccess є зациклювання, якого не позбутися, і обробку запиту mod_rewrite"ом примусово припинити. У цьому випадку використовується спеціальна конструкція - див. в кінці статті поради на цю тему.

А ось наведений нижче приклад зациклюватись не буде. Спробуйте визначити, чому, і який у результаті файл буде віддано Apache.

# Запит: http://example.com/a.html # Початок.htaccess RewriteBase / RewriteRule ^a.html$ b.html RewriteRule ^b.html$ a.html # Кінець.htaccess

Відгадка: У результаті виконання всіх RewriteRule запит змінюється таким чином, що кінцевий результат дорівнює вихідному. Apache бачить це і не запускає повторну обробку запиту. Повернеться файл a.html.

Як працює mod_rewrite? Прапор [R]

    Прапор [R]не зупиняє обробку запиту, повертаючи одразу зовнішній редирект. Натомість він запам'ятовує необхідність зовнішнього редиректу, і обробка запиту продовжується наступними RewriteRule. Рекомендується завжди використовувати з прапором [L].

    Прапор [R]повідомляє Apache, що потрібно виконати не внутрішній, а зовнішній редирект. Чим відрізняється зовнішній редирект від внутрішнього? Внутрішній редирект просто змінює шлях до файлу, який буде відданий користувачеві, при цьому користувач вважає, що отримує той файл, який спочатку запитав. При зовнішньому редиректі Apache замість вмісту файлу повертає користувачу статус відповіді 301 або 302 і повідомляє посилання, за яким браузер повинен звернутися для отримання файлу.

Здавалося б, при обробці прапора [R] Apache повинен відразу припинити обробку RewriteRule і повернути користувачеві зовнішній редирект. Однак давайте згадаємо фантастичний приклад розділу «Як працює RewriteRule». У ньому ми спочатку вказали прапор [R], Позначивши необхідність зовнішнього редиректу, після чого продовжили змінювати посилання наступними RewriteRule.

Саме так і працює Apache за вказівкою зовнішнього редиректу. Він просто «позначає» собі, що після виконання всіх правил необхідно повернути статус 302 (за умовчанням), але при цьому продовжує виконання всіх RewriteRule далі за списком. Ми можемо й надалі змінювати запит як нам потрібно, єдине, що не вийде – зробити редирект назад внутрішнім.

Тим не менш, навряд чи ви хочете після віддачі зовнішнього редиректа будь-яким чином змінювати його. Тому рекомендується при вживанні прапора [R]вказувати його спільно з [L]:

# BlackJack переїхав на красиве ім'я RewriteRule ^bj/(.*) blackjack/$1 # Можна використовувати просто зовнішнє посилання RewriteRule ^bj/(.*) http://blackjack.example.com/$1 [L]

Замість використання прапора [R]можна вказувати просто зовнішнє посилання. У цьому випадку Apache сам здогадається, що потрібно зробити зовнішній редирект. Тут, як і у випадку з явною вказівкою прапора [R], рекомендується використовувати прапор [L]. Якщо зовнішній редирект веде на той самий сайт, краще використовувати прапор [R]без вказівки повного посилання(Іншими словами, використовувати відносний зовнішній редирект). Це зробить правило незалежно від імені сайту. Якщо зовнішній редирект веде на інший сайт, інакше, як вказавши повне зовнішнє посилання, це зробити не вийде.

Як працює mod_rewrite? Вказує параметри запиту та прапор

Зміна параметрів запиту RewriteRule не змінює рядок, з яким працює наступний RewriteRule. Однак, змінюючи параметри, змінюється %(QUERY_STRING), з якою може працювати RewriteCond.

Термінологія, що використовується: «параметри» - параметри запиту, «аргументи» - аргументи RewriteRule.

За допомогою RewriteRule можна змінювати не тільки шлях до обробленого файлу, але й параметри запиту GET, які йому передаватимуться. Це часто використовується для передачі обробки ЧПУ в загальний скрипт-обробник, наприклад: RewriteBase /

# Запит: http://example.com/news/2010/07/12/grand-opening.html # На вході: "news/2010/07/12/grand-opening.html" RewriteRule ^news/(.* )$ index.php?act=news&what=$1 # Після RewriteRule: "news/2010/07/12/grand-opening.html" -> "index.php" # %(QUERY_STRING): "" -> "act= news&what=2010/07/12/grand-opening.html"

У момент, коли правило RewriteRule зустрічає знак питання у другому аргументі, воно розуміє, що відбувається зміна параметрів у запиті. В результаті відбувається наступне: RewriteRule замінює рядок, з якого воно працює, на частину другого аргументу до знака питання. Зверніть увагу, що нові параметри запиту не потрапляють у рядок, з яким будуть працювати наступні правила RewriteRule. Частина другого аргументу після знаку питання потрапляє в змінну %(QUERY_STRING). Якщо було вказано прапор , параметри запиту будуть додані на початок %(QUERY_STRING). Якщо прапор не вказано, %(QUERY_STRING) повністю заміниться параметрами запиту з RewriteRule. Ще кілька прикладів:

RewriteBase / # Запит: http://example.com/news/2010/?page=2 # На вході RewriteRule: "news/2010/" RewriteRule ^news/(.*)$ index.php?act=news&what=$1 # Після перетворення: "news/2010/" -> "index.php" # Значення %(QUERY_STRING): "page=2" -> "act=news&what=2010/" Швидше за все, правило вище працює неправильно, тому що губиться аргумент page. Виправимо це: RewriteBase / # Запит: http://example.com/news/2010/?page=2 # На вході RewriteRule: "news/2010/" RewriteRule ^news/(.*)$ index.php?act= news&what=$1 # Після перетворення: "news/2010/" -> "index.php" # Значення %(QUERY_STRING): "page=2" -> "act=news&what=2010/&page=2"

Важливо розуміти, що зміна параметрів запиту змінює %(QUERY_STRING), який може бути використаний надалі в RewriteCond. Це потрібно враховувати при складанні наступних правил, які перевіряють аргументи.

Звичайно, змінюється, адже запит йде на повторну обробку Apache!

Ні, %(QUERY_STRING) змінюється відразу. Доказ наводити не буду – про параметри і так уже написано більше, ніж цікаво читати:)

Що ж робити, щоб перевірити в RewriteCond саме ті параметри запиту, які передав користувач, а не модифіковані RewriteRule? Дивіться поради в кінці статті.

RewriteCond та продуктивність

Спочатку перевіряється збіг запиту з RewriteRule, а вже потім - додаткові умови RewriteCond.

Кілька слів варто сказати про те, в якому порядку mod_rewrite виконує директиви. Так як в.htaccess спочатку йдуть RewriteCond, а потім RewriteRule, здається, що mod_rewrite спочатку перевіряє всі умови, а потім приступає до виконання RewriteRule.

Насправді, все відбувається навпаки. Спочатку mod_rewrite перевіряє, чи підходить поточне значення запиту під регулярне вираження RewriteRule, а вже потім перевірятиме всі умови, перелічені в RewriteCond.

Так що якщо у вас у RewriteRule регулярний вираз на дві сторінки і ви, задумавшись про продуктивність, вирішили обмежити виконання цього правила додатковими RewriteCond, знайте – нічого не вийде. У цьому випадку краще використовувати прапори RewriteRule [C] або [S], щоб пропустити більше складне правило, якщо більше прості перевіркине спрацювали.

Змінні та прапори RewriteCond, інші прапори RewriteRule та інше

Читайте документацію.

Ми познайомилися із принципами роботи RewriteRule, RewriteBase, прапорів [L], [R]і , і розібрали механізм обробки запитів всередині mod_rewrite. З незайманого залишилися: інші прапори RewriteRule, директиви RewriteCond та RewriteMap.

На щастя, ці директиви та прапори не таять у собі якихось загадок і працюють саме так, як описано у більшості підручників. Для їхнього розуміння достатньо почитати офіційну документацію. Насамперед рекомендую вивчити список змінних, які можна перевіряти в RewriteCond - %(QUERY_STING), %(THE_REQUEST), %(REMOTE_ADDR), %(HTTP_HOST), %(HTTP:header) тощо.

Різниця в роботі mod_rewrite у контексті.htaccess та у контексті VirtualHost

В контексті mod_rewrite працює з точністю до навпаки.

Як я говорив на початку статті, все описане вище стосується застосування mod_rewrite у контексті.htaccess. Якщо ж mod_rewrite використовується в , він буде працювати по-іншому: у RewriteRule потрапляє весь шлях запиту, починаючи від першого слішу, закінчуючи початком параметрів GET: "http://example.com/some/news/category/post.html?comments_page=3" → "/news/category/post.html". Цей рядок завжди починається зі /. Другий аргумент RewriteRule також необхідно починати зі /, інакше буде "Bad Request". RewriteBase не має сенсу. Прохід правил відбувається лише один раз. Прапор [L]дійсно закінчує обробку всіх правил, описаних у без будь-яких подальших ітерацій.

Складання регулярних виразів

Намагайтеся складати регулярні висловлювання так, щоб вони найбільш вузько визначали саме запити, які ви хочете модифікувати - щоб правила RewriteRule випадково не спрацювали для іншого запиту. Наприклад:

# Починайте всі регулярні вирази з "^" (ознака початку рядка) # і закінчуйте "$" (ознака кінця рядка): RewriteRule ^news.php$ index.php # Навіть якщо в цьому немає необхідності - для універсальності та кращого розуміння конфігурації: RewriteRule ^news/(.*)$ index.php # Якщо під маску повинні потрапляти тільки цифри - вкажіть це явно. # Якщо якісь цифри є постійними, вкажіть їх явно. # Якщо в частині запиту, що залишилася, не можуть бути присутні слєші, обмежте їх присутність. # Не забувайте екранувати "." (Точки). # Наступне правило націлене на запити виду http://example.com/news/2009/07/28/b-effect.html RewriteRule ^news/20(2)/(2)/(2)/[^/]+ \.html index.php

Втім, про регулярних виразахна одному відомому сайті є цілий розділ.

Зміна зовнішніх редиректів

Незважаючи на те, що mod_rewrite дозволяє змінювати за допомогою RewriteRule навіть зовнішні редиректи, аж до протоколу, я вкрай не рекомендую це робити. У статті приклад зі зміною зовнішніх редиректів використовується тільки щоб відв'язатися від таких понять як «посилання» та «файли» і більш очевидно показати, що RewriteRule працює з простим рядком.

Не думаю, що розробники mod_rewrite припускали, що хтось так робитиме, тому можливі всякі артефакти. Не робіть так, будь ласка.

Як зупинити нескінченний цикл

Іноді логіка перенаправлень на сайті така, що без спеціальних дій mod_rewrite сприймає їх як нескінченний цикл перенаправлень. Візьмемо такий приклад.

На сайті була сторінка /info.html. Фахівець із SEO вирішив, що пошукові системикраще індексувати цю сторінку, якщо вона буде називатися /information.html і попросив зробити зовнішній редирект з info.html на information.html. Однак розробник сайту з якихось своїх міркувань не може просто перейменувати info.html на information.html і зробити редирект - йому потрібно, щоб дані обов'язково віддавалися безпосередньо з файлу info.html. Він пише наступне правило: # зробити зовнішній редирект RewriteRule ^info.html information.html # але за запитом /information.html все одно віддати info.html RewriteRule ^information.html info.html

… і стикається з нескінченним циклом. Кожен запит /information.html отримує зовнішній редирект знову на /information.html.

Вирішити цю проблему можна як мінімум двома способами. На Хабре був вже описаний один із них - потрібно встановити змінну оточенняі на підставі її значення припиняти перенаправлення. Код виглядатиме так:

RewriteCond %(ENV:REDIRECT_FINISH) !^$ RewriteRule ^ - [L] RewriteRule ^info.html$ information.html RewriteRule ^information.html$ info.html

Зауважте, що до імені змінної mod_rewrite додає "REDIRECT_".

Другий спосіб - перевірити в THE_REQUEST, що саме було запрошено користувачем:

# Зовнішній редиректвідбувається лише якщо користувач запросив info.html. # Якщо ж info.html – це результат внутрішнього перенаправлення, правило спрацьовувати не буде. RewriteCond %(THE_REQUEST) "^(GET|POST|HEAD) /info.html HTTP/+$" RewriteRule ^info.html$ information.html RewriteRule ^information.html$ info.html

Аналіз вихідного запиту користувача – боротьба з розкриттям посилань Apache

При обробці запиту Apache розкриває закодовані (URL-encoded) символи з початкового запиту. У деяких випадках це може бути небажано – розробник хоче перевіряти саме початковий, немодифікований запит користувача. Зробити це можна, перевіряючи в RewriteCond змінну %(THE_REQUEST):

RewriteCond %(THE_REQUEST) ^GET[\ ]+/tag/([^/]+)/[\ ]+HTTP.*$ RewriteRule ^(.*)$ index.php?tag=%1 [L]

При створенні консольної програми в мові програмування С++, автоматично створюється рядок дуже схожий на цей:

Int main(int argc, char* argv) // Параметри функції main()

Цей рядок - заголовок головної функції main() , в дужках оголошено параметри argс та argv. Так от, якщо програму запускати через командний рядок, то існує можливість передати будь-яку інформацію цій програмі, для цього існують параметри argc і argv. Параметр argcмає тип даних int, і містить кількість параметрів, що передаються функцію main . Причому argc завжди не менше 1, навіть коли ми не передаємо жодної інформації, оскільки першим параметром вважається ім'я функції. Параметр argv це масив покажчиків на рядки. Через командний рядок можна передати лише дані рядкового типу. Вказівники та рядки – це дві великі теми, під які створені окремі розділи. Так ось саме через параметр argv і передається будь-яка інформація. Розробимо програму, яку запускатимемо через командну рядок Windows, та передавати їй деяку інформацію.

// argc_argv.cpp: визначає точку входу для консольної програми. #include "stdafx.h" #include using namespace std; int main(int argc, char* argv) ( if (argc ><< argv<

// код Code::Blocks

// код Dev-C++

// argc_argv.cpp: визначає точку входу для консольної програми. #include using namespace std; int main(int argc, char* argv) ( if (argc > 1)// якщо передаємо аргументи, то argc буде більше 1(залежно від кількості аргументів) ( cout<< argv<

Після того як налагодили програму, відкриваємо командний рядок Windows і перетягуємо у вікно командного рядка екземшник нашої програми, у командному рядку відобразиться повний шлях до програми (але можна прописати шлях до програми вручну), після цього можна натискати ENTERта програма запуститься (див. малюнок 1).

Рисунок 1 — Параметри функції main

Оскільки ми просто запустили програму та не передавали їй жодних аргументів, з'явилося повідомлення Not arguments. На малюнку 2 зображено запуск цієї програми через командний рядок, але вже з передачею їй аргументу Open .

Рисунок 2 — Параметри функції main

Аргументом є слово Open, як видно з малюнка, це слово з'явилося на екрані. Можна передавати кілька параметрів відразу, відокремлюючи їх між собою комою. Якщо необхідно передати параметр, що складається з декількох слів, то їх необхідно взяти в подвійні лапки, і тоді ці слова будуть вважатися одним параметром. Наприклад, на малюнку зображено запуск програми з передачею їй аргументу, що складається з двох слів — It work.

Рисунок 3 — Параметри функції main

А якщо прибрати лапки. То побачимо лише слово It. Якщо не планується передавати будь-яку інформацію при запуску програми, можна видалити аргументи у функції main() , також можна змінювати імена даних аргументів. Іноді зустрічається модифікації параметрів argc і argv , але це залежить від типу створюваної програми чи середовища розробки.