Milestone: Values And Expressions
This milestone combines the first four lessons into one coherent script. It does not introduce branching, loops, arithmetic totals, comparisons, or functions of your own; those skills come next.
The goal is to prove that you can structure a PHP file, name values accurately, preserve their types, validate boundary text, produce stable output, and explain each expression that PHP evaluates.
What An Expression Is
An expression is code that produces a value. You have already used several forms without needing the formal name:
<?php
$productName = 'Notebook';
The string literal 'Notebook' is an expression whose value is a string. Assignment stores that value in $productName.
Reading the variable is also an expression:
<?php
$productName = 'Notebook';
echo $productName, PHP_EOL;
The variable expression produces its current string value for echo.
A function call can produce a value:
<?php
$typeName = get_debug_type(1299);
echo $typeName, PHP_EOL;
get_debug_type(1299) evaluates to the string 'int', which assignment stores in $typeName.
An array literal is another expression:
<?php
$tags = ['paper', 'office'];
var_dump($tags);
The next lesson introduces operators that combine expressions into calculations and comparisons. This checkpoint first makes sure the underlying values and boundaries are understood.
Build An Inventory Intake Report
Create inventory-report.php:
<?php
const REPORT_TITLE = 'Inventory intake';
const CURRENCY = 'GBP';
$productName = 'Notebook';
$sku = '00125';
$priceCents = 1299;
$quantityInput = '3';
$isActive = true;
$discountCents = null;
$tags = ['paper', 'office'];
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
echo REPORT_TITLE, PHP_EOL;
echo "Product: $productName\n";
echo "SKU: $sku\n";
echo "Currency: ", CURRENCY, PHP_EOL;
echo "Price: $priceCents cents\n";
echo "Quantity input: $quantityInput\n";
echo 'Quantity input type: ', get_debug_type($quantityInput), PHP_EOL;
echo 'Validated quantity type: ', get_debug_type($quantity), PHP_EOL;
echo 'Active value: ';
var_dump($isActive);
echo 'Discount value: ';
var_dump($discountCents);
echo 'Tags value: ';
var_dump($tags);
The report deliberately keeps the raw input and validated value in separate variables. $quantityInput remains the original string; $quantity contains the validation result. That separation preserves evidence and prevents a conversion from hiding what entered the program.
Predict The Stable Output First
Before running the file, predict the beginning of its output:
Inventory intake
Product: Notebook
SKU: 00125
Currency: GBP
Price: 1299 cents
Quantity input: 3
Quantity input type: string
Validated quantity type: int
Active value: bool(true)
Discount value: NULL
Tags value: array(2) {
The complete array dump continues with its indexed string elements. Exact var_dump() formatting is diagnostic output supplied by PHP, while the labelled lines above it are application output designed by the script.
Run both checks:
php -l inventory-report.php
php inventory-report.php
A successful lint confirms that PHP can parse the source. The execution confirms that expressions produce the intended values and output.
Trace Each Value From Source To Output
A useful review method is to trace one value at a time.
Product Name
$productName = 'Notebook';
The literal is a string. The variable preserves that string. Interpolation places it in the output line Product: Notebook.
SKU
$sku = '00125';
The SKU remains a string because it is an identifier, not a quantity. Keeping the type preserves both leading zeroes. Converting it to an integer would change its representation and meaning.
Price
$priceCents = 1299;
The price is an integer number of minor currency units. The variable name states the unit. No calculation is required yet; the next lesson will use integer arithmetic expressions.
Quantity Boundary
$quantityInput = '3';
The raw value is a string, matching how form fields, CLI arguments, and environment variables commonly enter PHP.
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
The function-call expression evaluates to integer 3 when the input satisfies the positive-integer contract. It evaluates to boolean false when the input is invalid. A later control-flow lesson will show how to stop or choose another path based on that result.
Boolean And Null
$isActive = true;
$discountCents = null;
The boolean explicitly records a two-state fact. null explicitly records the absence of a discount value. var_dump() makes both states visible instead of allowing string conversion to make false or null look blank.
Tags
$tags = ['paper', 'office'];
The array expression creates a two-item list of strings. This milestone only inspects the list. The arrays lesson later teaches access, iteration, filtering, mapping, and sorting.
Distinguish Application Output From Diagnostics
These lines are designed output:
echo "Product: $productName\n";
echo "SKU: $sku\n";
Their labels and layout form a small output contract.
These lines are diagnostic:
echo 'Active value: ';
var_dump($isActive);
The label helps the reader connect the dump to a variable, but var_dump() is still a debugging representation. A finished JSON endpoint, HTML page, or command should format its data deliberately rather than exposing arbitrary dumps.
A milestone can use both because the learner needs to verify the contract and inspect the underlying values. Know which purpose each line serves.
Keep Raw And Validated Values Separate
Do not overwrite the raw input immediately:
$quantityInput = filter_var($quantityInput, ...);
That style loses the original text. When validation fails, logs and debugging no longer have the exact boundary value unless it was preserved elsewhere.
Prefer names that describe the transition:
<?php
$quantityInput = '3';
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
Now the type change is visible in the names: input text enters, validated application value comes out.
This pattern scales to dates, email addresses, identifiers, configuration values, and decoded request data. The validator and target type change, but the boundary remains explicit.
Make One Controlled Change At A Time
Change $quantityInput and predict the validation result before running the file:
| Input string | Expected result type | Reason |
|---|---|---|
'3' |
int |
Valid integer at least 1 |
'0' |
bool containing false |
Below minimum |
'-2' |
bool containing false |
Below minimum |
'3 items' |
bool containing false |
Contains unsupported text |
'' |
bool containing false |
No integer value |
Do not change five values and then wonder which one caused the output difference. One controlled edit creates one interpretable observation.
You can also change $discountCents from null to 200. Predict that the dump changes from NULL to int(200). That is a domain-state change as well as a type change: the product moves from no assigned discount to a known 200-cent discount.
Check Naming And Representation
Review every name before considering the milestone complete:
- does
$priceCentsinclude its unit? - does
$quantityInputidentify boundary text? - does
$quantityrepresent the validated application value? - does
$isActiveread as a boolean question? - does
$discountCentsallownullaccording to a stated meaning? - does
$skuremain text because leading zeroes matter? - are constants reserved for stable code rules?
A script can execute successfully while its names hide important assumptions. Correct output is necessary, but readable contracts make later changes safer.
Know What This Milestone Does Not Prove
This script does not yet:
- calculate a subtotal;
- compare a price with a threshold;
- choose a message with
iformatch; - repeat work with a loop;
- define a reusable function;
- recover from invalid input;
- format currency for a user interface.
Those omissions are deliberate. A milestone should verify completed skills, not quietly depend on lessons that have not happened yet.
The next lesson introduces arithmetic, assignment, comparison, logical, concatenation, null-coalescing, and conditional expressions. Once those operators are understood, the same values can participate in calculations and decisions.
Report What The Program Actually Knows
At this stage, the script can inspect a failed validation result but does not yet choose a recovery path. Do not replace false with a made-up quantity merely to keep the report looking complete. Preserve the failure, inspect it clearly, and wait for the control-flow lessons to add an explicit decision such as rejecting input or requesting a correction.
A visible failed contract is better evidence than a plausible default whose origin is hidden.
Milestone Verification Checklist
Before moving on, confirm that you can:
- create a code-only PHP file with no closing tag;
- lint and execute it from the intended directory;
- distinguish variables from constants;
- explain the type and domain meaning of every value;
- preserve a digit-only identifier as a string;
- validate a positive integer input without blindly casting it;
- explain why invalid validation results are boolean
false; - use interpolation, concatenation, and comma-separated output deliberately;
- distinguish designed output from diagnostics;
- change one input and predict the resulting type before rerunning.
What You Should Be Able To Do
After this milestone, you should be able to trace a value from literal or boundary input through assignment, validation, type inspection, and output. You should also be able to explain the expressions already present in beginner PHP without relying on arithmetic or branching that has not yet been taught.
If any value's type, meaning, or output surprises you, revisit the relevant earlier lesson and rerun the smallest example that isolates that boundary.
Practice
Practice: Trace An Intake Record
<?php
const SOURCE = 'manual-entry';
$productName = 'Pencil';
$sku = '00042';
$quantityInput = '4';
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
$discountCents = null;
echo 'Source: ', SOURCE, PHP_EOL;
echo "Product: $productName\n";
echo "SKU: $sku\n";
echo 'Input type: ', get_debug_type($quantityInput), PHP_EOL;
echo 'Quantity type: ', get_debug_type($quantity), PHP_EOL;
echo 'Discount: ';
var_dump($discountCents);
Task
- correct any source-formatting issue that should not be retained
- write the exact output in a text block
- identify the type of every variable and the constant value
- identify one literal expression, one variable expression, and two function-call expressions
- explain why the SKU remains
00042 - lint and run the corrected script
Do not add arithmetic, comparisons, or branching.
Show solution
<?php
const SOURCE = 'manual-entry';
$productName = 'Pencil';
$sku = '00042';
$quantityInput = '4';
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
$discountCents = null;
echo 'Source: ', SOURCE, PHP_EOL;
echo "Product: $productName\n";
echo "SKU: $sku\n";
echo 'Input type: ', get_debug_type($quantityInput), PHP_EOL;
echo 'Quantity type: ', get_debug_type($quantity), PHP_EOL;
echo 'Discount: ';
var_dump($discountCents);
Output:
Source: manual-entry
Product: Pencil
SKU: 00042
Input type: string
Quantity type: int
Discount: NULL
$productName, $sku, and $quantityInput are strings. $quantity is an integer because validation succeeds, and $discountCents is null. SOURCE has the string value manual-entry.
'Pencil' is a literal expression. $productName is a variable expression. filter_var(...) and get_debug_type(...) are function-call expressions. The SKU remains 00042 because it stays a string; integer conversion would discard meaningful leading zeroes.
Practice: Change One Boundary Value
<?php
$quantityInput = '3';
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
echo 'Raw value: ';
var_dump($quantityInput);
echo 'Validated value: ';
var_dump($quantity);
Run the script separately with each input:
'3'
'0'
'-2'
'3 items'
''
Task
- change only
$quantityInputbetween runs - predict the two dump types and values before each run
- record the actual results in a five-row table
- keep the raw and validated variables separate
- do not add a cast, condition, comparison, or fallback value
Afterward, explain why invalid input should remain visibly failed at this milestone rather than being replaced by a made-up quantity.
Show solution
$quantityInput |
Raw dump | Validated dump |
|---|---|---|
'3' |
string(1) "3" |
int(3) |
'0' |
string(1) "0" |
bool(false) |
'-2' |
string(2) "-2" |
bool(false) |
'3 items' |
string(7) "3 items" |
bool(false) |
'' |
string(0) "" |
bool(false) |
The script remains structurally the same for every run:
<?php
$quantityInput = '3';
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
echo 'Raw value: ';
var_dump($quantityInput);
echo 'Validated value: ';
var_dump($quantity);
Only the first input satisfies the positive-integer contract. The other results remain false, preserving the fact that validation failed. Replacing failure with an arbitrary quantity such as 1 would hide bad input and make the report claim knowledge the program does not have. A later control-flow lesson will decide how the program responds to that failure.
Practice: Build A Typed Item Intake Summary
- report title:
New item intake - currency:
GBP - product:
Blue Pen - SKU:
00750 - price:
150cents - raw quantity input:
'4' - active state:
true - discount:
null - tags:
['stationery', 'blue']
Requirements
- use constants for the report title and currency
- use descriptive variables for all record values
- keep the SKU as a string
- validate the quantity as an integer with minimum
1 - keep raw input and validated quantity in different variables
- print labelled lines for the title, product, SKU, currency, price, and quantity types
- inspect the active state, discount, and tags with labelled
var_dump()output - use a code-only PHP file with no closing tag
- lint and execute the script
Afterward, write a short audit listing every expression that produces a value and explain why no subtotal or shipping decision belongs in this milestone yet.
Show solution
<?php
const REPORT_TITLE = 'New item intake';
const CURRENCY = 'GBP';
$productName = 'Blue Pen';
$sku = '00750';
$priceCents = 150;
$quantityInput = '4';
$isActive = true;
$discountCents = null;
$tags = ['stationery', 'blue'];
$quantity = filter_var(
$quantityInput,
FILTER_VALIDATE_INT,
['options' => ['min_range' => 1]],
);
echo REPORT_TITLE, PHP_EOL;
echo "Product: $productName\n";
echo "SKU: $sku\n";
echo 'Currency: ', CURRENCY, PHP_EOL;
echo "Price: $priceCents cents\n";
echo 'Quantity input type: ', get_debug_type($quantityInput), PHP_EOL;
echo 'Validated quantity type: ', get_debug_type($quantity), PHP_EOL;
echo 'Active: ';
var_dump($isActive);
echo 'Discount: ';
var_dump($discountCents);
echo 'Tags: ';
var_dump($tags);
The value-producing expressions include every literal on the right side of an assignment, the tag array literal, reads of constants and variables, filter_var(...), and each get_debug_type(...) call. The output constructs also evaluate their supplied values before writing them.
The SKU remains a five-character string, the price remains exact integer cents, the raw quantity remains a string, and the validated quantity becomes an integer.
A subtotal would require arithmetic operators, and a shipping decision would require comparison plus a choice. Those belong after the operators and control-flow lessons rather than being used before their behavior has been taught.