PHP Comments
Comments are notes you leave inside your PHP source code that the PHP engine completely ignores when it runs your script. They let you explain what a piece of code does, why a decision was made, or temporarily disable code without deleting it. Because comments have zero effect on program behavior, you can use them freely without worrying about breaking anything – as long as you follow PHP’s comment syntax rules.
Overview: How PHP Comments Work
PHP supports two families of comments: single-line comments, which run from a marker to the end of the physical line, and block comments, which span from an opening marker to a matching closing marker anywhere in the file, even across many lines.
Under the hood, before PHP ever executes your code, it runs the source through a lexer (tokenizer) that breaks the raw text into tokens like T_STRING, T_VARIABLE, and T_ECHO. Ordinary comments become a token called T_COMMENT, and the parser simply discards these tokens – they never make it into the abstract syntax tree (AST) or the compiled opcodes that the Zend Engine executes. This means comments have essentially no runtime cost: a heavily commented file runs the same as an uncommented one, because by the time execution happens the comments are already gone.
There is one important exception: comments that start with /** (two asterisks) are called doc comments, and they become a distinct token, T_DOC_COMMENT. Unlike regular comments, doc comments are not thrown away – PHP attaches them to the class, method, property, or function that follows and makes them retrievable at runtime through the Reflection API (for example, ReflectionMethod::getDocComment()). This is how IDEs offer autocompletion and type hints, how documentation generators like PHPDoc build reference pages, and how some frameworks read annotation-style configuration straight out of your comments. So while comments are inert to the PHP runtime itself, doc comments form a lightweight metadata channel that real, production libraries depend on.
Syntax
PHP recognizes four comment markers:
| Style | Marker | Scope | Typical Use |
|---|---|---|---|
| C++-style single-line | // |
To end of line, or a closing ?> tag |
General inline notes |
| Shell-style single-line | # |
To end of line, or a closing ?> tag |
Same behavior as //, style preference |
| Block comment | /* ... */ |
Everything between the markers, including multiple lines | Multi-line explanations, temporarily disabling code |
| Doc comment | /** ... */ |
Same as block comment, but retained for Reflection | Documenting classes/functions for IDEs and tools |
//and#cannot be nested inside another single-line comment – each simply ends at the line break./* */blocks cannot be nested inside another/* */block; the first*/encountered closes the comment, no matter how many/*appeared before it.- Both
//and#stop at the end of the current line, or at a closing?>tag, whichever comes first.
Examples
Example 1: Basic comment styles
<?php
// This is a single-line comment
# This is also a single-line comment (shell-style)
/*
* This is a multi-line comment
* that spans several lines
*/
$name = "Ada Lovelace"; // inline comment explaining the variable
echo "Hello, $name!" . PHP_EOL;
/* echo "This line is disabled"; */
echo "This line still runs." . PHP_EOL;
Output:
Hello, Ada Lovelace!
This line still runs.
The two comment lines at the top and the block comment produce no output at all – they’re discarded before execution. The inline // comment after the $name assignment doesn’t stop the statement from running; it just explains it. The block comment /* echo "This line is disabled"; */ completely removes that echo call, so only two lines are ever printed.
Example 2: Using comments to toggle debug output
<?php
function calculateTotal(float $price, int $quantity): float {
$total = $price * $quantity;
// Debugging line - uncomment to inspect intermediate value
// echo "Debug: total before tax = $total" . PHP_EOL;
$tax = $total * 0.08;
return $total + $tax;
}
$result = calculateTotal(19.99, 3);
echo "Final total: $" . number_format($result, 2) . PHP_EOL;
Output:
Final total: $64.77
This is one of the most common real-world uses of comments: a diagnostic echo is left in the code but disabled with //. A developer debugging a wrong total can delete the two leading slashes, rerun the script to see the intermediate value, and re-comment the line afterward – without retyping it.
Example 3: Doc comments for tooling and reflection
<?php
/**
* Represents a simple bank account.
*
* @property float $balance Current balance in USD
*/
class BankAccount
{
private float $balance;
/**
* Create a new account with an optional starting balance.
*
* @param float $balance Starting balance (default 0.0)
*/
public function __construct(float $balance = 0.0)
{
$this->balance = $balance;
}
/**
* Deposit money into the account.
*
* @param float $amount Amount to deposit
* @return float New balance
*/
public function deposit(float $amount): float
{
// Guard clause: ignore invalid deposits
if ($amount <= 0) {
return $this->balance;
}
$this->balance += $amount;
return $this->balance;
}
}
$account = new BankAccount(100.0);
echo $account->deposit(50.5) . PHP_EOL;
Output:
150.5
Nothing in this script explicitly reads the /** ... */ doc comments, yet they aren’t wasted: your IDE parses them statically to show parameter hints and return types as you type, and any code that calls (new ReflectionMethod('BankAccount', 'deposit'))->getDocComment() could retrieve that exact comment text at runtime. Regular // comments, like the guard clause note, get discarded and are never retrievable this way.
How It Works Step by Step (Under the Hood)
- The lexer scans the source file character by character.
- When it encounters
//,#, or/*, it switches into a comment-scanning state. - For
//and#, it consumes characters until it hits a newline or a closing?>tag. - For
/*, it consumes characters until it finds the exact two-character sequence*/– it does not count how many/*it has seen, so an inner/*has no special meaning. - If the comment began with
/**, the lexer emits aT_DOC_COMMENTtoken that gets attached to the following declaration; otherwise it emits a plainT_COMMENTtoken, which the parser drops entirely. - Parsing then continues immediately after the comment, exactly as if the comment text had never been there.
Common Mistakes
Mistake 1: Trying to nest block comments
<?php
/*
This is the outer comment
/* attempting a nested comment */
still meant to be inside the outer comment
*/
echo "Hello";
Why it’s wrong: PHP’s block comment ends at the first */ it finds. So the comment actually closes right after “attempting a nested comment */”, leaving the text “still meant to be inside the outer comment” followed by a stray */ as raw PHP code – which is a parse error.
<?php
/*
This is the outer comment.
// A nested block comment isn't needed - a line comment
// works fine inside a block comment.
still inside the outer comment
*/
echo "Hello";
Output:
Hello
The fix is simple: never put a second /* inside a block comment. If you need comment-like notes inside a larger block comment, plain text or //-prefixed lines are safe, since they carry no special meaning once you’re already inside a comment.
Mistake 2: Using non-PHP comment syntax
<?php
-- This looks like a SQL comment, but PHP doesn't support "--"
$x = 5;
echo $x;
Why it’s wrong: Developers coming from SQL or other languages sometimes assume -- starts a comment. PHP has no such syntax – the parser instead tries to interpret -- as two decrement operators applied to nothing, producing a parse error.
<?php
// This is the correct PHP comment style
$x = 5;
echo $x;
Output:
5
Always use //, #, or /* */ – PHP has no other comment syntax, regardless of what other languages you’re used to.
Best Practices
- Comment the “why”, not the “what” – well-named variables and functions should already say what the code does; use comments to explain reasoning, trade-offs, or workarounds.
- Use
/** ... */doc comments above classes, methods, and functions you want IDEs, static analyzers, or frameworks to understand. - Keep comments up to date – an outdated comment that contradicts the code is worse than no comment, because it actively misleads the next reader.
- Use
//or#for short, single-line notes; reserve/* */for longer explanations or temporarily disabling multi-line blocks of code while debugging. - Avoid leaving large chunks of commented-out dead code in the codebase permanently – delete it and rely on version control (like Git) to recover it if you ever need it again.
- Pick one single-line style, either
//or#, and use it consistently across a project; mixing both without reason makes a codebase feel inconsistent.
Practice Exercises
- Write a script that declares a variable
$price, uses a single-line comment to explain what it represents, and a block comment to temporarily disable an unused second calculation line. - Add a proper
/** ... */doc comment above a functioncalculateDiscount(float $price, float $percent): floatthat documents its parameters and return value. - Take any short PHP snippet you’ve written before and rewrite its comments to explain only the “why” behind any tricky lines, rather than restating what each line already makes obvious.
Summary
- PHP has two single-line comment styles,
//and#, that run to the end of the line or a closing?>tag. - Block comments (
/* ... */) can span multiple lines but cannot be nested – the first*/always closes the comment. - Regular comments are discarded by the lexer before execution and add no runtime cost.
- Doc comments (
/** ... */) are the exception: PHP retains them so the Reflection API, IDEs, and frameworks can read them at runtime or statically. - Good comments explain why code exists, not what it does; keep them accurate, and prefer deleting dead code over permanently commenting it out.
