-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
187: Practice Exercise: Building a URL Shortener with Analytics
Whenever I see a junior dev tackle a URL shortener, they almost always start with the same approach: they write a function that generates a random 6-character string, checks the database to see if it exists, and if it does, loops back to generate another one. It seems intuitive, right? You just keep rolling the dice until you hit a unique value.
The Myth: "Random Strings are the Safest Way to Avoid Collisions"
Here is why that approach is a trap. Imagine your database has 10 million entries. The probability of a collision increases as your table grows. Eventually, your code might have to hit the database five or six times just to find one available string for a single URL. You've essentially turned your "shortener" into a performance bottleneck that relies on luck.
// The "Wrong" Way: Collision-based loops
do {
$shortCode = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 6);
$exists = $db->query("SELECT id FROM urls WHERE code = '$shortCode'");
} while ($exists->num_rows > 0);
This is "hope-based programming." We can do much better by leveraging the database's own strengths.
The Reality: Mapping Sequential IDs to Base62
Instead of guessing a string, use the auto-incrementing primary key (the ID) of your database record. Every ID is guaranteed to be unique. To make that ID look like a "code," you convert that integer from Base10 (0-9) to Base62 (0-9, a-z, A-Z).
For example, if the ID is 125, you don't want the user to see your.url/125. You convert 125 into Base62, and it becomes your.url/2B. It's deterministic, lightning-fast, and you never have to run a SELECT query just to check if a code is taken.
function base62Encode($num) {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$res = '';
while ($num > 0) {
$res = $chars[$num % 62] . $res;
$num = intdiv($num, 62);
}
return $res;
}
Capturing Metadata Before the Jump
Now, the "analytics" part of this project. The biggest mistake here is trying to record the click after the redirect. Once you send a header("Location: ..."), the PHP script effectively ends for the user. You have to capture the data in the split second between the request arriving and the redirect firing.
I usually recommend creating a separate clicks table. You don't just want the timestamp; you want the context. Use the $_SERVER superglobal to grab the HTTP_USER_AGENT (to know if they're on mobile) and REMOTE_ADDR (for the IP). I'll be honest: IP-based location tracking is a bit of a rabbit hole involving external APIs, but for this exercise, just storing the raw IP is a great start.
The flow should look like this:
- User hits
/r/abc123 - You look up the destination URL based on the code
abc123 - You insert a row into the
clickstable with the current timestamp and user agent - You call
header("Location: $destination")
One last tip: use a 302 (Found/Temporary) redirect rather than a 301 (Permanent). If you use a 301, some browsers will cache the redirect locally, and your analytics will stop recording hits for returning users because the browser won't even hit your server the second time.
📋 Practical Task
Build a High-Performance URL Redirector with Click-Logging
Your task is to implement the logic for the redirection engine of a URL shortener. You are not required to build the URL creation form, but you must build the handler that processes the short codes.
Requirements:
- The Database: Create two tables. A
urlstable (id,original_url,short_code) and aclickstable (id,url_id,clicked_at,user_agent,ip_address). - The Logic: Write a PHP script that accepts a short code via a GET parameter (e.g.,
redirect.php?c=2B). - The Analytics: The script must log the user's IP address and Browser User Agent into the
clickstable before redirecting the user. - The Redirect: Use a 302 redirect to send the user to the
original_urlassociated with that code. - Error Handling: If the code doesn't exist in the database, redirect the user to a 404 page or display a friendly "Link Expired" message.
There are no comments for now.