What best practices should be followed when creating and handling custom exception classes in PHP, especially in the context of object-oriented programming?

When creating and handling custom exception classes in PHP, it is important to follow best practices to ensure clarity and maintainability of your code. Some best practices include creating custom exception classes that extend the base Exception class, providing informative error messages and codes, and handling exceptions appropriately in your code.

class CustomException extends Exception {
    public function __construct($message, $code = 0, Exception $previous = null) {
        parent::__construct($message, $code, $previous);
    }
    
    public function __toString() {
        return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
    }
}

try {
    // Code that may throw exceptions
    throw new CustomException('An error occurred.', 500);
} catch (CustomException $e) {
    echo 'Caught exception: ' . $e->getMessage() . "\n";
}