php-k8s works correctly at runtime, but some of its dynamic behavior is hard for static analyzers to understand. In particular, PHPStan flags valid code because a few APIs are powered by __call() and some resource-returning methods are documented too broadly.
I hit this in a downstream project and had to add local PHPStan stubs as a workaround.
Current behavior
Two patterns are difficult for static analysis:
RunsClusterOperations methods return the same concrete resource instance, but the docs widen them to K8sResource
HasAttributes::__call() provides runtime setX/getX/removeX accessors, but those methods are not visible to PHPStan/IDEs
Examples
1. Concrete resource methods lose type information
In src/Traits/RunsClusterOperations.php, methods like these currently widen the return type:
get()
create()
createOrUpdate()
syncWithCluster()
refresh()
refreshOriginal()
At runtime, if I call these on K8sSecret, K8sPod, etc., I still get that same concrete class back. But static analyzers only see K8sResource, so downstream code loses access to concrete methods.
Example downstream failure:
$secret = $cluster->secret()->setNamespace($ns)->getByName($name);
$cluster->secret()
->setName($name)
->setNamespace($targetNs)
->setData($secret->getData(), false)
->setType($secret->getType() ?? '')
->createOrUpdate();
PHPStan reports getData()/getType()/setType() as undefined once the type has been widened.
2. Magic attribute setters/getters are valid at runtime but invisible statically
In src/Traits/Resource/HasAttributes.php, __call() maps unknown:
setX(...) -> setAttribute('x', ...)
getX(...) -> getAttribute('x', ...)
removeX(...) -> removeAttribute('x')
So code like this works at runtime:
K8s::container()
->setName('sidecar')
->setImagePullPolicy('IfNotPresent')
->setCommand(['sidecar.php'])
->setArgs([json_encode($args)])
->setWorkingDir('/usr/code');
But static analyzers flag all of those as undefined methods on Instances\Container.
Suggested fix
Because the package now requires PHP ^8.2, I think the upstream fix can be stronger than just PHPDoc.
A. Preserve concrete resource return types
In src/Traits/RunsClusterOperations.php, update the relevant methods to use static return information, ideally native where possible.
Candidates:
getByName()
get()
create()
createOrUpdate()
syncWithCluster()
refresh()
refreshOriginal()
Even if native static is not used everywhere, at minimum the PHPDoc should reflect static/$this consistently instead of K8sResource.
B. Expose common magic methods for analyzers/IDEs
For classes that intentionally rely on HasAttributes::__call(), add @method annotations on the concrete classes for common accessors.
Examples that would help immediately:
src/Instances/Container.php
setName(string $name)
setImagePullPolicy(string $policy)
setCommand(array $command)
setArgs(array $args)
setWorkingDir(?string $workingDir)
src/Kinds/K8sSecret.php
setType(string $type)
getType(): ?string
There are likely other classes worth annotating too, but these cover a common real-world path.
C. Optional: ship official PHPStan/Psalm stubs
If you prefer not to add many @method tags inline, another option is to ship stub files for static analysis. That would still let the runtime API stay dynamic while making the intended API visible to tools.
Why this matters
Right now downstream users either:
- replace readable fluent calls with raw
setAttribute(...), or
- maintain local stub files, or
- suppress the errors in a PHPStan baseline
It would be better if the package exposed its intended API to static tools directly.
Repro summary
The repro is basically:
- use a concrete resource returned from
get()/getByName()
- call a concrete-only method like
K8sSecret::getData() or getType()
- use
Instances\Container fluent setters that are currently provided only by HasAttributes::__call()
Notes
The runtime behavior already exists in:
src/Traits/RunsClusterOperations.php
src/Traits/Resource/HasAttributes.php
This issue is specifically about surfacing that behavior to static analysis.
If this direction looks good, I can turn it into a PR.
php-k8sworks correctly at runtime, but some of its dynamic behavior is hard for static analyzers to understand. In particular, PHPStan flags valid code because a few APIs are powered by__call()and some resource-returning methods are documented too broadly.I hit this in a downstream project and had to add local PHPStan stubs as a workaround.
Current behavior
Two patterns are difficult for static analysis:
RunsClusterOperationsmethods return the same concrete resource instance, but the docs widen them toK8sResourceHasAttributes::__call()provides runtimesetX/getX/removeXaccessors, but those methods are not visible to PHPStan/IDEsExamples
1. Concrete resource methods lose type information
In
src/Traits/RunsClusterOperations.php, methods like these currently widen the return type:get()create()createOrUpdate()syncWithCluster()refresh()refreshOriginal()At runtime, if I call these on
K8sSecret,K8sPod, etc., I still get that same concrete class back. But static analyzers only seeK8sResource, so downstream code loses access to concrete methods.Example downstream failure:
PHPStan reports
getData()/getType()/setType()as undefined once the type has been widened.2. Magic attribute setters/getters are valid at runtime but invisible statically
In
src/Traits/Resource/HasAttributes.php,__call()maps unknown:setX(...)->setAttribute('x', ...)getX(...)->getAttribute('x', ...)removeX(...)->removeAttribute('x')So code like this works at runtime:
But static analyzers flag all of those as undefined methods on
Instances\Container.Suggested fix
Because the package now requires PHP
^8.2, I think the upstream fix can be stronger than just PHPDoc.A. Preserve concrete resource return types
In
src/Traits/RunsClusterOperations.php, update the relevant methods to usestaticreturn information, ideally native where possible.Candidates:
getByName()get()create()createOrUpdate()syncWithCluster()refresh()refreshOriginal()Even if native
staticis not used everywhere, at minimum the PHPDoc should reflectstatic/$thisconsistently instead ofK8sResource.B. Expose common magic methods for analyzers/IDEs
For classes that intentionally rely on
HasAttributes::__call(), add@methodannotations on the concrete classes for common accessors.Examples that would help immediately:
src/Instances/Container.phpsetName(string $name)setImagePullPolicy(string $policy)setCommand(array $command)setArgs(array $args)setWorkingDir(?string $workingDir)src/Kinds/K8sSecret.phpsetType(string $type)getType(): ?stringThere are likely other classes worth annotating too, but these cover a common real-world path.
C. Optional: ship official PHPStan/Psalm stubs
If you prefer not to add many
@methodtags inline, another option is to ship stub files for static analysis. That would still let the runtime API stay dynamic while making the intended API visible to tools.Why this matters
Right now downstream users either:
setAttribute(...), orIt would be better if the package exposed its intended API to static tools directly.
Repro summary
The repro is basically:
get()/getByName()K8sSecret::getData()orgetType()Instances\Containerfluent setters that are currently provided only byHasAttributes::__call()Notes
The runtime behavior already exists in:
src/Traits/RunsClusterOperations.phpsrc/Traits/Resource/HasAttributes.phpThis issue is specifically about surfacing that behavior to static analysis.
If this direction looks good, I can turn it into a PR.