Current File : /var/www/prestashop/modules/ps_mbo/vendor/sentry/sentry/src/Serializer/AbstractSerializer.php |
<?php
declare(strict_types=1);
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Sentry\Serializer;
use Sentry\Exception\InvalidArgumentException;
use Sentry\Options;
/**
* This helper is based on code from Facebook's Phabricator project.
*
* https://github.com/facebook/phabricator
*
* Specifically, it is an adaptation of the PhutilReadableSerializer class.
*/
abstract class AbstractSerializer
{
/**
* The default mb detect order.
*
* @see http://php.net/manual/en/function.mb-detect-encoding.php
*/
public const DEFAULT_MB_DETECT_ORDER = 'auto';
/**
* Suggested detect order for western countries.
*/
public const WESTERN_MB_DETECT_ORDER = 'UTF-8, ASCII, ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6, ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-10, ISO-8859-13, ISO-8859-14, ISO-8859-15, ISO-8859-16, Windows-1251, Windows-1252, Windows-1254';
/**
* The maximum depth to reach when serializing recursively.
*
* @var int
*/
private $maxDepth;
/**
* This is the default mb detect order for the detection of encoding.
*
* @var string
*/
protected $mbDetectOrder = self::DEFAULT_MB_DETECT_ORDER;
/**
* @var bool Flag to enable serialization of objects internal properties
*/
protected $serializeAllObjects = false;
/**
* @var Options The Sentry options
*/
protected $options;
/**
* AbstractSerializer constructor.
*
* @param Options $options The SDK configuration options
*/
public function __construct(Options $options, int $maxDepth = 3, ?string $mbDetectOrder = null)
{
$this->maxDepth = $maxDepth;
if (null != $mbDetectOrder) {
$this->mbDetectOrder = $mbDetectOrder;
}
$this->options = $options;
}
/**
* Serialize an object (recursively) into something safe for data
* sanitization and encoding.
*
* @param mixed $value
*
* @return string|bool|float|int|mixed[]|null
*/
protected function serializeRecursively($value, int $_depth = 0)
{
try {
if ($_depth >= $this->maxDepth) {
return $this->serializeValue($value);
}
try {
if (@\is_callable($value)) {
return $this->serializeCallable($value);
}
} catch (\Throwable $exception) {
// Do nothing on purpose
}
if (\is_array($value)) {
$serializedArray = [];
foreach ($value as $k => $v) {
$serializedArray[$k] = $this->serializeRecursively($v, $_depth + 1);
}
return $serializedArray;
}
if (\is_object($value)) {
$classSerializers = $this->resolveClassSerializers($value);
// Try each serializer until there is none left or the serializer returned data
foreach ($classSerializers as $classSerializer) {
try {
$serializedObjectData = $classSerializer($value);
if (\is_array($serializedObjectData)) {
return [
'class' => \get_class($value),
'data' => $this->serializeRecursively($serializedObjectData, $_depth + 1),
];
}
} catch (\Throwable $e) {
// Ignore any exceptions generated by a class serializer
}
}
if ($this->serializeAllObjects || ($value instanceof \stdClass)) {
return $this->serializeObject($value, $_depth);
}
}
return $this->serializeValue($value);
} catch (\Throwable $error) {
if (\is_string($value)) {
return $value . ' {serialization error}';
}
return '{serialization error}';
}
}
/**
* Find class serializers for a object.
*
* Registered serializers with the `class_serializers` option take precedence over
* objects implementing the `SerializableInterface`.
*
* @param object $object
*
* @return array<int, callable>
*/
protected function resolveClassSerializers($object): array
{
$serializers = [];
foreach ($this->options->getClassSerializers() as $type => $serializer) {
if ($object instanceof $type) {
$serializers[] = $serializer;
}
}
if ($object instanceof SerializableInterface) {
$serializers[] = static function (SerializableInterface $object): ?array {
return $object->toSentry();
};
}
return $serializers;
}
/**
* @param object $object
* @param string[] $hashes
*
* @return mixed[]|string|bool|float|int|null
*/
protected function serializeObject($object, int $_depth = 0, array $hashes = [])
{
if ($_depth >= $this->maxDepth || \in_array(spl_object_hash($object), $hashes, true)) {
return $this->serializeValue($object);
}
$hashes[] = spl_object_hash($object);
$serializedObject = [];
foreach ($object as $key => &$value) {
if (\is_object($value)) {
$serializedObject[$key] = $this->serializeObject($value, $_depth + 1, $hashes);
} else {
$serializedObject[$key] = $this->serializeRecursively($value, $_depth + 1);
}
}
return $serializedObject;
}
/**
* Serializes the given value to a string.
*
* @param mixed $value The value to serialize
*/
protected function serializeString($value): string
{
$value = (string) $value;
// we always guarantee this is coerced, even if we can't detect encoding
if ($currentEncoding = mb_detect_encoding($value, $this->mbDetectOrder)) {
$value = mb_convert_encoding($value, 'UTF-8', $currentEncoding);
} else {
$value = mb_convert_encoding($value, 'UTF-8');
}
if (mb_strlen($value) > $this->options->getMaxValueLength()) {
$value = mb_substr($value, 0, $this->options->getMaxValueLength() - 10, 'UTF-8') . ' {clipped}';
}
return $value;
}
/**
* @param mixed $value
*
* @return string|bool|float|int|null
*/
protected function serializeValue($value)
{
if ((null === $value) || \is_bool($value) || is_numeric($value)) {
return $value;
}
if (\is_object($value)) {
return 'Object ' . \get_class($value);
}
if (\is_resource($value)) {
return 'Resource ' . get_resource_type($value);
}
try {
if (\is_callable($value)) {
return $this->serializeCallable($value);
}
} catch (\Throwable $exception) {
// Do nothing on purpose
}
if (\is_array($value)) {
return 'Array of length ' . \count($value);
}
return $this->serializeString($value);
}
/**
* @param callable|mixed $callable
*/
protected function serializeCallable($callable): string
{
if (\is_string($callable) && !\function_exists($callable)) {
return $callable;
}
if (!\is_callable($callable)) {
throw new InvalidArgumentException(sprintf('Expecting callable, got %s', \is_object($callable) ? \get_class($callable) : \gettype($callable)));
}
try {
if (\is_array($callable)) {
$reflection = new \ReflectionMethod($callable[0], $callable[1]);
$class = $reflection->getDeclaringClass();
} elseif ($callable instanceof \Closure || (\is_string($callable) && \function_exists($callable))) {
$reflection = new \ReflectionFunction($callable);
$class = null;
} elseif (\is_object($callable) && method_exists($callable, '__invoke')) {
$reflection = new \ReflectionMethod($callable, '__invoke');
$class = $reflection->getDeclaringClass();
} else {
throw new \InvalidArgumentException('Unrecognized type of callable');
}
} catch (\ReflectionException $exception) {
return '{unserializable callable, reflection error}';
}
$callableType = $reflection->isClosure() ? 'Lambda ' : 'Callable ';
$callableReturnType = $reflection->getReturnType();
if ($callableReturnType instanceof \ReflectionNamedType) {
$callableType .= $callableReturnType->getName() . ' ';
}
if ($class) {
$callableType .= $class->getName() . '::';
}
return $callableType . $reflection->getName() . ' ' . $this->serializeCallableParameters($reflection);
}
private function serializeCallableParameters(\ReflectionFunctionAbstract $reflection): string
{
$params = [];
foreach ($reflection->getParameters() as &$param) {
$reflectionType = $param->getType();
if ($reflectionType instanceof \ReflectionNamedType) {
$paramType = $reflectionType->getName();
} else {
$paramType = 'mixed';
}
if ($param->allowsNull()) {
$paramType .= '|null';
}
$paramName = ($param->isPassedByReference() ? '&' : '') . $param->getName();
if ($param->isOptional()) {
$paramName = '[' . $paramName . ']';
}
$params[] = $paramType . ' ' . $paramName;
}
return '[' . implode('; ', $params) . ']';
}
public function getMbDetectOrder(): string
{
return $this->mbDetectOrder;
}
/**
* @return $this
*/
public function setMbDetectOrder(string $mbDetectOrder): self
{
$this->mbDetectOrder = $mbDetectOrder;
return $this;
}
public function setSerializeAllObjects(bool $value): void
{
$this->serializeAllObjects = $value;
}
public function getSerializeAllObjects(): bool
{
return $this->serializeAllObjects;
}
}