Skip to Content
Course content

101: Working with Symbolic Links in PHP

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've noticed a recurring pattern when developers first start managing files on a server: they treat symbolic links (symlinks) as if they are just "special copies" of files. Specifically, there's a common belief that is_file() or is_dir() can be used to detect whether a path is a symlink. It seems intuitive—if the link points to a file, is_file() should tell you it's a link, right?

Wrong. Here is why that breaks your logic:

// Assume 'current_log' is a symlink pointing to 'logs/2023-10-27.log'
if (is_file('current_log')) {
    echo "This is a regular file!"; 
}
// Result: "This is a regular file!"

PHP "follows" the link. When you call is_file() on a symlink, PHP doesn't care about the link itself; it looks at the target. If the target is a file, it returns true. This is a nightmare when you're trying to write a cleanup script to remove old links without accidentally deleting the actual data they point to.

is_file() identifies the target, not the link

To actually detect a symlink, you have to use is_link(). This function specifically checks if the path is a pointer. I always tell my team to check is_link() first before any other file-type check if they're working in a directory where symlinks are expected (like a deployment folder or a shared assets directory).

$path = 'current_release';

if (is_link($path)) {
    echo "This is a symlink. It's just a pointer.";
    echo "It points to: " . readlink($path);
} elseif (is_dir($path)) {
    echo "This is a real directory.";
}

The readlink() function is your best friend here. It resolves the link and tells you exactly where it's pointing. Without it, you're essentially flying blind.

unlink() destroys the pointer, not the source

Another point of anxiety I see is the fear of using unlink() on a symlink. New developers often worry that deleting the link will "reach through" and delete the original file on the disk. I can promise you it doesn't. When you call unlink() on a symlink, you are only destroying the shortcut.

Think of it like a desktop shortcut in Windows or an alias in macOS. Deleting the icon on your desktop doesn't uninstall the application from your Program Files. In PHP, it works exactly the same way. This is actually the secret sauce behind "zero-downtime deployments."

Here is the workflow I've used in production for years:

  • Upload your new code to a unique folder (e.g., /releases/202310271200/).
  • Create a symlink called /current that points to that folder.
  • When you're ready to update, create a new symlink for the new version and unlink() the old one (or simply overwrite it).
// Creating a symlink: symlink(target, link)
$target = '/var/www/releases/v2';
$link = '/var/www/html/current';

if (symlink($target, $link)) {
    echo "Deployment successful!";
} else {
    echo "Check your permissions; PHP might not have rights to create links here.";
}

One last tip: be careful with permissions. Your PHP process (like www-data) needs write permissions on the directory where the link is being created, not necessarily on the target file itself. If symlink() returns false, that's usually the first place I look.




📋 Practical Task

Building a Zero-Downtime Release Switcher

You are tasked with creating a script that simulates a production deployment switch. You need to manage two folders and a symlink to ensure the website always points to the "active" version of the code.

Requirements:

  • Create two directories: release_v1 and release_v2.
  • Place a dummy file named version.txt inside each folder with the text "Version 1" and "Version 2" respectively.
  • Write a PHP script that:
    1. Checks if a symlink named active_release already exists using is_link().
    2. If it exists, deletes it using unlink().
    3. Creates a new symlink named active_release pointing to release_v2.
    4. Uses readlink() to print the current target of the active_release link to the screen.
    5. Uses file_get_contents('active_release/version.txt') to prove that PHP is successfully following the link to the correct version of the file.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.