objects namespaces and application architecture
Properties and Methods
Properties store an object's data. Methods are functions that belong to the object and use or change that data.
In professional PHP, properties and methods should make a class easier to understand. Properties should represent state the object owns. Methods should represent behaviour the object can perform or questions it can answer.
Typed properties describe object state
Typed properties make the expected value clear.
<?php
declare(strict_types=1);
class Product
{
public string $sku;
public string $name;
public int $pricePennies;
}
$product = new Product();
$product->sku = 'KB-101';
$product->name = 'Keyboard';
$product->pricePennies = 2499;
echo $product->sku . ' / ' . $product->name . PHP_EOL;
// Prints:
// KB-101 / Keyboard
Typed properties catch obvious mistakes, but they do not replace business validation. An integer price can still be negative unless your class prevents it.
Methods use $this
Inside a method, $this refers to the current object.
<?php
declare(strict_types=1);
class ProductPrice
{
public int $pricePennies;
public function displayPrice(): string
{
return '£' . number_format($this->pricePennies / 100, 2);
}
}
$price = new ProductPrice();
$price->pricePennies = 2499;
echo $price->displayPrice() . PHP_EOL;
// Prints:
// £24.99
The method reads the object's property and returns a display value.
Give properties useful defaults
Defaults are useful when a new object has a sensible initial state.
<?php
declare(strict_types=1);
class ImportResult
{
public int $importedRows = 0;
public int $failedRows = 0;
public function hasFailures(): bool
{
return $this->failedRows > 0;
}
}
$result = new ImportResult();
$result->importedRows = 10;
echo $result->hasFailures() ? 'failed' : 'clean';
echo PHP_EOL;
// Prints:
// clean
Without defaults, reading a typed property before assigning it causes an error.
Methods can change object state
Some methods answer a question. Some methods change the object. Name them so the difference is clear.
<?php
declare(strict_types=1);
class Basket
{
public int $itemCount = 0;
public function addItem(): void
{
$this->itemCount++;
}
public function isEmpty(): bool
{
return $this->itemCount === 0;
}
}
$basket = new Basket();
$basket->addItem();
echo $basket->isEmpty() ? 'empty' : 'has items';
echo PHP_EOL;
// Prints:
// has items
addItem() changes state. isEmpty() returns information. Good names help reviewers predict side effects.
Avoid unrelated methods
A class should not become a random bag of functions.
<?php
declare(strict_types=1);
class Invoice
{
public int $totalPennies = 0;
public function totalForDisplay(): string
{
return '£' . number_format($this->totalPennies / 100, 2);
}
}
$invoice = new Invoice();
$invoice->totalPennies = 129950;
echo $invoice->totalForDisplay() . PHP_EOL;
// Prints:
// £1,299.50
Formatting an invoice total belongs close to invoice data. Sending emails, writing files, or querying a database probably belongs in a different class.
Watch uninitialised properties
Typed properties without defaults must be assigned before they are read.
<?php
declare(strict_types=1);
class Customer
{
public string $email;
}
$customer = new Customer();
$customer->email = 'nia@example.com';
echo $customer->email . PHP_EOL;
// Prints:
// nia@example.com
Later, constructors will make this safer by requiring important values when the object is created.
What to remember
Properties are the state an object owns. Methods are behaviour that reads or changes that state. Use types and defaults deliberately, name side-effecting methods clearly, and keep methods related to the class's responsibility.
Practice
Task: Track an import result
Create a class that tracks the result of an import.
Requirements
- Use
declare(strict_types=1);. - Create an
ImportResultclass. - Add typed public properties for imported row count, failed row count, and error messages.
- Give the counts sensible default values.
- Add a method that records a successful row.
- Add a method that records a failed row with an error message.
- Add a method that returns whether the import has failures.
- Print the final counts and failure status.
- Include the expected output as comments in the same PHP code block.
The goal is to show properties storing state and methods changing or reading that state.
Show solution
<?php
declare(strict_types=1);
class ImportResult
{
public int $importedRows = 0;
public int $failedRows = 0;
public array $errors = [];
public function recordImportedRow(): void
{
$this->importedRows++;
}
public function recordFailedRow(string $message): void
{
$this->failedRows++;
$this->errors[] = $message;
}
public function hasFailures(): bool
{
return $this->failedRows > 0;
}
}
$result = new ImportResult();
$result->recordImportedRow();
$result->recordImportedRow();
$result->recordFailedRow('Line 3 is missing an email.');
echo 'Imported: ' . $result->importedRows . PHP_EOL;
echo 'Failed: ' . $result->failedRows . PHP_EOL;
echo $result->hasFailures() ? 'has failures' : 'clean';
echo PHP_EOL;
echo $result->errors[0] . PHP_EOL;
// Prints:
// Imported: 2
// Failed: 1
// has failures
// Line 3 is missing an email.
The properties hold the current state of the import. The methods make the allowed state changes explicit and keep the failure check in one place.