PHP Advance



Laravel, Apache, Nginx, and PHP Built-in Server



create an apache virtual host


sudo nano /etc/apache2/sites-available/laravel.test.conf # Create a new virtual host
sudo a2ensite laravel.test.conf # Enable it
sudo systemctl reload apache2 # Reload Apache
# Apache reads only configs in /etc/apache2/sites-enabled/, but you always edit/add them in /etc/apache2/sites-available/.

Apache Log Directories



Create a new Nginx server block


sudo nano /etc/nginx/sites-available/laravel.test # Create a new server block
sudo ln -s /etc/nginx/sites-available/laravel.test /etc/nginx/sites-enabled/ # Enable it
sudo nginx -t # Test and reload
sudo systemctl reload nginx
# Nginx only reads configs in /etc/nginx/sites-enabled/, but you create/edit them in /etc/nginx/sites-available/.

Nginx Log Directories



How PHP works


  1. Browser requests a URL (e.g. https://example.com/ → server receives GET /).
  2. Web server (Apache, Nginx, etc.) receives the request and determines which file or resource to serve based on its configuration and routing rules.
  3. The server checks for a default index file (e.g. index.php, index.html), as defined by:
    • Apache → DirectoryIndex
    • Nginx → index
  4. If the target file is not a PHP file (e.g., .html, .css, .jpg), the web server serves it directly to the client.
  5. If the target file is a PHP script, the web server forwards it to the PHP interpreter using:
    • Apache: mod_php (built-in module) or php-fpm via FastCGI
    • Nginx: always uses php-fpm via FastCGI (Nginx cannot execute PHP directly)
  6. PHP Engine Execution Process:
  7. PHP reads the .php source file.
  8. Parses and compiles it into Zend opcodes (intermediate bytecode).
  9. Executes those opcodes using the Zend Engine.
  10. If OPcache is enabled: - PHP stores compiled opcodes in memory for faster reuse. - When a file changes, OPcache detects the change and recompiles that specific script (it doesn’t “update” the existing cache, it replaces it). - it doesn’t slow down the entire app. Only that one updated file gets recompiled once.
  11. PHP generates output (HTML, JSON, etc.) → returns it to the web server.
  12. Web server sends the response back to the browser.

What happens when there is no index file



How to prevent directory listing / common fixes


Apache (.htaccess)

# disable directory listing
Options -Indexes
# set preferred index files
DirectoryIndex index.php index.html

Nginx (server block)

# disable autoindex
autoindex off;

# common PHP try_files config
location / {
  try_files $uri $uri/ /index.php?$query_string;
}

Prevent serving source if PHP handler breaks



Extra security notes (don’t ignore)


Browser
   ↓
Web Server (Apache/Nginx)
   ↓ (detects .php)
PHP Engine (mod_php or PHP-FPM)
   ↓ (executes PHP)
Output (HTML/JSON)
   ↓
Browser
Browser
   ↓
[Optional] Forward Proxy (client-side, e.g., corporate proxy)
# Often used for filtering, caching, or anonymity.
   ↓
[Optional] Load Balancer (distributes requests across servers)
# Distributes requests across multiple backend servers.
# Can be before or after the reverse proxy, depending on architecture.
   ↓
Reverse Proxy (e.g., Nginx or Apache in front, handles SSL, caching, routing)
# Front-facing server that handles SSL termination, caching, request routing, or compression.
# Passes requests to the actual web server running PHP.
   ↓
Web Server (Apache/Nginx)
   ↓ (detects .php)
PHP Engine (mod_php or PHP-FPM)
   ↓ (executes PHP)
Output (HTML/JSON)
   ↓
Reverse Proxy
   ↓
[Optional] Load Balancer response aggregation
   ↓
[Optional] Forward Proxy
   ↓
Browser

🔧1. Apache with mod_php

⚙️ 2. PHP-FPM (FastCGI Process Manager)

⚙️ 3. CGI / FastCGI (legacy)


.htaccess (hypertext access)



What .htaccess Does



Example .htaccess file


# ============================================================
# Enable URL rewriting
# ============================================================
# Turn on the rewrite engine so we can use RewriteRule below.
RewriteEngine On

# ============================================================
# Force HTTPS (redirect all HTTP requests to HTTPS)
# ============================================================
RewriteCond %{HTTPS} !=on
RewriteRule ^(.)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# ============================================================
# Redirect to "www" version (optional — choose one)
# ============================================================
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteRule ^(.)$ https://www.%{HTTP_HOST}/$1 [L,R=301]

# ============================================================
# Remove "index.php" from URLs for clean routing
# ============================================================
# Example: example.com/index.php/about → example.com/about
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.)$ index.php?route=$1 [L,QSA]

# ============================================================
# Deny direct access to sensitive files
# ============================================================
<FilesMatch "\.(env|json|config|log|sh)$">
    Require all denied
</FilesMatch>

# ============================================================
# Set custom error pages
# ============================================================
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html

# ============================================================
# Enable browser caching for static files
# ============================================================
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpg "access plus 1 month"
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType image/png "access plus 1 month"
    ExpiresByType text/css "access plus 1 week"
    ExpiresByType application/javascript "access plus 1 week"
</IfModule>

# ============================================================
# Enable GZIP compression for faster load times
# ============================================================
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript
</IfModule>

# ============================================================
# Basic directory password protection (optional)
# ============================================================
# Protect sensitive admin areas by requiring login
# <Directory "/var/www/html/admin">
#     AuthType Basic
#     AuthName "Restricted Area"
#     AuthUserFile /path/to/.htpasswd
#     Require valid-user
# </Directory>

# ============================================================
# Override some PHP configurations (if allowed)
# ============================================================
php_value upload_max_filesize 20M
php_value post_max_size 25M
php_flag display_errors Off

test and debug .htaccess rules properly


  1. Make Sure .htaccess Is Even Working

Check Apache Config

<Directory /var/www/html>
    AllowOverride All
    Require all granted
</Directory>
 # `AllowOverride All` → lets `.htaccess` override settings (like rewrite rules).
 # Restart Apache after editing:
   sudo systemctl restart apache2
#If `AllowOverride` is `None`, `.htaccess` does nothing — period.
  1. Test That It’s Active
#Create a temporary `.htaccess` in your web root with
Options -Indexes
#Then put a random file in that folder (like `test.txt`), and open that folder URL in a browser 
e.g.:http://localhost/test-folder/
#If you get a 403 Forbidden instead of a directory listing, `.htaccess` is working.
  1. Debug Rewrite Rules
RewriteEngine On
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 3

#Then check the log
sudo tail -f /var/log/apache2/rewrite.log

#Note: On newer Apache versions (2.4+), `RewriteLog` is deprecated. Instead, use this in your main config (not `.htaccess`):

LogLevel alert rewrite:trace3

#Then view logs in:
sudo tail -f /var/log/apache2/error.log
  1. Use Built-In Testing Tools
curl -I http://example.com/old-page
HTTP/1.1 301 Moved Permanently
Location: https://example.com/new-page
  1. Check for Rule Conflicts
  1. Validate Access Restrictions
curl -I http://example.com/.env
HTTP/1.1 403 Forbidden
  1. Enable Error Reporting Temporarily
php_flag display_errors On
  1. Use a Local Debug Page
<?php
echo "<pre>";
print_r($_SERVER);
  1. Typical Apache Module Check
apache2ctl -M | grep rewrite

File Importing in PHP



include, include_once, require, require_once


include Loads and executes a file. If the file doesn’t exist → shows a warning but continues executing the rest of the script.

require Loads and executes a file. If the file doesn’t exist → throws a fatal error and stops execution.

include_once Works like include, but prevents re-including the same file. Useful to avoid “cannot redeclare function/variable/class” warnings.

require_once Works like require, but ensures the file is included only once. Commonly used in large projects to safely load config or class files.

When your project grows, using require_once everywhere can make the code messy. Instead, you can use spl_autoload_register() to automatically load classes on demand.


PHP Streams


PHP Streams are a unified way of working with file and network resources in PHP. They provide a common interface for reading from and writing to various data sources, abstracting away the differences between files, network sockets, compressed files, and other I/O operations.


Core Concepts


A stream is referenced using the syntax: scheme://target

Common stream wrappers include:


Why Streams Matter for Large Files


The key advantage is memory efficiency. Instead of loading an entire file into memory at once, streams let you process data in small chunks, making it possible to handle files larger than available RAM.


Practical Examples for Large File Handling



1. Reading Large Files Line by Line


$handle = fopen('large_file.csv', 'r');
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        // Process one line at a time
        processLine($line);
    }
    fclose($handle);
}

2. Using Stream Contexts for HTTP


$context = stream_context_create([
    'http' => [
        'method' => 'GET',
        'header' => 'Authorization: Bearer token123'
    ]
]);

$stream = fopen('https://api.example.com/large-data', 'r', false, $context);
while (!feof($stream)) {
    echo fread($stream, 8192); // Read 8KB chunks
}
fclose($stream);

3. Copying Large Files Efficiently


// stream_copy_to_stream handles buffering automatically
$source = fopen('large_source.zip', 'r');
$dest = fopen('large_dest.zip', 'w');
stream_copy_to_stream($source, $dest);
fclose($source);
fclose($dest);

4. Using php://temp for Memory-Efficient Processing


// Automatically switches from memory to temp file if data exceeds 5MB
$temp = fopen('php://temp/maxmemory:5242880', 'r+');
fwrite($temp, $largeData);
rewind($temp);

while (!feof($temp)) {
    $chunk = fread($temp, 8192);
    // Process chunk
}
fclose($temp);

5. Stream Filters for On-the-Fly Processing


// Read and decompress a gzipped file without loading it all into memory
$handle = fopen('compress.zlib://large_file.gz', 'r');

// Or apply filters to existing streams
$fp = fopen('large_file.txt', 'r');
stream_filter_append($fp, 'string.toupper');
while ($line = fgets($fp)) {
    echo $line; // Automatically converted to uppercase
}
fclose($fp);

6. Custom Stream Buffer Size


$handle = fopen('huge_file.log', 'r');
// Set 1MB buffer for better performance with large sequential reads
stream_set_read_buffer($handle, 1024  1024);

while (!feof($handle)) {
    $data = fread($handle, 8192);
    processData($data);
}
fclose($handle);

Best Practices


  1. Always close streams using fclose() to free resources
  2. Check for errors - fopen() returns false on failure
  3. Use appropriate chunk sizes - 8KB to 1MB depending on your use case
  4. Consider stream filters for transformations instead of loading data into memory
  5. Use php://temp instead of php://memory for potentially large data
  6. Leverage stream_copy_to_stream() for efficient file copying

Streams are essential for building scalable PHP applications that handle large datasets, file uploads, or API responses without exhausting server memory.