This commit is contained in:
root
2025-11-13 19:52:28 +03:00
parent 8aeeb05b7d
commit 807dec3b6c
4646 changed files with 163445 additions and 626017 deletions

View File

@@ -0,0 +1,109 @@
<?php
/**
* Bitrix Framework
* @package bitrix
* @subpackage main
* @copyright 2001-2025 Bitrix
*/
namespace Bitrix\Main\Web\Http;
use Bitrix\Main\ArgumentException;
/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Range
*/
class Range
{
protected int $start;
protected int $end;
public function __construct(?int $start, ?int $end, int $size)
{
if ($start === null && $end !== null)
{
// Returning requested suffix of the file
$this->start = $size - $end;
$this->end = $size - 1;
}
elseif ($start !== null && $end === null)
{
// Returning all bytes with offset
$this->start = $start;
$this->end = $size - 1;
}
else
{
$this->start = (int)$start;
$this->end = (int)$end;
}
if ($this->start > $this->end || $this->start >= $size)
{
throw new ArgumentException('Requested Range Not Satisfiable');
}
if ($this->end >= $size)
{
$this->end = $size - 1;
}
}
public function getStart(): int
{
return $this->start;
}
public function getEnd(): int
{
return $this->end;
}
/**
* @param string $header
* @param int $size
* @return Range[] | null
*/
public static function createFromString(string $header, int $size): ?array
{
if (str_contains($header, '='))
{
[$unit, $value] = explode("=", $header);
if (strtolower(trim($unit)) === 'bytes')
{
$ranges = [];
foreach (explode(',', $value) as $range)
{
if (str_contains($range, '-'))
{
[$start, $end] = explode('-', $range);
if (!is_numeric($start))
{
$start = null;
}
if (!is_numeric($end))
{
$end = null;
}
try
{
$ranges[] = new Range($start, $end, $size);
}
catch (ArgumentException)
{
// all ranges must be correct
return null;
}
}
}
return $ranges ?: null;
}
}
return null;
}
}

View File

@@ -61,6 +61,7 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
protected $useCurl = false;
protected $curlLogFile = null;
protected $shouldFetchBody = null;
protected $sendEvents = true;
protected Http\ResponseBuilderInterface $responseBuilder;
protected HttpHeaders $headers;
@@ -93,6 +94,7 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
* "headers" array of headers for HTTP request.
* "useCurl" bool Enable CURL (default false).
* "curlLogFile" string Full path to CURL log file.
* "sendEvents" bool Send events (default true).
* "responseBuilder" Http\ResponseBuilderInterface Response builder.
* Almost all options can be set separately with setters.
*/
@@ -177,6 +179,10 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
{
$this->curlLogFile = $options['curlLogFile'];
}
if (isset($options['sendEvents']))
{
$this->sendEvents = (bool)$options['sendEvents'];
}
if (isset($options['responseBuilder']))
{
$this->setResponseBuilder($options['responseBuilder']);
@@ -880,13 +886,16 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
}
}
// Here's the chance to tune up the client and to rebuild the request.
$event = new Http\RequestEvent($this, $request, 'OnHttpClientBuildRequest');
$event->send();
foreach ($event->getResults() as $eventResult)
if ($this->sendEvents)
{
$request = $eventResult->getRequest();
// Here's the chance to tune up the client and to rebuild the request.
$event = new Http\RequestEvent($this, $request, 'OnHttpClientBuildRequest');
$event->send();
foreach ($event->getResults() as $eventResult)
{
$request = $eventResult->getRequest();
}
}
return new Http\Request(

View File

@@ -63,13 +63,16 @@ class Json
public static function validate(string $data): bool
{
if (function_exists('json_validate'))
{
return json_validate($data);
}
// On PHP 8.3 replace to
// return json_validate($data);
try
{
if ($data === 'null') // consistency with json_validate
{
return true;
}
return json_decode(json: $data, associative: true, flags: JSON_THROW_ON_ERROR) !== null;
}
catch (JsonException)

View File

@@ -73,12 +73,27 @@ class Uri implements \JsonSerializable, UriInterface
}
$authority = $this->getAuthority();
$path = $this->getPath();
if ($authority != '')
{
$uri .= '//' . $authority;
if (!str_starts_with($path, '/'))
{
$uri .= '/';
}
$uri .= $path;
}
else
{
$uri .= $path;
}
$uri .= $this->getPathQuery();
$query = $this->getQuery();
if ($query != '')
{
$uri .= '?' . $query;
}
return $uri;
}
@@ -168,7 +183,7 @@ class Uri implements \JsonSerializable, UriInterface
}
/**
* Returns the path with the query.
* Returns the path with the query. Empty path is treated as a root (/)
* @return string
*/
public function getPathQuery()

View File

@@ -0,0 +1,123 @@
<?php
declare(strict_types = 1);
namespace Bitrix\Main\Web\UserAgent;
enum Platform: string
{
case Android = 'Android';
case Ios = 'iOS';
case Windows = 'Windows';
case Macos = 'macOS';
case LinuxRpm = 'Linux RPM';
case LinuxDeb = 'Linux DEB';
case Unknown = 'Unknown';
public function isMobile(): bool
{
return in_array($this, [self::Android, self::Ios], true);
}
public function isDesktop(): bool
{
return in_array($this, [self::Windows, self::Macos, self::LinuxRpm, self::LinuxDeb], true);
}
public function isLinux(): bool
{
return in_array($this, [self::LinuxRpm, self::LinuxDeb], true);
}
public static function fromUserAgent(string $userAgent): self
{
$userAgent = strtolower($userAgent);
if (str_contains($userAgent, 'bitrixmobile') || str_contains($userAgent, 'bitrix24/'))
{
if (
str_contains($userAgent, 'iphone')
|| str_contains($userAgent, 'ipad')
|| str_contains($userAgent, 'darwin')
)
{
return self::Ios;
}
return self::Android;
}
if (self::isWindowsByUserAgent($userAgent))
{
return self::Windows;
}
if (self::isMacosByUserAgent($userAgent))
{
return self::Macos;
}
if (self::isLinuxByUserAgent($userAgent))
{
return self::getLinuxPlatform($userAgent);
}
if (self::isIosByUserAgent($userAgent))
{
return self::Ios;
}
if (self::isAndroidByUserAgent($userAgent) || str_contains($userAgent, 'carddavbitrix24'))
{
return self::Android;
}
return self::Unknown;
}
private static function isWindowsByUserAgent(string $userAgent): bool
{
return str_contains($userAgent, 'windows')
|| str_contains($userAgent, 'win32')
|| str_contains($userAgent, 'win64');
}
private static function isLinuxByUserAgent(string $userAgent): bool
{
return str_contains($userAgent, 'linux') && !self::isAndroidByUserAgent($userAgent);
}
private static function isIosByUserAgent(string $userAgent): bool
{
return str_contains($userAgent, 'iphone')
|| str_contains($userAgent, 'ipad')
|| str_contains($userAgent, 'ipod')
|| (str_contains($userAgent, 'ios') && !str_contains($userAgent, 'windows'));
}
private static function isMacosByUserAgent(string $userAgent): bool
{
return (str_contains($userAgent, 'mac os') && !str_contains($userAgent, 'like mac os x'))
|| str_contains($userAgent, 'macintosh');
}
private static function isAndroidByUserAgent(string $userAgent): bool
{
return str_contains($userAgent, 'android');
}
private static function getLinuxPlatform(string $userAgent): self
{
if (
str_contains($userAgent, 'rpm')
|| str_contains($userAgent, 'fedora')
|| str_contains($userAgent, 'centos')
|| str_contains($userAgent, 'rhel')
)
{
return self::LinuxRpm;
}
return self::LinuxDeb;
}
}