From fdeb2f89bc52b33df5cba4ab73e2ef5d9cea9de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kozl=C3=ADk?= Date: Thu, 16 Apr 2026 14:22:02 +0200 Subject: [PATCH 1/3] Adding support for FrankenPHP logging --- Log/franken.php | 175 +++++++++++++++++++++++++++++++++++++++++++ docs/guide.txt | 35 +++++++++ examples/franken.php | 14 ++++ 3 files changed, 224 insertions(+) create mode 100644 Log/franken.php create mode 100644 examples/franken.php diff --git a/Log/franken.php b/Log/franken.php new file mode 100644 index 0000000..b8238a7 --- /dev/null +++ b/Log/franken.php @@ -0,0 +1,175 @@ + + * @package Log + * + * @example franken.php Using the frankenphp_log function. + */ +class Log_Franken extends Log +{ + /** + * Integer holding the log facility to use. + */ + private int $name; + + /** + * String containing the format of a message. + */ + private string $lineFormat = '%4$s'; + + /** + * String containing the timestamp format. It will be passed to date(). + * If timeFormatter configured, it will be used. + * current locale. + */ + private string $timeFormat = 'M d H:i:s'; + + /** + * @var callable + */ + private $timeFormatter; + + + /** + * Constructs a new log object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + */ + public function __construct( + string $name, + string $ident = '', + array $conf = [], + int $level = PEAR_LOG_DEBUG + ) { + + if (!empty($conf['lineFormat'])) { + $this->lineFormat = str_replace(array_keys($this->formatMap), + array_values($this->formatMap), + $conf['lineFormat']); + } + else { + if ($ident !== '') { + $this->lineFormat = '%2$s: ' . $this->lineFormat; + } + } + + if (!empty($conf['timeFormat'])) { + $this->timeFormat = $conf['timeFormat']; + } + + if (!empty($conf['timeFormatter'])) { + $this->timeFormatter = $conf['timeFormatter']; + } + + $this->id = md5(microtime().random_int(0, mt_getrandmax())); + $this->ident = $ident; + $this->mask = Log::MAX($level); + } + + /** + * Opens the FrankenPHP handler. + */ + public function open(): bool + { + $this->opened = true; + + return $this->opened; + } + + /** + * Closes the FrankenPHP handler. + */ + public function close(): bool + { + $this->opened = false; + + return true; + } + + /** + * Sends $message to the FrankenPHP logger. Also passes the message + * along to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param int|null $priority (optional) The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + */ + public function log($message, ?int $priority = null): bool + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->isMasked($priority)) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->extractMessage($message); + + /* Build a syslog priority value based on our current configuration. */ + $frankenPriority = $this->toFranken($priority); + + /* Apply the configured line format to the message string. */ + $message = $this->format($this->lineFormat, + $this->formatTime(time(), $this->timeFormat, $this->timeFormatter), + $priority, $message); + + frankenphp_log($message, $frankenPriority); + + $this->announce(['priority' => $priority, 'message' => $message]); + + return true; + } + + /** + * Converts a PEAR_LOG_* constant into a franken FRANKENPHP_LOG_LEVEL_* constant. + * + * @param int $priority PEAR_LOG_* value to convert to FRANKENPHP_LOG_LEVEL_* value. + * + * @return int The FRANKENPHP_LOG_LEVEL_* representation of $priority. + */ + private function toFranken(int $priority): int + { + static $priorities = [ + PEAR_LOG_EMERG => FRANKENPHP_LOG_LEVEL_ERROR, + PEAR_LOG_ALERT => FRANKENPHP_LOG_LEVEL_ERROR, + PEAR_LOG_CRIT => FRANKENPHP_LOG_LEVEL_ERROR, + PEAR_LOG_ERR => FRANKENPHP_LOG_LEVEL_ERROR, + PEAR_LOG_WARNING => FRANKENPHP_LOG_LEVEL_WARN, + PEAR_LOG_NOTICE => FRANKENPHP_LOG_LEVEL_INFO, + PEAR_LOG_INFO => FRANKENPHP_LOG_LEVEL_INFO, + PEAR_LOG_DEBUG => FRANKENPHP_LOG_LEVEL_DEBUG, + ]; + + /* If we're passed an unknown priority, default to FRANKENPHP_LOG_LEVEL_INFO. */ + if (!array_key_exists($priority, $priorities)) { + echo "Unknown priority level: $priority. Defaulting to FRANKENPHP_LOG_LEVEL_INFO.\n"; + var_dump($priority); + var_dump($priorities); + return FRANKENPHP_LOG_LEVEL_INFO; + } + + return $priorities[$priority]; + } +} diff --git a/docs/guide.txt b/docs/guide.txt index e359e59..adef1dd 100644 --- a/docs/guide.txt +++ b/docs/guide.txt @@ -566,6 +566,41 @@ Example } +The Franken Handler +------------------- +The Franken handler sends log events directly to the FrankenPHP logger using +the `frankenphp_log` function. This handler is only useful when running under +[FrankenPHP](https://frankenphp.dev/), and it will fail silently otherwise. +It supports configurable string formats. + +Configuration +~~~~~~~~~~~~~ ++-------------------+-----------+----------------+---------------------------+ +| Parameter | Type | Default | Description | ++===================+===========+================+===========================+ +| ``lineFormat`` | String | ``%2$s: %4$s`` | `Log line format`_ | +| | | | specification. | ++-------------------+-----------+----------------+---------------------------+ +| ``timeFormat`` | String | ``M d H:i:s`` | Time stamp format | +| | | | (for date_). | ++-------------------+-----------+----------------+---------------------------+ +| ``timeFormatter`` | Callable | null | Time formatter example | +| | | | fn() => date('H:i:s') | ++-------------------+-----------+----------------+---------------------------+ + +.. _STDOUT: http://www.php.net/wrappers.php +.. _date: http://www.php.net/date + +Example +~~~~~~~ +:: + + $logger = Log::singleton('franken', '', 'ident'); + for ($i = 0; $i < 10; $i++) { + $logger->log("Log entry $i"); + } + + The Mail Handler ---------------- diff --git a/examples/franken.php b/examples/franken.php new file mode 100644 index 0000000..565dfca --- /dev/null +++ b/examples/franken.php @@ -0,0 +1,14 @@ +emerg('PEAR_LOG_EMERG - System is unusable.'); +$logger->alert('PEAR_LOG_ALERT - Immediate action required.'); +$logger->crit('PEAR_LOG_CRIT - Critical conditions.'); +$logger->err('PEAR_LOG_ERR - Error conditions.'); +$logger->warning('PEAR_LOG_WARNING - Warning conditions.'); +$logger->notice('PEAR_LOG_NOTICE - Normal but significant.'); +$logger->info('PEAR_LOG_INFO - Informational.'); +$logger->debug('PEAR_LOG_DEBUG - Debug-level messages.'); From 7ce315db0172160733eeeba21a809990b9ccfe46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kozl=C3=ADk?= Date: Thu, 16 Apr 2026 14:54:51 +0200 Subject: [PATCH 2/3] Fix link to FrankenPHP --- docs/guide.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/guide.txt b/docs/guide.txt index adef1dd..95bd5c0 100644 --- a/docs/guide.txt +++ b/docs/guide.txt @@ -570,8 +570,10 @@ The Franken Handler ------------------- The Franken handler sends log events directly to the FrankenPHP logger using the `frankenphp_log` function. This handler is only useful when running under -[FrankenPHP](https://frankenphp.dev/), and it will fail silently otherwise. -It supports configurable string formats. +FrankenPHP_, and it will fail silently otherwise. It supports configurable +string formats. + +.. _FrankenPHP: https://frankenphp.dev/ Configuration ~~~~~~~~~~~~~ @@ -588,7 +590,6 @@ Configuration | | | | fn() => date('H:i:s') | +-------------------+-----------+----------------+---------------------------+ -.. _STDOUT: http://www.php.net/wrappers.php .. _date: http://www.php.net/date Example From 9aba399ec3348148dfb7e3f28d49d1de94222ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kozl=C3=ADk?= Date: Thu, 16 Apr 2026 15:14:16 +0200 Subject: [PATCH 3/3] Throw exeption when frankenphp_log is not available --- Log/franken.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Log/franken.php b/Log/franken.php index b8238a7..d097fea 100644 --- a/Log/franken.php +++ b/Log/franken.php @@ -57,6 +57,10 @@ public function __construct( int $level = PEAR_LOG_DEBUG ) { + if (!function_exists('frankenphp_log')) { + throw new \RuntimeException('frankenphp_log() is not available'); + } + if (!empty($conf['lineFormat'])) { $this->lineFormat = str_replace(array_keys($this->formatMap), array_values($this->formatMap),