-
Notifications
You must be signed in to change notification settings - Fork 2
Url
A comprehensive URL utility class for the Webrium framework. This class provides powerful tools for URL generation, parsing, manipulation, and request information retrieval.
- URL Generation: Create absolute and relative URLs
- URL Parsing: Parse and build URLs from components
- Query String Manipulation: Add, update, or remove query parameters
- Request Information: Get method, IP addresses, user agent
- Security Features: Check HTTPS, validate origins, detect AJAX requests
- URL Pattern Matching: Match URLs against patterns with wildcard support
- SEO Tools: Automatic trailing slash management
- Mobile Detection: Detect mobile devices
- CORS Support: Handle origin and referer validation
- No Installation Required: Part of the Webrium framework
- Basic URL Information
- URL Generation
- Request Information
- URL Parsing and Building
- Query String Operations
- URL Pattern Matching
- Origin and Referer
- Security Features
- Mobile and AJAX Detection
- SEO Features
Get the current domain/host.
use Webrium\Url;
$domain = Url::domain();
// Returns: example.com or www.example.com or localhost:8080Get the request scheme (http or https).
// Without separator
$scheme = Url::scheme();
// Returns: "http" or "https"
// With separator
$scheme = Url::scheme(true);
// Returns: "http://" or "https://"Check if the current request is using HTTPS.
if (Url::isSecure()) {
echo "Connection is secure";
} else {
echo "Warning: Insecure connection";
}Detects HTTPS from:
$_SERVER['HTTPS']-
$_SERVER['HTTP_X_FORWARDED_PROTO'](for proxies/load balancers) $_SERVER['HTTP_X_FORWARDED_SSL']-
$_SERVER['SERVER_PORT'](port 443)
Get the base URL with scheme and domain.
$homeUrl = Url::home();
// Returns: https://example.comGet the application base URL (includes subdirectory if exists).
$baseUrl = Url::base();
// Returns: https://example.com/myapp (if in subdirectory)
// Returns: https://example.com (if in root)Generate a full URL from a relative path.
// Basic usage
$url = Url::to('users');
// Returns: https://example.com/users
$url = Url::to('admin/dashboard');
// Returns: https://example.com/admin/dashboard
// With leading slash (automatically handled)
$url = Url::to('/products');
// Returns: https://example.com/products
// Empty path returns base URL
$url = Url::to();
// Returns: https://example.comGet the current full URL.
// Without query string
$url = Url::current();
// Returns: https://example.com/products/view
// With query string
$url = Url::current(true);
// Returns: https://example.com/products/view?id=5&sort=nameGet the current URL with query string (alias for current(true)).
$fullUrl = Url::full();
// Returns: https://example.com/products?category=electronics&page=2Get the current URI path (without domain).
// Without query string
$uri = Url::uri();
// Returns: /products/view
// With query string
$uri = Url::uri(true);
// Returns: /products/view?id=5Get the HTTP request method.
$method = Url::method();
// Returns: GET, POST, PUT, DELETE, PATCH, OPTIONS, etc.
// Usage in routing
if (Url::method() === 'POST') {
// Handle form submission
}Get the server's IP address.
$serverIp = Url::serverIp();
// Returns: 192.168.1.1Get the client's IP address (with proxy support).
$clientIp = Url::clientIp();
// Returns: 123.45.67.89
// Automatically handles proxies
// Checks headers in order:
// - HTTP_CLIENT_IP
// - HTTP_X_FORWARDED_FOR
// - HTTP_X_FORWARDED
// - HTTP_X_CLUSTER_CLIENT_IP
// - HTTP_FORWARDED_FOR
// - HTTP_FORWARDED
// - REMOTE_ADDRIP Blocking Example:
$blockedIps = ['123.45.67.89', '98.76.54.32'];
$clientIp = Url::clientIp();
if (in_array($clientIp, $blockedIps)) {
http_response_code(403);
die('Access denied');
}Get the user agent string.
$userAgent = Url::userAgent();
// Returns: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
// Check for specific browser
if (str_contains(Url::userAgent(), 'Chrome')) {
echo "User is using Chrome";
}Get $_SERVER variables safely.
// Get all server variables
$allVars = Url::server();
// Get specific variable
$requestMethod = Url::server('REQUEST_METHOD');
// With default value
$customHeader = Url::server('HTTP_X_CUSTOM_HEADER', 'default');Parse a URL into components.
// Parse current URL
$parts = Url::parse();
// Parse specific URL
$parts = Url::parse('https://example.com:8080/path?key=value#section');
print_r($parts);
// Output:
// Array (
// [scheme] => https
// [host] => example.com
// [port] => 8080
// [path] => /path
// [query] => key=value
// [fragment] => section
// )Build a URL from components.
$url = Url::build([
'scheme' => 'https',
'host' => 'example.com',
'port' => 8080,
'path' => '/api/users',
'query' => 'page=1&limit=10',
'fragment' => 'top'
]);
// Returns: https://example.com:8080/api/users?page=1&limit=10#topURL Modification Example:
// Parse, modify, and rebuild URL
$parts = Url::parse('https://example.com/old-path');
$parts['path'] = '/new-path';
$parts['query'] = 'updated=true';
$newUrl = Url::build($parts);
// Returns: https://example.com/new-path?updated=trueGet the raw query string.
$query = Url::queryString();
// Returns: category=electronics&page=2&sort=priceAdd or update query parameters.
// Add to current URL
$url = Url::withQuery(['page' => 2, 'sort' => 'name']);
// Current: https://example.com/products
// Returns: https://example.com/products?page=2&sort=name
// Update existing parameters
$url = Url::withQuery(['page' => 3]);
// Current: https://example.com/products?page=1&category=books
// Returns: https://example.com/products?page=3&category=books
// Add to specific URL
$url = Url::withQuery(
['utm_source' => 'email'],
'https://example.com/landing'
);Remove specific query parameters.
// Remove from current URL
$url = Url::withoutQuery(['page', 'sort']);
// Current: https://example.com/products?page=1&sort=name&category=books
// Returns: https://example.com/products?category=books
// Remove from specific URL
$cleanUrl = Url::withoutQuery(
['utm_source', 'utm_campaign'],
'https://example.com/page?id=5&utm_source=email&utm_campaign=spring'
);
// Returns: https://example.com/page?id=5Pagination Example:
function generatePaginationLinks($currentPage, $totalPages) {
$links = [];
for ($i = 1; $i <= $totalPages; $i++) {
$links[] = [
'page' => $i,
'url' => Url::withQuery(['page' => $i]),
'active' => $i === $currentPage
];
}
return $links;
}Check if current URL matches a pattern.
// Exact match
if (Url::is('admin/dashboard')) {
echo "On dashboard page";
}
// Wildcard match
if (Url::is('products/*')) {
echo "On any products page";
}
// Root page
if (Url::is('')) {
echo "On home page";
}Check if current URL matches any of the given patterns.
if (Url::isAny(['admin/*', 'settings/*'])) {
echo "In admin or settings section";
}
// Navigation active state
$adminActive = Url::isAny(['admin', 'admin/*']);
$profileActive = Url::isAny(['profile', 'profile/*', 'account/*']);Active Menu Example:
function isMenuActive($path) {
return Url::is($path) ? 'active' : '';
}
<nav>
<a href="<?= Url::to('home') ?>" class="<?= isMenuActive('') ?>">Home</a>
<a href="<?= Url::to('products') ?>" class="<?= isMenuActive('products/*') ?>">Products</a>
<a href="<?= Url::to('about') ?>" class="<?= isMenuActive('about') ?>">About</a>
</nav>Get URL path segments as an array.
// URL: https://example.com/admin/users/edit/123
$segments = Url::segments();
// Returns: ['admin', 'users', 'edit', '123']
// URL: https://example.com/
$segments = Url::segments();
// Returns: []Get a specific segment by index (0-based).
// URL: https://example.com/products/view/42
$section = Url::segment(0); // "products"
$action = Url::segment(1); // "view"
$id = Url::segment(2); // "42"
$notExist = Url::segment(3, 0); // 0 (default value)Dynamic Routing Example:
function handleRequest() {
$controller = Url::segment(0, 'home');
$action = Url::segment(1, 'index');
$id = Url::segment(2);
$controllerClass = ucfirst($controller) . 'Controller';
$actionMethod = $action . 'Action';
if (class_exists($controllerClass)) {
$instance = new $controllerClass();
if (method_exists($instance, $actionMethod)) {
$instance->$actionMethod($id);
}
}
}Get the referer URL (the page that linked to the current page).
$referer = Url::referer();
// Returns: https://google.com/search?q=example or null
// With default
$referer = Url::referer(Url::home());Get the previous URL (alias for referer with default to base URL).
$previousUrl = Url::previous();
// Use for "back" buttons
// Redirect back example
redirect(Url::previous());Get the referer domain without scheme and path.
$domain = Url::refererDomain();
// Referer: https://google.com/search?q=example
// Returns: google.comCheck if referer matches a specific domain.
if (Url::isRefererFrom('google.com')) {
echo "User came from Google";
}
// Analytics example
$source = match(true) {
Url::isRefererFrom('google.com') => 'Google',
Url::isRefererFrom('facebook.com') => 'Facebook',
Url::isRefererFrom('twitter.com') => 'Twitter',
default => 'Direct'
};Check if referer is from the same domain.
if (Url::isInternalReferer()) {
echo "User navigated from within the site";
} else {
echo "User came from external source";
}
// Track external traffic
if (!Url::isInternalReferer()) {
logExternalVisit(Url::referer());
}Get the Origin header (used in CORS requests).
$origin = Url::origin();
// Returns: https://api.example.com or nullGet the origin domain.
$domain = Url::originDomain();
// Returns: api.example.comCheck if origin matches a specific domain.
if (Url::isOriginFrom('api.example.com')) {
echo "Request from authorized API domain";
}Check if origin is from the current domain.
if (!Url::isSameOrigin()) {
echo "Cross-origin request detected";
}Get request source URL (tries origin first, then referer).
$source = Url::source();
// Useful for logging where requests come fromGet request source domain.
$domain = Url::sourceDomain();Check if request is from an allowed domain.
$allowedDomains = ['example.com', 'api.example.com', 'partner.com'];
if (Url::isFromAllowedDomain($allowedDomains)) {
// Process request
} else {
http_response_code(403);
die('Access denied');
}function checkCorsOrigin() {
$allowedOrigins = [
'https://app.example.com',
'https://mobile.example.com'
];
$origin = Url::origin();
if ($origin && !in_array($origin, $allowedOrigins)) {
http_response_code(403);
die('CORS policy: Origin not allowed');
}
}function protectImages() {
if (!Url::isInternalReferer() && Url::referer() !== null) {
// External site is linking to our images
header('HTTP/1.1 403 Forbidden');
exit('Hotlinking not allowed');
}
}function validateRequest() {
// Only check CSRF for same-origin requests
if (Url::isSameOrigin() && Url::method() === 'POST') {
$token = $_POST['csrf_token'] ?? '';
if (!validateCsrfToken($token)) {
http_response_code(403);
die('CSRF validation failed');
}
}
}Detect if request is from a mobile device.
if (Url::isMobile()) {
// Serve mobile-optimized version
include 'mobile/index.php';
} else {
// Serve desktop version
include 'desktop/index.php';
}
// Responsive image serving
$imageSize = Url::isMobile() ? 'small' : 'large';
$imagePath = "/images/banner-{$imageSize}.jpg";Detects:
- Mobile
- Android
- iPhone/iPad/iPod
- BlackBerry
- Windows Mobile
- Opera Mini
Check if request is an AJAX request.
if (Url::isAjax()) {
// Return JSON response
header('Content-Type: application/json');
echo json_encode(['status' => 'success', 'data' => $data]);
} else {
// Return HTML page
include 'template.php';
}AJAX Handler Example:
function handleRequest() {
$data = processRequest();
if (Url::isAjax()) {
// AJAX request - return JSON
echo json_encode($data);
} else {
// Normal request - return HTML
renderView('page', $data);
}
}Check if a string has a trailing slash.
$hasSlash = Url::hasTrailingSlash('/products/'); // true
$hasSlash = Url::hasTrailingSlash('/products'); // falseAdd trailing slash to a string.
$url = Url::addTrailingSlash('/products');
// Returns: /products/Remove trailing slash from a string.
$url = Url::removeTrailingSlash('/products/');
// Returns: /productsRedirect to URL without trailing slash (SEO friendly).
// In your bootstrap/index.php
Url::redirectWithoutTrailingSlash(301);
// This will automatically redirect:
// /products/ -> /products
// /about/ -> /aboutEnforce URL standards (removes trailing slashes).
// In App initialization
Url::enforce();
// This is called automatically by App::initialize()Why Remove Trailing Slashes?
- Prevents duplicate content issues for SEO
- Ensures consistent URLs
- Better for analytics tracking
class Router {
public function route() {
$path = Url::uri();
$method = Url::method();
// Match routes
if (Url::is('api/*') && Url::isAjax()) {
$this->handleApiRequest();
} elseif (Url::is('admin/*')) {
$this->handleAdminRequest();
} else {
$this->handlePublicRequest();
}
}
private function handleApiRequest() {
$endpoint = Url::segment(1);
$id = Url::segment(2);
// Validate origin for API
if (!Url::isSameOrigin()) {
http_response_code(403);
echo json_encode(['error' => 'CORS not allowed']);
return;
}
// Route to API controller
$controller = "Api\\" . ucfirst($endpoint) . "Controller";
// ... handle request
}
}class Analytics {
public function trackVisit() {
$data = [
'url' => Url::full(),
'path' => Url::uri(),
'method' => Url::method(),
'referer' => Url::referer(),
'source_domain' => Url::sourceDomain(),
'is_internal' => Url::isInternalReferer(),
'is_mobile' => Url::isMobile(),
'is_ajax' => Url::isAjax(),
'is_secure' => Url::isSecure(),
'ip' => Url::clientIp(),
'user_agent' => Url::userAgent(),
'timestamp' => time()
];
// Save to database or analytics service
$this->saveAnalytics($data);
}
public function getTrafficSources() {
$referer = Url::refererDomain();
return match(true) {
$referer === null => 'Direct',
Url::isInternalReferer() => 'Internal',
str_contains($referer, 'google') => 'Google',
str_contains($referer, 'facebook') => 'Facebook',
str_contains($referer, 'twitter') => 'Twitter',
default => 'Other: ' . $referer
};
}
}class UrlBuilder {
public static function pagination($page, $perPage = 10) {
return Url::withQuery([
'page' => $page,
'per_page' => $perPage
]);
}
public static function filter(array $filters) {
return Url::withQuery($filters);
}
public static function sort($field, $direction = 'asc') {
return Url::withQuery([
'sort' => $field,
'order' => $direction
]);
}
public static function cleanUrl() {
// Remove all tracking parameters
return Url::withoutQuery([
'utm_source',
'utm_medium',
'utm_campaign',
'fbclid',
'gclid'
]);
}
public static function canonical() {
// Get clean URL for canonical tag
$parts = Url::parse();
unset($parts['query']);
unset($parts['fragment']);
return Url::build($parts);
}
}class SecurityMiddleware {
public function handle() {
$this->enforceHttps();
$this->validateOrigin();
$this->blockIps();
$this->preventHotlinking();
}
private function enforceHttps() {
if (!Url::isSecure() && App::env('FORCE_HTTPS')) {
$secureUrl = str_replace('http://', 'https://', Url::full());
redirect($secureUrl, 301);
}
}
private function validateOrigin() {
$allowedDomains = ['example.com', 'api.example.com'];
if (Url::isAjax() && !Url::isFromAllowedDomain($allowedDomains)) {
http_response_code(403);
die('Access denied');
}
}
private function blockIps() {
$blockedIps = ['123.45.67.89'];
if (in_array(Url::clientIp(), $blockedIps)) {
http_response_code(403);
die('Your IP has been blocked');
}
}
private function preventHotlinking() {
$uri = Url::uri();
if (preg_match('/\.(jpg|jpeg|png|gif)$/i', $uri)) {
if (!Url::isInternalReferer() && Url::referer() !== null) {
http_response_code(403);
die('Hotlinking not allowed');
}
}
}
}class Breadcrumb {
public static function generate() {
$segments = Url::segments();
$breadcrumbs = [
['title' => 'Home', 'url' => Url::base()]
];
$path = '';
foreach ($segments as $segment) {
$path .= '/' . $segment;
$breadcrumbs[] = [
'title' => ucfirst($segment),
'url' => Url::to($path)
];
}
return $breadcrumbs;
}
public static function render() {
$breadcrumbs = self::generate();
$html = '<nav aria-label="breadcrumb"><ol class="breadcrumb">';
foreach ($breadcrumbs as $i => $crumb) {
$isLast = $i === count($breadcrumbs) - 1;
if ($isLast) {
$html .= '<li class="breadcrumb-item active">' . $crumb['title'] . '</li>';
} else {
$html .= '<li class="breadcrumb-item"><a href="' . $crumb['url'] . '">' . $crumb['title'] . '</a></li>';
}
}
$html .= '</ol></nav>';
return $html;
}
}// ❌ BAD - Hardcoded URLs
<a href="https://example.com/products">Products</a>
<img src="https://example.com/images/logo.png">
// ✓ GOOD - Dynamic URLs
<a href="<?= Url::to('products') ?>">Products</a>
<img src="<?= Url::to('images/logo.png') ?>">// ❌ BAD - Assuming POST
$data = $_POST['data'];
// ✓ GOOD - Verify method
if (Url::method() === 'POST') {
$data = $_POST['data'];
}// ❌ BAD - Trusting all requests
processApiRequest();
// ✓ GOOD - Validate origin
if (Url::isSameOrigin() || Url::isFromAllowedDomain($whitelist)) {
processApiRequest();
}// ✓ GOOD - Get real client IP (handles proxies)
$clientIp = Url::clientIp();// Remove tracking parameters for clean canonical URLs
$canonicalUrl = Url::withoutQuery([
'utm_source', 'utm_medium', 'utm_campaign',
'fbclid', 'gclid'
]);The Url class is fully compatible with PHP 8.0 and higher, including:
- PHP 8.0+
- PHP 8.1+
- PHP 8.2+
- PHP 8.3+
Uses PHP 8 features:
- Match expressions
- Named parameters
- Null-safe operators
- Part of the Webrium framework
- PHP 8.0 or higher
- Webrium\App class for some methods
- Base URL is cached after first generation
- Parsed URLs are cached to avoid repeated parsing
- Use
Url::reset()for testing to clear caches
- Always validate origins for AJAX/API requests
-
Use
clientIp()instead of direct$_SERVER['REMOTE_ADDR']to handle proxies -
Check
isSecure()before handling sensitive data - Validate referer for form submissions to prevent CSRF
- Never trust user input from URL segments
Part of the Webrium framework.
This is a framework component. For bug reports or feature requests, please contact the Webrium framework maintainers.