<?php
namespace Hephaestus;
/**
* Executes a callable with enhanced exception handling.
* Handles CheckedException specifically, providing detailed error messages.
*
* @param \Closure|array|string $func The function or callable to be executed.
* @param mixed ...$args Arguments to be passed to the callable.
* @return mixed The result of the callable execution, or null if a CheckedException was handled.
* @throws CheckedException if a CheckedException is thrown during the callable execution.
*/
function withCheckedExceptionHandling(\Closure|array|string $func, ...$args) {
try {
if ($func instanceof \Closure) {
return $func(...$args);
}
if (is_array($func) || is_string($func)) {
return call_user_func($func, ...$args);
}
} catch (CheckedException $e) {
echo "Handled CheckedException: " . $e->getMessage() . PHP_EOL;
return null;
}
}
/**
* Creates a retry wrapper that will attempt an operation multiple times before failing.
* The operation will be retried with a 1-second delay between attempts.
*
* @param int $retries The maximum number of retry attempts (default: 3)
* @return \Closure A function that takes an operation closure and executes it with retry logic
* @throws \Exception When all retry attempts fail, wrapping the last caught exception
*
* Example:
* $retrier = withRetryBeforeFailing(3);
* $result = $retrier(function() {
* // potentially failing operation
* });
*/
function withRetryBeforeFailing(int $retries = 3) {
return function (\Closure $operation) use ($retries) {
$attempt = 0;
while ($attempt < $retries) {
try {
return $operation();
} catch (\Exception $e) {
$attempt++;
error_log("Operation attempt $attempt failed: " . $e->getMessage());
if ($attempt >= $retries) {
throw new \Exception("All $retries attempts failed.", 0, $e);
}
sleep(1);
}
}
};
}
/**
* Creates an instance of Some, representing a present value.
*
* @param mixed $value The value to be wrapped in a Some instance.
* @return Some The Some instance wrapping the provided value.
*/
function Some($value): Some {
return Option::some($value);
}
/**
* Creates a None instance.
*
* @return None
*/
function None(): None {
return Option::none();
}
/**
* Executes a callable with pattern-matched exception handling using patterns from exceptions.json.
*
* @param \Closure|array|string $func The function or callable to be executed.
* @param string|null $patternsFile Optional path to the patterns JSON file. If null, looks in the current directory.
* @param string $defaultPattern Optional default message pattern for unmatched exceptions.
* @param mixed ...$args Arguments to be passed to the callable.
* @return mixed The result of the callable execution, or the error message if an exception occurs.
* @throws \RuntimeException If the patterns file cannot be found or is invalid.
*
* Example:
* $result = withMatchedExceptions(
* fn() => $connection->connect()
* );
*/
function withMatchedExceptions(
\Closure|array|string $func,
?string $patternsFile = null,
string $defaultPattern = "Unexpected error",
mixed ...$args
) {
// Load patterns from JSON file
$patternsFile = $patternsFile ?? getcwd() . '/exceptions.json';
if (!file_exists($patternsFile)) {
throw new \RuntimeException(
"Exception patterns file not found. Run 'hephaestus init' to generate it."
);
}
$patterns = json_decode(file_get_contents($patternsFile), true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException(
"Invalid exception patterns file. Run 'hephaestus init' to regenerate it."
);
}
try {
if ($func instanceof \Closure) {
return $func(...$args);
}
if (is_array($func) || is_string($func)) {
return call_user_func($func, ...$args);
}
} catch (\Exception $e) {
// Find the first matching exception class
foreach ($patterns as $class => $pattern) {
if ($e instanceof $class) {
return sprintf(
"%s: %s\n\nDescription: %s",
$pattern['message'],
$e->getMessage(),
$pattern['description']
);
}
}
// If no match found, use default pattern
return sprintf(
"%s: %s\n\nDescription: A general error has occurred.",
$defaultPattern,
$e->getMessage()
);
}
}
|