0

I'm makin a framework to make my own server, to do this i had implemented many classes, one of this is a class named model who is a generic parent class that suports other specific models. this is the construction of the class model:

namespace app\core;
use app\models\RegisterModel;
abstract class Model
{ 

  /*just definition of generic properties and rules to the model*/


  public array $errors = [];
  public const RULE_REQUIRED = 'required';
  public const RULE_EMAIL = 'email';
  public const RULE_MIN = 'min';
  public const RULE_MAX = 'max';
  public const RULE_MATCH = 'match';
  
  /*the purpouse of this function is load data to the object, when i save the data this way it works well*/

  public function loadData($data){
  foreach($data as $key => $value){
      if (property_exists($this, $key)){
        $this->{$key} = $value;
      }
    };
  }

/here is the function that is giving troubles to me/

public function validate(){

    foreach ($this->rules() as $attibute => $rules) {
      // code...
      $value = $this->{$attibute};
      foreach ($rules as $rule) {
        // code...
          $ruleName = $rule;
          if (!is_string($ruleName)) {
            // code...
            $ruleName = $rule[0];
          }
          if ($ruleName == self::RULE_REQUIRED && !$value){
               $this->addError($attribute, self::RULE_REQUIRED);
          }

      }
    }
  }

this is the child class

namespace app\models;
use app\core\Model;
class RegisterModel extends Model
{
  public string $firstname;
  public string $lastname;
  public string $email;
  public string $password;
  public string $confirmPassword;
  /*los nombres tienen que coincidir con los deldocumento register.php*/
  public function register(){
    return true;
  }
  public function rules(): array{
    return [
        'firstname'=> [self::RULE_REQUIRED],
        'lastname'=> [self::RULE_REQUIRED],
        'email'=> [self::RULE_REQUIRED, self::RULE_EMAIL],
        'password'=> [self::RULE_REQUIRED, [self::RULE_MIN, 'min'=>8], [self::RULE_MAX, 'max'=>24]],
        'password'=> [self::RULE_REQUIRED, [self::RULE_MATCH, 'match'=>'password']]
    ];
  }

}

when i create a instance (lets call it ChildInstance) of the class and load the information of $firstname, $lastname, $email, $password, $confirmpasword and then use var_dump on ChildInstance I can see the information i load on this instance. but when i use the function validate (both functions validate and loadData belongs to the parent class) it raise this error:

 Uncaught Error: Typed property app\models\RegisterModel::$firstname must not be accessed before initialization 

I already solved this problem by using inheritance, but I can't solve it using this aproach, why the information stored cannot be accesed using a function of the parent class? thank you

  • 1
    Does this answer your question? [Why I am suddenly getting a "Typed property must not be accessed before initialization" error when introducing properties type hints?](https://stackoverflow.com/questions/59265625/why-i-am-suddenly-getting-a-typed-property-must-not-be-accessed-before-initiali) – Ron van der Heijden Feb 26 '21 at 21:48
  • hello ron, it doesn't, i can acces to variables between the class, my problem is that i can't use variables of the child using functions of the father – alexander garcia Feb 26 '21 at 22:09

1 Answers1

0

This happens because of the typed properties, i.e. public string $firstname.

When you define the type for a property, but don't give it a default value, it has an uninitialized state under the PHP hood. This error is essentially saying: you have tried to access a typed property before you have given it a value.

Borrowing an example from madewithlove, this will cause the same error:

class User
{
    private int $id;
    private string $username;

    public function __construct(int $id)
    {
        $this->id = $id;
    }

    public function getUsername(): string
    {
        return $this->username;
    }
}

$user = new User(1);
echo $user->getUsername();

Notice we never gave $username a value, nor does it have a default set on the property.

I'm not sure what the best solution would be; it depends on what your shared Model is designed to do, how child instances are instantiated, etc. Setting default property values is one option. Instantiating models and property values via reflection may be another.

Aken Roberts
  • 13,012
  • 3
  • 34
  • 40
  • but the function loadData isloading the data to the child class, i also test that using var_dump – alexander garcia Feb 26 '21 at 22:12
  • I first give the value tothe child object and then try to use it using the validate function, and is at that moment that raises error – alexander garcia Feb 26 '21 at 22:14
  • I assume that your `loadData` phase is not going as you expect, then. I can clearly reproduce the problem using your code, and having `loadData` skip certain properties. Inheritance is not an issue - focus on the typed properties and how their values are being populated. Check for misspellings or changes in capitalization - the keys passed to `loadData` should match the model properties exactly. – Aken Roberts Feb 26 '21 at 22:26