cphalcon: [BUG]: Access to undefined property in SETTER function while calling findFirst model function
Access to undefined property exception thrown in setter function when I fetch record by findFirst($id) method
The findFirst() model function internally calling custom setter functions written in the model. But it throws exception when it finds accessing other property of the same model which is $this->property_five next to $this->property_four in mysql table
-------------------------------------------
id | property_four | property_five
-------------------------------------------
1 | Phalcon 3.4.4 | PHP 7.3
-------------------------------------------
E_USER_NOTICE: Access to undefined property Example::property_five in /path/to/Example.php on line xxx
Steps to reproduce the behavior:
class Example extends \Phalcon\Mvc\Model
{
...
public function setPropertyFour()
{
$this->property_four = $this->property_five;
}
}
Example::findFirst($id)
Details
- Phalcon version: 3.4.4 Web framework delivered as a C-extension for PHP phalcon => enabled Author => Phalcon Team and contributors Version => 3.4.4 Build Date => Nov 18 2019 14:21:45 Powered by Zephir => Version 0.10.16-6826149172 Directive => Local Value => Master Value phalcon.db.escape_identifiers => On => On phalcon.db.force_casting => Off => Off phalcon.orm.events => On => On phalcon.orm.virtual_foreign_keys => On => On phalcon.orm.column_renaming => On => On phalcon.orm.not_null_validations => On => On phalcon.orm.exception_on_failed_save => Off => Off phalcon.orm.enable_literals => On => On phalcon.orm.late_state_binding => Off => Off phalcon.orm.enable_implicit_joins => On => On phalcon.orm.cast_on_hydrate => Off => Off phalcon.orm.ignore_unknown_columns => Off => Off phalcon.orm.update_snapshot_on_save => On => On phalcon.orm.disable_assign_setters => Off => Off
- PHP Version: PHP 7.3.11-1+ubuntu16.04.1+deb.sury.org+1
- Operating System: linux mint 18.1 cinnamon 64-bit
- Installation type: Compiling from source
- Zephir version (if any):
- Server: Nginx
- Other related info (Database, table schema): MySQL
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 17 (8 by maintainers)
The issue described is not a bug. It is actually a side effect on how the whole application works. You will notice the same thing with pure PHP also.
I copied the model like so:
The table is:
When I get one record from the table using
findFirst, Phalcon does the following:The issue happens when the data is transferred or assigned to the model, in particular the
cloneResultsetMapmethod.There is a loop which traverses the database resultset, and depending on the column map (if defined) and a few other conditions, the loop will assign the data to the model
When this line is executed, the
__setmethod is immediately called. The reason is because the property does not exist in the model (see model definition above). The code in the__setmethod also checks if my assignment is a possible setter in the model. For the first few assignments, nothing exists so no setters are called.Remember we are still in the loop that is assigning data to the model.
So when the loop reaches
property_fourit :__set__setchecks if there is a setter and calls itproperty_fivetoproperty_fourproperty_fiveis not assigned yet so__getis calledBy the above, we have successfully created a circular reference or an impossible solution to resolve.
I experimented with assigning a temporary flag before the population from database to model happens so that I do not invoke any setters but then that stopped the setters from running. With a bit of experimentation I managed to get the temporary variable to allow or not the setters to run and then leave everything as is. This created more problems than what we are trying to solve. In addition, to make that theory work, I had to run the loop twice, one without the setters and one with them, which reduces performance.
We will not be fixing this since it does impact performance.
Potential solutions:
afterFetchevent to manipulateproperty_fourwithproperty_fiveJust declare it inside model class, without value