Константи й змінні сервера

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

    Користувацькі константи

    Define Resalt Comment
    URL https://nvkarta.synology.me/ URL хоста, незалежно від протоколу передачі
    HOST nvkarta.synology.me Ім`я хоста без протоколу передачі
    ROOT /volume1/web/ Корінь папки, в якій виконується скрипт
    DIR /project/project-constants/ Відносний шлях до поточного каталогу від URL хоста
    FILE /project/project-constants/index.php Відносний шлях до поточного файлу від URL хоста

    Кастомні константи залежать від регістру, тобто $FILE не те саме, що й $file

    Формування кастомних констант:

    URL хоста, незалежно від протоколу передачі, з урахуванням випадків, коли заголовки виставлені проксі-сервером
    if (!defined("URL")) { $URL = isset($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] === "https" || isset($_SERVER["HTTP_X_FORWARDED_SSL"]) && $_SERVER["HTTP_X_FORWARDED_SSL"] === "on" ? "https://" : "http://"; $URL .= $_SERVER["HTTP_HOST"]; define("URL", $URL . "/"); }
    Ім`я хоста без протоколу передачі
    if (!defined("HOST")) define("HOST", $_SERVER["SERVER_NAME"]);
    Відносний шлях до поточного каталогу від URL хоста
    if (!defined("DIR")) define("DIR", dirname($_SERVER["PHP_SELF"]));
    Відносний шлях до поточного файлу від URL хоста
    if (!defined("FILE")) define("FILE", $_SERVER["PHP_SELF"]);

    Парсінг імені файлу

    PHP Resalt Comment
    $phpSelf /project/project-constants/index.php Строка для парсінгу (приклад)
    $foldName project-constants Ім`я поточної папки
    $baseName index.php Ім`я файла включно з типом
    $fileName index Ім`я файла без його типу
    $extName php Тип файлу (розширення)

    Формування змінних:

    Ім`я поточної папки, в якій виконується скрипт
    $foldName = basename(dirname(__FILE__));
    Видалення усіх символів, крім літер, цифр з адреси (при її використанні), що є захистом від ін`єкцій
    $phpSelf = filter_var($_SERVER["PHP_SELF"], FILTER_SANITIZE_URL);
    $pathParts = pathinfo($phpSelf);
    Виділення з адреси імені файла, включно з розширенням, де ім`я файла окремо, а розширення окремо
    $baseName = $pathParts["basename"];
    $fileName = $pathParts["filename"];
    $extName = $pathParts["extension"];

    Магічні константи PHP

    PHP Resalt Comment
    __DIR__ /volume1/web/project/project-constants Фізичний повний шлях до поточного каталогу
    __FILE__ /volume1/web/project/project-constants/index.php Фізичний повний шлях до поточного файлу
    __LINE__ 619 Номер поточного рядка у файлі
    __CLASS__ Example W3schools Якщо використовується всередині класу, повертається ім`я класу.
    __FUNCTION__ Example W3schools Якщо всередині функції, повертається ім`я функції
    __METHOD__ Example W3schools При використанні всередині функції, яка належить до класу, повертається ім`я класу та функції
    __NAMESPACE__ Example W3schools Якщо використовується всередині простору імен, повертається ім`я простору імен
    __TRAIT__ Example W3schools Якщо використовується всередині трейта, повертається ім`я трейта

    Магічні константи не залежать від регістру, тобто __LINE__ повертає те саме, що й __line__

    Суперґлобальний масив

    PHP script Resalt Comment
    $_SERVER["DOCUMENT_ROOT"] /volume1/web Кореневий каталог документа, в якому виконується поточний скрипт, як визначено у файлі конфігурації сервера
    $_SERVER["PHP_SELF"] /project/project-constants/index.php Ім`я файлу скрипта, що виконується в даний момент, відносно кореня документа
    $_SERVER["HTTP_REFERER"] https://nvkarta.synology.me/project/project-constants Заголовок HTTP з адресою веб-сторінки (тобто URI або IRI), з якої був запит
    $_SERVER["REMOTE_ADDR"] 3.145.33.230 IP-адреса, з якої користувач переглядає поточну сторінку
    $_SERVER["REMOTE_HOST"] ec2-3-145-33-230.us-east-2.compute.amazonaws.com Ім`я хоста, з якого користувач переглядає поточну сторінку, або інтернет-вузол, вказаний за параметром IP
    $_SERVER["SERVER_NAME"] nvkarta.synology.me Отримується з імені сервера, базується на конфігурації веб-сервера
    $_SERVER["HTTP_HOST"] nvkarta.synology.me Отримується із заголовка HTTP-запиту, базується на запиті клієнта
    $_SERVER["HTTP_USER_AGENT"] Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com) Заголовок HTTP про тип і налащтування клієнтського браузера
    $_SERVER["SERVER_SOFTWARE"] Apache/2.4.58 (Unix) Рядок ідентифікації сервера (середовища)

    $_SERVER - це суперглобальна змінна PHP, що містить інформацію про заголовки, шляхи та розташування скриптів.

    Приклади окремих серверних змінних:

    Заголовок HTTP з адресою веб-сторінки (тобто URI або IRI), з якої був запит. В разі прямого звертання до сторінки, можна повернути про це попередження
    $refer = isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "The URI is not available through direct access to this page.";
    Ім`я хоста, з якого користувач переглядає поточну сторінку. Зворотний пошук DNS базується на REMOTE_ADDR користувача, тому веб-сервер має бути налаштований для створення цієї змінної. Наприклад, в httpd.conf має бути встановлено Apache HostnameLookups On, щоб змінна могла існувати. В разі відсутності таких налаштувань, можна повернути ім`я хоста інтернет-вузла, вказаного за параметром IP таким чином
    $getHost = gethostbyaddr($_SERVER["REMOTE_ADDR"]);
    $remoteHost = isset($_SERVER["REMOTE_HOST"]) ? $_SERVER["REMOTE_HOST"] : $getHost;

    Геоплагин для IP

    Title GeoData GeoPlugin
    Continent Name North America $ipdat->geoplugin_continentName
    Country Name United States $ipdat->geoplugin_countryName
    City Columbus $ipdat->geoplugin_city
    Latitude 39.9625 $ipdat->geoplugin_latitude
    Longitude -83.0061 $ipdat->geoplugin_longitude
    Currency Symbol $ $ipdat->geoplugin_currencySymbol
    Currency Code USD $ipdat->geoplugin_currencyCode
    Timezone America/New_York $ipdat->geoplugin_timezone

    Дані про користувача були отримані з безплатного онлайн-сервісу geoplugin.com на основі IP 3.145.33.230 взятого з $_SERVER["HTTP_CLIENT_IP"] клієнта.

    Безпека

    Використання target="_blank"

    Рекомендовано додавати rel="noreferrer noopener" до елемента anchor щоразу, коли застосовується атрибут перенаправлення сторінок target="_blank".

    
    <a href="https://karta.com" target="_blank" rel="noreferrer noopener">перейти</a>
    	

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

    Використання електронної адреси

    Захист e-mail від краулерів за рахунок маскування його у JS-коді. Боти не вміють виконувати скрипти, тому такий варіант захисту працює досить надійно.

    
    <div id="mail"></div>
    
    <script>
      const mail = {login: "geo", server: "karta", domain: "com"};
      const email = `${mail.login}@${mail.server}.${mail.domain}`;
      const string = `<a href="mailto:${email}?subject=Mail from our Website&body=Some body text here">${email}</a>`;
      document.getElementById("mail").innerHTML = string;
    </script>
    	

    Безпека веб-сервера Apache

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

    
    Options +SymLinksIfOwnerMatch
    Options -Indexes
    DirectoryIndex index.php index.html
    AddDefaultCharset UTF-8
    
    # Mодуль для обробки та перезапису URL-запитів
    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule ^ index.php [QSA,L]
    </IfModule>
    
    # Mодуль для визначення заголовків HTML файлів
    <FilesMatch "\.(html)$">
        Header set Cache-Control "no-cache, no-store"
        Header unset ETag
    </FilesMatch>
    
    # Mодуль для визначення HTTP-заголовків
    <IfModule mod_headers.c>
        Header set X-Content-Type-Options "nosniff"
        Header set X-XSS-Protection "1; mode=block"
        Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
        Header set Referrer-Policy "no-referrer-when-downgrade"
    </IfModule>
    	

    Конфігурація, наведена вище, налаштовує веб-сервер Apache на безпечну й ефективну роботу з веб-додатками. Mодуль для визначення заголовків HTML файлів блокує кешування, але, при необхідності, його можна видалити.

    Дерево заголовків сторінки

    Для автоматичного формування списку розділів поточної сторінки використовується скрипт на JS, який формує вивід по тегам заголовків на льоту. Списки можуть бути одинарними, окремими, або комбінованими.

    Одинарний список елементів <h2>

    
    <ul class="height" id="h2-list"></ul>
    
    <script>
    	// Знаходимо всі елементи <h2>
    	const h2Elements = document.querySelectorAll('h2');
    	const ulElement = document.getElementById('h2-list');
    
    	// Перетворюємо NodeList у масив та перебираємо кожен заголовок
    	h2Elements.forEach((h2, index) => {
    		// Додаємо унікальний id для кожного <h2>
    		const id = `section${index + 1}`;
    		h2.setAttribute('id', id);
    
    		// Створюємо елемент <li> зі посиланням <a>
    		const li = document.createElement('li');
    		const anchor = document.createElement('a');
    
    		// Створюємо текст і посилання для анкора
    		anchor.textContent = h2.textContent;
    		anchor.href = `#${id}`;
    
    		// Додаємо посилання до <li> і <li> до <ul>
    		li.appendChild(anchor);
    		ulElement.appendChild(li);
    	});
    </script>
    	

    Окремі списки з <h2> та <h3>

    
    Список 1:
    <ul class="height" id="h2-list"></ul>
    
    Список 2:
    <ul class="height" id="h3-list"></ul>
    
    <script>
    	// Знаходимо всі елементи <h2>
    	const h2Elements = document.querySelectorAll('h2');
    	const ulElement = document.getElementById('h2-list');
    
    	// Перетворюємо NodeList у масив та перебираємо кожен заголовок
    	h2Elements.forEach((h2, index) => {
    		// Додаємо унікальний id для кожного <h2>
    		const id = `section1-${index + 1}`;
    		h2.setAttribute('id', id);
    
    		// Створюємо елемент <li> зі посиланням <a>
    		const li = document.createElement('li');
    		const anchor = document.createElement('a');
    
    		// Створюємо текст і посилання для анкора
    		anchor.textContent = h2.textContent;
    		anchor.href = `#${id}`;
    
    		// Додаємо посилання до <li> і <li> до <ul>
    		li.appendChild(anchor);
    		ulElement.appendChild(li);
    	});
    
    	// Знаходимо всі елементи <h3>
    	const h3Elements = document.querySelectorAll('h3');
    	const ul3Element = document.getElementById('h3-list');
    	
    	// Перетворюємо NodeList у масив та перебираємо кожен заголовок
    	h3Elements.forEach((h3, index) => {
    		// Додаємо унікальний id для кожного <h3>
    		const id = `section2-${index + 1}`;
    		h3.setAttribute('id', id);
    
    		// Створюємо елемент <li> зі посиланням <a>
    		const li = document.createElement('li');
    		const anchor = document.createElement('a');
    
    		// Створюємо текст і посилання для анкора
    		anchor.textContent = h3.textContent;
    		anchor.href = `#${id}`;
    
    		// Додаємо посилання до <li> і <li> до <ul>
    		li.appendChild(anchor);
    		ul3Element.appendChild(li);
    	});
    </script>
    	

    Комбіновані списки з <h2> та <h3>

    Генерація древовидної структури сторінки, зформованої по тегам заголовків на льоту. Виводиться одним блоком.

    
    <ul id="toc-list"></ul>
    	
    <script>
    	// Знаходимо всі елементи <h2> та <h3>
    	const headings = document.querySelectorAll('h2, h3');
    	const ulElement = document.getElementById('toc-list');
    	let currentUl = ulElement;
    
    	headings.forEach((heading, index) => {
    		// Створюємо унікальний id для кожного заголовка
    		const id = `section${index + 1}`;
    		heading.setAttribute('id', id);
    
    		// Створюємо елемент <li> зі посиланням <a>
    		const li = document.createElement('li');
    		const anchor = document.createElement('a');
    		anchor.textContent = heading.textContent;
    		anchor.href = `#${id}`;
    
    		// Додаємо посилання до <li>
    		li.appendChild(anchor);
    
    		// Якщо це <h2>, додаємо його в основний список
    		if (heading.tagName.toLowerCase() === 'h2') {
    			ulElement.appendChild(li);
    			// Створюємо новий підсписок для <h3>, якщо він з'явиться
    			currentUl = document.createElement('ul');
    			li.appendChild(currentUl);
    		} else if (heading.tagName.toLowerCase() === 'h3') {
    			// Якщо це <h3>, додаємо його у підсписок до поточного <h2>
    			currentUl.appendChild(li);
    		}
    	});
    </script>