Arrays and Objects

Overview

Arrays and objects are fundamental data structures in PHP. Arrays store collections of data, while objects encapsulate data and behavior. This chapter covers array operations, object-oriented programming basics, and handling complex data structures.

Arrays in Depth

Array Creation and Types

<?php
// Indexed arrays
$fruits = array("apple", "banana", "orange");
$numbers = [1, 2, 3, 4, 5]; // Short syntax (PHP 5.4+)

// Associative arrays
$person = array(
    "name" => "Zhang San",
    "age" => 30,
    "city" => "Beijing"
);

$config = [
    "database" => "mysql",
    "host" => "localhost",
    "port" => 3306
];

// Mixed arrays
$mixed = [
    0 => "First item",
    "key" => "Value",
    1 => "Second item",
    "nested" => ["a", "b", "c"]
];

// Multidimensional arrays
$matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

$users = [
    [
        "id" => 1,
        "name" => "Xiaoming",
        "email" => "xiaoming@example.com",
        "roles" => ["user", "editor"]
    ],
    [
        "id" => 2,
        "name" => "Xiaohong",
        "email" => "xiaohong@example.com",
        "roles" => ["user"]
    ]
];
?>

Array Access and Modification

<?php
$fruits = ["apple", "banana", "orange"];

// Access elements
echo $fruits[0]; // "apple"
echo $fruits[1]; // "banana"

// Modify elements
$fruits[1] = "grape";
echo $fruits[1]; // "grape"

// Add elements
$fruits[] = "kiwi";           // Append to end
$fruits[10] = "mango";        // Specify index
array_push($fruits, "peach"); // Append using function
array_unshift($fruits, "strawberry"); // Prepend

// Remove elements
unset($fruits[2]);            // Remove specified index
$lastFruit = array_pop($fruits);     // Remove and return last
$firstFruit = array_shift($fruits);  // Remove and return first

// Associative array operations
$person = ["name" => "Zhang San", "age" => 30];

// Add/modify
$person["email"] = "zhangsan@example.com";
$person["age"] = 31;

// Check existence
if (isset($person["email"])) {
    echo "Email exists: " . $person["email"];
}

if (array_key_exists("phone", $person)) {
    echo "Phone exists";
} else {
    echo "Phone not set";
}

// Delete
unset($person["age"]);
?>

Array Functions and Operations

<?php
$numbers = [3, 1, 4, 1, 5, 9, 2, 6];

// Array information
echo count($numbers);        // 8 (length)
echo sizeof($numbers);       // 8 (alias of count)
echo array_sum($numbers);    // 31 (sum)
echo array_product($numbers); // 6480 (product)

// Min/max values
echo min($numbers);          // 1
echo max($numbers);          // 9

// Search
$position = array_search(5, $numbers);  // Returns first matching index
$exists = in_array(4, $numbers);        // true/false

// Sorting
$sorted = $numbers;
sort($sorted);               // Sort by value, lose keys
print_r($sorted);

$reversed = $numbers;
rsort($reversed);            // Reverse sort
print_r($reversed);

// Associative array sorting
$people = [
    "zhangsan" => 30,
    "lisi" => 25,
    "wangwu" => 35
];

asort($people);              // Sort by value, keep keys
print_r($people);            // lisi=>25, zhangsan=>30, wangwu=>35

ksort($people);              // Sort by key
print_r($people);            // lisi=>25, wangwu=>35, zhangsan=>30

// Custom sorting
$users = [
    ["name" => "Zhang San", "age" => 30],
    ["name" => "Li Si", "age" => 25],
    ["name" => "Wang Wu", "age" => 35]
];

usort($users, function($a, $b) {
    return $a["age"] <=> $b["age"]; // Sort by age
});

print_r($users);
?>

Array Operation Functions

<?php
$numbers = [1, 2, 3, 4, 5];

// Map - transform each element
$doubled = array_map(function($n) {
    return $n * 2;
}, $numbers);
print_r($doubled); // [2, 4, 6, 8, 10]

// Filter - filter elements based on condition
$evens = array_filter($numbers, function($n) {
    return $n % 2 === 0;
});
print_r($evens); // [2, 4]

// Reduce - combine elements into single value
$sum = array_reduce($numbers, function($carry, $item) {
    return $carry + $item;
}, 0);
echo $sum; // 15

// Array merging
$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$merged = array_merge($arr1, $arr2);
print_r($merged); // [1, 2, 3, 4, 5, 6]

// Array slicing
$slice = array_slice($numbers, 1, 3); // Start at index 1, take 3 elements
print_r($slice); // [2, 3, 4]

// Array chunking
$chunks = array_chunk($numbers, 2);
print_r($chunks); // [[1, 2], [3, 4], [5]]

// Array keys and values
$person = ["name" => "Zhang San", "age" => 30, "city" => "Beijing"];
$keys = array_keys($person);     // ["name", "age", "city"]
$values = array_values($person); // ["Zhang San", 30, "Beijing"]

// Flip keys and values
$flipped = array_flip($person);
print_r($flipped); // ["Zhang San" => "name", 30 => "age", "Beijing" => "city"]
?>

Advanced Array Techniques

<?php
// Array walk - apply function to each element
$fruits = ["apple", "banana", "orange"];

array_walk($fruits, function(&$fruit, $key) {
    $fruit = strtoupper($fruit);
});
print_r($fruits); // ["APPLE", "BANANA", "ORANGE"]

// Array column extraction - extract column from multidimensional array
$users = [
    ["id" => 1, "name" => "Zhang San", "email" => "zhangsan@example.com"],
    ["id" => 2, "name" => "Li Si", "email" => "lisi@example.com"],
    ["id" => 3, "name" => "Wang Wu", "email" => "wangwu@example.com"]
];

$names = array_column($users, "name");
print_r($names); // ["Zhang San", "Li Si", "Wang Wu"]

$emailsById = array_column($users, "email", "id");
print_r($emailsById); // [1 => "zhangsan@example.com", 2 => "lisi@example.com", ...]

// Array combine - create array using one array as keys, another as values
$keys = ["name", "age", "city"];
$values = ["Zhang San", 30, "Beijing"];
$combined = array_combine($keys, $values);
print_r($combined); // ["name" => "Zhang San", "age" => 30, "city" => "Beijing"]

// Array intersection and difference
$array1 = [1, 2, 3, 4, 5];
$array2 = [3, 4, 5, 6, 7];

$intersection = array_intersect($array1, $array2); // [3, 4, 5]
$difference = array_diff($array1, $array2);        // [1, 2]

// Unique values
$duplicates = [1, 2, 2, 3, 3, 3, 4];
$unique = array_unique($duplicates);
print_r($unique); // [1, 2, 3, 4]
?>

Object Basics

Class Definition and Instantiation

<?php
// Basic class definition
class Person {
    // Properties (attributes)
    public $name;
    public $age;
    public $email;
    
    // Constructor
    public function __construct($name, $age, $email) {
        $this->name = $name;
        $this->age = $age;
        $this->email = $email;
    }
    
    // Methods (functions)
    public function introduce() {
        return "Hello, I am {$this->name}, {$this->age} years old.";
    }
    
    public function getEmail() {
        return $this->email;
    }
    
    public function setEmail($email) {
        if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->email = $email;
            return true;
        }
        return false;
    }
}

// Create objects (instantiation)
$person1 = new Person("Xiaohong", 25, "xiaohong@example.com");
$person2 = new Person("Xiaoming", 30, "xiaoming@example.com");

// Using objects
echo $person1->introduce(); // "Hello, I am Xiaohong, 25 years old."
echo $person1->name;        // "Xiaohong"

$person1->setEmail("xiaohong.new@example.com");
echo $person1->getEmail();  // "xiaohong.new@example.com"
?>

Property Visibility

<?php
class BankAccount {
    public $accountNumber;    // Accessible from anywhere
    protected $balance;       // Accessible from this class and subclasses
    private $pin;            // Accessible only from this class
    
    public function __construct($accountNumber, $initialBalance, $pin) {
        $this->accountNumber = $accountNumber;
        $this->balance = $initialBalance;
        $this->pin = $pin;
    }
    
    public function getBalance() {
        return $this->balance;
    }
    
    public function deposit($amount) {
        if ($amount > 0) {
            $this->balance += $amount;
            return true;
        }
        return false;
    }
    
    public function withdraw($amount, $enteredPin) {
        if ($this->validatePin($enteredPin) && $amount > 0 && $amount <= $this->balance) {
            $this->balance -= $amount;
            return true;
        }
        return false;
    }
    
    private function validatePin($enteredPin) {
        return $this->pin === $enteredPin;
    }
    
    protected function calculateInterest($rate) {
        return $this->balance * $rate;
    }
}

$account = new BankAccount("12345", 1000, "1234");

echo $account->accountNumber;  // OK (public)
echo $account->getBalance();   // OK (public method)

// These would cause errors:
// echo $account->balance;     // Error (protected)
// echo $account->pin;         // Error (private)
// $account->validatePin("1234"); // Error (private method)
?>

Static Properties and Methods

<?php
class Counter {
    private static $count = 0;
    public static $instances = [];
    
    public $id;
    
    public function __construct() {
        self::$count++;
        $this->id = self::$count;
        self::$instances[] = $this;
    }
    
    public static function getCount() {
        return self::$count;
    }
    
    public static function resetCount() {
        self::$count = 0;
        self::$instances = [];
    }
    
    public function getId() {
        return $this->id;
    }
}

// Using static methods and properties
echo Counter::getCount(); // 0

$counter1 = new Counter();
$counter2 = new Counter();
$counter3 = new Counter();

echo Counter::getCount(); // 3
echo $counter2->getId();  // 2

print_r(Counter::$instances); // Array of Counter objects

Counter::resetCount();
echo Counter::getCount(); // 0
?>

Magic Methods

<?php
class MagicExample {
    private $data = [];
    
    // Constructor and destructor
    public function __construct($initialData = []) {
        $this->data = $initialData;
        echo "Object created\n";
    }
    
    public function __destruct() {
        echo "Object destroyed\n";
    }
    
    // Property access
    public function __get($name) {
        return $this->data[$name] ?? null;
    }
    
    public function __set($name, $value) {
        $this->data[$name] = $value;
    }
    
    public function __isset($name) {
        return isset($this->data[$name]);
    }
    
    public function __unset($name) {
        unset($this->data[$name]);
    }
    
    // Method calls
    public function __call($method, $args) {
        echo "Calling method '$method' with arguments: " . implode(', ', $args) . "\n";
    }
    
    public static function __callStatic($method, $args) {
        echo "Calling static method '$method' with arguments: " . implode(', ', $args) . "\n";
    }
    
    // String conversion
    public function __toString() {
        return json_encode($this->data);
    }
    
    // Array access
    public function __invoke($arg) {
        echo "Object called as function with argument: $arg\n";
    }
}

$obj = new MagicExample(["name" => "Zhang San"]);

// Property access
echo $obj->name;        // "Zhang San" (calls __get)
$obj->age = 30;         // Calls __set
echo isset($obj->age);  // true (calls __isset)

// Method calls
$obj->nonExistentMethod("arg1", "arg2"); // Calls __call

// String conversion
echo $obj; // Calls __toString, outputs JSON

// Function call
$obj("hello"); // Calls __invoke
?>

Object-Oriented Programming Concepts

Inheritance

<?php
// Base class
class Animal {
    protected $name;
    protected $species;
    
    public function __construct($name, $species) {
        $this->name = $name;
        $this->species = $species;
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getSpecies() {
        return $this->species;
    }
    
    public function makeSound() {
        return "Some generic animal sound";
    }
    
    public function sleep() {
        return "{$this->name} is sleeping";
    }
}

// Derived class
class Dog extends Animal {
    private $breed;
    
    public function __construct($name, $breed) {
        parent::__construct($name, "Canine");
        $this->breed = $breed;
    }
    
    public function makeSound() {
        return "Woof! Woof!";
    }
    
    public function fetch() {
        return "{$this->name} is fetching the ball";
    }
    
    public function getBreed() {
        return $this->breed;
    }
}

class Cat extends Animal {
    public function __construct($name) {
        parent::__construct($name, "Feline");
    }
    
    public function makeSound() {
        return "Meow!";
    }
    
    public function purr() {
        return "{$this->name} is purring";
    }
}

// Usage
$dog = new Dog("Xiaobai", "Golden Retriever");
$cat = new Cat("Whiskers");

echo $dog->getName();     // "Xiaobai"
echo $dog->makeSound();   // "Woof! Woof!"
echo $dog->fetch();       // "Xiaobai is fetching the ball"
echo $dog->getBreed();    // "Golden Retriever"

echo $cat->getName();     // "Whiskers"
echo $cat->makeSound();   // "Meow!"
echo $cat->purr();        // "Whiskers is purring"

// Polymorphism
$animals = [$dog, $cat];
foreach ($animals as $animal) {
    echo $animal->getName() . " says: " . $animal->makeSound() . "\n";
}
?>

Abstract Classes and Interfaces

<?php
// Abstract class
abstract class Shape {
    protected $color;
    
    public function __construct($color) {
        $this->color = $color;
    }
    
    public function getColor() {
        return $this->color;
    }
    
    // Abstract methods - must be implemented by subclasses
    abstract public function calculateArea();
    abstract public function calculatePerimeter();
}

// Interfaces
interface Drawable {
    public function draw();
}

interface Resizable {
    public function resize($factor);
}

// Concrete classes
class Circle extends Shape implements Drawable, Resizable {
    private $radius;
    
    public function __construct($color, $radius) {
        parent::__construct($color);
        $this->radius = $radius;
    }
    
    public function calculateArea() {
        return pi() * $this->radius * $this->radius;
    }
    
    public function calculatePerimeter() {
        return 2 * pi() * $this->radius;
    }
    
    public function draw() {
        return "Drawing a {$this->color} circle with radius {$this->radius}";
    }
    
    public function resize($factor) {
        $this->radius *= $factor;
    }
}

class Rectangle extends Shape implements Drawable {
    private $width;
    private $height;
    
    public function __construct($color, $width, $height) {
        parent::__construct($color);
        $this->width = $width;
        $this->height = $height;
    }
    
    public function calculateArea() {
        return $this->width * $this->height;
    }
    
    public function calculatePerimeter() {
        return 2 * ($this->width + $this->height);
    }
    
    public function draw() {
        return "Drawing a {$this->color} rectangle ({$this->width}x{$this->height})";
    }
}

// Usage
$circle = new Circle("red", 5);
$rectangle = new Rectangle("blue", 4, 6);

echo $circle->draw();                    // "Drawing a red circle with radius 5"
echo "Area: " . $circle->calculateArea(); // Area: 78.54

$circle->resize(2);
echo "New area: " . $circle->calculateArea(); // New area: 314.16

// Polymorphism using interfaces
$shapes = [$circle, $rectangle];
foreach ($shapes as $shape) {
    if ($shape instanceof Drawable) {
        echo $shape->draw() . "\n";
    }
}
?>

Traits

<?php
// Traits - reusable code blocks
trait Timestampable {
    private $createdAt;
    private $updatedAt;
    
    public function setCreatedAt($timestamp = null) {
        $this->createdAt = $timestamp ?: date('Y-m-d H:i:s');
    }
    
    public function setUpdatedAt($timestamp = null) {
        $this->updatedAt = $timestamp ?: date('Y-m-d H:i:s');
    }
    
    public function getCreatedAt() {
        return $this->createdAt;
    }
    
    public function getUpdatedAt() {
        return $this->updatedAt;
    }
}

trait Validatable {
    private $errors = [];
    
    public function addError($field, $message) {
        $this->errors[$field][] = $message;
    }
    
    public function getErrors() {
        return $this->errors;
    }
    
    public function hasErrors() {
        return !empty($this->errors);
    }
    
    public function clearErrors() {
        $this->errors = [];
    }
}

// Using traits
class User {
    use Timestampable, Validatable;
    
    private $name;
    private $email;
    
    public function __construct($name, $email) {
        $this->name = $name;
        $this->email = $email;
        $this->setCreatedAt();
        $this->validate();
    }
    
    private function validate() {
        if (empty($this->name)) {
            $this->addError('name', 'Name is required');
        }
        
        if (!filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
            $this->addError('email', 'Invalid email format');
        }
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getEmail() {
        return $this->email;
    }
    
    public function setName($name) {
        $this->name = $name;
        $this->setUpdatedAt();
        $this->validate();
    }
}

$user = new User("Zhang San", "zhangsan@example.com");

echo "Created at: " . $user->getCreatedAt();
echo "Has errors: " . ($user->hasErrors() ? 'Yes' : 'No');

$user->setName("");
if ($user->hasErrors()) {
    print_r($user->getErrors());
}
?>

Handling Complex Data Structures

Nested Arrays and Objects

<?php
// Complex nested structure
$company = [
    "name" => "Tech Company",
    "founded" => 2010,
    "departments" => [
        "engineering" => [
            "manager" => "Manager Zhang",
            "employees" => [
                ["name" => "Developer Li", "role" => "Senior Developer", "salary" => 90000],
                ["name" => "Designer Wang", "role" => "Junior Developer", "salary" => 60000],
                ["name" => "DevOps Zhao", "role" => "DevOps Engineer", "salary" => 85000]
            ]
        ],
        "marketing" => [
            "manager" => "Manager Liu",
            "employees" => [
                ["name" => "Manager Chen", "role" => "Marketing Manager", "salary" => 75000],
                ["name" => "Creator Sun", "role" => "Content Creator", "salary" => 55000]
            ]
        ]
    ]
];

// Access nested data
echo $company["name"]; // "Tech Company"
echo $company["departments"]["engineering"]["manager"]; // "Manager Zhang"
echo $company["departments"]["engineering"]["employees"][0]["name"]; // "Developer Li"

// Process nested data
function calculateDepartmentPayroll($department) {
    $total = 0;
    foreach ($department["employees"] as $employee) {
        $total += $employee["salary"];
    }
    return $total;
}

function getCompanyStats($company) {
    $stats = [
        "total_employees" => 0,
        "total_payroll" => 0,
        "departments" => []
    ];
    
    foreach ($company["departments"] as $deptName => $department) {
        $employeeCount = count($department["employees"]);
        $payroll = calculateDepartmentPayroll($department);
        
        $stats["departments"][$deptName] = [
            "manager" => $department["manager"],
            "employee_count" => $employeeCount,
            "payroll" => $payroll
        ];
        
        $stats["total_employees"] += $employeeCount;
        $stats["total_payroll"] += $payroll;
    }
    
    return $stats;
}

$stats = getCompanyStats($company);
print_r($stats);
?>

Array and Object Conversion

<?php
// Array to object conversion
$array = [
    "name" => "Zhang San",
    "age" => 30,
    "address" => [
        "street" => "Main Street 123",
        "city" => "Beijing"
    ]
];

// Simple conversion (shallow)
$obj = (object) $array;
echo $obj->name; // "Zhang San"
echo $obj->address["street"]; // "Main Street 123" (still array)

// Deep conversion function
function arrayToObject($array) {
    if (is_array($array)) {
        $obj = new stdClass();
        foreach ($array as $key => $value) {
            $obj->$key = arrayToObject($value);
        }
        return $obj;
    }
    return $array;
}

$deepObj = arrayToObject($array);
echo $deepObj->name; // "Zhang San"
echo $deepObj->address->street; // "Main Street 123" (now object property)

// Object to array conversion
function objectToArray($obj) {
    if (is_object($obj)) {
        $array = [];
        foreach (get_object_vars($obj) as $key => $value) {
            $array[$key] = objectToArray($value);
        }
        return $array;
    }
    return $obj;
}

$backToArray = objectToArray($deepObj);
print_r($backToArray);

// JSON conversion (handles nested structures)
$jsonString = json_encode($array);
$objFromJson = json_decode($jsonString); // Object
$arrayFromJson = json_decode($jsonString, true); // Array
?>

Data Manipulation Patterns

<?php
// Repository pattern for data management
class UserRepository {
    private $users = [];
    
    public function add($user) {
        $this->users[] = $user;
    }
    
    public function findById($id) {
        foreach ($this->users as $user) {
            if ($user['id'] === $id) {
                return $user;
            }
        }
        return null;
    }
    
    public function findByEmail($email) {
        return array_filter($this->users, function($user) use ($email) {
            return $user['email'] === $email;
        });
    }
    
    public function update($id, $data) {
        foreach ($this->users as &$user) {
            if ($user['id'] === $id) {
                $user = array_merge($user, $data);
                return true;
            }
        }
        return false;
    }
    
    public function delete($id) {
        foreach ($this->users as $index => $user) {
            if ($user['id'] === $id) {
                unset($this->users[$index]);
                $this->users = array_values($this->users); // Reindex
                return true;
            }
        }
        return false;
    }
    
    public function getAll() {
        return $this->users;
    }
    
    public function filter($callback) {
        return array_filter($this->users, $callback);
    }
    
    public function map($callback) {
        return array_map($callback, $this->users);
    }
}

// Usage
$repo = new UserRepository();

$repo->add(['id' => 1, 'name' => 'Zhang San', 'email' => 'zhangsan@example.com', 'active' => true]);
$repo->add(['id' => 2, 'name' => 'Li Si', 'email' => 'lisi@example.com', 'active' => false]);
$repo->add(['id' => 3, 'name' => 'Wang Wu', 'email' => 'wangwu@example.com', 'active' => true]);

// Find operations
$user = $repo->findById(2);
echo "Found user: " . $user['name'];

$activeUsers = $repo->filter(function($user) {
    return $user['active'];
});
echo "Active users count: " . count($activeUsers);

// Transform data
$userNames = $repo->map(function($user) {
    return $user['name'];
});
print_r($userNames); // ['Zhang San', 'Li Si', 'Wang Wu']

// Update and delete
$repo->update(2, ['active' => true]);
$repo->delete(3);

print_r($repo->getAll());
?>

Next Steps

Now that you understand arrays and objects, let's explore APIs and interfaces in APIs and Interfaces.

Practice Exercises

  1. Create a shopping cart system using arrays and objects
  2. Build a simple database system with CRUD operations
  3. Implement a class hierarchy for different types of vehicles
  4. Create a data processing pipeline using array functions
  5. Build a configuration management system using nested arrays

Understanding arrays and objects is fundamental to building complex PHP applications!