Side-by-side, interactive cheatsheets for PHP programmers
comparing PHP to other languages. Every example runs live in your browser β no setup, no installation.
Choose your own path by reordering languages
The language a PHP developer already half-knows. Ruby shares PHP's dynamic typing, heredocs, and => hash syntax, but drops the $ sigils and global function soup for a model where everything is an object β the language behind Rails, and the inspiration for much of modern Laravel.
$ sigils and no <?php tag β a file is Ruby from the first byte; bare names are variables.upcase or .length on the value itself instead of reaching for global strtoupper()/strlen()array.map { ... } and each replace foreach and standalone array_map/array_filternil and false are falsy β 0 and "" are truthy, and == never juggles types the way PHP's loose == doesmodule with include Comparable or Enumerable works like a trait but composes through the method-lookup chainThe web backend, recompiled. A PHP developer reaches for Go when a request handler needs to be a fast, concurrent, single static binary β trading dynamic typing and per-request scripts for a compiler, goroutines, and errors-as-values.
null surprises, no type jugglingif err != nil β there are no exceptions for ordinary failuresimplements; visibility is set by capitalizationThe other half of the web you already half-share. A PHP developer knows JavaScript exists in every browser; this is the leap to using it as a real language β one runtime, an event loop instead of per-request scripts, and an async model PHP has no equivalent for.
async/await β non-blocking I/O and concurrent work, where PHP blocks and runs each request freshnumber type and + overloaded for concat β 5 + "3" is "53", unlike PHP's separate . operatornull and undefined, where PHP has only null=== with coercion rules that differ from PHP's == β your juggling intuition does not transferthis depends on how a method is calledThe other dominant dynamic language β and PHP's closest peer. Python keeps the gradual typing and quick-iteration feel you know, but trades $ sigils, semicolons, <?php tags, and one all-purpose array for significant whitespace, distinct list/dict/tuple/set types, and comprehensions.
$ on variables, no semicolons, no <?php β significant whitespace defines blocks[n*2 for n in items] β replace array_map / array_filter== with no type juggling and no ===; is only for identity. and an explicit self instead of -> and implicit $thisvendor/The machine underneath the web. PHP itself is written in C β this is the leap from a garbage-collected, dynamically typed scripting language down to manual memory, raw pointers, and a compiler that checks every type before the program runs.
malloc/free with no garbage collector; what a PHP variable hides\0 β no string object, no .length, size buffers yourself== that compares addressesModern C, made memory-safe. Rust is a compiled systems language with no garbage collector β but where C hands you raw pointers, Rust gives you ownership and borrowing, no null, no exceptions, exhaustive matching, and traits, all checked before the program runs.
malloc/freenull: absence is Option<T>, and the compiler makes you handle itResult<T, E> with the ? operatorlet mut to opt into mutationString (owned) and &str (borrowed) β where PHP has onemixedGradual typing, taken further. PHP 8 gave you type declarations, union types, ?->, and ?? β TypeScript is that same instinct made structural and checked by a real compiler, sitting on top of the JavaScript runtime (one number type, ===, an event loop).
number | string) and nullable types β the same idea as PHP 8's int|string and ?stringimplements clause<T>) and typed collections, where PHP has only @template docblocksnumber type, both null and undefined, and built-in async/awaitThe language modern PHP keeps borrowing from. Match expressions, backed enums, named arguments, nullable types, readonly, first-class callables, string interpolation, and #[Attributes] all came to PHP from C# β so much of C# will feel like coming home, now compiled and statically typed.
??, ?., and #[Attributes] β features PHP 8 adopted straight from C#Where/Select/GroupBy chains that read left-to-right, replacing nested array_map/array_filterinit-only properties β value equality and immutability beyond PHP's readonly__get/__set magic methodsasync/await and Task.WhenAll β concurrency PHP needs Fibers or Swoole forThe enterprise backend PHP is so often weighed against. Java is statically typed, compiled to JVM bytecode, and object-oriented to the core β no top-level code, a main class, explicit types everywhere β trading PHP's quick-script feel for generics, streams, records, sealed types, and a checked-exception contract.
main method is the entry pointmixed and @template docblocksfilter/map/collect) in place of array_map/array_filter== vs .equals(), Integer caching, and NullPointerExceptionThe language you inherit rather than choose. A PHP shop rarely picks Erlang β it ends up on call for RabbitMQ, ejabberd, CouchDB or the deepest layer of an Elixir dependency, and has to read the source. This page is angled at exactly that: the punctuation, the module header, the raw OTP callbacks and the three kinds of string, in the order you meet them opening a .erl file.
Uppercase is a variable, lowercase is an atom, and a bare word you do not recognize is almost certainly a value rather than a name{ok, Value} and {error, Reason} are the convention behind every API you will read, and {ok, X} = call() is an assertion that deliberately crashesgen_server file is two halves: the public API on top that sends messages, the callbacks below that run inside the process β and the return tuples are the whole protocollist_to_atom on user input can take a node down β the atom table is fixed-size and never garbage collectedYour front end, with the errors moved to build time. PHP 8 already taught you type declarations, enums, match and ?-> β Elm is those instincts taken to the end of the road. Every type is checked before the program exists rather than when the line runs, null is not a value the language has, and there is no throw anywhere: markup is a typed value, not a string you assembled and hoped was valid HTML.
TypeError on somebody's requestMaybe is a different type from String, so the null check cannot be forgotten the way ?string lets it bethrow, no try, no catch β failure is a Result in the signature, visible to every caller instead of unwinding to one that lost the contextdiv [ attributes ] [ children ] is a function call, so htmlspecialchars has nothing to protect and an unclosed tag cannot existjson_decode trusts the payload; a decoder states what you expect and fails at the boundary, naming the field the server renamedThe language that keeps running between requests. PHP builds a whole world, serves one request with it, and throws it away β which is why every PHP application reaches for Redis or the database to remember anything at all. The BEAM starts once and stays up, so state lives in memory, in processes that are cheap enough to have millions of, and a failure restarts one of them instead of the request.
= is pattern matching, not assignment, and it reshapes everything: case takes data apart while it chooses, and a function gets several heads instead of a branching body{:ok, value} / {:error, reason} replaces the exception you throw and the null that json_decode returns β the failure is in the return value, so it cannot be ignored by accidentreport(rows, separator: " | ")return, no while and no elseif: with replaces the stack of guard clauses, recursion replaces the loop, and cond replaces the chain