upside / option
PHP implementation of Option type for safe null handling with snake_case method names
Requires
- php: ^8.4
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.3
This package is auto-updated.
Last update: 2025-09-30 08:34:50 UTC
README
Библиотека для работы с опциональными значениями в PHP, вдохновленная подходом языков Rust и Scala.
Предоставляет тип Option для безопасной работы с значениями, которые могут отсутствовать (null).
Установка
composer require upside/option
Преимущества
- Избегание ошибок типа "Null pointer exception"
- Явное указание на возможность отсутствия значения
- Богатый API для безопасной работы с опциональными значениями
- Полная поддержка статического анализа (PHPStan)
- Строгая типизация
Базовое использование
Создание Option
use Upside\Std\Option;
// Создание Option с значением
$some = Option::some(42);
$some = Option::of('hello'); // автоматически создаст Some для не-null
// Создание пустого Option
$none = Option::none();
$none = Option::of(null); // автоматически создаст None для null
Полное руководство по методам
isNone(), isSome()
Проверка состояния Option:
use Upside\Std\Option;
$value = Option::of(5);
if ($value->isSome()) {
echo "Значение существует: " . $value->unwrap();
}
if ($value->isNone()) {
echo "Значение отсутствует";
}
isSomeAnd()
Проверка значения с помощью предиката:
use Upside\Std\Option;
$value = Option::some(10);
$isEven = $value->isSomeAnd(fn($x) => $x % 2 === 0); // true
$value = Option::none();
$isEven = $value->isSomeAnd(fn($x) => $x % 2 === 0); // false
expect(), expectOr()
Получение значения с обработкой ошибок:
use Upside\Std\Option;
$value = Option::some(42);
// Бросает RuntimeException с сообщением при None
$result = $value->expect("Значение обязательно должно существовать");
// Бросает кастомное исключение при None
$result = $value->expectOr(new InvalidArgumentException("Значение отсутствует"));
unwrap(), unwrapOr(), unwrapOrElse()
Безопасное извлечение значений:
use Upside\Std\Option;
$value = Option::some(5);
// Бросает исключение если None
$result = $value->unwrap();
// Возвращает значение по умолчанию если None
$result = $value->unwrapOr(0);
// Вычисляет значение по умолчанию если None
$result = $value->unwrapOrElse(fn() => rand(1, 10));
map(), andThen()
Преобразование значений:
use Upside\Std\Option;
$value = Option::some(5);
// Преобразование значения
$doubled = $value->map(fn($x) => $x * 2); // Some(10)
// Цепочка преобразований с возможностью вернуть None
$result = $value->andThen(fn($x) =>
$x > 0 ? Option::some($x * 2) : Option::none()
);
filter()
Фильтрация значений:
use Upside\Std\Option;
$value = Option::some(10);
// Возвращает Some только если значение четное
$even = $value->filter(fn($x) => $x % 2 === 0); // Some(10)
$value = Option::some(5);
$even = $value->filter(fn($x) => $x % 2 === 0); // None
or(), orElse()
Работа с альтернативными значениями:
use Upside\Std\Option;
$value = Option::none();
// Возвращает первый Some из двух Option
$result = $value->or(Option::some('backup')); // Some('backup')
// Вычисляет альтернативу лениво
$result = $value->orElse(fn() => Option::some(calculate_backup()));
mapOr(), mapOrElse()
Преобразование с значениями по умолчанию:
use Upside\Std\Option;
$value = Option::some("hello");
// Преобразование с дефолтным значением
$length = $value->mapOr(0, fn($s) => strlen($s)); // 5
$value = Option::none();
$length = $value->mapOrElse(
fn() => rand(1, 10),
fn($s) => strlen($s)
); // случайное число
flatten()
Упрощение вложенных Option:
use Upside\Std\Option;
$nested = Option::some(Option::some(42));
$flattened = $nested->flatten(); // Some(42)
$deeplyNested = Option::some(Option::some(Option::some(42)));
$flattened = $deeplyNested->flatten(2); // Some(42)
inspect()
Побочные эффекты для отладки:
use Upside\Std\Option;
$value = Option::some(42)
->inspect(fn($x) => log_debug("Получено значение: $x"))
->map(fn($x) => $x * 2)
->inspect(fn($x) => log_debug("После преобразования: $x"));
match()
Pattern matching:
use Upside\Std\Option;
$value = Option::some(42);
$result = $value->match(
some: fn($x) => "Значение: $x",
none: fn() => "Значение отсутствует"
); // "Значение: 42"
pipe()
Применение функций к Option:
use Upside\Std\Option;
$value = Option::some(5);
$result = $value->pipe(fn($opt) =>
$opt->map(fn($x) => $x * 2)->unwrapOr(0)
); // 10
Реальные кейсы применения
1. Безопасная работа с базой данных
use Upside\Std\Option;
function findUser(int $id): Option {
$user = $db->query("SELECT * FROM users WHERE id = ?", [$id]);
return Option::of($user);
}
// Безопасная обработка результата
findUser(123)
->map(fn($user) => $user->name)
->map(fn($name) => strtoupper($name))
->unwrapOr('Гость');
2. Обработка конфигурации
use Upside\Std\Option;
function getConfigValue(string $key): Option {
$value = $_ENV[$key] ?? null;
return Option::of($value);
}
// Безопасное получение значения конфигурации
$apiKey = getConfigValue('API_KEY')
->expect("API_KEY обязателен для работы приложения");
3. Цепочка преобразований
use Upside\Std\Option;
function parseInteger(string $input): Option {
return is_numeric($input)
? Option::some((int)$input)
: Option::none();
}
// Безопасный парсинг и преобразование
$result = Option::from($_GET['page'])
->andThen(fn($page) => parseInteger($page))
->filter(fn($page) => $page > 0)
->unwrapOr(1);
4. Работа с API
use Upside\Std\Option;
function fetchUserData(int $userId): Option {
try {
$response = $httpClient->get("/users/{$userId}");
return Option::of($response->getBody());
} catch (RequestException $e) {
return Option::none();
}
}
// Безопасная обработка ответа API
$userEmail = fetchUserData(123)
->map(fn($data) => json_decode($data))
->andThen(fn($user) => Option::of($user->email ?? null))
->unwrapOr('default@example.com');
5. Валидация входных данных
use Upside\Std\Option;
function validateEmail(string $email): Option {
return filter_var($email, FILTER_VALIDATE_EMAIL)
? Option::some($email)
: Option::none();
}
// Цепочка валидаций
$validatedInput = Option::of($_POST['email'])
->andThen(fn($email) => validateEmail($email))
->filter(fn($email) => domain_exists($email))
->expectOr(new ValidationException("Неверный email"));
Совместимость с PHPStan
Библиотека полностью совместима с PHPStan и предоставляет точные аннотации типов для статического анализа. Для максимального использования возможностей типизации рекомендуется использовать PHPStan на уровне 6 или выше.
Лицензия
MIT