HELGE SVERREAll-stack Developer
Bergen, Norwayv13.0
est. 2012  |  197 repos  |  12.8k+ contributions
Tools  |   Theme:
Hidden Gems in the PHP Source Code
August 6, 2026

Here's a collection of interesting comments, easter eggs, and developer confessions found in the PHP source code. Some are deliberate easter eggs. Most are developers being honest: venting about compilers, leaving notes for their future selves, annotating hacks they hope someone eventually cleans up.

All links point to a stable commit hash so line numbers won't drift.


Every year on April 1st, phpinfo() displays the PHP elephpant mascot instead of the normal round logo. The check is a simple date comparison against tm_mon==3 && tm_mday==1.

Normal logoApril 1st logo
Normal PHP logoApril Fools PHP logo (elephpant)
the_time = time(NULL);
ta = php_localtime_r(&the_time, &tmbuf);

php_info_print("<a href=\"https://www.php.net/\"><img src=\"");
if (ta && (ta->tm_mon==3) && (ta->tm_mday==1)) {
    php_info_print(PHP_EGG_LOGO_DATA_URI "\" alt=\"PHP logo\" /></a>");
} else {
    php_info_print(PHP_LOGO_DATA_URI "\" alt=\"PHP logo\" /></a>");
}

This replaced the original logo GUID easter egg in 2012 (commit d12f8d67903). The old approach appended ?=PHPE9568F36-D428-11d2-A769-00AA001ACF42 to the image URL on April 1st, a COM class ID that returned a different image. When Colin Viebrock added it in April 2000 his commit message was simply "A little easter egg for April 1st ... :)". The data URI approach keeps the tradition alive without the server round-trip.

↗ permalink

The Beer-Ware License

PHP's Unix MD5 crypt implementation, borrowed from FreeBSD via OpenBSD and NetBSD, carries an unmodified license from Poul-Henning Kamp.

/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
 * ----------------------------------------------------------------------------
 */

The Beer-Ware License is PHK's way of saying "do whatever, maybe buy me a drink." It sits at the top of PHP's php_crypt_r.c, which provides the CRYPT_MD5 algorithm ($1$ prefix hashes). The file has survived multiple PHP releases essentially unchanged.

↗ permalink

"WTF?!"

Three separate locations in php-src use "WTF" in comments. The first is in the PHP-FPM environment variable cleanup code, where a deletion loop annotates two consecutive lines:

while(environ[ct] != NULL) {
    if (nvmatch(name, environ[ct]) != 0) del=ct; /* <--- WTF?! */
    { ct++; } /* <--- WTF?! */
}

The function removes an environment variable from environ by scanning the array and swapping the deleted slot with the last entry. The { } block around ct++ looks redundant but isn't: the if body only sets del, so without those braces the increment would otherwise be outside the loop body due to how the code is structured without braces. The original author found it confusing enough to annotate twice.

↗ permalink

The second is in Zend's memory allocator. NetBSD ships an mremap() with an incompatible signature:

/* NetBSD has an mremap() function with a signature that is incompatible
   with Linux (WTF?), so pretend it doesn't exist. */
#ifndef __linux__
# undef HAVE_MREMAP
#endif

mremap() on Linux can grow/shrink an existing mapping in place; NetBSD's version has different semantics. Rather than add a platform-specific code path, the Zend allocator simply disables mremap on non-Linux systems.

↗ permalink

The third is in iconv's MIME header decoding. When encountering an encoded word whose charset can't be determined, the code compares two fallback strategies:

/* both of which seem to have
 * a higher WTF factor than leaving it undecoded. */

"Swallow the encoded word entirely" and "decode it with an arbitrary single-byte encoding" both ranked worse on the WTF factor scale than leaving the text undecoded, so that's what the code does.

↗ permalink

"HERE BE DRAGONS"

phpdbg's watchpoint system tracks variables and array elements. When a watched array is freed, the cleanup code reaches into the HashTable internals with a blunt warning:

if (!element->parent) {
    /* HERE BE DRAGONS; i.e. we assume HashTable is directly allocated via
       emalloc() ... (which *should be* the case for every user-accessible
       array and symbol tables) */
    zend_hash_index_add_empty_element(&PHPDBG_G(watch_free),
        (zend_ulong)(uintptr_t) element->parent_container);
}

"HERE BE DRAGONS" comes from old nautical maps marking uncharted waters. Here it warns that the code relies on an internal memory allocation invariant. If the HashTable implementation ever changes its allocation strategy, this assumption would break and the watchpoint cleanup would corrupt memory. The comment acknowledges the risk explicitly.

↗ permalink

"just ignore this shit"

The CLI SAPI's built-in HTTP parser (derived from Node.js's http-parser) handles chunked transfer encoding. Chunk extension parameters, the key=value pairs after the chunk size, are discarded with this remark:

case s_chunk_parameters:
{
    assert(parser->flags & F_CHUNKED);
    /* just ignore this shit. TODO check for overflow */
    if (ch == CR) {
        state = s_chunk_size_almost_done;
        break;
    }
    break;
}

Chunk extensions are defined in RFC 7230 but rarely used in practice. The PHP built-in development server (php -S) uses this parser for incoming requests. The "TODO check for overflow" is legitimate: unbounded extension parameters are a memory exhaustion vector if someone sends a chunked body with infinitely long extensions.

↗ permalink

"There's been some horrible disaster"

Deep inside PCRE2's match loop, over 6,000 lines into pcre2_match.c, the default case of the main opcode switch is a code path that should never execute:

/* There's been some horrible disaster. Arrival here can only mean there is
   something seriously wrong in the code above or the OP_xxx definitions. */

default:
return PCRE2_ERROR_INTERNAL;

PCRE2's JIT compiler and interpreter share the same opcode set. The match engine dispatches on opcodes in a large switch; if execution reaches the default case, it means an opcode was defined but no handler was written, or the interpreter reached an instruction it doesn't understand. Returning PCRE2_ERROR_INTERNAL surfaces this as a hard error rather than silently continuing with corrupted state.

↗ permalink

"Compilers are dumb"

The __debugInfo() magic method must return an array. If it doesn't, the engine calls zend_error_noreturn(), which halts execution. Despite this, the function still has a return statement:

zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array");

return NULL; /* Compilers are dumb and don't understand that noreturn means
                that the function does NOT need a return value... */

zend_error_noreturn is annotated with ZEND_NORETURN (which expands to _Noreturn in C11 or the compiler-specific equivalent). In theory, the compiler should know execution never continues past it. In practice, some compiler versions still emit "control reaches end of non-void function" warnings without the dummy return. The comment is a small act of compiler appeasement.

↗ permalink

"STREAMS suck big time"

mysqlnd (MySQL Native Driver) manages persistent database connections. PHP's stream layer assumes connections are request-scoped, so cleanup is automatic. When mysqlnd wants to keep a connection alive across requests, it has to fight the stream system:

/*
  in_free will let streams code skip destructing - big HACK,
  but STREAMS suck big time regarding persistent streams.
  Just not compatible for extensions that need persistency.
*/
EG(persistent_list).pDestructor = NULL;
zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
EG(persistent_list).pDestructor = origin_dtor;

The approach: save the destructor, null it out, delete the entry (so the stream closing during request shutdown won't find it), then restore the destructor. The debug build adds:

/* Shut-up the streams, they don't know what they are doing */
php_stream_auto_cleanup(net_stream);

This push-pull between PHP's request-oriented stream architecture and persistent resources has been a pain point for extension authors since forever.

↗ permalink

"UGLY HACK" in xxHash

xxHash is a fast non-cryptographic hash algorithm. Its XXH32 loop has to fight the compiler's auto-vectorizer, which tries to use SSE4.1 to process four integers at once. According to the xxHash authors, this makes things slower:

/*
 * UGLY HACK:
 * A compiler fence is the only thing that prevents GCC and Clang from
 * autovectorizing the XXH32 loop (pragmas and attributes don't work for some
 * reason) without globally disabling SSE4.1.
 *
 * The reason we want to avoid vectorization is because despite working on
 * 4 integers at a time, there are multiple factors slowing XXH32 down on
 * SSE4:
 * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on
 *   newer chips!) making it slightly slower to multiply four integers at
 *   once compared to four integers independently.
 * ...
 */

The "UGLY HACK" comment appears seven times in the xxHash header. The fix, a compiler fence via XXH_COMPILER_GUARD(var), forces the compiler to treat the variable as clobbered between iterations, preventing vectorization. The inline assembly analysis of rotate latency (comparing roll vs SSE pslld/psrld/por) shows how deep this rabbit hole goes.

↗ permalink

goto statme_baby

PHP's phar extension intercepts filesystem stat calls to make files inside .phar archives appear as regular files. Two different code paths, one for files found directly in the manifest and one for files found after stripping the CWD prefix, both jump to the same label:

/* file found via direct manifest lookup */
goto statme_baby;

/* file found after stripping CWD, fallback path */
goto statme_baby;

The label applies write-protection adjustments before returning stat results:

statme_baby:
    if (!phar->is_writeable) {
        sb.st_mode = (sb.st_mode & 0555) | (sb.st_mode & ~0777);
    }

    sb.st_nlink = 1;
    sb.st_rdev = -1;
    /* this is only for APC, so use /dev/null device - no chance of conflict there! */
    sb.st_dev = 0xc;

The statme_baby name is unusual for a goto label in a C codebase. Most phar labels use boring names like stat_entry, notfound, or error. This one is the author having fun with the label that converges all writable-archive permission hardening in one place.

↗ permalink L523 · ↗ permalink L560 · ↗ permalink L597

"fire it up baby!"

The PDO Firebird driver connects to an InterBase/Firebird database with isc_attach_database(). Right before the call:

/* fire it up baby! */
if (isc_attach_database(H->isc_status, 0, vars[0].optval, &H->db,
    (short)(dpb-dpb_buffer), dpb_buffer)) {
    break;
}

The InterBase API is notoriously verbose. The database parameter buffer (DPB) has to be assembled byte-by-byte with type-length-value tuples before this call. By the time you reach isc_attach_database, you've earned some enthusiasm.

↗ permalink

"Ripemd laughs in the face of logic"

The RIPEMD hash family implementation opens with a note about byte ordering:

/* Heavily borrowed from md5.c & sha1.c of PHP archival fame
   Note that ripemd laughs in the face of logic and uses
   little endian byte ordering */

Most hash algorithms (MD5, SHA-1, SHA-2) use big-endian byte ordering in their specifications. RIPEMD-128 and RIPEMD-160, developed at KU Leuven as part of an EU project, specify little-endian. This means the reference implementation's byte-swapping logic runs in the opposite direction from the MD5/SHA1 code it was "heavily borrowed" from, earning it the comment about laughing in the face of logic.

↗ permalink

"i know, this is ugly, but i works"

On Windows, disk free space is reported through a ULARGE_INTEGER struct, two 32-bit values that together form a 64-bit number. The C type system doesn't map this cleanly:

/* i know - this is ugly, but i works <thies@thieso.net> */
*space = TotalNumberOfBytes.HighPart * (double) (((zend_ulong)1) << 31) * 2.0
         + TotalNumberOfBytes.LowPart;

The computation reconstructs the full 64-bit value as a double by multiplying the high part by 2^32 and adding the low part. The double cast is necessary because the intermediate values would overflow a 32-bit integer. The comment is signed by Thies C. Arntzen, one of the early PHP core developers who contributed heavily to the initial PHP 4 object model.

↗ permalink

"This is stupid way to do"

PostgreSQL has a rich type system. PHP's pgsql extension maps PG types to PHP types using a function that matches type name strings:

/* This is stupid way to do. I'll fix it when I decide how to support
   user defined types. (Yasuo) */
/* boolean */
if (zend_string_equals(type_name, ZSTR_KNOWN(ZEND_STR_BOOL))
    || zend_string_equals(type_name, ZSTR_KNOWN(ZEND_STR_BOOLEAN)))
    return PG_BOOL;

The function uses a chain of string comparisons against known PostgreSQL type names (bool, int2, int4, int8, float4, float8, etc.). A proper fix would use PostgreSQL's type OIDs, which are stable integer identifiers. The comment, signed by Yasuo Ohgaki, was written in 2002 and is still there. Classic temporary solution becoming permanent.

↗ permalink

"sharing globals is *evil*"

A one-line declaration of principle, sitting above a static variable in the file extension:

/* sharing globals is *evil* */
static int le_stream_context = FAILURE;

le_stream_context is a list entry type identifier for PHP stream contexts. In PHP's resource system, each resource type gets a unique integer ID assigned at module startup. The static qualifier keeps it file-local. If other parts of the file extension needed access, they'd go through the public php_le_stream_context() accessor instead of sharing the global directly. This comes from the ZTS (Zend Thread Safety) era where shared mutable globals were the enemy.

↗ permalink

"...return bollocks"

Phar archives support OpenSSL signatures. Both the verify and sign functions have a fallback for the edge case where the OpenSSL extension is loaded but its internal functions have been redefined in userland PHP code:

/* Unlikely, but the openssl_verify() function may be disabled and redefined
   in userland and return bollocks */
zval_ptr_dtor(&retval);
return false;
/* Unlikely, but the openssl_sign() function may be disabled and redefined in
   userland and return bollocks */
zval_ptr_dtor(&retval);
zval_ptr_dtor(&zp[1]);
return FAILURE;

The term "bollocks" (British slang for nonsense) appears twice, each time in a context where the phar extension calls an internal function that could theoretically be overridden by disable_functions combined with userland redefinition, a supported but unlikely configuration. Both code paths safely tear down the return value and fail explicitly rather than proceeding with potentially invalid signature data.

↗ permalink verify · ↗ permalink sign

"cross your fingers" / "voodoo approach"

The COM extension has some of the most self-aware comments in php-src. When resolving type information for a COM object, there's no guarantee the type library matches the dispatch interface:

/* cross your fingers... there is no guarantee that this ITypeInfo
 * instance has any relation to this IDispatch instance... */
ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo);

The .NET interop layer is explicitly described as a hack from the start:

/* Since there is no official public mscorlib.h header file, and since
 * generating your own version from the elusive binary .tlb file takes a lot of
 * hacking and results in a 3MB header file (!), we opt for this slightly
 * voodoo approach. ...
 *
 * The following info was obtained using OleView to export the IDL from
 * mscorlib.tlb.  Note that OleView is unable to generate C headers for this
 * particular tlb... hence this mess.
 */

The "voodoo approach" is manually declaring just enough of the _AppDomain interface to call CreateInstance, writing C struct definitions that match the vtable layout of the .NET runtime's COM-callable wrapper. Microsoft never shipped a public C header for mscorlib, so the options were a 3 MB generated header or hand-writing the minimal interface. "Hence this mess" sums it up.

↗ permalink cross-fingers · ↗ permalink voodoo

"TODO: Less crazy" / "ridiculously complex"

Two TODOs that capture the gap between what the code does and what the author wishes it did.

In Zend's object handlers, the comparison logic for objects that can't be directly compared has a clear directive:

// TODO: Less crazy.
if (target_type == IS_LONG || target_type == IS_DOUBLE) {
    zend_error(E_NOTICE, "Object of class %s could not be converted to %s", ...);

The Whirlpool hash implementation's Update and Final functions earned a similar note from an author who signed and dated their frustration:

/*
 * TODO: simplify Update and Final, those look ridiculously complex
 * Mike, 2005-11-23
 */

Mike's Whirlpool TODO is dated November 2005. Over two decades later, the implementation hasn't changed. That's a long-surviving TODO.

↗ permalink less-crazy · ↗ permalink whirlpool

"Stupid typo in PSDK 6.1"

Microsoft's Platform SDK 6.1 shipped with a typo in WinDNS.h, DnsSectionAddtional instead of DnsSectionAdditional. The PHP Windows DNS code works around it:

/* Stupid typo in PSDK 6.1, WinDNS.h(1258)... */
#ifndef DnsSectionAdditional
# ifdef DnsSectionAddtional
#  define DnsSectionAdditional DnsSectionAddtional
# else

The SDK header defined the symbol as DnsSectionAddtional (missing the 'i' before the 't'). Rather than break compilation on affected SDK versions, the code maps the misspelled name to the correct one. The comment's tone suggests the author spent real time tracking down why a valid Windows API constant was undefined.

↗ permalink

slimyhorror.com

The FastCGI SAPI file header credits Ben Mansell with an email address that has remained unchanged since the FastCGI code was first committed:

| FastCGI: Ben Mansell <php@slimyhorror.com>                           |

slimyhorror.com appears in the header comments of sapi/cgi/cgi_main.c and sapi/fpm/fpm/fpm_main.c. Ben Mansell was the original author of PHP's FastCGI implementation, which later became the basis for PHP-FPM. The domain name is a personal choice from the early 2000s that has survived two decades of code churn.

↗ permalink

Quake Save Files in fileinfo

The fileinfo extension embeds a compiled magic database that identifies file types. Among the professional MIME type signatures are entries for Quake I save files, identified by their map names:

Quake I save: e3m4 Satan's dark delight

Quake I save: e1m2 Castle of the damned

Quake I save: end Shub-Niggurath's pit

Quake I save: ddm5 Slaughterhouse

Quake I save: e4m3 The elder god shrine

These strings are embedded as binary data in ext/fileinfo/data_file.c, the compiled form of the file(1) magic database. The database is automatically generated from the upstream file utility's magic patterns, which include Quake save file detection. They're not comments, they're actual magic byte signatures that the file identification routines match against. But the map names embedded in them are a glimpse into id Software's level design naming habits circa 1996.




<!-- generated with nested tables and zero regrets -->