Generics via properties instead of parameters #4594
-
I read through the "Generics in PHP using PHPDocs" article, and I'm wondering if there's a way to implement generics with a slightly different paradigm. You cite an example:
and that's a decent analog to what I'm doing in my code, but I'm using a class to build database queries, and rather than passing in the $className, I'm setting a property on my query object. So the equivalent code in my software would look like (this is an oversimplification):
This allows me to have one base class for database queries, but which can return virtually any class of object in my system, e.g.
Is there any way to squeeze this use case into the static analysis methodology? Right now, I've had to resort to "@return object|null;" which means that every time I call a class-specific method on that object, PHPStan yells at me. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 3 replies
-
Hi, the easiest solution would be: /** @template T of object */
class Queryer {
/** @var class-string<T> */
private $object_class;
/** @param class-string<T> $className */
public function __construct(string $className) {
$this->object_class = $className;
}
public function set_tablename(string $tbl) {
$this->tablename = $tbl;
}
/** @return T|null */
public function findEntity(int $id) {
$results = [get record from $this->tablename];
$class = $this->object_class;
$obj = new $class($results);
return $obj;
}
}
$q = new Queryer(User::class);
$q->set_tablename('users');
$user = $q->findEntity(23); // knows it's User Playground link: https://phpstan.org/r/67ace8a3-2ce7-4166-aaeb-95b4553b0f01 |
Beta Was this translation helpful? Give feedback.
-
Great! Seems to be working so far. I have a bunch of variations that I’ll need to tweak, but thanks for the starting point.
spud.
…On Feb 23, 2021, at 1:33 PM, Ondřej Mirtes ***@***.***> wrote:
Hi, the easiest solution would be:
/** @template T of object */
class Queryer {
/** @var class-string<T> */
private $object_class;
/** @param class-string<T> $className */
public function __construct(string $className) {
$this->object_class = $className;
}
public function set_tablename(string $tbl) {
$this->tablename = $tbl;
}
/** @param class-string<T> */
public function set_object_class(string $className) {
$this->object_class = $className;
}
/** @return T|null */
public function findEntity(int $id) {
$results = [get record from $this->tablename];
$class = $this->object_class;
$obj = new $class($results);
return $obj;
}
}
$q = new Queryer(User::class);
$q->set_tablename('users');
$user = $q->findEntity(23); // knows it's User
Playground link: https://phpstan.org/r/67ace8a3-2ce7-4166-aaeb-95b4553b0f01 <https://phpstan.org/r/67ace8a3-2ce7-4166-aaeb-95b4553b0f01>
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <#4594 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAEATJDBEQARFQJHSJ5MJULTAPYIDANCNFSM4YC7F47Q>.
|
Beta Was this translation helpful? Give feedback.
-
Well, I spoke too soon. Your example works great, with the object-class dependency passed into the constructor. My objects, however, use a setter to establish the object class, so it's more like
where the constructor internally calls
The idea is to have a top-level generic query class, but to be able to quickly generate content type-specific query classes, allowing me to create e.g. But I'd love also to have PHPStan be happy with it. So far not so happy. :-( |
Beta Was this translation helpful? Give feedback.
Hi, the easiest solution would be:
Playground…