fixing more psalm complaints

This commit is contained in:
Brett McBride 2025-07-16 17:02:47 +10:00
parent 7c9f012de4
commit 26b1143b4d
30 changed files with 119 additions and 54 deletions

View File

@ -1,4 +1,4 @@
FROM composer:2 AS composer
FROM composer:2.8 AS composer
FROM debian:bullseye
WORKDIR /usr/src/myapp
@ -38,7 +38,7 @@ RUN apt-get install -y \
COPY --from=composer /usr/bin/composer /usr/local/bin/composer
RUN echo ";grpc.enable_fork_support = 1" > $(php-config --ini-dir)/40-otel-dev.ini \
RUN echo "grpc.enable_fork_support = 1" > $(php-config --ini-dir)/40-otel-dev.ini \
&& echo "grpc.poll_strategy = epoll1" >> $(php-config --ini-dir)/40-otel-dev.ini \
&& echo "zend.assertions = 1" >> $(php-config --ini-dir)/40-otel-dev.ini

View File

@ -60,7 +60,7 @@ $otelHandler = new class(LogLevel::INFO) extends AbstractProcessingHandler {
{
return (new LogRecord($record['message']))
->setSeverityText($record->level->toPsrLogLevel())
->setTimestamp((int) (microtime(true) * LogRecord::NANOS_PER_SECOND))
->setTimestamp((int) (microtime(true) * (float) LogRecord::NANOS_PER_SECOND))
->setObservedTimestamp((int) $record->datetime->format('U') * LogRecord::NANOS_PER_SECOND)
->setSeverityNumber(Severity::fromPsr3($record->level->toPsrLogLevel()))
->setAttributes($record->context + $record->extra);

View File

@ -15,6 +15,9 @@ use OpenTelemetry\SDK\Trace\TracerProvider;
$filename = sys_get_temp_dir() . '/traces.jsonl';
$file = fopen($filename, 'a');
if ($file === false) {
throw new \RuntimeException('Failed to open file for writing: ' . $filename);
}
$transport = (new StreamTransportFactory())->create($file, ContentTypes::NDJSON);
$exporter = new SpanExporter($transport);

View File

@ -15,6 +15,7 @@
<directory name="./examples/traces/demo/"/>
<directory name="tests/Unit/Config/SDK/Configuration/ExampleSdk"/>
<directory name="tests/TraceContext/W3CTestService"/>
<directory name="vendor"/>
</ignoreFiles>
</projectFiles>
<plugins>
@ -27,11 +28,6 @@
<referencedClass name="GMP"/>
</errorLevel>
</UndefinedClass>
<UndefinedFunction>
<errorLevel type="suppress">
<referencedFunction name="OpenTelemetry\Instrumentation\hook"/>
</errorLevel>
</UndefinedFunction>
<UndefinedMethod>
<errorLevel type="suppress">
<referencedMethod name="Google\Protobuf\Internal\RepeatedField::offsetGet"/>
@ -43,11 +39,6 @@
<directory name="./examples"/>
</errorLevel>
</ArgumentTypeCoercion>
<InvalidArgument>
<errorLevel type="suppress">
<directory name="src/Config/SDK/Configuration/Internal"/>
</errorLevel>
</InvalidArgument>
<UndefinedInterfaceMethod>
<errorLevel type="suppress">
<directory name="src/Config/SDK/ComponentProvider"/>
@ -57,34 +48,56 @@
<PossiblyInvalidArgument>
<errorLevel type="suppress">
<directory name="src/Config/SDK/Configuration"/>
<directory name="tests/Integration/Config"/>
</errorLevel>
</PossiblyInvalidArgument>
<PossiblyNullReference>
<errorLevel type="suppress">
<directory name="src/Config/SDK/ComponentProvider"/>
<directory name="tests/Integration/Config/ComponentProvider"/>
</errorLevel>
</PossiblyNullReference>
<MoreSpecificImplementedParamType>
<errorLevel type="suppress">
<directory name="src/Config/SDK/ComponentProvider"/>
<directory name="tests/Integration/Config/ComponentProvider"/>
<directory name="tests"/>
</errorLevel>
</MoreSpecificImplementedParamType>
<InvalidDocblock>
<UndefinedMagicMethod>
<errorLevel type="suppress">
<directory name="src/Config/SDK/ComponentProvider"/>
<referencedMethod name="Prophecy\Prophecy\ObjectProphecy::accepts"/>
<referencedMethod name="Prophecy\Prophecy\ObjectProphecy::fields"/>
</errorLevel>
</InvalidDocblock>
<NonInvariantDocblockPropertyType>
</UndefinedMagicMethod>
<PossiblyFalseArgument>
<errorLevel type="suppress">
<directory name="src/Config/SDK/Configuration/Internal"/>
<directory name="tests"/>
</errorLevel>
</NonInvariantDocblockPropertyType>
<NonInvariantPropertyType>
</PossiblyFalseArgument>
<PossiblyFalseOperand>
<errorLevel type="suppress">
<directory name="src/Config/SDK/Configuration/Internal"/>
<directory name="examples/baggage"/>
<file name="src/SDK/Metrics/MetricExporter/ConsoleMetricExporter.php"/>
</errorLevel>
</NonInvariantPropertyType>
</PossiblyFalseOperand>
<InvalidOperand>
<errorLevel type="suppress">
<directory name="tests"/>
</errorLevel>
</InvalidOperand>
<FalsableReturnStatement>
<errorLevel type="suppress">
<directory name="src/API/Trace"/>
</errorLevel>
</FalsableReturnStatement>
<PossiblyNullArgument>
<errorLevel type="suppress">
<file name="src/API/Instrumentation/CachedInstrumentation.php"/>
</errorLevel>
</PossiblyNullArgument>
<InvalidNullableReturnType>
<errorLevel type="suppress">
<file name="src/API/Instrumentation/CachedInstrumentation.php"/>
</errorLevel>
</InvalidNullableReturnType>
<NullableReturnStatement>
<errorLevel type="suppress">
<file name="src/API/Instrumentation/CachedInstrumentation.php"/>
</errorLevel>
</NullableReturnStatement>
</issueHandlers>
</psalm>

View File

@ -48,6 +48,6 @@ final class SystemClock implements ClockInterface
*/
private static function calculateReferenceTime(float $wallClockMicroTime, int $upTime): int
{
return ((int) ($wallClockMicroTime * ClockInterface::NANOS_PER_SECOND)) - $upTime;
return ((int) ($wallClockMicroTime * (float) ClockInterface::NANOS_PER_SECOND)) - $upTime;
}
}

View File

@ -62,6 +62,7 @@ final class CachedInstrumentation
return $this->meters[$meterProvider] ??= $meterProvider->getMeter($this->name, $this->version, $this->schemaUrl, $this->attributes);
}
public function logger(): LoggerInterface
{
$loggerProvider = Globals::loggerProvider();

View File

@ -94,6 +94,9 @@ class TraceState implements TraceStateInterface
foreach ([128, 0] as $threshold) {
// Then entries SHOULD be removed starting from the end of tracestate.
for ($value = end($traceState); $key = key($traceState);) {
if ($value === false) {
continue;
}
$entry = strlen($key) + 1 + strlen($value);
$value = prev($traceState);
if ($entry <= $threshold) {

View File

@ -27,6 +27,7 @@ use OpenTelemetry\Config\SDK\Configuration\Internal\ResourceCollection;
use OpenTelemetry\Config\SDK\Configuration\Internal\TrackingEnvReader;
use OpenTelemetry\Config\SDK\Configuration\Loader\YamlExtensionFileLoader;
use OpenTelemetry\Config\SDK\Configuration\Loader\YamlSymfonyFileLoader;
use RuntimeException;
use function serialize;
use function sprintf;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
@ -107,7 +108,11 @@ final class ConfigurationFactory
}
$loader = new ConfigurationLoader($resources);
$locator = new FileLocator(getcwd());
$cwd = getcwd();
if ($cwd === false) {
throw new RuntimeException('Current working directory could not be determined.');
}
$locator = new FileLocator($cwd);
$fileLoader = new DelegatingLoader(new LoaderResolver([
new YamlSymfonyFileLoader($loader, $locator),
new YamlExtensionFileLoader($loader, $locator),

View File

@ -32,6 +32,9 @@ final class FiberBoundContextStorage implements ContextStorageInterface, Context
return $this->heads[Fiber::getCurrent() ?? $this] ?? null;
}
/**
* @psalm-suppress PossiblyNullPropertyFetch
*/
#[\Override]
public function scope(): ?ContextStorageScopeInterface
{
@ -62,6 +65,9 @@ final class FiberBoundContextStorage implements ContextStorageInterface, Context
return $head->node->context ?? Context::getRoot();
}
/**
* @psalm-suppress PossiblyNullArgument,PossiblyNullPropertyFetch
*/
#[\Override]
public function attach(ContextInterface $context): ContextStorageScopeInterface
{

View File

@ -43,6 +43,9 @@ final class SanitizeCombinedHeadersPropagationGetter implements ExtendedPropagat
);
}
/**
* @psalm-suppress PossiblyNullArgument
*/
#[\Override]
public function getAll($carrier, string $key): array
{

View File

@ -33,6 +33,9 @@ final class ZendObserverFiber
: (bool) $enabled;
}
/**
* @psalm-suppress PossiblyNullReference,UndefinedMethod
*/
public static function init(): bool
{
static $fibers;

View File

@ -84,7 +84,7 @@ final class GrpcTransportFactory implements TransportFactoryInterface
$opts,
$method,
$headers,
(int) ($timeout * self::MILLIS_PER_SECOND),
(int) ($timeout * (float) self::MILLIS_PER_SECOND),
);
}

View File

@ -214,6 +214,9 @@ final class MetricConverter
return $pHistogramDataPoint;
}
/**
* @psalm-suppress PossiblyFalseArgument
*/
private function convertExemplar(SDK\Metrics\Data\Exemplar $exemplar): Exemplar
{
$pExemplar = new Exemplar();

View File

@ -19,6 +19,7 @@ use function json_encode;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;
use function lcfirst;
use OpenTelemetry\API\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\Common\Export\TransportInterface;
use function property_exists;
use function sprintf;
@ -30,6 +31,7 @@ use function ucwords;
*/
final class ProtobufSerializer
{
use LogsMessagesTrait;
private function __construct(private readonly string $contentType)
{
}
@ -121,7 +123,14 @@ final class ProtobufSerializer
unset($payload);
self::traverseDescriptor($data, $desc);
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$encoded = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if ($encoded === false) {
self::logWarning('Failed to encode protobuf to JSON: ' . json_last_error_msg());
return '';
}
return $encoded;
}
private static function traverseDescriptor(object $data, Descriptor $desc): void

View File

@ -101,7 +101,7 @@ final class PsrTransport implements TransportInterface
$delay = PsrUtils::retryDelay($retries, $this->retryDelay, $response);
$sec = (int) $delay;
$nsec = (int) (($delay - $sec) * 1e9);
$nsec = (int) (($delay - (float) $sec) * 1e9);
/** @psalm-suppress ArgumentTypeCoercion */
if (time_nanosleep($sec, $nsec) !== true) {

View File

@ -64,7 +64,7 @@ final class StreamTransport implements TransportInterface
}
if ($bytesWritten !== strlen($payload)) {
return new ErrorFuture(new RuntimeException(sprintf('Write failure, wrote %d of %d bytes', $bytesWritten, strlen($payload))));
return new ErrorFuture(new RuntimeException(sprintf('Write failure, wrote %d of %d bytes', $bytesWritten ?: 0, strlen($payload))));
}
return new CompletedFuture(null);

View File

@ -28,8 +28,8 @@ final class StreamTransportFactory implements TransportFactoryInterface
*
* @psalm-template CONTENT_TYPE of string
* @psalm-param CONTENT_TYPE $contentType
* @psalm-return TransportInterface<CONTENT_TYPE>
* @throws ErrorException
* @psalm-return TransportInterface<CONTENT_TYPE>
*/
#[\Override]
public function create(

View File

@ -51,6 +51,7 @@ final class Configurator
/**
* @return T
* @psalm-suppress PossiblyNullArgument
*/
public function resolve(InstrumentationScopeInterface $instrumentationScope): Config
{

View File

@ -46,6 +46,7 @@ final class ShutdownHandler
* @param callable $shutdownFunction function to register
*
* @see register_shutdown_function
* @psalm-suppress PossiblyNullArgument,PossiblyNullPropertyFetch
*/
public static function register(callable $shutdownFunction): void
{

View File

@ -47,7 +47,7 @@ function weaken(Closure $closure, ?object &$target = null): Closure
$ref = WeakReference::create($target);
/**
* @psalm-suppress all
* @psalm-suppress PossiblyNullReference,PossiblyNullFunctionCall
*/
return $scope && $target::class === $scope->name && !$scope->isInternal()
? static fn (...$args) => ($obj = $ref->get()) ? $closure->call($obj, ...$args) : null

View File

@ -4,9 +4,11 @@ declare(strict_types=1);
namespace OpenTelemetry\SDK\Logs\Exporter;
use OpenTelemetry\API\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\Common\Export\TransportInterface;
use OpenTelemetry\SDK\Common\Future\CancellationInterface;
use OpenTelemetry\SDK\Common\Future\CompletedFuture;
use OpenTelemetry\SDK\Common\Future\ErrorFuture;
use OpenTelemetry\SDK\Common\Future\FutureInterface;
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeInterface;
use OpenTelemetry\SDK\Logs\LogRecordExporterInterface;
@ -45,7 +47,13 @@ class ConsoleExporter implements LogRecordExporterInterface
'resource' => $resource,
'scopes' => array_values($scopes),
];
$this->transport->send(json_encode($output, JSON_PRETTY_PRINT));
$payload = json_encode($output, JSON_PRETTY_PRINT);
if ($payload === false) {
return new ErrorFuture(
new \RuntimeException('Failed to encode log records to JSON: ' . json_last_error_msg())
););
}
$this->transport->send($payload);
return new CompletedFuture(true);
}

View File

@ -31,7 +31,7 @@ class ReadableLogRecord extends LogRecord
parent::__construct($logRecord->body);
$this->timestamp = $logRecord->timestamp;
$this->observedTimestamp = $logRecord->observedTimestamp
?? (int) (microtime(true) * LogRecord::NANOS_PER_SECOND);
?? (int) (microtime(true) * (float) LogRecord::NANOS_PER_SECOND);
$this->context = $logRecord->context;
$context = $this->context ?? Context::getCurrent();
$this->spanContext = Span::fromContext($context)->getContext();

View File

@ -62,7 +62,9 @@ final class ExplicitBucketHistogramAggregation implements AggregationInterface
public function merge($left, $right): ExplicitBucketHistogramSummary
{
$count = $left->count + $right->count;
$sum = $left->sum + $right->sum;
$sum = (is_int($left->sum) && is_int($right->sum))
? ($left->sum + $right->sum)
: ((float) $left->sum + (float) $right->sum);
$min = self::min($left->min, $right->min);
$max = self::max($left->max, $right->max);
$buckets = $right->buckets;
@ -87,7 +89,9 @@ final class ExplicitBucketHistogramAggregation implements AggregationInterface
public function diff($left, $right): ExplicitBucketHistogramSummary
{
$count = -$left->count + $right->count;
$sum = -$left->sum + $right->sum;
$sum = (is_int($left->sum) && is_int($right->sum))
? (-$left->sum + $right->sum)
: (-(float) $left->sum + (float) $right->sum);
$min = $left->min > $right->min ? $right->min : NAN;
$max = $left->max < $right->max ? $right->max : NAN;
$buckets = $right->buckets;

View File

@ -40,11 +40,11 @@ final class SumAggregation implements AggregationInterface
#[\Override]
public function merge($left, $right): SumSummary
{
$sum = $left->value + $right->value;
$sum = (is_int($left->value) && is_int($right->value))
? ($left->value + $right->value)
: ((float) $left->value + (float) $right->value);
return new SumSummary(
$sum,
);
return new SumSummary($sum);
}
/**
@ -54,11 +54,11 @@ final class SumAggregation implements AggregationInterface
#[\Override]
public function diff($left, $right): SumSummary
{
$sum = -$left->value + $right->value;
$diff = (is_int($left->value) && is_int($right->value))
? (-$left->value + $right->value)
: (-(float) $left->value + (float) $right->value);
return new SumSummary(
$sum,
);
return new SumSummary($diff);
}
/**

View File

@ -42,9 +42,9 @@ interface AggregationInterface
/**
* @param array<AttributesInterface> $attributes
* @psalm-param array<T> $summaries
* @param array<list<Exemplar>> $exemplars
* @param string|Temporality $temporality
* @psalm-param array<T> $summaries
*/
public function toData(
array $attributes,

View File

@ -18,6 +18,7 @@ final class AsynchronousInstruments
/**
* @param ArrayAccess<object, ObservableCallbackDestructor> $destructors
* @param non-empty-list<Instrument> $instruments
* @psalm-suppress PossiblyNullArgument,PossiblyNullPropertyAssignment,PossiblyNullPropertyFetch
*/
public static function observe(
MetricWriterInterface $writer,

View File

@ -99,6 +99,7 @@ class TraceIdRatioBasedSampler implements SamplerInterface
assert($precision >= 1);
assert($wordSize >= 1 && ($wordSize & $wordSize - 1) === 0);
/** @psalm-suppress PossiblyInvalidArrayAccess */
$b = unpack('J', pack('E', $probability))[1];
$e = $b >> 52 & (1 << 11) - 1;
$f = $b & (1 << 52) - 1 | ($e ? 1 << 52 : 0);

View File

@ -53,8 +53,8 @@ final class Span extends API\Span implements ReadWriteSpanInterface
* End users should use a {@see API\TracerInterface} in order to create spans.
*
* @param non-empty-string $name
* @psalm-param API\SpanKind::KIND_* $kind
* @param list<LinkInterface> $links
* @psalm-param API\SpanKind::KIND_* $kind
*
* @internal
* @psalm-internal OpenTelemetry

View File

@ -831,8 +831,8 @@ class SpanTest extends MockeryTestCase
}
/**
* @psalm-param API\SpanKind::KIND_* $kind
* @param list<LinkInterface> $links
* @psalm-param API\SpanKind::KIND_* $kind
*/
private function createTestSpan(
int $kind = API\SpanKind::KIND_INTERNAL,

View File

@ -1,7 +1,7 @@
{
"require-dev": {
"vimeo/psalm": "^5.24",
"vimeo/psalm": "^6",
"psalm/plugin-mockery": "^1",
"psalm/plugin-phpunit": "^0.18.4"
"psalm/plugin-phpunit": "^0.19"
}
}