add announce patcher for qBittorrent 4.4+

This commit is contained in:
hxsf 2022-04-13 14:09:38 +08:00
parent 83a16b7eb6
commit af3bca56b0
266 changed files with 11379 additions and 3306 deletions

View File

@ -28,7 +28,7 @@
"workerman/webman-framework": "^1.0",
"monolog/monolog": "^2.0",
"vlucas/phpdotenv": "5.1.0",
"rhilip/bencode": "^1.1",
"rhilip/bencode": "^2.1",
"workerman/crontab": "^1.0",
"curl/curl": "^2.3",
"ext-posix": "*",

0
runtime/.gitignore vendored Normal file → Executable file
View File

0
runtime/logs/.gitignore vendored Normal file → Executable file
View File

0
runtime/views/.gitignore vendored Normal file → Executable file
View File

View File

@ -4,6 +4,8 @@ namespace IYUU\Reseed;
use app\domain\ConfigParser\Move as domainMove;
use app\domain\Crontab as domainCrontab;
use IYUU\Client\ClientException;
use Rhilip\Bencode\Bencode;
use Rhilip\Bencode\ParseException;
class MoveTorrent extends AutoReseed
{
@ -97,6 +99,13 @@ class MoveTorrent extends AutoReseed
echo "clients_".$k." 全部转移成功,本次无需转移!".PHP_EOL.PHP_EOL;
continue;
}
$qBittorrent_version_lg_4_4 = false;
if ($v['type'] == 'qBittorrent') {
$arr = preg_split(ltrim(static::getRpc($k)->appVersion(), "v"), ".", 3, PREG_SPLIT_NO_EMPTY);
if ($arr[0] > '4' && $arr[1] > 4) {
$qBittorrent_version_lg_4_4 = true;
}
}
//遍历当前客户端种子
foreach ($infohash_Dir as $info_hash => $downloadDir) {
// 调用路径过滤器、选择器
@ -115,6 +124,8 @@ class MoveTorrent extends AutoReseed
// 种子目录:脚本要能够读取到
$path = self::$links[$k]['BT_backup'];
$torrentPath = '';
$fast_resumePath = '';
$needPatchTorrent = $qBittorrent_version_lg_4_4;
// 待删除种子
$torrentDelete = '';
// 获取种子文件的实际路径
@ -135,17 +146,52 @@ class MoveTorrent extends AutoReseed
die("clients_".$k." IYUUPlus内下载器未设置种子目录无法完成转移");
}
$torrentPath = $path .DS. $info_hash . '.torrent';
$fast_resumePath = $path .DS. $info_hash . '.fastresume';
$torrentDelete = $info_hash;
break;
default:
break;
}
if (!is_file($torrentPath)) {
if (!is_file($fast_resumePath)) {
echo $help_msg;
die("clients_" . $k . " 的`{$move[$info_hash]['name']}`resume文件`{$fast_resumePath}`不存在,无法完成转移!");
}
echo $help_msg;
die("clients_".$k." 的`{$move[$info_hash]['name']}`,种子文件`{$torrentPath}`不存在,无法完成转移!");
}
echo '存在种子:'.$torrentPath.PHP_EOL;
$torrent = file_get_contents($torrentPath);
$parsed_torrent = [];
try {
global $parsed_torrent;
$parsed_torrent = Bencode::decode($torrent);
if (empty($parsed_torrent['announce'])) {
$needPatchTorrent = true;
}
} catch (ParseException $e) {}
if ($needPatchTorrent) {
echo '未发现tracker信息尝试补充tracker信息...'.PHP_EOL;
if (empty($parsed_torrent)) {
die("clients_".$k." 的`{$move[$info_hash]['name']}`,种子文件`{$torrentPath}`解析失败,无法完成转移!");
}
if (empty($parsed_torrent['announce'])) {
try {
$parsed_fast_resume = Bencode::load($fast_resumePath);
$trackers = $parsed_fast_resume['trackers'];
if (count($trackers) > 0 && !empty($trackers[0])) {
if (is_array($trackers[0]) && count($trackers[0]) > 0 && !empty($trackers[0][0])) {
$parsed_torrent['announce'] = $trackers[0][0];
}
} else {
die("clients_".$k." 的`{$move[$info_hash]['name']}`resume文件`{$fast_resumePath}`不包含tracker地址无法完成转移");
}
} catch (ParseException $e) {
die("clients_".$k." 的`{$move[$info_hash]['name']}`resume文件`{$fast_resumePath}`解析失败`{$e->getMessage()}`,无法完成转移!");
}
}
$torrent = Bencode::encode($parsed_torrent);
}
// 正式开始转移
echo "种子已推送给下载器,正在转移做种...".PHP_EOL;
@ -166,7 +212,7 @@ class MoveTorrent extends AutoReseed
/**
* 转移成功的种子写日志
*/
$log = $info_hash.PHP_EOL.$torrentPath.PHP_EOL.$downloadDir.PHP_EOL.PHP_EOL;
$log = $info_hash.PHP_EOL.$torrentPath.PHP_EOL.$downloadDir.PHP_EOL.PHP_EOL;
if ($ret) {
//转移成功时,删除做种,不删资源
if (isset(self::$conf['delete_torrent']) && self::$conf['delete_torrent']) {
@ -180,7 +226,7 @@ class MoveTorrent extends AutoReseed
// 失败的种子
static::wLog($log, 'MoveError'.$k);
static::$wechatMsg['MoveError']++;
}
}
}
}
}

2
vendor/autoload.php vendored
View File

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit687dbaa10a29b36f43b2c9b7d90f4c97::getLoader();
return ComposerAutoloaderInit4dfbd30c142d9169bdd5809ad6beb22e::getLoader();

View File

@ -37,57 +37,130 @@ namespace Composer\Autoload;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-var array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
@ -102,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
@ -147,11 +222,13 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@ -195,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
@ -211,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
@ -276,6 +361,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
@ -323,6 +429,8 @@ class ClassLoader
return true;
}
return null;
}
/**
@ -367,6 +475,21 @@ class ClassLoader
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{

337
vendor/composer/InstalledVersions.php vendored Normal file
View File

@ -0,0 +1,337 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
private static $installed;
private static $canGetVendors;
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}

View File

@ -7,6 +7,8 @@ $baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',

View File

@ -6,9 +6,9 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
'da5b71a9ad8465d48da441e2f36823b6' => $baseDir . '/support/helpers.php',
);

View File

@ -6,12 +6,17 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'support\\' => array($vendorDir . '/workerman/webman-framework/src/support'),
'Workerman\\Crontab\\' => array($vendorDir . '/workerman/crontab/src'),
'Workerman\\' => array($vendorDir . '/workerman/workerman'),
'Webman\\' => array($vendorDir . '/workerman/webman-framework/src'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Support\\View\\' => array($vendorDir . '/workerman/webman-framework/src/support/view'),
'Support\\Exception\\' => array($vendorDir . '/workerman/webman-framework/src/support/exception'),
'Support\\Bootstrap\\' => array($vendorDir . '/workerman/webman-framework/src/support/bootstrap'),
'Support\\' => array($vendorDir . '/workerman/webman-framework/src/support'),
'Rhilip\\Bencode\\' => array($vendorDir . '/rhilip/bencode/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit687dbaa10a29b36f43b2c9b7d90f4c97
class ComposerAutoloaderInit4dfbd30c142d9169bdd5809ad6beb22e
{
private static $loader;
@ -22,15 +22,17 @@ class ComposerAutoloaderInit687dbaa10a29b36f43b2c9b7d90f4c97
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit687dbaa10a29b36f43b2c9b7d90f4c97', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit687dbaa10a29b36f43b2c9b7d90f4c97', 'loadClassLoader'));
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit4dfbd30c142d9169bdd5809ad6beb22e', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit4dfbd30c142d9169bdd5809ad6beb22e', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit4dfbd30c142d9169bdd5809ad6beb22e::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
@ -51,19 +53,19 @@ class ComposerAutoloaderInit687dbaa10a29b36f43b2c9b7d90f4c97
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97::$files;
$includeFiles = Composer\Autoload\ComposerStaticInit4dfbd30c142d9169bdd5809ad6beb22e::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire687dbaa10a29b36f43b2c9b7d90f4c97($fileIdentifier, $file);
composerRequire4dfbd30c142d9169bdd5809ad6beb22e($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire687dbaa10a29b36f43b2c9b7d90f4c97($fileIdentifier, $file)
function composerRequire4dfbd30c142d9169bdd5809ad6beb22e($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;

View File

@ -4,17 +4,21 @@
namespace Composer\Autoload;
class ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97
class ComposerStaticInit4dfbd30c142d9169bdd5809ad6beb22e
{
public static $files = array (
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
'da5b71a9ad8465d48da441e2f36823b6' => __DIR__ . '/../..' . '/support/helpers.php',
);
public static $prefixLengthsPsr4 = array (
's' =>
array (
'support\\' => 8,
),
'W' =>
array (
'Workerman\\Crontab\\' => 18,
@ -26,6 +30,10 @@ class ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97
'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Polyfill\\Ctype\\' => 23,
'Support\\View\\' => 13,
'Support\\Exception\\' => 18,
'Support\\Bootstrap\\' => 18,
'Support\\' => 8,
),
'R' =>
array (
@ -61,6 +69,10 @@ class ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97
);
public static $prefixDirsPsr4 = array (
'support\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support',
),
'Workerman\\Crontab\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/crontab/src',
@ -85,6 +97,22 @@ class ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
),
'Support\\View\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support/view',
),
'Support\\Exception\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support/exception',
),
'Support\\Bootstrap\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support/bootstrap',
),
'Support\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support',
),
'Rhilip\\Bencode\\' =>
array (
0 => __DIR__ . '/..' . '/rhilip/bencode/src',
@ -139,6 +167,8 @@ class ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97
public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
@ -147,10 +177,10 @@ class ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97::$prefixesPsr0;
$loader->classMap = ComposerStaticInit687dbaa10a29b36f43b2c9b7d90f4c97::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit4dfbd30c142d9169bdd5809ad6beb22e::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit4dfbd30c142d9169bdd5809ad6beb22e::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit4dfbd30c142d9169bdd5809ad6beb22e::$prefixesPsr0;
$loader->classMap = ComposerStaticInit4dfbd30c142d9169bdd5809ad6beb22e::$classMap;
}, null, ClassLoader::class);
}

File diff suppressed because it is too large Load Diff

175
vendor/composer/installed.php vendored Normal file
View File

@ -0,0 +1,175 @@
<?php return array(
'root' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'db7e63dc33f2ffe163b45bf7cf138e07c4edf4a3',
'name' => 'workerman/webman',
'dev' => true,
),
'versions' => array(
'curl/curl' => array(
'pretty_version' => '2.3.3',
'version' => '2.3.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../curl/curl',
'aliases' => array(),
'reference' => 'ec22ad27dead47093f0944f5e651df4b12846f5a',
'dev_requirement' => false,
),
'graham-campbell/result-type' => array(
'pretty_version' => 'v1.0.4',
'version' => '1.0.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../graham-campbell/result-type',
'aliases' => array(),
'reference' => '0690bde05318336c7221785f2a932467f98b64ca',
'dev_requirement' => false,
),
'ledccn/bittorrentclient' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'type' => 'library',
'install_path' => __DIR__ . '/../ledccn/bittorrentclient',
'aliases' => array(
0 => '9999999-dev',
),
'reference' => '9aead6a8750befb518adec30dd27dc92d3ec88fa',
'dev_requirement' => false,
),
'monolog/monolog' => array(
'pretty_version' => '2.4.0',
'version' => '2.4.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../monolog/monolog',
'aliases' => array(),
'reference' => 'd7fd7450628561ba697b7097d86db72662f54aef',
'dev_requirement' => false,
),
'nikic/fast-route' => array(
'pretty_version' => 'v1.3.0',
'version' => '1.3.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../nikic/fast-route',
'aliases' => array(),
'reference' => '181d480e08d9476e61381e04a71b34dc0432e812',
'dev_requirement' => false,
),
'phpoption/phpoption' => array(
'pretty_version' => '1.8.1',
'version' => '1.8.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../phpoption/phpoption',
'aliases' => array(),
'reference' => 'eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15',
'dev_requirement' => false,
),
'psr/container' => array(
'pretty_version' => '2.0.2',
'version' => '2.0.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/container',
'aliases' => array(),
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
'dev_requirement' => false,
),
'psr/log' => array(
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
'dev_requirement' => false,
),
'psr/log-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0.0 || 2.0.0 || 3.0.0',
),
),
'rhilip/bencode' => array(
'pretty_version' => 'v2.1.1',
'version' => '2.1.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../rhilip/bencode',
'aliases' => array(),
'reference' => '38a445a04db54034a155b4a40985fec715b607bc',
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.25.0',
'version' => '1.25.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
'reference' => '30885182c981ab175d4d034db0f6f469898070ab',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.25.0',
'version' => '1.25.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.25.0',
'version' => '1.25.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => '4407588e0d3f1f52efb65fbe92babe41f37fe50c',
'dev_requirement' => false,
),
'vlucas/phpdotenv' => array(
'pretty_version' => 'v5.1.0',
'version' => '5.1.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../vlucas/phpdotenv',
'aliases' => array(),
'reference' => '448c76d7a9e30c341ff5bc367a923af74ae18467',
'dev_requirement' => false,
),
'workerman/crontab' => array(
'pretty_version' => 'v1.0.3',
'version' => '1.0.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../workerman/crontab',
'aliases' => array(),
'reference' => '2a2b9f8c2dbfa47ad16599a10f584dd09863bebd',
'dev_requirement' => false,
),
'workerman/webman' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'db7e63dc33f2ffe163b45bf7cf138e07c4edf4a3',
'dev_requirement' => false,
),
'workerman/webman-framework' => array(
'pretty_version' => 'v1.3.9',
'version' => '1.3.9.0',
'type' => 'library',
'install_path' => __DIR__ . '/../workerman/webman-framework',
'aliases' => array(),
'reference' => 'ffadeae56857ef4d6029d4dff5f763013083ca35',
'dev_requirement' => false,
),
'workerman/workerman' => array(
'pretty_version' => 'v4.0.33',
'version' => '4.0.33.0',
'type' => 'library',
'install_path' => __DIR__ . '/../workerman/workerman',
'aliases' => array(),
'reference' => '7e7a95d38abf2a878438070aa8934029059d6866',
'dev_requirement' => false,
),
),
);

26
vendor/composer/platform_check.php vendored Normal file
View File

@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 70400)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@ -0,0 +1,59 @@
name: Tests
on: [push, pull_request]
env:
DEFAULT_COMPOSER_FLAGS: "--prefer-dist --no-interaction"
CC_TEST_REPORTER_ID: 40d4890deed3bca8888c04ca67b9768edf11d7a089d2960977997791daea31f6
jobs:
phpunit:
name: PHP ${{ matrix.php }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
php: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
steps:
## checkout the repoistory §
- name: Checkout Repo
uses: actions/checkout@v2
- name: Build server image
run: docker build . -t localserver
working-directory: ./tests/server
- name: Run test server
run: docker run -d -p 1234:80 localserver
## Install php
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: curl, mbstring, imagick, gd
ini-values: date.timezone='UTC'
coverage: xdebug
## install composer
- name: Install dependencies
run: composer install $DEFAULT_COMPOSER_FLAGS
## run unit tests
- name: PHP Unit tests for PHP
run: vendor/bin/phpunit --verbose --configuration actions.phpunit.xml
if: matrix.php == '8.0' || matrix.php == '7.4' || matrix.php == '7.3' || matrix.php == '7.2' || matrix.php == '7.0' || matrix.php == '5.6'
## unit test with coverage
- name: PHP Unit tests for PHP 7.1
run: vendor/bin/phpunit --verbose --coverage-clover=clover.xml --configuration actions.phpunit.xml
if: matrix.php == '7.1'
## coverage
- name: Code coverage
run: |
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter
./cc-test-reporter after-build -t clover
if: matrix.php == '7.1'
continue-on-error: true # if is fork

33
vendor/curl/curl/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,33 @@
# CHANGELOG
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
## 2.4.0 (in progress)
+ [#61](https://github.com/php-mod/curl/issues/61) Adjust user agent version to latest version.
+ [#74](https://github.com/php-mod/curl/pull/75) Fixed PHPDoc Block, Added more Unit Tests.
+ Added GitHub Actions Tests (from 5.6 - 8.0)
## 2.3.1 (21. January 2021)
+ Supports PHP8
## 2.3.0 (19. March 2019)
+ add asJson option (#67)
## 2.2.0 (4. December 2018)
+ Added some getters.
## 2.1.0 (17. November 2018)
+ CurlFile fix
+ This is not tested, but we are facing the same problem with CurlFile Uploads (https://github.com/php-mod/curl/issues/46) - This *should* do the trick.
+ Update README.md
+ cs fix
## 2.0.0 (15. November 2018)
+ Drop php 5.3, 5.4 and 5.5 support.
+ Use Gitlab CI instead of Travis CI.

View File

@ -2,8 +2,14 @@
This library provides an object-oriented wrapper of the PHP cURL extension.
[![Maintainability](https://api.codeclimate.com/v1/badges/6c34bb31f3eb6df36c7d/maintainability)](https://codeclimate.com/github/php-mod/curl/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/6c34bb31f3eb6df36c7d/test_coverage)](https://codeclimate.com/github/php-mod/curl/test_coverage)
[![Total Downloads](https://poser.pugx.org/curl/curl/downloads)](//packagist.org/packages/curl/curl)
If you have questions or problems with installation or usage [create an Issue](https://github.com/php-mod/curl/issues).
## Installation
In order to install this library via composer run the following command in the console:
@ -122,4 +128,5 @@ In order to test the library:
1. Create a fork
2. Clone the fork to your machine
3. Install the depencies `composer install`
4. Build and start the docker image (in `tests/server`) `docker build . -t curlserver` start `docker run -p 1234:80 curlserver`
4. Run the unit tests `./vendor/bin/phpunit tests`

23
vendor/curl/curl/actions.phpunit.xml vendored Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
verbose="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="PHP-MOD CURL">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -21,12 +21,11 @@
}
],
"require": {
"php": "^5.6 | ^7.0",
"php": "^5.6 | ^7.0 | ^8.0",
"ext-curl": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "~2.1"
"yoast/phpunit-polyfills": "^0.2.0"
},
"autoload": {
"psr-0": {

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="true"
verbose="false"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="display_errors" value="on"/>
</php>
<testsuites>
<testsuite name="PHP MP4Box Tests Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -66,7 +66,7 @@ class Curl
/**
* @var string The user agent name which is set when making a request
*/
const USER_AGENT = 'PHP Curl/1.9 (+https://github.com/php-mod/curl)';
const USER_AGENT = 'PHP Curl/2.3 (+https://github.com/php-mod/curl)';
private $_cookies = array();
@ -217,7 +217,7 @@ class Curl
$this->http_error = $this->isError();
$this->error = $this->curl_error || $this->http_error;
$this->error_code = $this->error ? ($this->curl_error ? $this->getErrorCode() : $this->getHttpStatus()) : 0;
$this->request_headers = preg_split('/\r\n/', curl_getinfo($this->curl, CURLINFO_HEADER_OUT), null, PREG_SPLIT_NO_EMPTY);
$this->request_headers = preg_split('/\r\n/', curl_getinfo($this->curl, CURLINFO_HEADER_OUT), -1, PREG_SPLIT_NO_EMPTY);
$this->http_error_message = $this->error ? (isset($this->response_headers['0']) ? $this->response_headers['0'] : '') : '';
$this->error_message = $this->curl_error ? $this->getErrorMessage() : $this->http_error_message;
@ -318,7 +318,7 @@ class Curl
* Make a post request with optional post data.
*
* @param string $url The url to make the post request
* @param array $data Post data to pass to the url
* @param array|object|string $data Post data to pass to the url
* @param boolean $asJson Whether the data should be passed as json or not. {@insce 2.2.1}
* @return self
*/
@ -715,8 +715,8 @@ class Curl
foreach ($this->response_headers as $header) {
$parts = explode(":", $header, 2);
$key = isset($parts[0]) ? $parts[0] : null;
$value = isset($parts[1]) ? $parts[1] : null;
$key = isset($parts[0]) ? $parts[0] : '';
$value = isset($parts[1]) ? $parts[1] : '';
$headers[trim(strtolower($key))] = trim($value);
}

View File

@ -2,276 +2,310 @@
namespace Curl;
class CurlTest extends \PHPUnit_Framework_TestCase
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
class CurlTest extends TestCase
{
const TEST_URL = 'http://localhost:1234';
const TEST_URL = 'http://server_test';
/**
*
* @var Curl
*/
protected $curl;
/**
*
* @var Curl
*/
protected $curl;
function setUp() {
$this->curl = new Curl();
$this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, FALSE);
$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, FALSE);
}
function server($request_method, $data='') {
$request_method = strtolower($request_method);
$this->curl->$request_method(self::TEST_URL . '/server.php', $data);
return $this->curl->response;
}
public function testExtensionLoaded() {
$this->assertTrue(extension_loaded('curl'));
}
public function testUserAgent() {
$this->curl->setUserAgent(Curl::USER_AGENT);
$this->assertEquals(Curl::USER_AGENT, $this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_USER_AGENT',
)));
}
public function testGet() {
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'GET');
}
public function testPostRequestMethod() {
$this->assertTrue($this->server('POST', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'POST');
}
public function testPostData() {
$this->assertTrue($this->server('POST', array(
'test' => 'post',
'key' => 'test',
)) === 'post');
}
public function testPostMultidimensionalData() {
$data = array(
'key' => 'file',
'file' => array(
'wibble',
'wubble',
'wobble',
),
);
$this->curl->post(self::TEST_URL . '/post_multidimensional.php', $data);
$this->assertEquals(
'key=file&file%5B0%5D=wibble&file%5B1%5D=wubble&file%5B2%5D=wobble',
$this->curl->response);
}
public function testPostFilePathUpload()
public function set_up()
{
parent::set_up();
$this->curl = new Curl();
$this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, false);
}
$file_path = $this->get_png();
public function server($request_method, $data='')
{
$request_method = strtolower($request_method);
$this->curl->$request_method(self::TEST_URL . '/server.php', $data);
return $this->curl->response;
}
$data = array(
'key' => 'image',
'image' => '@' . $file_path,
);
public function testExtensionLoaded()
{
$this->assertTrue(extension_loaded('curl'));
}
public function testUserAgent()
{
$this->curl->setUserAgent(Curl::USER_AGENT);
$this->assertEquals(Curl::USER_AGENT, $this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_USER_AGENT',
)));
}
public function testGet()
{
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'GET');
}
public function testPostRequestMethod()
{
$this->assertTrue($this->server('POST', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'POST');
}
public function testPostData()
{
$this->assertTrue($this->server('POST', array(
'test' => 'post',
'key' => 'test',
)) === 'post');
}
public function testPostJsonData()
{
$resp = $this->curl->post(self::TEST_URL.'/server.php', ['foo' => 'bar'], true);
$this->assertTrue($resp->isSuccess());
$this->assertArrayHasKey('x-powered-by', $resp->getResponseHeaders());
// syntax error check
$resp->reset();
}
public function testPostMultidimensionalData()
{
$data = array(
'key' => 'file',
'file' => array(
'wibble',
'wubble',
'wobble',
),
);
$this->curl->post(self::TEST_URL . '/post_multidimensional.php', $data);
$this->assertEquals(
'key=file&file%5B0%5D=wibble&file%5B1%5D=wubble&file%5B2%5D=wobble',
$this->curl->response
);
}
public function testPostFilePathUpload()
{
$file_path = $this->get_png();
$data = array(
'key' => 'image',
'image' => '@' . $file_path,
);
$this->curl->setOpt(CURLOPT_RETURNTRANSFER, true);
$this->curl->post(self::TEST_URL . '/post_file_path_upload.php', $data);
$this->curl->post(self::TEST_URL . '/post_file_path_upload.php', $data);
$this->assertEquals(
array(
'request_method' => 'POST',
'key' => 'image',
'mime_content_type' => 'ERROR', // Temp change the image response, but assuming this is not fixing the issue indeed.
//'mime_content_type' => 'image/png'
),
json_decode($this->curl->response, true));
$this->assertEquals(
array(
'request_method' => 'POST',
'key' => 'image',
'mime_content_type' => 'ERROR', // Temp change the image response, but assuming this is not fixing the issue indeed.
//'mime_content_type' => 'image/png'
),
json_decode($this->curl->response, true)
);
unlink($file_path);
}
unlink($file_path);
}
public function testPutRequestMethod() {
$this->assertTrue($this->server('PUT', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'PUT');
}
public function testPutRequestMethod()
{
$this->assertTrue($this->server('PUT', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'PUT');
}
public function testPutData() {
$this->assertTrue($this->server('PUT', array(
'test' => 'put',
'key' => 'test',
)) === 'put');
}
public function testPutData()
{
$this->assertTrue($this->server('PUT', array(
'test' => 'put',
'key' => 'test',
)) === 'put');
}
public function testPutFileHandle() {
$png = $this->create_png();
$tmp_file = $this->create_tmp_file($png);
public function testPutFileHandle()
{
$png = $this->create_png();
$tmp_file = $this->create_tmp_file($png);
$this->curl->setOpt(CURLOPT_PUT, TRUE);
$this->curl->setOpt(CURLOPT_INFILE, $tmp_file);
$this->curl->setOpt(CURLOPT_INFILESIZE, strlen($png));
$this->curl->put(self::TEST_URL . '/server.php', array(
'test' => 'put_file_handle',
));
$this->curl->setOpt(CURLOPT_PUT, true);
$this->curl->setOpt(CURLOPT_INFILE, $tmp_file);
$this->curl->setOpt(CURLOPT_INFILESIZE, strlen($png));
$this->curl->put(self::TEST_URL . '/server.php', array(
'test' => 'put_file_handle',
));
fclose($tmp_file);
fclose($tmp_file);
$this->assertTrue($this->curl->response === 'image/png');
}
$this->assertTrue($this->curl->response === 'image/png');
}
public function testDelete() {
$this->assertTrue($this->server('DELETE', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'DELETE');
public function testDelete()
{
$this->assertTrue($this->server('DELETE', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'DELETE');
$this->assertTrue($this->server('DELETE', array(
'test' => 'delete',
'key' => 'test',
)) === 'delete');
}
$this->assertTrue($this->server('DELETE', array(
'test' => 'delete',
'key' => 'test',
)) === 'delete');
}
public function testBasicHttpAuth() {
public function testBasicHttpAuth()
{
$data = array();
$data = array();
$this->curl->get(self::TEST_URL . '/http_basic_auth.php', $data);
$this->curl->get(self::TEST_URL . '/http_basic_auth.php', $data);
$this->assertEquals('canceled', $this->curl->response);
$this->assertEquals('canceled', $this->curl->response);
$username = 'myusername';
$password = 'mypassword';
$username = 'myusername';
$password = 'mypassword';
$this->curl->setBasicAuthentication($username, $password);
$this->curl->setBasicAuthentication($username, $password);
$this->curl->get(self::TEST_URL . '/http_basic_auth.php', $data);
$this->curl->get(self::TEST_URL . '/http_basic_auth.php', $data);
$this->assertEquals(
'{"username":"myusername","password":"mypassword"}',
$this->curl->response
);
}
$this->assertEquals(
'{"username":"myusername","password":"mypassword"}',
$this->curl->response);
}
public function testReferrer()
{
$this->curl->setReferer('myreferrer');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_REFERER',
)) === 'myreferrer');
}
public function testDeprecatedReferrer()
{
$this->curl->setReferrer('myreferrer');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_REFERER',
)) === 'myreferrer');
}
public function testReferrer() {
$this->curl->setReferer('myreferrer');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_REFERER',
)) === 'myreferrer');
}
public function testDeprecatedReferrer() {
$this->curl->setReferrer('myreferrer');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_REFERER',
)) === 'myreferrer');
}
public function testCookies()
{
$this->curl->setCookie('mycookie', 'yum');
$this->assertTrue($this->server('GET', array(
'test' => 'cookie',
'key' => 'mycookie',
)) === 'yum');
}
public function testCookies() {
$this->curl->setCookie('mycookie', 'yum');
$this->assertTrue($this->server('GET', array(
'test' => 'cookie',
'key' => 'mycookie',
)) === 'yum');
}
public function testError()
{
$this->curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000);
$this->curl->get('http://1.2.3.4/');
$this->assertTrue($this->curl->error === true);
$this->assertTrue($this->curl->curl_error === true);
$this->assertTrue($this->curl->curl_error_code === CURLE_OPERATION_TIMEOUTED);
}
public function testError() {
$this->curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000);
$this->curl->get('http://1.2.3.4/');
$this->assertTrue($this->curl->error === TRUE);
$this->assertTrue($this->curl->curl_error === TRUE);
$this->assertTrue($this->curl->curl_error_code === CURLE_OPERATION_TIMEOUTED);
}
public function testHeaders()
{
$this->curl->setHeader('Content-Type', 'application/json');
$this->curl->setHeader('X-Requested-With', 'XMLHttpRequest');
$this->curl->setHeader('Accept', 'application/json');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'CONTENT_TYPE',
)) === 'application/json');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_X_REQUESTED_WITH',
)) === 'XMLHttpRequest');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_ACCEPT',
)) === 'application/json');
}
public function testHeaders() {
$this->curl->setHeader('Content-Type', 'application/json');
$this->curl->setHeader('X-Requested-With', 'XMLHttpRequest');
$this->curl->setHeader('Accept', 'application/json');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'CONTENT_TYPE',
)) === 'application/json');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_X_REQUESTED_WITH',
)) === 'XMLHttpRequest');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_ACCEPT',
)) === 'application/json');
}
public function testHeadersWithContinue()
{
$headers = file(dirname(__FILE__) . '/data/response_headers_with_continue.txt');
$this->curl->response_headers = array();
foreach ($headers as $header_line) {
$this->curl->addResponseHeaderLine(null, $header_line);
}
public function testHeadersWithContinue() {
$headers = file(dirname(__FILE__) . '/data/response_headers_with_continue.txt');
$this->curl->response_headers = array();
foreach($headers as $header_line) {
$this->curl->addResponseHeaderLine(null, $header_line);
}
$expected_headers = array_values(array_filter(array_map(function ($l) {
return trim($l, "\r\n");
}, array_slice($headers, 1))));
$expected_headers = array_values(array_filter(array_map(function($l) { return trim($l, "\r\n"); }, array_slice($headers, 1))));
$this->assertEquals($expected_headers, $this->curl->response_headers);
}
public function testReset()
{
$curl = $this->getMockBuilder(get_class($this->curl))->getMock();
$curl->expects($this->once())->method('reset')->with();
// lets make small request
$curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000);
$curl->get('http://1.2.3.4/');
$curl->reset();
$this->assertFalse($curl->error);
$this->assertSame(0, $curl->error_code);
$this->assertNull($curl->error_message);
$this->assertFalse($curl->curl_error);
$this->assertSame(0, $curl->curl_error_code);
$this->assertNull($curl->curl_error_message);
$this->assertFalse($curl->http_error);
$this->assertSame(0, $curl->http_status_code);
$this->assertNull($curl->http_error_message);
$this->assertNull($curl->request_headers);
$this->assertEmpty($curl->response_headers);
$this->assertNull($curl->response);
}
$this->assertEquals($expected_headers, $this->curl->response_headers);
}
public function testReset()
{
$curl = $this->getMockBuilder(get_class($this->curl))->getMock();
$curl->expects($this->once())->method('reset')->with();
// lets make small request
$curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000);
$curl->get('http://1.2.3.4/');
$curl->reset();
$this->assertFalse($curl->error);
$this->assertSame(0, $curl->error_code);
$this->assertNull($curl->error_message);
$this->assertFalse($curl->curl_error);
$this->assertSame(0, $curl->curl_error_code);
$this->assertNull($curl->curl_error_message);
$this->assertFalse($curl->http_error);
$this->assertSame(0, $curl->http_status_code);
$this->assertNull($curl->http_error_message);
$this->assertNull($curl->request_headers);
$this->assertEmpty($curl->response_headers);
$this->assertNull($curl->response);
}
public function create_png()
{
// PNG image data, 1 x 1, 1-bit colormap, non-interlaced
ob_start();
imagepng(imagecreatefromstring(base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7')));
$raw_image = ob_get_contents();
ob_end_clean();
return $raw_image;
}
function create_png() {
// PNG image data, 1 x 1, 1-bit colormap, non-interlaced
ob_start();
imagepng(imagecreatefromstring(base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7')));
$raw_image = ob_get_contents();
ob_end_clean();
return $raw_image;
}
public function create_tmp_file($data)
{
$tmp_file = tmpfile();
fwrite($tmp_file, $data);
rewind($tmp_file);
return $tmp_file;
}
function create_tmp_file($data) {
$tmp_file = tmpfile();
fwrite($tmp_file, $data);
rewind($tmp_file);
return $tmp_file;
}
function get_png() {
$tmp_filename = tempnam('/tmp', 'php-curl-class.');
file_put_contents($tmp_filename, $this->create_png());
return $tmp_filename;
}
public function get_png()
{
$tmp_filename = tempnam('/tmp', 'php-curl-class.');
file_put_contents($tmp_filename, $this->create_png());
return $tmp_filename;
}
}

View File

@ -7,10 +7,10 @@ $commands = array(
// Run the commands for output
$output = '';
foreach($commands AS $command){
// Run it
foreach ($commands as $command) {
// Run it
$tmp = shell_exec($command);
// Output
// Output
$output .= "<span style=\"color: #6BE234;\">\$</span> <span style=\"color: #729FCF;\">{$command}\n</span>";
$output .= htmlentities(trim($tmp)) . "\n";
}

View File

@ -1,14 +1,14 @@
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'canceled';
exit;
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'canceled';
exit;
}
header('Content-Type: application/json');
echo json_encode(array(
'username' => $_SERVER['PHP_AUTH_USER'],
'password' => $_SERVER['PHP_AUTH_PW'],
));
'username' => $_SERVER['PHP_AUTH_USER'],
'password' => $_SERVER['PHP_AUTH_PW'],
));

View File

@ -1,7 +1,7 @@
<?php
$request_method = isset($_SERVER['REQUEST_METHOD']) ?
$_SERVER['REQUEST_METHOD'] : '';
$_SERVER['REQUEST_METHOD'] : '';
$data_values = $request_method === 'POST' ? $_POST : $_GET;
@ -12,10 +12,12 @@ $response = array();
$response['request_method'] = $request_method;
$response['key'] = $key;
if(isset($_FILES[$key])) {
$response['mime_content_type'] = mime_content_type($_FILES[$key]['tmp_name']);
if (isset($_FILES[$key])) {
$response['mime_content_type'] = mime_content_type($_FILES[$key]['tmp_name']);
} else {
$response['mime_content_type'] = 'ERROR';
$response['mime_content_type'] = 'ERROR';
}
echo json_encode($response);
header('Content-Type: application/json');
echo json_encode($response);

View File

@ -1,4 +1,7 @@
<?php
$http_raw_post_data = file_get_contents('php://input');
echo $http_raw_post_data;
header('Content-Type: text/plain');
echo $http_raw_post_data;

View File

@ -22,10 +22,10 @@ $data_mapping = array(
'server' => '_SERVER',
);
if(isset($data_mapping[$test])) {
if (isset($data_mapping[$test])) {
$data = ${$data_mapping[$test]};
$value = isset($data[$key]) ? $data[$key] : '';
echo $value;
echo $value;
} else {
echo "Error.";
}

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2020 Graham Campbell <graham@alt-three.com>
Copyright (c) 2020-2021 Graham Campbell <hello@gjcampbell.co.uk>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -6,15 +6,16 @@
"authors": [
{
"name": "Graham Campbell",
"email": "graham@alt-three.com"
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"require": {
"php": "^7.0|^8.0",
"phpoption/phpoption": "^1.7.3"
"php": "^7.0 || ^8.0",
"phpoption/phpoption": "^1.8"
},
"require-dev": {
"phpunit/phpunit": "^6.5|^7.5|^8.5|^9.0"
"phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8"
},
"autoload": {
"psr-4": {
@ -28,12 +29,5 @@
},
"config": {
"preferred-install": "dist"
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
}

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
/*
* This file is part of Result Type.
*
* (c) Graham Campbell <graham@alt-three.com>
* (c) Graham Campbell <hello@gjcampbell.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
/*
* This file is part of Result Type.
*
* (c) Graham Campbell <graham@alt-three.com>
* (c) Graham Campbell <hello@gjcampbell.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
/*
* This file is part of Result Type.
*
* (c) Graham Campbell <graham@alt-three.com>
* (c) Graham Campbell <hello@gjcampbell.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.

View File

@ -12,7 +12,7 @@
}
],
"require": {
"php": "^5.6 | ^7.0",
"php": "^5.6|^7.0|^8.0",
"curl/curl": "^2.3",
"ext-curl": "*",
"ext-json": "*",

View File

@ -1,3 +1,64 @@
### 2.4.0 (2022-03-14)
* Added `[Monolog\LogRecord](src/Monolog/LogRecord.php)` interface that can be used to type-hint records like `array|\Monolog\LogRecord $record` to be forward compatible with the upcoming Monolog 3 changes
* Added `includeStacktraces` constructor params to LineFormatter & JsonFormatter (#1603)
* Added `persistent`, `timeout`, `writingTimeout`, `connectionTimeout`, `chunkSize` constructor params to SocketHandler and derivatives (#1600)
* Added `AsMonologProcessor` PHP attribute which can help autowiring / autoconfiguration of processors if frameworks / integrations decide to make use of it. This is useless when used purely with Monolog (#1637)
* Added support for keeping native BSON types as is in MongoDBFormatter (#1620)
* Added support for a `user_agent` key in WebProcessor, disabled by default but you can use it by configuring the $extraFields you want (#1613)
* Added support for username/userIcon in SlackWebhookHandler (#1617)
* Added extension points to BrowserConsoleHandler (#1593)
* Added record message/context/extra info to exceptions thrown when a StreamHandler cannot open its stream to avoid completely losing the data logged (#1630)
* Fixed error handler signature to accept a null $context which happens with internal PHP errors (#1614)
* Fixed a few setter methods not returning `self` (#1609)
* Fixed handling of records going over the max Telegram message length (#1616)
### 2.3.5 (2021-10-01)
* Fixed regression in StreamHandler since 2.3.3 on systems with the memory_limit set to >=20GB (#1592)
### 2.3.4 (2021-09-15)
* Fixed support for psr/log 3.x (#1589)
### 2.3.3 (2021-09-14)
* Fixed memory usage when using StreamHandler and calling stream_get_contents on the resource you passed to it (#1578, #1577)
* Fixed support for psr/log 2.x (#1587)
* Fixed some type annotations
### 2.3.2 (2021-07-23)
* Fixed compatibility with PHP 7.2 - 7.4 when experiencing PCRE errors (#1568)
### 2.3.1 (2021-07-14)
* Fixed Utils::getClass handling of anonymous classes not being fully compatible with PHP 8 (#1563)
* Fixed some `@inheritDoc` annotations having the wrong case
### 2.3.0 (2021-07-05)
* Added a ton of PHPStan type annotations as well as type aliases on Monolog\Logger for Record, Level and LevelName that you can import (#1557)
* Added ability to customize date format when using JsonFormatter (#1561)
* Fixed FilterHandler not calling reset on its internal handler when reset() is called on it (#1531)
* Fixed SyslogUdpHandler not setting the timezone correctly on DateTimeImmutable instances (#1540)
* Fixed StreamHandler thread safety - chunk size set to 2GB now to avoid interlacing when doing concurrent writes (#1553)
### 2.2.0 (2020-12-14)
* Added JSON_PARTIAL_OUTPUT_ON_ERROR to default json encoding flags, to avoid dropping entire context data or even records due to an invalid subset of it somewhere
* Added setDateFormat to NormalizerFormatter (and Line/Json formatters by extension) to allow changing this after object creation
* Added RedisPubSubHandler to log records to a Redis channel using PUBLISH
* Added support for Elastica 7, and deprecated the $type argument of ElasticaFormatter which is not in use anymore as of Elastica 7
* Added support for millisecond write timeouts in SocketHandler, you can now pass floats to setWritingTimeout, e.g. 0.2 is 200ms
* Added support for unix sockets in SyslogUdpHandler (set $port to 0 to make the $host a unix socket)
* Added handleBatch support for TelegramBotHandler
* Added RFC5424e extended date format including milliseconds to SyslogUdpHandler
* Added support for configuring handlers with numeric level values in strings (coming from e.g. env vars)
* Fixed Wildfire/FirePHP/ChromePHP handling of unicode characters
* Fixed PHP 8 issues in SyslogUdpHandler
* Fixed internal type error when mbstring is missing
### 2.1.1 (2020-07-23)
* Fixed removing of json encoding options
@ -86,6 +147,14 @@
* Added support for the PHP 7.x `mongodb` extension in the MongoDBHandler
* Fixed many minor issues in various handlers, and probably added a few regressions too
### 1.26.1 (2021-05-28)
* Fixed PHP 8.1 deprecation warning
### 1.26.0 (2020-12-14)
* Added $dateFormat and $removeUsedContextFields arguments to PsrLogMessageProcessor (backport from 2.x)
### 1.25.5 (2020-07-23)
* Fixed array access on null in RavenHandler

View File

@ -1,4 +1,4 @@
# Monolog - Logging for PHP [![Build Status](https://travis-ci.org/Seldaek/monolog.svg?branch=master)](https://travis-ci.org/Seldaek/monolog)
# Monolog - Logging for PHP [![Continuous Integration](https://github.com/Seldaek/monolog/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/Seldaek/monolog/actions)
[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
@ -64,7 +64,11 @@ can also add your own there if you publish one.
### Requirements
- Monolog 2.x works with PHP 7.2 or above, use Monolog `^1.0` for PHP 5.3+ support.
- Monolog `^2.0` works with PHP 7.2 or above, use Monolog `^1.25` for PHP 5.3+ support.
### Support
Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 where possible to benefit from all the latest features and fixes.
### Submitting bugs and feature requests
@ -82,13 +86,16 @@ Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/mono
- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer.
- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog.
- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog.
- [Nette Framework](http://nette.org/en/) can be used with Monolog via [contributte/monolog](https://github.com/contributte/monolog) extension.
- [Nette Framework](http://nette.org/en/) is usable with Monolog via the [contributte/monolog](https://github.com/contributte/monolog) or [orisai/nette-monolog](https://github.com/orisai/nette-monolog) extensions.
- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog.
- [FuelPHP](http://fuelphp.com/) comes out of the box with Monolog.
- [Equip Framework](https://github.com/equip/framework) comes out of the box with Monolog.
- [Yii 2](http://www.yiiframework.com/) is usable with Monolog via the [yii2-monolog](https://github.com/merorafael/yii2-monolog) or [yii2-psr-log-target](https://github.com/samdark/yii2-psr-log-target) plugins.
- [Hawkbit Micro Framework](https://github.com/HawkBitPhp/hawkbit) comes out of the box with Monolog.
- [SilverStripe 4](https://www.silverstripe.org/) comes out of the box with Monolog.
- [Drupal](https://www.drupal.org/) is usable with Monolog via the [monolog](https://www.drupal.org/project/monolog) module.
- [Aimeos ecommerce framework](https://aimeos.org/) is usable with Monolog via the [ai-monolog](https://github.com/aimeos/ai-monolog) extension.
- [Magento](https://magento.com/) comes out of the box with Monolog.
### Author

View File

@ -2,34 +2,35 @@
"name": "monolog/monolog",
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
"keywords": ["log", "logging", "psr-3"],
"homepage": "http://github.com/Seldaek/monolog",
"homepage": "https://github.com/Seldaek/monolog",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
"homepage": "https://seld.be"
}
],
"require": {
"php": ">=7.2",
"psr/log": "^1.0.1"
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
},
"require-dev": {
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
"doctrine/couchdb": "~1.0@dev",
"elasticsearch/elasticsearch": "^6.0",
"elasticsearch/elasticsearch": "^7",
"mongodb/mongodb": "^1.8",
"graylog2/gelf-php": "^1.4.2",
"php-amqplib/php-amqplib": "~2.4",
"php-amqplib/php-amqplib": "~2.4 || ^3",
"php-console/php-console": "^3.1.3",
"php-parallel-lint/php-parallel-lint": "^1.0",
"phpspec/prophecy": "^1.6.1",
"phpunit/phpunit": "^8.5",
"predis/predis": "^1.1",
"rollbar/rollbar": "^1.3",
"ruflin/elastica": ">=0.90 <3.0",
"swiftmailer/swiftmailer": "^5.3|^6.0"
"rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": ">=0.90@dev",
"swiftmailer/swiftmailer": "^5.3|^6.0",
"phpstan/phpstan": "^0.12.91"
},
"suggest": {
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
@ -43,7 +44,10 @@
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"php-console/php-console": "Allow sending log messages to Google Chrome",
"ext-mbstring": "Allow to work properly with unicode symbols"
"ext-mbstring": "Allow to work properly with unicode symbols",
"ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
"ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
"ext-openssl": "Required to send log messages using SSL"
},
"autoload": {
"psr-4": {"Monolog\\": "src/Monolog"}
@ -52,24 +56,20 @@
"psr-4": {"Monolog\\": "tests/Monolog"}
},
"provide": {
"psr/log-implementation": "1.0.0"
"psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
"dev-main": "2.x-dev"
}
},
"scripts": {
"lint": [
"parallel-lint . --exclude vendor"
],
"test": [
"phpunit"
]
"test": "@php vendor/bin/phpunit",
"phpstan": "@php vendor/bin/phpstan analyse"
},
"config": {
"lock": false,
"sort-packages": true,
"platform-check": false
},
"lock": false
}
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Attribute;
/**
* A reusable attribute to help configure a class or a method as a processor.
*
* Using it offers no guarantee: it needs to be leveraged by a Monolog third-party consumer.
*
* Using it with the Monolog library only has no effect at all: processors should still be turned into a callable if
* needed and manually pushed to the loggers and to the processable handlers.
*/
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AsMonologProcessor
{
/**
* @param string|null $channel The logging channel the processor should be pushed to.
* @param string|null $handler The handler the processor should be pushed to.
* @param string|null $method The method that processes the records (if the attribute is used at the class level).
*/
public function __construct(
public ?string $channel = null,
public ?string $handler = null,
public ?string $method = null,
) {
}
}

View File

@ -25,19 +25,30 @@ use Psr\Log\LogLevel;
*/
class ErrorHandler
{
/** @var LoggerInterface */
private $logger;
private $previousExceptionHandler;
private $uncaughtExceptionLevelMap;
/** @var ?callable */
private $previousExceptionHandler = null;
/** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */
private $uncaughtExceptionLevelMap = [];
private $previousErrorHandler;
private $errorLevelMap;
private $handleOnlyReportedErrors;
/** @var callable|true|null */
private $previousErrorHandler = null;
/** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */
private $errorLevelMap = [];
/** @var bool */
private $handleOnlyReportedErrors = true;
private $hasFatalErrorHandler;
private $fatalLevel;
private $reservedMemory;
/** @var bool */
private $hasFatalErrorHandler = false;
/** @var LogLevel::* */
private $fatalLevel = LogLevel::ALERT;
/** @var ?string */
private $reservedMemory = null;
/** @var ?mixed */
private $lastFatalTrace;
/** @var int[] */
private static $fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
public function __construct(LoggerInterface $logger)
@ -50,14 +61,15 @@ class ErrorHandler
*
* By default it will handle errors, exceptions and fatal errors
*
* @param LoggerInterface $logger
* @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
* @param array|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
* @param string|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
* @param LoggerInterface $logger
* @param array<int, LogLevel::*>|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
* @param array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
* @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
* @return ErrorHandler
*/
public static function register(LoggerInterface $logger, $errorLevelMap = [], $exceptionLevelMap = [], $fatalLevel = null): self
{
/** @phpstan-ignore-next-line */
$handler = new static($logger);
if ($errorLevelMap !== false) {
$handler->registerErrorHandler($errorLevelMap);
@ -72,9 +84,15 @@ class ErrorHandler
return $handler;
}
public function registerExceptionHandler($levelMap = [], $callPrevious = true): self
/**
* @param array<class-string, LogLevel::*> $levelMap an array of class name to LogLevel::* constant mapping
* @return $this
*/
public function registerExceptionHandler(array $levelMap = [], bool $callPrevious = true): self
{
$prev = set_exception_handler([$this, 'handleException']);
$prev = set_exception_handler(function (\Throwable $e): void {
$this->handleException($e);
});
$this->uncaughtExceptionLevelMap = $levelMap;
foreach ($this->defaultExceptionLevelMap() as $class => $level) {
if (!isset($this->uncaughtExceptionLevelMap[$class])) {
@ -88,12 +106,18 @@ class ErrorHandler
return $this;
}
public function registerErrorHandler(array $levelMap = [], $callPrevious = true, $errorTypes = -1, $handleOnlyReportedErrors = true): self
/**
* @param array<int, LogLevel::*> $levelMap an array of E_* constant to LogLevel::* constant mapping
* @return $this
*/
public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self
{
$prev = set_error_handler([$this, 'handleError'], $errorTypes);
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
if ($callPrevious) {
$this->previousErrorHandler = $prev ?: true;
} else {
$this->previousErrorHandler = null;
}
$this->handleOnlyReportedErrors = $handleOnlyReportedErrors;
@ -102,20 +126,23 @@ class ErrorHandler
}
/**
* @param string|null $level a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
* @param int $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done
* @param LogLevel::*|null $level a LogLevel::* constant, null to use the default LogLevel::ALERT
* @param int $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done
*/
public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self
{
register_shutdown_function([$this, 'handleFatalError']);
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
$this->fatalLevel = $level;
$this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
$this->hasFatalErrorHandler = true;
return $this;
}
/**
* @return array<class-string, LogLevel::*>
*/
protected function defaultExceptionLevelMap(): array
{
return [
@ -124,6 +151,9 @@ class ErrorHandler
];
}
/**
* @return array<int, LogLevel::*>
*/
protected function defaultErrorLevelMap(): array
{
return [
@ -146,10 +176,9 @@ class ErrorHandler
}
/**
* @private
* @param \Exception $e
* @phpstan-return never
*/
public function handleException($e)
private function handleException(\Throwable $e): void
{
$level = LogLevel::ERROR;
foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
@ -178,11 +207,13 @@ class ErrorHandler
/**
* @private
*
* @param mixed[] $context
*/
public function handleError($code, $message, $file = '', $line = 0, $context = [])
public function handleError(int $code, string $message, string $file = '', int $line = 0, ?array $context = []): bool
{
if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) {
return;
return false;
}
// fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
@ -198,7 +229,7 @@ class ErrorHandler
if ($this->previousErrorHandler === true) {
return false;
} elseif ($this->previousErrorHandler) {
return ($this->previousErrorHandler)($code, $message, $file, $line, $context);
return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context);
}
return true;
@ -207,14 +238,14 @@ class ErrorHandler
/**
* @private
*/
public function handleFatalError()
public function handleFatalError(): void
{
$this->reservedMemory = '';
$lastError = error_get_last();
if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) {
$this->logger->log(
$this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel,
$this->fatalLevel,
'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
['code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace]
);
@ -227,6 +258,9 @@ class ErrorHandler
}
}
/**
* @param int $code
*/
private static function codeToString($code): string
{
switch ($code) {

View File

@ -22,6 +22,8 @@ class ChromePHPFormatter implements FormatterInterface
{
/**
* Translates Monolog log levels to Wildfire levels.
*
* @var array<int, 'log'|'info'|'warn'|'error'>
*/
private $logLevels = [
Logger::DEBUG => 'log',
@ -35,7 +37,7 @@ class ChromePHPFormatter implements FormatterInterface
];
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function format(array $record)
{
@ -66,7 +68,7 @@ class ChromePHPFormatter implements FormatterInterface
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function formatBatch(array $records)
{

View File

@ -17,6 +17,8 @@ use Elastica\Document;
* Format a log message into an Elastica Document
*
* @author Jelle Vink <jelle.vink@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class ElasticaFormatter extends NormalizerFormatter
{
@ -26,15 +28,15 @@ class ElasticaFormatter extends NormalizerFormatter
protected $index;
/**
* @var string Elastic search document type
* @var ?string Elastic search document type
*/
protected $type;
/**
* @param string $index Elastic Search index name
* @param string $type Elastic Search document type
* @param string $index Elastic Search index name
* @param ?string $type Elastic Search document type, deprecated as of Elastica 7
*/
public function __construct(string $index, string $type)
public function __construct(string $index, ?string $type)
{
// elasticsearch requires a ISO 8601 format date with optional millisecond precision.
parent::__construct('Y-m-d\TH:i:s.uP');
@ -44,7 +46,7 @@ class ElasticaFormatter extends NormalizerFormatter
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function format(array $record)
{
@ -58,21 +60,28 @@ class ElasticaFormatter extends NormalizerFormatter
return $this->index;
}
/**
* @deprecated since Elastica 7 type has no effect
*/
public function getType(): string
{
/** @phpstan-ignore-next-line */
return $this->type;
}
/**
* Convert a log message into an Elastica Document
* @param array $record
* @return Document
*
* @phpstan-param Record $record
*/
protected function getDocument(array $record): Document
{
$document = new Document();
$document->setData($record);
$document->setType($this->type);
if (method_exists($document, 'setType')) {
/** @phpstan-ignore-next-line */
$document->setType($this->type);
}
$document->setIndex($this->index);
return $document;

View File

@ -11,7 +11,7 @@
namespace Monolog\Formatter;
use DateTime;
use DateTimeInterface;
/**
* Format a log message into an Elasticsearch record
@ -37,14 +37,14 @@ class ElasticsearchFormatter extends NormalizerFormatter
public function __construct(string $index, string $type)
{
// Elasticsearch requires an ISO 8601 format date with optional millisecond precision.
parent::__construct(DateTime::ISO8601);
parent::__construct(DateTimeInterface::ISO8601);
$this->index = $index;
$this->type = $type;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function format(array $record)
{
@ -76,8 +76,8 @@ class ElasticsearchFormatter extends NormalizerFormatter
/**
* Convert a log message into an Elasticsearch record
*
* @param array $record Log message
* @return array
* @param mixed[] $record Log message
* @return mixed[]
*/
protected function getDocument(array $record): array
{

View File

@ -35,7 +35,9 @@ class FlowdockFormatter implements FormatterInterface
}
/**
* {@inheritdoc}
* {@inheritDoc}
*
* @return mixed[]
*/
public function format(array $record): array
{
@ -69,7 +71,9 @@ class FlowdockFormatter implements FormatterInterface
}
/**
* {@inheritdoc}
* {@inheritDoc}
*
* @return mixed[][]
*/
public function formatBatch(array $records): array
{

View File

@ -15,6 +15,8 @@ namespace Monolog\Formatter;
* Interface for formatters
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
interface FormatterInterface
{
@ -23,6 +25,8 @@ interface FormatterInterface
*
* @param array $record A record to format
* @return mixed The formatted record
*
* @phpstan-param Record $record
*/
public function format(array $record);
@ -31,6 +35,8 @@ interface FormatterInterface
*
* @param array $records A set of records to format
* @return mixed The formatted set of records
*
* @phpstan-param Record[] $records
*/
public function formatBatch(array $records);
}

View File

@ -20,6 +20,8 @@ use Monolog\Utils;
* @see http://docs.graylog.org/en/latest/pages/gelf.html
*
* @author Matt Lehner <mlehner@gmail.com>
*
* @phpstan-import-type Level from \Monolog\Logger
*/
class GelfMessageFormatter extends NormalizerFormatter
{
@ -47,6 +49,10 @@ class GelfMessageFormatter extends NormalizerFormatter
/**
* Translates Monolog log levels to Graylog2 log priorities.
*
* @var array<int, int>
*
* @phpstan-var array<Level, int>
*/
private $logLevels = [
Logger::DEBUG => 7,
@ -61,9 +67,13 @@ class GelfMessageFormatter extends NormalizerFormatter
public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null)
{
if (!class_exists(Message::class)) {
throw new \RuntimeException('Composer package graylog2/gelf-php is required to use Monolog\'s GelfMessageFormatter');
}
parent::__construct('U.u');
$this->systemName = (is_null($systemName) || $systemName === '') ? gethostname() : $systemName;
$this->systemName = (is_null($systemName) || $systemName === '') ? (string) gethostname() : $systemName;
$this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix;
$this->contextPrefix = $contextPrefix;
@ -71,15 +81,18 @@ class GelfMessageFormatter extends NormalizerFormatter
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function format(array $record): Message
{
$context = $extra = [];
if (isset($record['context'])) {
$record['context'] = parent::format($record['context']);
/** @var mixed[] $context */
$context = parent::normalize($record['context']);
}
if (isset($record['extra'])) {
$record['extra'] = parent::format($record['extra']);
/** @var mixed[] $extra */
$extra = parent::normalize($record['extra']);
}
if (!isset($record['datetime'], $record['message'], $record['level'])) {
@ -103,39 +116,40 @@ class GelfMessageFormatter extends NormalizerFormatter
if (isset($record['channel'])) {
$message->setFacility($record['channel']);
}
if (isset($record['extra']['line'])) {
$message->setLine($record['extra']['line']);
unset($record['extra']['line']);
if (isset($extra['line'])) {
$message->setLine($extra['line']);
unset($extra['line']);
}
if (isset($record['extra']['file'])) {
$message->setFile($record['extra']['file']);
unset($record['extra']['file']);
if (isset($extra['file'])) {
$message->setFile($extra['file']);
unset($extra['file']);
}
foreach ($record['extra'] as $key => $val) {
foreach ($extra as $key => $val) {
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
$len = strlen($this->extraPrefix . $key . $val);
if ($len > $this->maxLength) {
$message->setAdditional($this->extraPrefix . $key, Utils::substr($val, 0, $this->maxLength));
$message->setAdditional($this->extraPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength));
continue;
}
$message->setAdditional($this->extraPrefix . $key, $val);
}
foreach ($record['context'] as $key => $val) {
foreach ($context as $key => $val) {
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
$len = strlen($this->contextPrefix . $key . $val);
if ($len > $this->maxLength) {
$message->setAdditional($this->contextPrefix . $key, Utils::substr($val, 0, $this->maxLength));
$message->setAdditional($this->contextPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength));
continue;
}
$message->setAdditional($this->contextPrefix . $key, $val);
}
if (null === $message->getFile() && isset($record['context']['exception']['file'])) {
if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) {
/** @phpstan-ignore-next-line */
if (null === $message->getFile() && isset($context['exception']['file'])) {
if (preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) {
$message->setFile($matches[1]);
$message->setLine($matches[2]);
}

View File

@ -25,6 +25,8 @@ class HtmlFormatter extends NormalizerFormatter
{
/**
* Translates Monolog log levels to html color priorities.
*
* @var array<int, string>
*/
protected $logLevels = [
Logger::DEBUG => '#CCCCCC',
@ -79,7 +81,6 @@ class HtmlFormatter extends NormalizerFormatter
/**
* Formats a log record.
*
* @param array $record A record to format
* @return string The formatted record
*/
public function format(array $record): string
@ -113,7 +114,6 @@ class HtmlFormatter extends NormalizerFormatter
/**
* Formats a set of log records.
*
* @param array $records A set of records to format
* @return string The formatted set of records
*/
public function formatBatch(array $records): string
@ -126,6 +126,9 @@ class HtmlFormatter extends NormalizerFormatter
return $message;
}
/**
* @param mixed $data
*/
protected function convertToString($data): string
{
if (null === $data || is_scalar($data)) {

View File

@ -19,26 +19,34 @@ use Throwable;
* This can be useful to log to databases or remote APIs
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class JsonFormatter extends NormalizerFormatter
{
public const BATCH_MODE_JSON = 1;
public const BATCH_MODE_NEWLINES = 2;
/** @var self::BATCH_MODE_* */
protected $batchMode;
/** @var bool */
protected $appendNewline;
/** @var bool */
protected $ignoreEmptyContextAndExtra;
/**
* @var bool
*/
/** @var bool */
protected $includeStacktraces = false;
public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false)
/**
* @param self::BATCH_MODE_* $batchMode
*/
public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false)
{
$this->batchMode = $batchMode;
$this->appendNewline = $appendNewline;
$this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
$this->includeStacktraces = $includeStacktraces;
parent::__construct();
}
/**
@ -62,9 +70,7 @@ class JsonFormatter extends NormalizerFormatter
}
/**
* {@inheritdoc}
*
* @suppress PhanTypeComparisonToArray
* {@inheritDoc}
*/
public function format(array $record): string
{
@ -89,7 +95,7 @@ class JsonFormatter extends NormalizerFormatter
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function formatBatch(array $records): string
{
@ -103,13 +109,20 @@ class JsonFormatter extends NormalizerFormatter
}
}
public function includeStacktraces(bool $include = true)
/**
* @return self
*/
public function includeStacktraces(bool $include = true): self
{
$this->includeStacktraces = $include;
return $this;
}
/**
* Return a JSON-encoded array of records.
*
* @phpstan-param Record[] $records
*/
protected function formatBatchJson(array $records): string
{
@ -119,6 +132,8 @@ class JsonFormatter extends NormalizerFormatter
/**
* Use new lines to separate records instead of a
* JSON-encoded array.
*
* @phpstan-param Record[] $records
*/
protected function formatBatchNewlines(array $records): string
{
@ -163,6 +178,10 @@ class JsonFormatter extends NormalizerFormatter
return $normalized;
}
if ($data instanceof \DateTimeInterface) {
return $this->formatDate($data);
}
if ($data instanceof Throwable) {
return $this->normalizeException($data, $depth);
}
@ -177,6 +196,8 @@ class JsonFormatter extends NormalizerFormatter
/**
* Normalizes given exception with or without its own stack trace based on
* `includeStacktraces` property.
*
* {@inheritDoc}
*/
protected function normalizeException(Throwable $e, int $depth = 0): array
{

View File

@ -25,9 +25,13 @@ class LineFormatter extends NormalizerFormatter
{
public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
/** @var string */
protected $format;
/** @var bool */
protected $allowInlineLineBreaks;
/** @var bool */
protected $ignoreEmptyContextAndExtra;
/** @var bool */
protected $includeStacktraces;
/**
@ -36,34 +40,41 @@ class LineFormatter extends NormalizerFormatter
* @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
* @param bool $ignoreEmptyContextAndExtra
*/
public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false)
public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false)
{
$this->format = $format === null ? static::SIMPLE_FORMAT : $format;
$this->allowInlineLineBreaks = $allowInlineLineBreaks;
$this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
$this->includeStacktraces($includeStacktraces);
parent::__construct($dateFormat);
}
public function includeStacktraces(bool $include = true)
public function includeStacktraces(bool $include = true): self
{
$this->includeStacktraces = $include;
if ($this->includeStacktraces) {
$this->allowInlineLineBreaks = true;
}
return $this;
}
public function allowInlineLineBreaks(bool $allow = true)
public function allowInlineLineBreaks(bool $allow = true): self
{
$this->allowInlineLineBreaks = $allow;
return $this;
}
public function ignoreEmptyContextAndExtra(bool $ignore = true)
public function ignoreEmptyContextAndExtra(bool $ignore = true): self
{
$this->ignoreEmptyContextAndExtra = $ignore;
return $this;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function format(array $record): string
{
@ -106,6 +117,10 @@ class LineFormatter extends NormalizerFormatter
// remove leftover %extra.xxx% and %context.xxx% if any
if (false !== strpos($output, '%')) {
$output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
if (null === $output) {
$pcreErrorCode = preg_last_error();
throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
}
}
return $output;
@ -121,14 +136,14 @@ class LineFormatter extends NormalizerFormatter
return $message;
}
/**
* @param mixed $value
*/
public function stringify($value): string
{
return $this->replaceNewlines($this->convertToString($value));
}
/**
* @suppress PhanParamSignatureMismatch
*/
protected function normalizeException(\Throwable $e, int $depth = 0): string
{
$str = $this->formatException($e);
@ -142,6 +157,9 @@ class LineFormatter extends NormalizerFormatter
return $str;
}
/**
* @param mixed $data
*/
protected function convertToString($data): string
{
if (null === $data || is_bool($data)) {

View File

@ -52,14 +52,14 @@ class LogstashFormatter extends NormalizerFormatter
// logstash requires a ISO 8601 format date with optional millisecond precision.
parent::__construct('Y-m-d\TH:i:s.uP');
$this->systemName = $systemName === null ? gethostname() : $systemName;
$this->systemName = $systemName === null ? (string) gethostname() : $systemName;
$this->applicationName = $applicationName;
$this->extraKey = $extraKey;
$this->contextKey = $contextKey;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function format(array $record): string
{

View File

@ -11,6 +11,7 @@
namespace Monolog\Formatter;
use MongoDB\BSON\Type;
use MongoDB\BSON\UTCDateTime;
use Monolog\Utils;
@ -21,8 +22,11 @@ use Monolog\Utils;
*/
class MongoDBFormatter implements FormatterInterface
{
/** @var bool */
private $exceptionTraceAsString;
/** @var int */
private $maxNestingLevel;
/** @var bool */
private $isLegacyMongoExt;
/**
@ -34,53 +38,66 @@ class MongoDBFormatter implements FormatterInterface
$this->maxNestingLevel = max($maxNestingLevel, 0);
$this->exceptionTraceAsString = $exceptionTraceAsString;
$this->isLegacyMongoExt = version_compare(phpversion('mongodb'), '1.1.9', '<=');
$this->isLegacyMongoExt = extension_loaded('mongodb') && version_compare((string) phpversion('mongodb'), '1.1.9', '<=');
}
/**
* {@inheritDoc}
*
* @return mixed[]
*/
public function format(array $record): array
{
return $this->formatArray($record);
/** @var mixed[] $res */
$res = $this->formatArray($record);
return $res;
}
/**
* {@inheritDoc}
*
* @return array<mixed[]>
*/
public function formatBatch(array $records): array
{
$formatted = [];
foreach ($records as $key => $record) {
$records[$key] = $this->format($record);
$formatted[$key] = $this->format($record);
}
return $records;
return $formatted;
}
/**
* @return array|string Array except when max nesting level is reached then a string "[...]"
* @param mixed[] $array
* @return mixed[]|string Array except when max nesting level is reached then a string "[...]"
*/
protected function formatArray(array $record, int $nestingLevel = 0)
protected function formatArray(array $array, int $nestingLevel = 0)
{
if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
foreach ($record as $name => $value) {
if ($value instanceof \DateTimeInterface) {
$record[$name] = $this->formatDate($value, $nestingLevel + 1);
} elseif ($value instanceof \Throwable) {
$record[$name] = $this->formatException($value, $nestingLevel + 1);
} elseif (is_array($value)) {
$record[$name] = $this->formatArray($value, $nestingLevel + 1);
} elseif (is_object($value)) {
$record[$name] = $this->formatObject($value, $nestingLevel + 1);
}
}
} else {
$record = '[...]';
if ($this->maxNestingLevel > 0 && $nestingLevel > $this->maxNestingLevel) {
return '[...]';
}
return $record;
foreach ($array as $name => $value) {
if ($value instanceof \DateTimeInterface) {
$array[$name] = $this->formatDate($value, $nestingLevel + 1);
} elseif ($value instanceof \Throwable) {
$array[$name] = $this->formatException($value, $nestingLevel + 1);
} elseif (is_array($value)) {
$array[$name] = $this->formatArray($value, $nestingLevel + 1);
} elseif (is_object($value) && !$value instanceof Type) {
$array[$name] = $this->formatObject($value, $nestingLevel + 1);
}
}
return $array;
}
/**
* @param mixed $value
* @return mixed[]|string
*/
protected function formatObject($value, int $nestingLevel)
{
$objectVars = get_object_vars($value);
@ -89,6 +106,9 @@ class MongoDBFormatter implements FormatterInterface
return $this->formatArray($objectVars, $nestingLevel);
}
/**
* @return mixed[]|string
*/
protected function formatException(\Throwable $exception, int $nestingLevel)
{
$formattedException = [
@ -118,7 +138,7 @@ class MongoDBFormatter implements FormatterInterface
private function getMongoDbDateTime(\DateTimeInterface $value): UTCDateTime
{
return new UTCDateTime((int) (string) floor($value->format('U.u') * 1000));
return new UTCDateTime((int) floor(((float) $value->format('U.u')) * 1000));
}
/**
@ -130,12 +150,13 @@ class MongoDBFormatter implements FormatterInterface
*/
private function legacyGetMongoDbDateTime(\DateTimeInterface $value): UTCDateTime
{
$milliseconds = floor($value->format('U.u') * 1000);
$milliseconds = floor(((float) $value->format('U.u')) * 1000);
$milliseconds = (PHP_INT_SIZE == 8) //64-bit OS?
? (int) $milliseconds
: (string) $milliseconds;
// @phpstan-ignore-next-line
return new UTCDateTime($milliseconds);
}
}

View File

@ -24,10 +24,14 @@ class NormalizerFormatter implements FormatterInterface
{
public const SIMPLE_DATE = "Y-m-d\TH:i:sP";
/** @var string */
protected $dateFormat;
/** @var int */
protected $maxNormalizeDepth = 9;
/** @var int */
protected $maxNormalizeItemCount = 1000;
/** @var int */
private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;
/**
@ -42,7 +46,9 @@ class NormalizerFormatter implements FormatterInterface
}
/**
* {@inheritdoc}
* {@inheritDoc}
*
* @param mixed[] $record
*/
public function format(array $record)
{
@ -50,7 +56,7 @@ class NormalizerFormatter implements FormatterInterface
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function formatBatch(array $records)
{
@ -61,6 +67,18 @@ class NormalizerFormatter implements FormatterInterface
return $records;
}
public function getDateFormat(): string
{
return $this->dateFormat;
}
public function setDateFormat(string $dateFormat): self
{
$this->dateFormat = $dateFormat;
return $this;
}
/**
* The maximum number of normalization levels to go through
*/
@ -106,8 +124,8 @@ class NormalizerFormatter implements FormatterInterface
}
/**
* @param mixed $data
* @return int|bool|string|null|array
* @param mixed $data
* @return null|scalar|array<array|scalar|null>
*/
protected function normalize($data, int $depth = 0)
{
@ -154,17 +172,15 @@ class NormalizerFormatter implements FormatterInterface
}
if ($data instanceof \JsonSerializable) {
/** @var null|scalar|array<array|scalar|null> $value */
$value = $data->jsonSerialize();
} elseif (method_exists($data, '__toString')) {
/** @var string $value */
$value = $data->__toString();
} else {
// the rest is normalized by json encoding and decoding it
$encoded = $this->toJson($data, true);
if ($encoded === false) {
$value = 'JSON_ERROR';
} else {
$value = json_decode($encoded, true);
}
/** @var null|scalar|array<array|scalar|null> $value */
$value = json_decode($this->toJson($data, true), true);
}
return [Utils::getClass($data) => $value];
@ -178,7 +194,7 @@ class NormalizerFormatter implements FormatterInterface
}
/**
* @return array
* @return mixed[]
*/
protected function normalizeException(Throwable $e, int $depth = 0)
{
@ -237,6 +253,9 @@ class NormalizerFormatter implements FormatterInterface
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
}
/**
* @return string
*/
protected function formatDate(\DateTimeInterface $date)
{
// in case the date format isn't custom then we defer to the custom DateTimeImmutable
@ -248,13 +267,17 @@ class NormalizerFormatter implements FormatterInterface
return $date->format($this->dateFormat);
}
public function addJsonEncodeOption($option)
public function addJsonEncodeOption(int $option): self
{
$this->jsonEncodeOptions |= $option;
return $this;
}
public function removeJsonEncodeOption($option)
public function removeJsonEncodeOption(int $option): self
{
$this->jsonEncodeOptions &= ~$option;
return $this;
}
}

View File

@ -20,26 +20,29 @@ namespace Monolog\Formatter;
class ScalarFormatter extends NormalizerFormatter
{
/**
* {@inheritdoc}
* {@inheritDoc}
*
* @phpstan-return array<string, scalar|null> $record
*/
public function format(array $record): array
{
$result = [];
foreach ($record as $key => $value) {
$record[$key] = $this->normalizeValue($value);
$result[$key] = $this->normalizeValue($value);
}
return $record;
return $result;
}
/**
* @param mixed $value
* @return mixed
* @param mixed $value
* @return scalar|null
*/
protected function normalizeValue($value)
{
$normalized = $this->normalize($value);
if (is_array($normalized) || is_object($normalized)) {
if (is_array($normalized)) {
return $this->toJson($normalized, true);
}

View File

@ -19,11 +19,15 @@ use Monolog\Logger;
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
* @author Christophe Coevoet <stof@notk.org>
* @author Kirill chEbba Chebunin <iam@chebba.org>
*
* @phpstan-import-type Level from \Monolog\Logger
*/
class WildfireFormatter extends NormalizerFormatter
{
/**
* Translates Monolog log levels to Wildfire levels.
*
* @var array<Level, string>
*/
private $logLevels = [
Logger::DEBUG => 'LOG',
@ -37,7 +41,20 @@ class WildfireFormatter extends NormalizerFormatter
];
/**
* {@inheritdoc}
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
*/
public function __construct(?string $dateFormat = null)
{
parent::__construct($dateFormat);
// http headers do not like non-ISO-8559-1 characters
$this->removeJsonEncodeOption(JSON_UNESCAPED_UNICODE);
}
/**
* {@inheritDoc}
*
* @return string
*/
public function format(array $record): string
{
@ -52,6 +69,7 @@ class WildfireFormatter extends NormalizerFormatter
unset($record['extra']['line']);
}
/** @var mixed[] $record */
$record = $this->normalize($record);
$message = ['message' => $record['message']];
$handleError = false;
@ -96,7 +114,9 @@ class WildfireFormatter extends NormalizerFormatter
}
/**
* {@inheritdoc}
* {@inheritDoc}
*
* @phpstan-return never
*/
public function formatBatch(array $records)
{
@ -104,8 +124,9 @@ class WildfireFormatter extends NormalizerFormatter
}
/**
* {@inheritdoc}
* @suppress PhanTypeMismatchReturn
* {@inheritDoc}
*
* @return null|scalar|array<array|scalar|null>|object
*/
protected function normalize($data, int $depth = 0)
{

View File

@ -13,20 +13,31 @@ namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Psr\Log\LogLevel;
/**
* Base Handler class providing basic level/bubble support
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
abstract class AbstractHandler extends Handler implements ResettableInterface
{
/**
* @var int
* @phpstan-var Level
*/
protected $level = Logger::DEBUG;
/** @var bool */
protected $bubble = true;
/**
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function __construct($level = Logger::DEBUG, bool $bubble = true)
{
@ -35,7 +46,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@ -45,7 +56,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
/**
* Sets minimum logging level at which this handler will be triggered.
*
* @param int|string $level Level or level name
* @param Level|LevelName|LogLevel::* $level Level or level name
* @return self
*/
public function setLevel($level): self
@ -59,6 +70,8 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
* Gets minimum logging level at which this handler will be triggered.
*
* @return int
*
* @phpstan-return Level
*/
public function getLevel(): int
{
@ -90,6 +103,9 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
return $this->bubble;
}
/**
* {@inheritDoc}
*/
public function reset()
{
}

View File

@ -18,6 +18,11 @@ namespace Monolog\Handler;
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type LevelName from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-type FormattedRecord array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[], formatted: mixed}
*/
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
@ -25,7 +30,7 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
use FormattableHandlerTrait;
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
@ -34,6 +39,7 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
}
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@ -46,9 +52,14 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
/**
* Writes the record down to the log of the implementing handler
*
* @phpstan-param FormattedRecord $record
*/
abstract protected function write(array $record): void;
/**
* @return void
*/
public function reset()
{
parent::reset();

View File

@ -17,13 +17,18 @@ use Monolog\Formatter\LineFormatter;
/**
* Common syslog functionality
*
* @phpstan-import-type Level from \Monolog\Logger
*/
abstract class AbstractSyslogHandler extends AbstractProcessingHandler
{
/** @var int */
protected $facility;
/**
* Translates Monolog log levels to syslog log priorities.
* @var array
* @phpstan-var array<Level, int>
*/
protected $logLevels = [
Logger::DEBUG => LOG_DEBUG,
@ -38,6 +43,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
/**
* List of valid log facility names.
* @var array<string, int>
*/
protected $facilities = [
'auth' => LOG_AUTH,
@ -55,8 +61,6 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
/**
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true)
{
@ -93,7 +97,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function getDefaultFormatter(): FormatterInterface
{

View File

@ -18,6 +18,9 @@ use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Channel\AMQPChannel;
use AMQPExchange;
/**
* @phpstan-import-type Record from \Monolog\Logger
*/
class AmqpHandler extends AbstractProcessingHandler
{
/**
@ -33,8 +36,6 @@ class AmqpHandler extends AbstractProcessingHandler
/**
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use
* @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($exchange, ?string $exchangeName = null, $level = Logger::DEBUG, bool $bubble = true)
{
@ -93,6 +94,7 @@ class AmqpHandler extends AbstractProcessingHandler
continue;
}
/** @var Record $record */
$record = $this->processRecord($record);
$data = $this->getFormatter()->format($record);
@ -108,6 +110,8 @@ class AmqpHandler extends AbstractProcessingHandler
/**
* Gets the routing key for the AMQP exchange
*
* @phpstan-param Record $record
*/
protected function getRoutingKey(array $record): string
{

View File

@ -11,20 +11,35 @@
namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Utils;
use function count;
use function headers_list;
use function stripos;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Handler sending logs to browser's javascript console with no browser extension required
*
* @author Olivier Poitrey <rs@dailymotion.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class BrowserConsoleHandler extends AbstractProcessingHandler
{
/** @var bool */
protected static $initialized = false;
/** @var FormattedRecord[] */
protected static $records = [];
protected const FORMAT_HTML = 'html';
protected const FORMAT_JS = 'js';
protected const FORMAT_UNKNOWN = 'unknown';
/**
* {@inheritDoc}
*
@ -61,14 +76,14 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
public static function send(): void
{
$format = static::getResponseFormat();
if ($format === 'unknown') {
if ($format === self::FORMAT_UNKNOWN) {
return;
}
if (count(static::$records)) {
if ($format === 'html') {
if ($format === self::FORMAT_HTML) {
static::writeOutput('<script>' . static::generateScript() . '</script>');
} elseif ($format === 'js') {
} elseif ($format === self::FORMAT_JS) {
static::writeOutput(static::generateScript());
}
static::resetStatic();
@ -121,25 +136,37 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
* If Content-Type is anything else -> unknown
*
* @return string One of 'js', 'html' or 'unknown'
* @phpstan-return self::FORMAT_*
*/
protected static function getResponseFormat(): string
{
// Check content type
foreach (headers_list() as $header) {
if (stripos($header, 'content-type:') === 0) {
// This handler only works with HTML and javascript outputs
// text/javascript is obsolete in favour of application/javascript, but still used
if (stripos($header, 'application/javascript') !== false || stripos($header, 'text/javascript') !== false) {
return 'js';
}
if (stripos($header, 'text/html') === false) {
return 'unknown';
}
break;
return static::getResponseFormatFromContentType($header);
}
}
return 'html';
return self::FORMAT_HTML;
}
/**
* @return string One of 'js', 'html' or 'unknown'
* @phpstan-return self::FORMAT_*
*/
protected static function getResponseFormatFromContentType(string $contentType): string
{
// This handler only works with HTML and javascript outputs
// text/javascript is obsolete in favour of application/javascript, but still used
if (stripos($contentType, 'application/javascript') !== false || stripos($contentType, 'text/javascript') !== false) {
return self::FORMAT_JS;
}
if (stripos($contentType, 'text/html') !== false) {
return self::FORMAT_HTML;
}
return self::FORMAT_UNKNOWN;
}
private static function generateScript(): string
@ -165,6 +192,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
}
/**
* @return string[]
*/
private static function handleStyles(string $formatted): array
{
$args = [];
@ -190,7 +220,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'];
static $labels = [];
return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) {
$style = preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) {
if (trim($m[1]) === 'autolabel') {
// Format the string as a label with consistent auto assigned background color
if (!isset($labels[$string])) {
@ -203,8 +233,19 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return $m[1];
}, $style);
if (null === $style) {
$pcreErrorCode = preg_last_error();
throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
}
return $style;
}
/**
* @param mixed[] $dict
* @return mixed[]
*/
private static function dump(string $title, array $dict): array
{
$script = [];
@ -229,13 +270,22 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return '"' . addcslashes($arg, "\"\n\\") . '"';
}
/**
* @param mixed $args
*/
private static function call(...$args): string
{
$method = array_shift($args);
if (!is_string($method)) {
throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true));
}
return static::call_array($method, $args);
}
/**
* @param mixed[] $args
*/
private static function call_array(string $method, array $args): string
{
return 'c.' . $method . '(' . implode(', ', $args) . ');';

View File

@ -22,23 +22,29 @@ use Monolog\Formatter\FormatterInterface;
* sending one per log message.
*
* @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
use ProcessableHandlerTrait;
/** @var HandlerInterface */
protected $handler;
/** @var int */
protected $bufferSize = 0;
/** @var int */
protected $bufferLimit;
/** @var bool */
protected $flushOnOverflow;
/** @var Record[] */
protected $buffer = [];
/** @var bool */
protected $initialized = false;
/**
* @param HandlerInterface $handler Handler.
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
*/
public function __construct(HandlerInterface $handler, int $bufferLimit = 0, $level = Logger::DEBUG, bool $bubble = true, bool $flushOnOverflow = false)
@ -50,7 +56,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
@ -74,6 +80,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
}
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@ -101,7 +108,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function close(): void
{
@ -133,20 +140,28 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
$this->handler->setFormatter($formatter);
if ($this->handler instanceof FormattableHandlerInterface) {
$this->handler->setFormatter($formatter);
return $this;
return $this;
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.');
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getFormatter(): FormatterInterface
{
return $this->handler->getFormatter();
if ($this->handler instanceof FormattableHandlerInterface) {
return $this->handler->getFormatter();
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.');
}
}

View File

@ -22,6 +22,8 @@ use Monolog\Utils;
* This also works out of the box with Firefox 43+
*
* @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class ChromePHPHandler extends AbstractProcessingHandler
{
@ -42,6 +44,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
*/
protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
/** @var bool */
protected static $initialized = false;
/**
@ -53,18 +56,16 @@ class ChromePHPHandler extends AbstractProcessingHandler
*/
protected static $overflowed = false;
/** @var mixed[] */
protected static $json = [
'version' => self::VERSION,
'columns' => ['label', 'log', 'backtrace', 'type'],
'rows' => [],
];
/** @var bool */
protected static $sendHeaders = true;
/**
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($level = Logger::DEBUG, bool $bubble = true)
{
parent::__construct($level, $bubble);
@ -74,7 +75,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -88,7 +89,9 @@ class ChromePHPHandler extends AbstractProcessingHandler
if ($record['level'] < $this->level) {
continue;
}
$messages[] = $this->processRecord($record);
/** @var Record $message */
$message = $this->processRecord($record);
$messages[] = $message;
}
if (!empty($messages)) {
@ -145,7 +148,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? '';
}
$json = Utils::jsonEncode(self::$json, null, true);
$json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true);
$data = base64_encode(utf8_encode($json));
if (strlen($data) > 3 * 1024) {
self::$overflowed = true;

View File

@ -22,8 +22,12 @@ use Monolog\Logger;
*/
class CouchDBHandler extends AbstractProcessingHandler
{
/** @var mixed[] */
private $options;
/**
* @param mixed[] $options
*/
public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{
$this->options = array_merge([

View File

@ -22,11 +22,17 @@ use Monolog\Utils;
*/
class CubeHandler extends AbstractProcessingHandler
{
private $udpConnection;
private $httpConnection;
/** @var resource|\Socket|null */
private $udpConnection = null;
/** @var resource|\CurlHandle|null */
private $httpConnection = null;
/** @var string */
private $scheme;
/** @var string */
private $host;
/** @var int */
private $port;
/** @var string[] */
private $acceptedSchemes = ['http', 'udp'];
/**
@ -40,7 +46,7 @@ class CubeHandler extends AbstractProcessingHandler
{
$urlInfo = parse_url($url);
if (!isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) {
if ($urlInfo === false || !isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) {
throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
}
@ -53,7 +59,7 @@ class CubeHandler extends AbstractProcessingHandler
$this->scheme = $urlInfo['scheme'];
$this->host = $urlInfo['host'];
$this->port = $urlInfo['port'];
$this->port = (int) $urlInfo['port'];
parent::__construct($level, $bubble);
}
@ -70,11 +76,12 @@ class CubeHandler extends AbstractProcessingHandler
throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler');
}
$this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
if (!$this->udpConnection) {
$udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
if (false === $udpConnection) {
throw new \LogicException('Unable to create a socket');
}
$this->udpConnection = $udpConnection;
if (!socket_connect($this->udpConnection, $this->host, $this->port)) {
throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port);
}
@ -92,18 +99,18 @@ class CubeHandler extends AbstractProcessingHandler
throw new MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler');
}
$this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
if (!$this->httpConnection) {
$httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
if (false === $httpConnection) {
throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port);
}
$this->httpConnection = $httpConnection;
curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true);
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function write(array $record): void
{
@ -144,6 +151,10 @@ class CubeHandler extends AbstractProcessingHandler
$this->connectHttp();
}
if (null === $this->httpConnection) {
throw new \LogicException('No connection could be established');
}
curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']');
curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',

View File

@ -11,6 +11,8 @@
namespace Monolog\Handler\Curl;
use CurlHandle;
/**
* This class is marked as internal and it is not under the BC promise of the package.
*
@ -18,6 +20,7 @@ namespace Monolog\Handler\Curl;
*/
final class Util
{
/** @var array<int> */
private static $retriableErrorCodes = [
CURLE_COULDNT_RESOLVE_HOST,
CURLE_COULDNT_CONNECT,
@ -31,10 +34,10 @@ final class Util
/**
* Executes a CURL request with optional retries and exception on failure
*
* @param resource $ch curl handler
* @param int $retries
* @param bool $closeAfterDone
* @return bool|string @see curl_exec
* @param resource|CurlHandle $ch curl handler
* @param int $retries
* @param bool $closeAfterDone
* @return bool|string @see curl_exec
*/
public static function execute($ch, int $retries = 5, bool $closeAfterDone = true)
{

View File

@ -12,6 +12,7 @@
namespace Monolog\Handler;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Simple handler wrapper that deduplicates log records across multiple requests
@ -32,6 +33,10 @@ use Monolog\Logger;
* same way.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/
class DeduplicationHandler extends BufferHandler
{
@ -41,7 +46,7 @@ class DeduplicationHandler extends BufferHandler
protected $deduplicationStore;
/**
* @var int
* @var Level
*/
protected $deduplicationLevel;
@ -61,6 +66,8 @@ class DeduplicationHandler extends BufferHandler
* @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
* @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*
* @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel
*/
public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true)
{
@ -100,6 +107,9 @@ class DeduplicationHandler extends BufferHandler
}
}
/**
* @phpstan-param Record $record
*/
private function isDuplicate(array $record): bool
{
if (!file_exists($this->deduplicationStore)) {
@ -166,6 +176,9 @@ class DeduplicationHandler extends BufferHandler
$this->gc = false;
}
/**
* @phpstan-param Record $record
*/
private function appendRecord(array $record): void
{
file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND);

View File

@ -23,6 +23,7 @@ use Doctrine\CouchDB\CouchDBClient;
*/
class DoctrineCouchDBHandler extends AbstractProcessingHandler
{
/** @var CouchDBClient */
private $client;
public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true)

View File

@ -48,11 +48,9 @@ class DynamoDbHandler extends AbstractProcessingHandler
*/
protected $marshaler;
/**
* @param int|string $level
*/
public function __construct(DynamoDbClient $client, string $table, $level = Logger::DEBUG, bool $bubble = true)
{
/** @phpstan-ignore-next-line */
if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) {
$this->version = 3;
$this->marshaler = new Marshaler;
@ -67,7 +65,7 @@ class DynamoDbHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function write(array $record): void
{
@ -75,6 +73,7 @@ class DynamoDbHandler extends AbstractProcessingHandler
if ($this->version === 3) {
$formatted = $this->marshaler->marshalItem($filtered);
} else {
/** @phpstan-ignore-next-line */
$formatted = $this->client->formatAttributes($filtered);
}
@ -84,6 +83,10 @@ class DynamoDbHandler extends AbstractProcessingHandler
]);
}
/**
* @param mixed[] $record
* @return mixed[]
*/
protected function filterEmptyFields(array $record): array
{
return array_filter($record, function ($value) {
@ -92,7 +95,7 @@ class DynamoDbHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function getDefaultFormatter(): FormatterInterface
{

View File

@ -11,6 +11,7 @@
namespace Monolog\Handler;
use Elastica\Document;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticaFormatter;
use Monolog\Logger;
@ -25,7 +26,7 @@ use Elastica\Exception\ExceptionInterface;
* $client = new \Elastica\Client();
* $options = array(
* 'index' => 'elastic_index_name',
* 'type' => 'elastic_doc_type',
* 'type' => 'elastic_doc_type', Types have been removed in Elastica 7
* );
* $handler = new ElasticaHandler($client, $options);
* $log = new Logger('application');
@ -41,15 +42,13 @@ class ElasticaHandler extends AbstractProcessingHandler
protected $client;
/**
* @var array Handler config options
* @var mixed[] Handler config options
*/
protected $options = [];
/**
* @param Client $client Elastica Client object
* @param array $options Handler configuration
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param Client $client Elastica Client object
* @param mixed[] $options Handler configuration
*/
public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{
@ -74,7 +73,7 @@ class ElasticaHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
@ -85,6 +84,9 @@ class ElasticaHandler extends AbstractProcessingHandler
throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter');
}
/**
* @return mixed[]
*/
public function getOptions(): array
{
return $this->options;
@ -99,7 +101,7 @@ class ElasticaHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -109,6 +111,9 @@ class ElasticaHandler extends AbstractProcessingHandler
/**
* Use Elasticsearch bulk API to send list of documents
*
* @param Document[] $documents
*
* @throws \RuntimeException
*/
protected function bulkSend(array $documents): void

View File

@ -49,15 +49,13 @@ class ElasticsearchHandler extends AbstractProcessingHandler
protected $client;
/**
* @var array Handler config options
* @var mixed[] Handler config options
*/
protected $options = [];
/**
* @param Client $client Elasticsearch Client object
* @param array $options Handler configuration
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param Client $client Elasticsearch Client object
* @param mixed[] $options Handler configuration
*/
public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{
@ -82,7 +80,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
@ -96,7 +94,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/**
* Getter options
*
* @return array
* @return mixed[]
*/
public function getOptions(): array
{
@ -112,7 +110,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -123,7 +121,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/**
* Use Elasticsearch bulk API to send list of documents
*
* @param array $records
* @param array[] $records Records + _index/_type keys
* @throws \RuntimeException
*/
protected function bulkSend(array $records): void
@ -162,7 +160,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
*
* Only the first error is converted into an exception.
*
* @param array $responses returned by $this->client->bulk()
* @param mixed[] $responses returned by $this->client->bulk()
*/
protected function createExceptionFromResponses(array $responses): ElasticsearchRuntimeException
{
@ -178,7 +176,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/**
* Creates elasticsearch exception from error array
*
* @param array $error
* @param mixed[] $error
*/
protected function createExceptionFromError(array $error): ElasticsearchRuntimeException
{

View File

@ -14,6 +14,7 @@ namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Logger;
use Monolog\Utils;
/**
* Stores to PHP error_log() handler.
@ -25,14 +26,14 @@ class ErrorLogHandler extends AbstractProcessingHandler
public const OPERATING_SYSTEM = 0;
public const SAPI = 4;
/** @var int */
protected $messageType;
/** @var bool */
protected $expandNewlines;
/**
* @param int $messageType Says where the error should go.
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
* @param int $messageType Says where the error should go.
* @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
*/
public function __construct(int $messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, bool $bubble = true, bool $expandNewlines = false)
{
@ -49,7 +50,7 @@ class ErrorLogHandler extends AbstractProcessingHandler
}
/**
* @return array With all available types
* @return int[] With all available types
*/
public static function getAvailableTypes(): array
{
@ -68,7 +69,7 @@ class ErrorLogHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function write(array $record): void
{
@ -79,6 +80,10 @@ class ErrorLogHandler extends AbstractProcessingHandler
}
$lines = preg_split('{[\r\n]+}', (string) $record['formatted']);
if ($lines === false) {
$pcreErrorCode = preg_last_error();
throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode));
}
foreach ($lines as $line) {
error_log($line, $this->messageType);
}

View File

@ -13,14 +13,24 @@ namespace Monolog\Handler;
use Throwable;
/**
* Forwards records to at most one handler
*
* If a handler fails, the exception is suppressed and the record is forwarded to the next handler.
*
* As soon as one handler handles a record successfully, the handling stops there.
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class FallbackGroupHandler extends GroupHandler
{
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
foreach ($this->handlers as $handler) {
@ -36,7 +46,7 @@ class FallbackGroupHandler extends GroupHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -45,6 +55,7 @@ class FallbackGroupHandler extends GroupHandler
foreach ($records as $record) {
$processed[] = $this->processRecord($record);
}
/** @var Record[] $records */
$records = $processed;
}

View File

@ -14,6 +14,7 @@ namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;
/**
* Simple handler wrapper that filters records based on a list of levels
@ -22,6 +23,10 @@ use Monolog\Formatter\FormatterInterface;
*
* @author Hennadiy Verkh
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{
@ -30,7 +35,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/**
* Handler or factory callable($record, $this)
*
* @var callable|\Monolog\Handler\HandlerInterface
* @var callable|HandlerInterface
* @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
*/
protected $handler;
@ -38,6 +44,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
* Minimum level for logs that are passed to handler
*
* @var int[]
* @phpstan-var array<Level, int>
*/
protected $acceptedLevels;
@ -49,12 +56,15 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
protected $bubble;
/**
* @psalm-param HandlerInterface|callable(?array, HandlerInterface): HandlerInterface $handler
* @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
*
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler).
* @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
* @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*
* @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
* @phpstan-param Level|LevelName|LogLevel::* $maxLevel
*/
public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true)
{
@ -67,6 +77,9 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
}
/**
* @phpstan-return array<int, Level>
*/
public function getAcceptedLevels(): array
{
return array_flip($this->acceptedLevels);
@ -75,6 +88,9 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/**
* @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
* @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array
*
* @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
* @phpstan-param Level|LevelName|LogLevel::* $maxLevel
*/
public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self
{
@ -93,7 +109,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@ -101,7 +117,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
@ -110,6 +126,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@ -119,7 +136,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -141,6 +158,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @return HandlerInterface
*
* @phpstan-param Record $record
*/
public function getHandler(array $record = null)
{
@ -155,25 +174,39 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
$this->getHandler()->setFormatter($formatter);
$handler = $this->getHandler();
if ($handler instanceof FormattableHandlerInterface) {
$handler->setFormatter($formatter);
return $this;
return $this;
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getFormatter(): FormatterInterface
{
return $this->getHandler()->getFormatter();
$handler = $this->getHandler();
if ($handler instanceof FormattableHandlerInterface) {
return $handler->getFormatter();
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
}
public function reset()
{
$this->resetProcessors();
if ($this->getHandler() instanceof ResettableInterface) {
$this->getHandler()->reset();
}
}
}

View File

@ -15,11 +15,15 @@ namespace Monolog\Handler\FingersCrossed;
* Interface for activation strategies for the FingersCrossedHandler.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
interface ActivationStrategyInterface
{
/**
* Returns whether the given record activates the handler.
*
* @phpstan-param Record $record
*/
public function isHandlerActivated(array $record): bool;
}

View File

@ -12,6 +12,7 @@
namespace Monolog\Handler\FingersCrossed;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Channel and Error level based monolog activation strategy. Allows to trigger activation
@ -32,22 +33,29 @@ use Monolog\Logger;
* </code>
*
* @author Mike Meessen <netmikey@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class ChannelLevelActivationStrategy implements ActivationStrategyInterface
{
/**
* @var int
* @var Level
*/
private $defaultActionLevel;
/**
* @var array
* @var array<string, Level>
*/
private $channelToActionLevel;
/**
* @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any
* @param array $channelToActionLevel An array that maps channel names to action levels.
* @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any
* @param array<string, int> $channelToActionLevel An array that maps channel names to action levels.
*
* @phpstan-param array<string, Level> $channelToActionLevel
* @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel
*/
public function __construct($defaultActionLevel, array $channelToActionLevel = [])
{
@ -55,6 +63,9 @@ class ChannelLevelActivationStrategy implements ActivationStrategyInterface
$this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel);
}
/**
* @phpstan-param Record $record
*/
public function isHandlerActivated(array $record): bool
{
if (isset($this->channelToActionLevel[$record['channel']])) {

View File

@ -12,21 +12,27 @@
namespace Monolog\Handler\FingersCrossed;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Error level based activation strategy.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class ErrorLevelActivationStrategy implements ActivationStrategyInterface
{
/**
* @var int
* @var Level
*/
private $actionLevel;
/**
* @param int|string $actionLevel Level or name or value
*
* @phpstan-param Level|LevelName|LogLevel::* $actionLevel
*/
public function __construct($actionLevel)
{

View File

@ -16,6 +16,7 @@ use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;
/**
* Buffers all records until a certain level is reached
@ -32,22 +33,40 @@ use Monolog\Formatter\FormatterInterface;
* Monolog\Handler\FingersCrossed\ namespace.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{
use ProcessableHandlerTrait;
/**
* @var callable|HandlerInterface
* @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
*/
protected $handler;
/** @var ActivationStrategyInterface */
protected $activationStrategy;
/** @var bool */
protected $buffering = true;
/** @var int */
protected $bufferSize;
/** @var Record[] */
protected $buffer = [];
/** @var bool */
protected $stopBuffering;
/**
* @var ?int
* @phpstan-var ?Level
*/
protected $passthruLevel;
/** @var bool */
protected $bubble;
/**
* @psalm-param HandlerInterface|callable(?array, FingersCrossedHandler): HandlerInterface $handler
* @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
*
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler).
* @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
@ -55,6 +74,9 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true)
* @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered
*
* @phpstan-param Level|LevelName|LogLevel::* $passthruLevel
* @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy
*/
public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null)
{
@ -83,7 +105,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@ -104,11 +126,12 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@ -128,13 +151,13 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function close(): void
{
$this->flushBuffer();
$this->handler->close();
$this->getHandler()->close();
}
public function reset()
@ -170,7 +193,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
return $record['level'] >= $level;
});
if (count($this->buffer) > 0) {
$this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
$this->getHandler(end($this->buffer))->handleBatch($this->buffer);
}
}
@ -184,6 +207,8 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @return HandlerInterface
*
* @phpstan-param Record $record
*/
public function getHandler(array $record = null)
{
@ -198,20 +223,30 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
$this->getHandler()->setFormatter($formatter);
$handler = $this->getHandler();
if ($handler instanceof FormattableHandlerInterface) {
$handler->setFormatter($formatter);
return $this;
return $this;
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getFormatter(): FormatterInterface
{
return $this->getHandler()->getFormatter();
$handler = $this->getHandler();
if ($handler instanceof FormattableHandlerInterface) {
return $handler->getFormatter();
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
}
}

View File

@ -18,6 +18,8 @@ use Monolog\Formatter\FormatterInterface;
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
*
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class FirePHPHandler extends AbstractProcessingHandler
{
@ -45,6 +47,7 @@ class FirePHPHandler extends AbstractProcessingHandler
/**
* Whether or not Wildfire vendor-specific headers have been generated & sent yet
* @var bool
*/
protected static $initialized = false;
@ -54,14 +57,18 @@ class FirePHPHandler extends AbstractProcessingHandler
*/
protected static $messageIndex = 1;
/** @var bool */
protected static $sendHeaders = true;
/**
* Base header creation function used by init headers & record headers
*
* @param array $meta Wildfire Plugin, Protocol & Structure Indexes
* @param string $message Log message
* @return array Complete header string ready for the client as key and message as value
* @param array<int|string> $meta Wildfire Plugin, Protocol & Structure Indexes
* @param string $message Log message
*
* @return array<string, string> Complete header string ready for the client as key and message as value
*
* @phpstan-return non-empty-array<string, string>
*/
protected function createHeader(array $meta, string $message): array
{
@ -73,7 +80,13 @@ class FirePHPHandler extends AbstractProcessingHandler
/**
* Creates message header from record
*
* @return array<string, string>
*
* @phpstan-return non-empty-array<string, string>
*
* @see createHeader()
*
* @phpstan-param FormattedRecord $record
*/
protected function createRecordHeader(array $record): array
{
@ -98,6 +111,8 @@ class FirePHPHandler extends AbstractProcessingHandler
*
* @see createHeader()
* @see sendHeader()
*
* @return array<string, string>
*/
protected function getInitHeaders(): array
{
@ -124,7 +139,6 @@ class FirePHPHandler extends AbstractProcessingHandler
*
* @see sendHeader()
* @see sendInitHeaders()
* @param array $record
*/
protected function write(array $record): void
{

View File

@ -22,6 +22,8 @@ use Monolog\Logger;
*
* @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
* @author Ando Roots <ando@sqroot.eu>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class FleepHookHandler extends SocketHandler
{
@ -41,12 +43,18 @@ class FleepHookHandler extends SocketHandler
* see https://fleep.io/integrations/webhooks/
*
* @param string $token Webhook token
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @throws MissingExtensionException
*/
public function __construct(string $token, $level = Logger::DEBUG, bool $bubble = true)
{
public function __construct(
string $token,
$level = Logger::DEBUG,
bool $bubble = true,
bool $persistent = false,
float $timeout = 0.0,
float $writingTimeout = 10.0,
?float $connectionTimeout = null,
?int $chunkSize = null
) {
if (!extension_loaded('openssl')) {
throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler');
}
@ -54,7 +62,16 @@ class FleepHookHandler extends SocketHandler
$this->token = $token;
$connectionString = 'ssl://' . static::FLEEP_HOST . ':443';
parent::__construct($connectionString, $level, $bubble);
parent::__construct(
$connectionString,
$level,
$bubble,
$persistent,
$timeout,
$writingTimeout,
$connectionTimeout,
$chunkSize
);
}
/**
@ -79,7 +96,7 @@ class FleepHookHandler extends SocketHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function generateDataStream(array $record): string
{
@ -104,6 +121,8 @@ class FleepHookHandler extends SocketHandler
/**
* Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/
private function buildContent(array $record): string
{

View File

@ -26,6 +26,8 @@ use Monolog\Formatter\FormatterInterface;
*
* @author Dominik Liebler <liebler.dominik@gmail.com>
* @see https://www.flowdock.com/api/push
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class FlowdockHandler extends SocketHandler
{
@ -35,23 +37,37 @@ class FlowdockHandler extends SocketHandler
protected $apiToken;
/**
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*
* @throws MissingExtensionException if OpenSSL is missing
*/
public function __construct(string $apiToken, $level = Logger::DEBUG, bool $bubble = true)
{
public function __construct(
string $apiToken,
$level = Logger::DEBUG,
bool $bubble = true,
bool $persistent = false,
float $timeout = 0.0,
float $writingTimeout = 10.0,
?float $connectionTimeout = null,
?int $chunkSize = null
) {
if (!extension_loaded('openssl')) {
throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler');
}
parent::__construct('ssl://api.flowdock.com:443', $level, $bubble);
parent::__construct(
'ssl://api.flowdock.com:443',
$level,
$bubble,
$persistent,
$timeout,
$writingTimeout,
$connectionTimeout,
$chunkSize
);
$this->apiToken = $apiToken;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
@ -71,9 +87,7 @@ class FlowdockHandler extends SocketHandler
}
/**
* {@inheritdoc}
*
* @param array $record
* {@inheritDoc}
*/
protected function write(array $record): void
{
@ -83,7 +97,7 @@ class FlowdockHandler extends SocketHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function generateDataStream(array $record): string
{
@ -94,6 +108,8 @@ class FlowdockHandler extends SocketHandler
/**
* Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/
private function buildContent(array $record): string
{

View File

@ -22,13 +22,12 @@ use Monolog\Formatter\LineFormatter;
trait FormattableHandlerTrait
{
/**
* @var FormatterInterface
* @var ?FormatterInterface
*/
protected $formatter;
/**
* {@inheritdoc}
* @suppress PhanTypeMismatchReturn
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
@ -38,7 +37,7 @@ trait FormattableHandlerTrait
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getFormatter(): FormatterInterface
{

View File

@ -25,14 +25,12 @@ use Monolog\Formatter\FormatterInterface;
class GelfHandler extends AbstractProcessingHandler
{
/**
* @var PublisherInterface|null the publisher object that sends the message to the server
* @var PublisherInterface the publisher object that sends the message to the server
*/
protected $publisher;
/**
* @param PublisherInterface $publisher a publisher object
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param PublisherInterface $publisher a gelf publisher object
*/
public function __construct(PublisherInterface $publisher, $level = Logger::DEBUG, bool $bubble = true)
{
@ -42,7 +40,7 @@ class GelfHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function write(array $record): void
{

View File

@ -18,12 +18,16 @@ use Monolog\ResettableInterface;
* Forwards records to multiple handlers
*
* @author Lenar Lõhmus <lenar@city.ee>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface
{
use ProcessableHandlerTrait;
/** @var HandlerInterface[] */
protected $handlers;
/** @var bool */
protected $bubble;
/**
@ -43,7 +47,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@ -57,11 +61,12 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@ -73,7 +78,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -82,6 +87,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
foreach ($records as $record) {
$processed[] = $this->processRecord($record);
}
/** @var Record[] $records */
$records = $processed;
}
@ -111,12 +117,14 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
foreach ($this->handlers as $handler) {
$handler->setFormatter($formatter);
if ($handler instanceof FormattableHandlerInterface) {
$handler->setFormatter($formatter);
}
}
return $this;

View File

@ -19,7 +19,7 @@ namespace Monolog\Handler;
abstract class Handler implements HandlerInterface
{
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -29,7 +29,7 @@ abstract class Handler implements HandlerInterface
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function close(): void
{

View File

@ -15,6 +15,9 @@ namespace Monolog\Handler;
* Interface that all Monolog Handlers must implement
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/
interface HandlerInterface
{
@ -30,6 +33,8 @@ interface HandlerInterface
* @param array $record Partial log record containing only a level key
*
* @return bool
*
* @phpstan-param array{level: Level} $record
*/
public function isHandling(array $record): bool;
@ -46,6 +51,8 @@ interface HandlerInterface
* @param array $record The record to handle
* @return bool true means that this handler handled the record, and that bubbling is not permitted.
* false means the record was either not processed or that this handler allows bubbling.
*
* @phpstan-param Record $record
*/
public function handle(array $record): bool;
@ -53,6 +60,8 @@ interface HandlerInterface
* Handles a set of records at once.
*
* @param array $records The records to handle (an array of record arrays)
*
* @phpstan-param Record[] $records
*/
public function handleBatch(array $records): void;

View File

@ -44,7 +44,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@ -52,7 +52,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
@ -60,7 +60,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -68,7 +68,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function close(): void
{
@ -76,7 +76,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function pushProcessor(callable $callback): HandlerInterface
{
@ -90,7 +90,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function popProcessor(): callable
{
@ -102,19 +102,21 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
if ($this->handler instanceof FormattableHandlerInterface) {
$this->handler->setFormatter($formatter);
return $this;
}
throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class);
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getFormatter(): FormatterInterface
{
@ -128,7 +130,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
public function reset()
{
if ($this->handler instanceof ResettableInterface) {
return $this->handler->reset();
$this->handler->reset();
}
}
}

View File

@ -27,17 +27,21 @@ use Monolog\Utils;
*/
class IFTTTHandler extends AbstractProcessingHandler
{
/** @var string */
private $eventName;
/** @var string */
private $secretKey;
/**
* @param string $eventName The name of the IFTTT Maker event that should be triggered
* @param string $secretKey A valid IFTTT secret key
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $eventName The name of the IFTTT Maker event that should be triggered
* @param string $secretKey A valid IFTTT secret key
*/
public function __construct(string $eventName, string $secretKey, $level = Logger::ERROR, bool $bubble = true)
{
if (!extension_loaded('curl')) {
throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler');
}
$this->eventName = $eventName;
$this->secretKey = $secretKey;
@ -45,7 +49,7 @@ class IFTTTHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function write(array $record): void
{

View File

@ -30,13 +30,21 @@ class InsightOpsHandler extends SocketHandler
* @param string $token Log token supplied by InsightOps
* @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'.
* @param bool $useSSL Whether or not SSL encryption should be used
* @param string|int $level The minimum logging level to trigger this handler
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
*
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
*/
public function __construct(string $token, string $region = 'us', bool $useSSL = true, $level = Logger::DEBUG, bool $bubble = true)
{
public function __construct(
string $token,
string $region = 'us',
bool $useSSL = true,
$level = Logger::DEBUG,
bool $bubble = true,
bool $persistent = false,
float $timeout = 0.0,
float $writingTimeout = 10.0,
?float $connectionTimeout = null,
?int $chunkSize = null
) {
if ($useSSL && !extension_loaded('openssl')) {
throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler');
}
@ -45,12 +53,21 @@ class InsightOpsHandler extends SocketHandler
? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443'
: $region . '.data.logs.insight.rapid7.com:80';
parent::__construct($endpoint, $level, $bubble);
parent::__construct(
$endpoint,
$level,
$bubble,
$persistent,
$timeout,
$writingTimeout,
$connectionTimeout,
$chunkSize
);
$this->logToken = $token;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function generateDataStream(array $record): string
{

View File

@ -26,25 +26,42 @@ class LogEntriesHandler extends SocketHandler
/**
* @param string $token Log token supplied by LogEntries
* @param bool $useSSL Whether or not SSL encryption should be used.
* @param string|int $level The minimum logging level to trigger this handler
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
* @param string $host Custom hostname to send the data to if needed
*
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
*/
public function __construct(string $token, bool $useSSL = true, $level = Logger::DEBUG, bool $bubble = true, string $host = 'data.logentries.com')
{
public function __construct(
string $token,
bool $useSSL = true,
$level = Logger::DEBUG,
bool $bubble = true,
string $host = 'data.logentries.com',
bool $persistent = false,
float $timeout = 0.0,
float $writingTimeout = 10.0,
?float $connectionTimeout = null,
?int $chunkSize = null
) {
if ($useSSL && !extension_loaded('openssl')) {
throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler');
}
$endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80';
parent::__construct($endpoint, $level, $bubble);
parent::__construct(
$endpoint,
$level,
$bubble,
$persistent,
$timeout,
$writingTimeout,
$connectionTimeout,
$chunkSize
);
$this->logToken = $token;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function generateDataStream(array $record): string
{

View File

@ -15,6 +15,7 @@ use Monolog\Logger;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LogglyFormatter;
use function array_key_exists;
use CurlHandle;
/**
* Sends errors to Loggly.
@ -32,18 +33,18 @@ class LogglyHandler extends AbstractProcessingHandler
/**
* Caches the curl handlers for every given endpoint.
*
* @var array
* @var resource[]|CurlHandle[]
*/
protected $curlHandlers = [];
/** @var string */
protected $token;
/** @var string[] */
protected $tag = [];
/**
* @param string $token API token supplied by Loggly
* @param string|int $level The minimum logging level to trigger this handler
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
* @param string $token API token supplied by Loggly
*
* @throws MissingExtensionException If the curl extension is missing
*/
@ -63,12 +64,12 @@ class LogglyHandler extends AbstractProcessingHandler
*
* @param string $endpoint
*
* @return resource
* @return resource|CurlHandle
*/
protected function getCurlHandler(string $endpoint)
{
if (!array_key_exists($endpoint, $this->curlHandlers)) {
$this->curlHandlers[$endpoint] = $this->loadCurlHandler($endpoint);
$this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint);
}
return $this->curlHandlers[$endpoint];
@ -79,9 +80,9 @@ class LogglyHandler extends AbstractProcessingHandler
*
* @param string $endpoint
*
* @return resource
* @return resource|CurlHandle
*/
private function loadCurlHandler(string $endpoint)
private function loadCurlHandle(string $endpoint)
{
$url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token);

View File

@ -40,13 +40,22 @@ class LogmaticHandler extends SocketHandler
* @param string $hostname Host name supplied by Logmatic.
* @param string $appname Application name supplied by Logmatic.
* @param bool $useSSL Whether or not SSL encryption should be used.
* @param int|string $level The minimum logging level to trigger this handler.
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
*
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
*/
public function __construct(string $token, string $hostname = '', string $appname = '', bool $useSSL = true, $level = Logger::DEBUG, bool $bubble = true)
{
public function __construct(
string $token,
string $hostname = '',
string $appname = '',
bool $useSSL = true,
$level = Logger::DEBUG,
bool $bubble = true,
bool $persistent = false,
float $timeout = 0.0,
float $writingTimeout = 10.0,
?float $connectionTimeout = null,
?int $chunkSize = null
) {
if ($useSSL && !extension_loaded('openssl')) {
throw new MissingExtensionException('The OpenSSL PHP extension is required to use SSL encrypted connection for LogmaticHandler');
}
@ -54,7 +63,16 @@ class LogmaticHandler extends SocketHandler
$endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514';
$endpoint .= '/v1/';
parent::__construct($endpoint, $level, $bubble);
parent::__construct(
$endpoint,
$level,
$bubble,
$persistent,
$timeout,
$writingTimeout,
$connectionTimeout,
$chunkSize
);
$this->logToken = $token;
$this->hostname = $hostname;
@ -62,7 +80,7 @@ class LogmaticHandler extends SocketHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function generateDataStream(array $record): string
{
@ -70,7 +88,7 @@ class LogmaticHandler extends SocketHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function getDefaultFormatter(): FormatterInterface
{

View File

@ -18,11 +18,13 @@ use Monolog\Formatter\HtmlFormatter;
* Base class for all mail handlers
*
* @author Gyula Sallai
*
* @phpstan-import-type Record from \Monolog\Logger
*/
abstract class MailHandler extends AbstractProcessingHandler
{
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@ -32,7 +34,9 @@ abstract class MailHandler extends AbstractProcessingHandler
if ($record['level'] < $this->level) {
continue;
}
$messages[] = $this->processRecord($record);
/** @var Record $message */
$message = $this->processRecord($record);
$messages[] = $message;
}
if (!empty($messages)) {
@ -45,17 +49,23 @@ abstract class MailHandler extends AbstractProcessingHandler
*
* @param string $content formatted email body to be sent
* @param array $records the array of log records that formed this content
*
* @phpstan-param Record[] $records
*/
abstract protected function send(string $content, array $records): void;
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function write(array $record): void
{
$this->send((string) $record['formatted'], [$record]);
}
/**
* @phpstan-param non-empty-array<Record> $records
* @phpstan-return Record
*/
protected function getHighestRecord(array $records): array
{
$highestRecord = null;
@ -70,7 +80,7 @@ abstract class MailHandler extends AbstractProcessingHandler
protected function isHtmlBody(string $body): bool
{
return substr($body, 0, 1) === '<';
return ($body[0] ?? null) === '<';
}
/**

View File

@ -13,6 +13,7 @@ namespace Monolog\Handler;
use Monolog\Logger;
use Swift;
use Swift_Message;
/**
* MandrillHandler uses cURL to send the emails to the Mandrill API
@ -21,25 +22,25 @@ use Swift;
*/
class MandrillHandler extends MailHandler
{
/** @var Swift_Message */
protected $message;
/** @var string */
protected $apiKey;
/**
* @psalm-param Swift_Message|callable(string, array): Swift_Message $message
* @psalm-param Swift_Message|callable(): Swift_Message $message
*
* @param string $apiKey A valid Mandrill API key
* @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $apiKey A valid Mandrill API key
* @param callable|Swift_Message $message An example message for real messages, only the body will be replaced
*/
public function __construct(string $apiKey, $message, $level = Logger::ERROR, bool $bubble = true)
{
parent::__construct($level, $bubble);
if (!$message instanceof \Swift_Message && is_callable($message)) {
if (!$message instanceof Swift_Message && is_callable($message)) {
$message = $message();
}
if (!$message instanceof \Swift_Message) {
if (!$message instanceof Swift_Message) {
throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it');
}
$this->message = $message;
@ -47,7 +48,7 @@ class MandrillHandler extends MailHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function send(string $content, array $records): void
{
@ -58,9 +59,11 @@ class MandrillHandler extends MailHandler
$message = clone $this->message;
$message->setBody($content, $mime);
/** @phpstan-ignore-next-line */
if (version_compare(Swift::VERSION, '6.0.0', '>=')) {
$message->setDate(new \DateTimeImmutable());
} else {
/** @phpstan-ignore-next-line */
$message->setDate(time());
}

View File

@ -33,8 +33,11 @@ use Monolog\Formatter\MongoDBFormatter;
*/
class MongoDBHandler extends AbstractProcessingHandler
{
/** @var \MongoDB\Collection */
private $collection;
/** @var Client|Manager */
private $manager;
/** @var string */
private $namespace;
/**
@ -43,8 +46,6 @@ class MongoDBHandler extends AbstractProcessingHandler
* @param Client|Manager $mongodb MongoDB library or driver client
* @param string $database Database name
* @param string $collection Collection name
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($mongodb, string $database, string $collection, $level = Logger::DEBUG, bool $bubble = true)
{

View File

@ -24,7 +24,7 @@ class NativeMailerHandler extends MailHandler
{
/**
* The email addresses to which the message will be sent
* @var array
* @var string[]
*/
protected $to;
@ -36,13 +36,13 @@ class NativeMailerHandler extends MailHandler
/**
* Optional headers for the message
* @var array
* @var string[]
*/
protected $headers = [];
/**
* Optional parameters for the message
* @var array
* @var string[]
*/
protected $parameters = [];
@ -65,12 +65,10 @@ class NativeMailerHandler extends MailHandler
protected $encoding = 'utf-8';
/**
* @param string|array $to The receiver of the mail
* @param string $subject The subject of the mail
* @param string $from The sender of the mail
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param int $maxColumnWidth The maximum column width that the message lines will have
* @param string|string[] $to The receiver of the mail
* @param string $subject The subject of the mail
* @param string $from The sender of the mail
* @param int $maxColumnWidth The maximum column width that the message lines will have
*/
public function __construct($to, string $subject, string $from, $level = Logger::ERROR, bool $bubble = true, int $maxColumnWidth = 70)
{
@ -84,7 +82,7 @@ class NativeMailerHandler extends MailHandler
/**
* Add headers to the message
*
* @param string|array $headers Custom added headers
* @param string|string[] $headers Custom added headers
*/
public function addHeader($headers): self
{
@ -101,7 +99,7 @@ class NativeMailerHandler extends MailHandler
/**
* Add parameters to the message
*
* @param string|array $parameters Custom added parameters
* @param string|string[] $parameters Custom added parameters
*/
public function addParameter($parameters): self
{
@ -111,7 +109,7 @@ class NativeMailerHandler extends MailHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function send(string $content, array $records): void
{

View File

@ -30,14 +30,14 @@ class NewRelicHandler extends AbstractProcessingHandler
/**
* Name of the New Relic application that will receive logs from this handler.
*
* @var string|null
* @var ?string
*/
protected $appName;
/**
* Name of the current transaction
*
* @var string|null
* @var ?string
*/
protected $transactionName;
@ -52,8 +52,6 @@ class NewRelicHandler extends AbstractProcessingHandler
/**
* {@inheritDoc}
*
* @param string|int $level The minimum logging level at which this handler will be triggered.
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not.
* @param string|null $appName
* @param bool $explodeArrays
* @param string|null $transactionName
@ -135,6 +133,8 @@ class NewRelicHandler extends AbstractProcessingHandler
/**
* Returns the appname where this log should be sent. Each log can override the default appname, set in this
* handler's constructor, by providing the appname in it's context.
*
* @param mixed[] $context
*/
protected function getAppName(array $context): ?string
{
@ -148,6 +148,8 @@ class NewRelicHandler extends AbstractProcessingHandler
/**
* Returns the name of the current transaction. Each log can override the default transaction name, set in this
* handler's constructor, by providing the transaction_name in it's context
*
* @param mixed[] $context
*/
protected function getTransactionName(array $context): ?string
{

View File

@ -23,7 +23,7 @@ namespace Monolog\Handler;
class NoopHandler extends Handler
{
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@ -31,7 +31,7 @@ class NoopHandler extends Handler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{

View File

@ -12,6 +12,7 @@
namespace Monolog\Handler;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Blackhole
@ -20,6 +21,9 @@ use Monolog\Logger;
* to put on top of an existing stack to override it temporarily.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class NullHandler extends Handler
{
@ -30,6 +34,8 @@ class NullHandler extends Handler
/**
* @param string|int $level The minimum logging level at which this handler will be triggered
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function __construct($level = Logger::DEBUG)
{
@ -37,7 +43,7 @@ class NullHandler extends Handler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@ -45,7 +51,7 @@ class NullHandler extends Handler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{

View File

@ -61,8 +61,6 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
/**
* @param HandlerInterface $handler
* @param int[] $thresholdMap Dictionary of logger level => threshold
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble
*/
public function __construct(
HandlerInterface $handler,
@ -87,10 +85,7 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on
* calling further handlers in the stack with a given log record.
*
* @param array $record The record to handle
*
* @return Boolean true means that this handler handled the record, and that bubbling is not permitted.
* false means the record was either not processed or that this handler allows bubbling.
* {@inheritDoc}
*/
public function handle(array $record): bool
{
@ -127,20 +122,28 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
$this->handler->setFormatter($formatter);
if ($this->handler instanceof FormattableHandlerInterface) {
$this->handler->setFormatter($formatter);
return $this;
return $this;
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.');
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getFormatter(): FormatterInterface
{
return $this->handler->getFormatter();
if ($this->handler instanceof FormattableHandlerInterface) {
return $this->handler->getFormatter();
}
throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.');
}
}

View File

@ -37,9 +37,12 @@ use PhpConsole\Helper;
* PC::debug($_SERVER); // PHP Console debugger for any type of vars
*
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class PHPConsoleHandler extends AbstractProcessingHandler
{
/** @var array<string, mixed> */
private $options = [
'enabled' => true, // bool Is PHP Console server enabled
'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
@ -67,10 +70,8 @@ class PHPConsoleHandler extends AbstractProcessingHandler
private $connector;
/**
* @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional)
* @param string|int $level The minimum logging level at which this handler will be triggered.
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not.
* @param array<string, mixed> $options See \Monolog\Handler\PHPConsoleHandler::$options for more details
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional)
* @throws \RuntimeException
*/
public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true)
@ -83,6 +84,11 @@ class PHPConsoleHandler extends AbstractProcessingHandler
$this->connector = $this->initConnector($connector);
}
/**
* @param array<string, mixed> $options
*
* @return array<string, mixed>
*/
private function initOptions(array $options): array
{
$wrongOptions = array_diff(array_keys($options), array_keys($this->options));
@ -93,9 +99,6 @@ class PHPConsoleHandler extends AbstractProcessingHandler
return array_replace($this->options, $options);
}
/**
* @suppress PhanTypeMismatchArgument
*/
private function initConnector(?Connector $connector = null): Connector
{
if (!$connector) {
@ -156,6 +159,9 @@ class PHPConsoleHandler extends AbstractProcessingHandler
return $this->connector;
}
/**
* @return array<string, mixed>
*/
public function getOptions(): array
{
return $this->options;
@ -184,6 +190,9 @@ class PHPConsoleHandler extends AbstractProcessingHandler
}
}
/**
* @phpstan-param Record $record
*/
private function handleDebugRecord(array $record): void
{
$tags = $this->getRecordTags($record);
@ -194,11 +203,17 @@ class PHPConsoleHandler extends AbstractProcessingHandler
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
}
/**
* @phpstan-param Record $record
*/
private function handleExceptionRecord(array $record): void
{
$this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']);
}
/**
* @phpstan-param Record $record
*/
private function handleErrorRecord(array $record): void
{
$context = $record['context'];
@ -212,6 +227,10 @@ class PHPConsoleHandler extends AbstractProcessingHandler
);
}
/**
* @phpstan-param Record $record
* @return string
*/
private function getRecordTags(array &$record)
{
$tags = null;

View File

@ -44,12 +44,12 @@ class ProcessHandler extends AbstractProcessingHandler
private $cwd;
/**
* @var array
* @var resource[]
*/
private $pipes = [];
/**
* @var array
* @var array<int, string[]>
*/
protected const DESCRIPTOR_SPEC = [
0 => ['pipe', 'r'], // STDIN is a pipe that the child will read from
@ -60,8 +60,6 @@ class ProcessHandler extends AbstractProcessingHandler
/**
* @param string $command Command for the process to start. Absolute paths are recommended,
* especially if you do not use the $cwd parameter.
* @param string|int $level The minimum logging level at which this handler will be triggered.
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not.
* @param string|null $cwd "Current working directory" (CWD) for the process to be executed in.
* @throws \InvalidArgumentException
*/
@ -164,7 +162,7 @@ class ProcessHandler extends AbstractProcessingHandler
*/
protected function readProcessErrors(): string
{
return stream_get_contents($this->pipes[2]);
return (string) stream_get_contents($this->pipes[2]);
}
/**
@ -178,7 +176,7 @@ class ProcessHandler extends AbstractProcessingHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function close(): void
{

Some files were not shown because too many files have changed in this diff Show More