3v4l.org

run code in 300+ PHP versions simultaneously
<?php declare(strict_types=1); /** * Shared by all CRUD DTO classes. Understood by service responsible for validation and persistence: CrudEntityManager. */ interface CrudDto { public function apply(): CrudEntity; public static function belongsTo(): string; } abstract class CrudEntity { // Shared stuff such as ID props etc, not important for the question at hand. } /** * Notice a few benefits:. * * 1. No setters * 2. The entity is in charge of updating itself from DTOs it declares it understands * 3. The entity can be created an updated using multiple different DTO objects, as long as the Entity has relevant methods to accept them * 4. No setters means guaranteed read-only properties where a value can be set only during the object creation are possible * 5. Using a static method instead of constructor with a lot of arguments makes inheritance easier */ class Person extends CrudEntity { protected string $name; protected string $writeOnceProperty; protected function __construct() { // Protecting the constructor forces usage of CreatePersonDto } public static function createWith(CreatePersonDto $dto): self { $self = new self(); $self->name = $dto->name; $self->writeOnceProperty = $dto->writeOnceProperty; return $self; } public function updateWith(UpdatePersonDto $dto): self { $this->name = $dto->name; return $this; } public function getName(): string { return $this->name; } public function getWriteOnceProperty(): string { return $this->writeOnceProperty; } } abstract class PersonDto implements CrudDto { public static function belongsTo(): string { return Person::class; } } class CreatePersonDto extends PersonDto { public function __construct(public string $name, public string $writeOnceProperty) { // Void } public function apply(): Person { return Person::createWith($this); } } class UpdatePersonDto extends PersonDto { public string $name; public function __construct(private readonly Person $source) { $this->name = $source->getName(); } public function apply(): Person { return $this->source->updateWith($this); } } class CrudManager { /** * IRL this would validate the DTO and store the $entity in the database. */ public function save(CrudDto $dto): CrudEntity { $entity = $dto->apply(); assert(is_a($entity, $dto::belongsTo())); return $entity; } /** * @template T of CrudEntity * * @param class-string<T> $className * * @return T */ public function saveWithTypeCheck(CrudDto $dto, string $className = CrudEntity::class): CrudEntity { $entity = $this->save($dto); assert($entity instanceof $className); return $entity; } } $manager = new CrudManager(); $dto = new CreatePersonDto(name: 'John Doe', writeOnceProperty: 'Born on 1/1/1970'); $person1 = $manager->save($dto); echo $person1->getName(); echo "\n"; echo get_debug_type($person1); // Prints Person, but static tools such as PHPStan or PHPStorm say it's a CrudEntity echo "\n\n\n"; // We can even use one DTO to create multiple different objects. Almost a prototype pattern, aye?! $person2 = $manager->saveWithTypeCheck($dto, Person::class); // Demo of the update action, just to prove the point $updateDto = new UpdatePersonDto($person2); $updateDto->name = 'New John'; $manager->save($updateDto); echo $person2->getName(); // New John echo "\n"; echo get_debug_type($person2); // Prints Person, but this time PHPStan and PHPStorm know it's CrudEntity|Person. // Conclusion: // // Using $person2 allows my IDE to do autocompletion, and PHPStan check types. // Using $person1 does none of that. // // Question: // // Is there any way to tell CrudManager to get the class-string<T of CrudEntity> from CrudDto::belongsTo()? // It would get rid of the need to pass the class name manually, as demonstrated in CrudManager::saveWithTypeCheck().
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  (null)
number of ops:  48
compiled vars:  !0 = $manager, !1 = $dto, !2 = $person1, !3 = $person2, !4 = $updateDto
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   67     0  E >   DECLARE_CLASS                                            'persondto'
   75     1        DECLARE_CLASS                                            'createpersondto', 'persondto'
   88     2        DECLARE_CLASS                                            'updatepersondto', 'persondto'
  132     3        NEW                                              $5      'CrudManager'
          4        DO_FCALL                                      0          
          5        ASSIGN                                                   !0, $5
  133     6        NEW                                              $8      'CreatePersonDto'
          7        SEND_VAL_EX                                              'John+Doe', 'name'
          8        SEND_VAL_EX                                              'Born+on+1%2F1%2F1970', 'writeOnceProperty'
          9        CHECK_UNDEF_ARGS                                         
         10        DO_FCALL                                      1          
         11        ASSIGN                                                   !1, $8
  134    12        INIT_METHOD_CALL                                         !0, 'save'
         13        SEND_VAR_EX                                              !1
         14        DO_FCALL                                      0  $11     
         15        ASSIGN                                                   !2, $11
  136    16        INIT_METHOD_CALL                                         !2, 'getName'
         17        DO_FCALL                                      0  $13     
         18        ECHO                                                     $13
  137    19        ECHO                                                     '%0A'
  138    20        INIT_FCALL                                               'get_debug_type'
         21        SEND_VAR                                                 !2
         22        DO_ICALL                                         $14     
         23        ECHO                                                     $14
  139    24        ECHO                                                     '%0A%0A%0A'
  142    25        INIT_METHOD_CALL                                         !0, 'saveWithTypeCheck'
         26        SEND_VAR_EX                                              !1
         27        SEND_VAL_EX                                              'Person'
         28        DO_FCALL                                      0  $15     
         29        ASSIGN                                                   !3, $15
  145    30        NEW                                              $17     'UpdatePersonDto'
         31        SEND_VAR_EX                                              !3
         32        DO_FCALL                                      0          
         33        ASSIGN                                                   !4, $17
  146    34        ASSIGN_OBJ                                               !4, 'name'
         35        OP_DATA                                                  'New+John'
  147    36        INIT_METHOD_CALL                                         !0, 'save'
         37        SEND_VAR_EX                                              !4
         38        DO_FCALL                                      0          
  151    39        INIT_METHOD_CALL                                         !3, 'getName'
         40        DO_FCALL                                      0  $22     
         41        ECHO                                                     $22
  152    42        ECHO                                                     '%0A'
  153    43        INIT_FCALL                                               'get_debug_type'
         44        SEND_VAR                                                 !3
         45        DO_ICALL                                         $23     
         46        ECHO                                                     $23
  163    47      > RETURN                                                   1

Class CrudDto:
Function apply:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  apply
number of ops:  2
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   10     0  E >   VERIFY_RETURN_TYPE                                       
          1      > RETURN                                                   null

End of function apply

Function belongsto:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  belongsTo
number of ops:  2
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   12     0  E >   VERIFY_RETURN_TYPE                                       
          1      > RETURN                                                   null

End of function belongsto

End of class CrudDto.

Class CrudEntity: [no user functions]
Class Person:
Function __construct:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  __construct
number of ops:  1
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   38     0  E > > RETURN                                                   null

End of function __construct

Function createwith:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  createWith
number of ops:  14
compiled vars:  !0 = $dto, !1 = $self
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   40     0  E >   RECV                                             !0      
   42     1        NEW                          self                $2      
          2        DO_FCALL                                      0          
          3        ASSIGN                                                   !1, $2
   43     4        FETCH_OBJ_R                                      ~6      !0, 'name'
          5        ASSIGN_OBJ                                               !1, 'name'
          6        OP_DATA                                                  ~6
   44     7        FETCH_OBJ_R                                      ~8      !0, 'writeOnceProperty'
          8        ASSIGN_OBJ                                               !1, 'writeOnceProperty'
          9        OP_DATA                                                  ~8
   46    10        VERIFY_RETURN_TYPE                                       !1
         11      > RETURN                                                   !1
   47    12*       VERIFY_RETURN_TYPE                                       
         13*     > RETURN                                                   null

End of function createwith

Function updatewith:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  updateWith
number of ops:  9
compiled vars:  !0 = $dto
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   49     0  E >   RECV                                             !0      
   51     1        FETCH_OBJ_R                                      ~2      !0, 'name'
          2        ASSIGN_OBJ                                               'name'
          3        OP_DATA                                                  ~2
   53     4        FETCH_THIS                                       ~3      
          5        VERIFY_RETURN_TYPE                                       ~3
          6      > RETURN                                                   ~3
   54     7*       VERIFY_RETURN_TYPE                                       
          8*     > RETURN                                                   null

End of function updatewith

Function getname:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  getName
number of ops:  5
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   58     0  E >   FETCH_OBJ_R                                      ~0      'name'
          1        VERIFY_RETURN_TYPE                                       ~0
          2      > RETURN                                                   ~0
   59     3*       VERIFY_RETURN_TYPE                                       
          4*     > RETURN                                                   null

End of function getname

Function getwriteonceproperty:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  getWriteOnceProperty
number of ops:  5
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   63     0  E >   FETCH_OBJ_R                                      ~0      'writeOnceProperty'
          1        VERIFY_RETURN_TYPE                                       ~0
          2      > RETURN                                                   ~0
   64     3*       VERIFY_RETURN_TYPE                                       
          4*     > RETURN                                                   null

End of function getwriteonceproperty

End of class Person.

Class PersonDto:
Function belongsto:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  belongsTo
number of ops:  3
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   71     0  E > > RETURN                                                   'Person'
   72     1*       VERIFY_RETURN_TYPE                                       
          2*     > RETURN                                                   null

End of function belongsto

End of class PersonDto.

Class CreatePersonDto:
Function __construct:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  __construct
number of ops:  7
compiled vars:  !0 = $name, !1 = $writeOnceProperty
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   77     0  E >   RECV                                             !0      
          1        RECV                                             !1      
          2        ASSIGN_OBJ                                               'name'
          3        OP_DATA                                                  !0
          4        ASSIGN_OBJ                                               'writeOnceProperty'
          5        OP_DATA                                                  !1
   80     6      > RETURN                                                   null

End of function __construct

Function apply:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  apply
number of ops:  8
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   84     0  E >   INIT_STATIC_METHOD_CALL                                  'Person', 'createWith'
          1        FETCH_THIS                                       ~0      
          2        SEND_VAL                                                 ~0
          3        DO_FCALL                                      0  $1      
          4        VERIFY_RETURN_TYPE                                       $1
          5      > RETURN                                                   $1
   85     6*       VERIFY_RETURN_TYPE                                       
          7*     > RETURN                                                   null

End of function apply

End of class CreatePersonDto.

Class UpdatePersonDto:
Function __construct:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  __construct
number of ops:  8
compiled vars:  !0 = $source
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   92     0  E >   RECV                                             !0      
          1        ASSIGN_OBJ                                               'source'
          2        OP_DATA                                                  !0
   94     3        INIT_METHOD_CALL                                         !0, 'getName'
          4        DO_FCALL                                      0  $2      
          5        ASSIGN_OBJ                                               'name'
          6        OP_DATA                                                  $2
   95     7      > RETURN                                                   null

End of function __construct

Function apply:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  apply
number of ops:  9
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   99     0  E >   FETCH_OBJ_R                                      ~0      'source'
          1        INIT_METHOD_CALL                                         ~0, 'updateWith'
          2        FETCH_THIS                                       $1      
          3        SEND_VAR_EX                                              $1
          4        DO_FCALL                                      0  $2      
          5        VERIFY_RETURN_TYPE                                       $2
          6      > RETURN                                                   $2
  100     7*       VERIFY_RETURN_TYPE                                       
          8*     > RETURN                                                   null

End of function apply

End of class UpdatePersonDto.

Class CrudManager:
Function save:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  save
number of ops:  20
compiled vars:  !0 = $dto, !1 = $entity
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  108     0  E >   RECV                                             !0      
  110     1        INIT_METHOD_CALL                                         !0, 'apply'
          2        DO_FCALL                                      0  $2      
          3        ASSIGN                                                   !1, $2
  111     4        ASSERT_CHECK                                             
          5        INIT_FCALL                                               'assert'
          6        INIT_FCALL                                               'is_a'
          7        SEND_VAR                                                 !1
          8        FETCH_CLASS                                   0  $4      !0
          9        INIT_STATIC_METHOD_CALL                                  $4, 'belongsTo'
         10        DO_FCALL                                      0  $5      
         11        SEND_VAR                                                 $5
         12        DO_ICALL                                         $6      
         13        SEND_VAR                                                 $6
         14        SEND_VAL                                                 'assert%28is_a%28%24entity%2C+%24dto%3A%3AbelongsTo%28%29%29%29'
         15        DO_ICALL                                                 
  113    16        VERIFY_RETURN_TYPE                                       !1
         17      > RETURN                                                   !1
  114    18*       VERIFY_RETURN_TYPE                                       
         19*     > RETURN                                                   null

End of function save

Function savewithtypecheck:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/QFA6m
function name:  saveWithTypeCheck
number of ops:  17
compiled vars:  !0 = $dto, !1 = $className, !2 = $entity
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  123     0  E >   RECV                                             !0      
          1        RECV_INIT                                        !1      'CrudEntity'
  125     2        INIT_METHOD_CALL                                         'save'
          3        SEND_VAR_EX                                              !0
          4        DO_FCALL                                      0  $3      
          5        ASSIGN                                                   !2, $3
  126     6        ASSERT_CHECK                                             
          7        INIT_FCALL                                               'assert'
          8        FETCH_CLASS                                   0  $5      !1
          9        INSTANCEOF                                       ~6      !2, $5
         10        SEND_VAL                                                 ~6
         11        SEND_VAL                                                 'assert%28%24entity+instanceof+%24className%29'
         12        DO_ICALL                                                 
  128    13        VERIFY_RETURN_TYPE                                       !2
         14      > RETURN                                                   !2
  129    15*       VERIFY_RETURN_TYPE                                       
         16*     > RETURN                                                   null

End of function savewithtypecheck

End of class CrudManager.

Generated using Vulcan Logic Dumper, using php 8.0.0


preferences:
155.71 ms | 1029 KiB | 16 Q