Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG]: Segmentation Fault Resultset/Complex, keepSnapshots=true #14783

Closed
maxc62 opened this issue Jan 27, 2020 · 10 comments
Closed

[BUG]: Segmentation Fault Resultset/Complex, keepSnapshots=true #14783

maxc62 opened this issue Jan 27, 2020 · 10 comments
Assignees
Labels
5.0 The issues we want to solve in the 5.0 release bug A bug report external dependency This issue depends on external issue to be resolved. status: medium Medium

Comments

@maxc62
Copy link

maxc62 commented Jan 27, 2020

Describe the bug
When I read model attribute on Resultset\Complex and affect it in another model, I get segmentation fault

To Reproduce
Create Model1:

class Model1 extends \Phalcon\Mvc\Model
{
    public function initialize()
    {
        $this->keepSnapshots(true);

        $this->belongsTo(
            'model2_id',
            __NAMESPACE__.'\Model2',
            'id',
            ['alias' => 'model2', 'reusable' => true]
        );
    }
    /**
     * @var int
     * @Primary
     * @Identity
     * @Column(type="serial", nullable=false)
     */
    public $id;
    /**
     * @var string
     * @Column(type="text", nullable=true)
     */
    public $name;
    /**
     * @var int
     * @Column(type="integer", nullable=true)
     */
    public $model2_id;
}

Create Model2:

class Model2 extends \Phalcon\Mvc\Model
{
    public function initialize()
    {
        $this->keepSnapshots(true);
    }
    /**
     * @var int
     * @Primary
     * @Identity
     * @Column(type="serial", nullable=false)
     */
    public $id;

    /**
     * @var string
     * @Column(type="text", nullable=true)
     */
    public $name;
}

IndexController

class IndexController extends Controller
{
    public function traitement($resultset)
    {
        $model1 = $resultset->readAttribute(lcfirst(Model1::class));
        $model2 = $resultset->readAttribute('join_1');
        $model1->model2 = $model2;
        return $model1;
    }

    public function searchAction()
    {
        $query = Model1::query();
        $query->columns([Model1::class . '.*', 'join_1.*']);
        $query->leftJoin(Model2::class, 'join_1.id = ' . Model1::class . '.model2_id', 'join_1');
        $query->limit(20, 0);//I have 50 rows in my db
        $resultsets = $query->execute();

        foreach ($resultsets as $resultset) {
            $model1 = $this->traitement($resultset);
        }
        echo 'OK';
    }
}

Expected behavior
Display "OK"

Test

  • When I disabled "keepSnapshots", it works
//        $this->keepSnapshots(true);
  • When I replace the call traitement by the content of the function, it works
        foreach ($resultsets as $resultset) {
//            $model1 = $this->traitement($resultset);
            $model1 = $resultset->readAttribute(lcfirst(Model1::class));
            $model2 = $resultset->readAttribute('join_1');
            $model1->model2 = $model2;
        }
  • When I disabled belongsTo on Model1, it works
//        $this->belongsTo(
//            'model2_id',
//            __NAMESPACE__.'\Model2',
//            'id',
//            ['alias' => 'model2', 'reusable' => true]
//        );
  • When I affect $model1 before execute traitement, it works
        foreach ($resultsets as $resultset) {
            $model1 = null;
            $model1 = $this->traitement($resultset);
        }
  • When I unset $model1 before end of for, it works
        foreach ($resultsets as $resultset) {
            $model1 = $this->traitement($resultset);
            unset($model1);
        }

Additional context
I try with php 7.3 & php 7.4 with phalcon 4 and same result (Failed)
With php 7.3 or php 7.2 and phalcon 3 (last version), it works

Details

  • Phalcon version: 4.0.3 (same with 4.0.2)
  • PHP Version: 7.4.2
  • Operating System: debian
  • Installation type: package
  • Other related info (Database, table schema): Postgresql

Backtrace PHP 7.4

#0  zend_mm_alloc_small (bin_num=6, heap=0x7fe740800040) at ./Zend/zend_alloc.c:1255
#1  _emalloc_56 () at ./Zend/zend_alloc.c:2465
#2  0x0000562997d3ca72 in zend_array_dup (source=0x7fe7408f60a8) at ./Zend/zend_hash.c:2047
#3  0x00007fe73d8ce865 in zephir_update_property_zval (object=0x7fe7408135d0, property_name=0x7fe73dd12c14 "snapshot", property_length=<optimized out>, value=<optimized out>, value=<optimized out>)
    at ./build/php7/64bits/phalcon.zep.c:5320
#4  0x00007fe73da6fa2e in zim_Phalcon_Mvc_Model_setSnapshotData (execute_data=<optimized out>, return_value=<optimized out>) at ./build/php7/64bits/phalcon.zep.c:54958
#5  0x0000562997d1d132 in zend_call_function (fci=fci@entry=0x7ffcbf3e9ea0, fci_cache=<optimized out>) at ./Zend/zend_execute_API.c:824
#6  0x00007fe73d86360b in zephir_call_user_function (object_pp=object_pp@entry=0x7ffcbf3ea1b0, obj_ce=obj_ce@entry=0x7fe73d42c110, type=type@entry=zephir_fcall_method, function_name=function_name@entry=0x7ffcbf3ea090,
    retval_ptr=retval_ptr@entry=0x0, cache_entry=0x7ffcbf3e9e48, cache_entry@entry=0x0, cache_slot=<optimized out>, param_count=<optimized out>, params=0x7ffcbf3ea330) at ./build/php7/64bits/phalcon.zep.c:8659
#7  0x00007fe73d863e6b in zephir_call_class_method_aparams (return_value=0x0, ce=0x7fe73d42c110, type=zephir_fcall_method, object=0x7ffcbf3ea1b0, method_name=0x7fe73dd12b98 "setsnapshotdata", method_len=<optimized out>,
    cache_entry=0x0, cache_slot=0, param_count=2, params=0x7ffcbf3ea330) at ./build/php7/64bits/phalcon.zep.c:8797
#8  0x00007fe73dccfb56 in zim_Phalcon_Mvc_Model_cloneResultMap (execute_data=<optimized out>, return_value=0x7ffcbf3ea8e0) at /usr/include/php/20190902/Zend/zend_types.h:440
#9  0x0000562997d1d132 in zend_call_function (fci=fci@entry=0x7ffcbf3ea570, fci_cache=<optimized out>) at ./Zend/zend_execute_API.c:824
#10 0x00007fe73d86360b in zephir_call_user_function (object_pp=object_pp@entry=0x0, obj_ce=obj_ce@entry=0x5629993df5b0, type=type@entry=zephir_fcall_ce, function_name=function_name@entry=0x7ffcbf3ea760,
    retval_ptr=retval_ptr@entry=0x7ffcbf3ea8e0, cache_entry=0x7ffcbf3ea518, cache_entry@entry=0x7ffcbf3ea840, cache_slot=<optimized out>, param_count=<optimized out>, params=0x7ffcbf3eab00) at ./build/php7/64bits/phalcon.zep.c:8659
#11 0x00007fe73d863e6b in zephir_call_class_method_aparams (return_value=0x7ffcbf3ea8e0, ce=0x5629993df5b0, type=zephir_fcall_ce, object=0x0, method_name=0x7fe73dd1750b "cloneresultmap", method_len=<optimized out>,
    cache_entry=0x7ffcbf3ea840, cache_slot=0, param_count=5, params=0x7ffcbf3eab00) at ./build/php7/64bits/phalcon.zep.c:8797
#12 0x00007fe73dc4248f in zim_Phalcon_Mvc_Model_Resultset_Complex_current (execute_data=<optimized out>, return_value=0x7fe7409f65a0) at ./build/php7/64bits/phalcon.zep.c:86177
#13 0x0000562997d1d132 in zend_call_function (fci=fci@entry=0x7ffcbf3eac70, fci_cache=<optimized out>, fci_cache@entry=0x7ffcbf3eac50) at ./Zend/zend_execute_API.c:824
#14 0x0000562997d4786d in zend_call_method (object=0x7fe7409f6578, obj_ce=<optimized out>, fn_proxy=<optimized out>, function_name=0x562997dbd023 "current", function_name_len=7, retval_ptr=0x7fe7409f65a0, param_count=0, arg1=0x0,
    arg2=0x0) at ./Zend/zend_interfaces.c:103
#15 0x0000562997d47c32 in zend_user_it_get_current_data (_iter=0x7fe7409f6540) at ./Zend/zend_interfaces.c:179
#16 0x0000562997da17e5 in ZEND_FE_FETCH_R_SPEC_VAR_HANDLER () at ./Zend/zend_vm_execute.h:21506
#17 0x0000562997daa2c7 in execute_ex (ex=0x7fe740800040) at ./Zend/zend_vm_execute.h:55891
#18 0x0000562997d1d256 in zend_call_function (fci=fci@entry=0x7ffcbf3eaf50, fci_cache=<optimized out>, fci_cache@entry=0x7ffcbf3eaf30) at ./Zend/zend_execute_API.c:811
#19 0x00007fe73d861fa3 in zephir_call_user_func_array_noex (return_value=0x7ffcbf3ebb00, handler=<optimized out>, params=0x7ffcbf3eaff0) at ./build/php7/64bits/phalcon.zep.c:8855
#20 0x00007fe73d883fac in zephir_call_user_func_array (params=0x7ffcbf3eaff0, handler=0x7ffcbf3eb000, return_value=0x7ffcbf3ebb00) at ./build/php7/64bits/phalcon.zep.c:178613
#21 zim_Phalcon_Dispatcher_AbstractDispatcher_callActionMethod (execute_data=<optimized out>, return_value=0x7ffcbf3ebb00) at ./build/php7/64bits/phalcon.zep.c:47541
#22 0x0000562997d1d132 in zend_call_function (fci=fci@entry=0x7ffcbf3eb240, fci_cache=<optimized out>) at ./Zend/zend_execute_API.c:824
#23 0x00007fe73d86360b in zephir_call_user_function (object_pp=object_pp@entry=0x7fe7408132e0, obj_ce=obj_ce@entry=0x5629993d4700, type=type@entry=zephir_fcall_method, function_name=function_name@entry=0x7ffcbf3eb430,
    retval_ptr=retval_ptr@entry=0x7ffcbf3ebb00, cache_entry=cache_entry@entry=0x7ffcbf3eb5f8, cache_slot=<optimized out>, param_count=<optimized out>, params=0x7ffcbf3ebc60) at ./build/php7/64bits/phalcon.zep.c:8659
#24 0x00007fe73d863e6b in zephir_call_class_method_aparams (return_value=0x7ffcbf3ebb00, ce=0x5629993d4700, type=zephir_fcall_method, object=0x7fe7408132e0, method_name=0x7fe73dd15db2 "callactionmethod", method_len=<optimized out>,
    cache_entry=0x7ffcbf3eb5f8, cache_slot=0, param_count=3, params=0x7ffcbf3ebc60) at ./build/php7/64bits/phalcon.zep.c:8797
#25 0x00007fe73db1f006 in zim_Phalcon_Dispatcher_AbstractDispatcher_dispatch (execute_data=<optimized out>, return_value=0x7ffcbf3ec200) at /usr/include/php/20190902/Zend/zend_types.h:440
#26 0x0000562997d1d132 in zend_call_function (fci=fci@entry=0x7ffcbf3ebe60, fci_cache=<optimized out>) at ./Zend/zend_execute_API.c:824
#27 0x00007fe73d86360b in zephir_call_user_function (object_pp=object_pp@entry=0x7ffcbf3ec170, obj_ce=obj_ce@entry=0x5629993d4700, type=type@entry=zephir_fcall_method, function_name=function_name@entry=0x7ffcbf3ec050,
    retval_ptr=retval_ptr@entry=0x7ffcbf3ec200, cache_entry=cache_entry@entry=0x0, cache_slot=<optimized out>, param_count=<optimized out>, params=0x7ffcbf3ec4e0) at ./build/php7/64bits/phalcon.zep.c:8659
#28 0x00007fe73d863e6b in zephir_call_class_method_aparams (return_value=0x7ffcbf3ec200, ce=0x5629993d4700, type=zephir_fcall_method, object=0x7ffcbf3ec170, method_name=0x7fe73dd1522d "dispatch", method_len=<optimized out>,
    cache_entry=0x0, cache_slot=0, param_count=0, params=0x7ffcbf3ec4e0) at ./build/php7/64bits/phalcon.zep.c:8797
#29 0x00007fe73dc2f1a0 in zim_Phalcon_Mvc_Application_handle (execute_data=<optimized out>, return_value=0x7fe740813200) at /usr/include/php/20190902/Zend/zend_types.h:440
#30 0x0000562997db13fc in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at ./Zend/zend_vm_execute.h:1730
#31 execute_ex (ex=0x7fe740800040) at ./Zend/zend_vm_execute.h:53821
#32 0x0000562997db1f01 in zend_execute (op_array=0x7fe74087d2a0, return_value=<optimized out>) at ./Zend/zend_vm_execute.h:57913
#33 0x0000562997d2bb13 in zend_execute_scripts (type=type@entry=8, retval=0x7fe740813020, retval@entry=0x0, file_count=file_count@entry=3) at ./Zend/zend.c:1665
#34 0x0000562997ccb590 in php_execute_script (primary_file=<optimized out>) at ./main/main.c:2617
#35 0x0000562997db3fa6 in do_cli (argc=2, argv=0x5629990408b0) at ./sapi/cli/php_cli.c:961
#36 0x0000562997b938bf in main (argc=2, argv=0x5629990408b0) at ./sapi/cli/php_cli.c:1352

zBacktrace PHP 7.4

[0x7fe7408135b0] Phalcon\Mvc\Model->setSnapshotData(array(2)[0x7fe740813600], NULL) [internal function]
[0x7fe740813510] Phalcon\Mvc\Model->cloneResultMap(object[0x7fe740813560], array(2)[0x7fe740813570], NULL, 0, true) [internal function]
[0x7fe7408134c0] Phalcon\Mvc\Model\Resultset\Complex->current() [internal function]
[0x7ffcbf3eabb0] ???
[0x7fe740813390] Main\Controllers\SearchController->searchAction() /dev/app/controllers/SearchController.php:37
[0x7fe740813310] Phalcon\Dispatcher\AbstractDispatcher->callActionMethod(object[0x7fe740813360], "searchAction", array(0)[0x7fe740813380]) [internal function]
[0x7fe7408132c0] Phalcon\Dispatcher\AbstractDispatcher->dispatch() [internal function]
[0x7fe740813260] Phalcon\Mvc\Application->handle("/search") [internal function]
[0x7fe740813020] (main) /dev/public/index.php:43
@maxc62 maxc62 added bug A bug report status: unverified Unverified labels Jan 27, 2020
@ruudboon
Copy link
Member

Thnx for reporting @maxc62 We're going to investigate this.

@ruudboon ruudboon added the 4.0.4 label Feb 7, 2020
@ruudboon ruudboon self-assigned this Feb 8, 2020
@ruudboon ruudboon mentioned this issue Feb 8, 2020
5 tasks
@ruudboon
Copy link
Member

ruudboon commented Feb 8, 2020

@maxc62 Currently we're unable to reproduce your issue. Can you have a look at our test? Did we missed something? https://github.com/phalcon/cphalcon/pull/14825/files#diff-9dd2e736386869acb3d7b6c87d328974R62

@maxc62
Copy link
Author

maxc62 commented Feb 9, 2020

@ruudboon
In your method transform, exchange "customer" by "invoices"

    private function transform($resultset)
    {
        $customer  = $resultset->readAttribute(lcfirst(CustomersKeepSnapshots::class));
        $invoices = $resultset->readAttribute('join_1');
        $customer->invoices = $invoices;
        return $customer;
    }

@maxc62
Copy link
Author

maxc62 commented Feb 9, 2020

You need also to change "mvcModelQueryIssue14783"

        foreach ($resultsets as $resultset) {
            $model = $this->transform($resultset);
            $I->assertInstanceOf(CustomersKeepSnapshots::class, $model);
            $I->assertInstanceOf(InvoicesKeepSnapshots::class, $model->invoices);
        }

@ruudboon
Copy link
Member

ruudboon commented Feb 10, 2020

@maxc62 I adjusted the tests but wasn't able to reproduce it locally. Do you have the possibility to clone the repo, switch to 4.0.x (after pull merge) and run the tests on your system?

cp tests/ci/.env.default .env
cd tests 
docker-compose up -d
cd ..
php tests/_ci/generate-db-schemas.php
codecept build
vendor/bin/codecept run database --env mysql --verbose tests/database/Mvc/Model/QueryCest.php

If you have a local MySql server running you can also adjust the .env and skip the docker-compose part.

@maxc62
Copy link
Author

maxc62 commented Feb 10, 2020

I'm having a little trouble running your test.
With your data inserted, I modified to bypass all creation (db and data)
1

Finally I manage to test it, but this one seems to pass.
2

So I created a single page with all the code on it.
Before I create database with data (I use postgresql)

DROP TABLE IF EXISTS co_customers;
create table co_customers
(
    cst_id          serial not null primary key,
    cst_status_flag smallint,
    cst_name_last       varchar(100),
    cst_name_first       varchar(100)
);
 alter table co_customers owner to phalcon;

DROP TABLE IF EXISTS co_invoices;
create table co_invoices
(
    inv_id          serial not null constraint co_invoices_pk primary key,
    inv_cst_id      integer,
    inv_status_flag smallint,
    inv_title       varchar(100),
    inv_total       numeric(10, 2),
    inv_created_at  timestamp
);
 alter table co_invoices owner to phalcon;

INSERT INTO co_customers (cst_id, cst_status_flag, cst_name_last, cst_name_first) 
SELECT generate_series, 1, 'last' || generate_series::text, 'first' || generate_series::text  FROM generate_series(1, 50);

INSERT INTO co_invoices (inv_cst_id, inv_status_flag, inv_title) 
SELECT generate_series, 1, 'title' || generate_series::text  FROM generate_series(1, 50);

3
And
4

And the php code

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model;

$di = new FactoryDefault();

$di->set('db', function () {
	$adapter = new \Phalcon\Db\Adapter\Pdo\Postgresql([
		'host' => '127.0.0.1',
		'username' => 'phalcon',
		'password' => 'phalcon',
		'dbname' => 'phalcon',
		'schema' => 'public'
	]);
	return $adapter;
});


class CustomersKeepSnapshots extends Model
{
    public $cst_id;
    public $cst_status_flag;
    public $cst_name_last;
    public $cst_name_first;

    public function initialize()
    {
        $this->keepSnapshots(true);
        $this->setSource('co_customers');

        $this->belongsTo(
            'cst_id',
            'InvoicesKeepSnapshots',
            'inv_cst_id',
            [
                'alias'    => 'invoices'
            ]
        );
    }
}

class InvoicesKeepSnapshots extends Model
{
	public $inv_id;
	public $inv_cst_id;
	public $inv_status_flag;
	public $inv_title;
	public $inv_total;
	public $inv_created_at;

	private $secretValue;
	private $superSecret;

	public function initialize()
	{
			$this->setSource('co_invoices');
			$this->keepSnapshots(true);
	}
}

function transform($resultset)
{
	$customer = $resultset->readAttribute(lcfirst(CustomersKeepSnapshots::class));
	$invoices = $resultset->readAttribute('join_1');
	$customer->invoices = $invoices;

	return $customer;
}

$query = CustomersKeepSnapshots::query();
$query->columns(
	[
			CustomersKeepSnapshots::class . '.*',
			'join_1.*'
	]
);
$query->leftJoin(
		InvoicesKeepSnapshots::class,
		'join_1.inv_cst_id = ' . CustomersKeepSnapshots::class . '.cst_id',
		'join_1'
);
$query->limit(20, 0);//I have 50 rows in my db
$resultsets = $query->execute();

foreach ($resultsets as $resultset) {
	$model = transform($resultset);
}
echo 'OK';

5

It's strange,
With your test, I see only 2 assertions (Perhaps due to my modifications to be able to run the test)
But shouldn't we have 40?
20 row by 2 asserts.

I test this code with 2 servers with up to date php
Can you test the entire last code I gave you without any dependencies?

Thank you

ruudboon added a commit that referenced this issue Feb 10, 2020
Adjusted test according to feedback (#14783)
@ruudboon
Copy link
Member

@maxc62 Great this works! ....well... I'm able to reproduce a segfault ;) Thnx for the extended info etc. I will try to dive into this tomorrow.

@ruudboon ruudboon added status: medium Medium and removed status: unverified Unverified labels Feb 10, 2020
@Jeckerson Jeckerson added 4.0.5 and removed 4.0.4 labels Feb 15, 2020
@maxc62
Copy link
Author

maxc62 commented Mar 7, 2020

Hi,
I don't know if this helps, but by replacing the __set() method in the CustomersKeepSnapshots model by the equivalent of the zep in PHP, the problem no longer exists.

class CustomersKeepSnapshots extends Model
{
    public $cst_id;
    public $cst_status_flag;
    public $cst_name_last;
    public $cst_name_first;

    public function initialize()
    {
        $this->keepSnapshots(true);
        $this->setSource('co_customers');

        $this->belongsTo(
            'cst_id',
            'InvoicesKeepSnapshots',
            'inv_cst_id',
            [
                'alias'    => 'invoices'
            ]
        );
    }
	
    public function __set($property, $value)
    {
        /**
         * Values are probably relationships if they are objects
         */
        if (is_object($value) && $value instanceof ModelInterface) {
            $lowerProperty = strtolower($property);
            $modelName     = get_class($this);
            $manager       = $this->getModelsManager();
            $relation      = $manager->getRelationByAlias(
                $modelName,
                $lowerProperty
            );

            if (is_object($relation)) {
                $dirtyState = $this->dirtyState;

                if ($value->getDirtyState() != $dirtyState) {
                    $dirtyState = self::DIRTY_STATE_TRANSIENT;
                }

                unset($this->related[$lowerProperty]);

                $this->dirtyRelated[$lowerProperty] = $value;
                $this->dirtyState                  = $dirtyState;

                return $value;
            }
        }

        /**
         * Check if the value is an array
         */
        elseif (is_array($value)) {
            $lowerProperty = strtolower($property);
            $modelName = get_class($this);
            $manager   = $this->getModelsManager();
            $relation  = $manager->getRelationByAlias(
				$modelName,
				$lowerProperty
			);

            if (is_object($relation)) {
                switch ($relation->getType()) {
                    case Relation::BELONGS_TO:
                    case Relation::HAS_ONE:
                        /**
                         * Load referenced model from local cache if its possible
                         */
                        $referencedModel = $manager->load(
                            $relation->getReferencedModel()
                        );

                        if (is_object($referencedModel)) {
                            $referencedModel->assign($value);

                            unset($this->related[$lowerProperty]);

                            $this->dirtyRelated[$lowerProperty] = $referencedModel;
                            $this->dirtyState = self::DIRTY_STATE_TRANSIENT;

                            return $value;
                        }

                        break;

                    case Relation::HAS_MANY:
                    case Relation::HAS_MANY_THROUGH:
                        $related = [];

                        foreach($value as $item) {
                            if (is_object($item)) {
                                if ($item instanceof ModelInterface) {
                                    $related[] = $item;
                                }
                            }
                        }

                        if (count($related) > 0) {
                            unset($this->related[$lowerProperty]);

                            $this->dirtyRelated[$lowerProperty] = $related;
                            $this->dirtyState = self::DIRTY_STATE_TRANSIENT;

                            return $value;
                        }

                        break;
                }
            }
        }

        // Use possible setter.
        if ($this->_possibleSetter($property, $value)) {
            return $value;
        }

        /**
         * Throw an exception if there is an attempt to set a non-public
         * property.
         */
        if (property_exists($this, $property)) {
            $manager = $this->getModelsManager();

            if (!$manager->isVisibleModelProperty($this, $property)) {
                throw new Exception(
                    "Cannot access property '" . $property . "' (not public)."
                );
            }
        }

        $this->{$property} = $value;

        return $value;
    }
}

@maxc62
Copy link
Author

maxc62 commented Mar 7, 2020

@ruudboon
The problem comes when "unset" calls.

In method "__set", I added a test before deleting a variable that didn't exist.

if typeof relation == "object" {
	let dirtyState = this->dirtyState;

	if (value->getDirtyState() != dirtyState) {
		let dirtyState = self::DIRTY_STATE_TRANSIENT;
	}
	if isset this->related[lowerProperty] {
		unset this->related[lowerProperty];
	}

	let this->dirtyRelated[lowerProperty] = value,
		this->dirtyState                  = dirtyState;

	return value;
}

With this modification the problem no longer exists

I've checked all the unset calls in phalcon and there's very little use of unset with an "unset($obj->var)".

In Acl/Adapter/Memory.zep, method "dropComponentAccess": already test, before unset

if isset this->accessList[accessKey] {
	unset this->accessList[accessKey];
}

In Events/Manager.zep, method "detachAll": already test, before unset

if isset this->events[type] {
	unset this->events[type];
}

In Filter.zep, method "set": maybe the property services are assigned beforehand?

public function set(string! name, callable service) -> void
{
	let this->mapper[name] = service;

	unset this->services[name];
}

In Events/Form.zep, method "remove": already test, before unset

if isset this->elements[name] {
	unset this->elements[name];

	return true;
}

In Factory/AbstractFactory.zep method "init": maybe the property services are assigned beforehand?

protected function init(array! services = []) -> void
{
	var adapters, name, service;

	let adapters = this->getAdapters(),
		adapters = array_merge(adapters, services);

	for name, service in adapters {
		let this->mapper[name] = service;
		unset(this->services[name]);
	}
}

And in our case Mvc/Model.zep, method "__set":

if typeof relation == "object" {
	let dirtyState = this->dirtyState;

	if (value->getDirtyState() != dirtyState) {
		let dirtyState = self::DIRTY_STATE_TRANSIENT;
	}

	unset this->related[lowerProperty];

	let this->dirtyRelated[lowerProperty] = value,
		this->dirtyState                  = dirtyState;

	return value;
}

And with an array, I tried this case by modifying our test by

$customer->invoices = [$invoices];

I got the same segmentation error. With the same patch, no problem (be careful twice here)

case Relation::HAS_ONE:
	/**
	 * Load referenced model from local cache if its possible
	 */
	 let referencedModel = manager->load(
		relation->getReferencedModel()
	);

	if typeof referencedModel == "object" {
		referencedModel->assign(value);

		unset this->related[lowerProperty];

		let this->dirtyRelated[lowerProperty] = referencedModel,
			this->dirtyState = self::DIRTY_STATE_TRANSIENT;

		return value;
	}

	break;

case Relation::HAS_MANY:
case Relation::HAS_MANY_THROUGH:
	let related = [];

	for item in value {
		if typeof item == "object" {
			if item instanceof ModelInterface {
				let related[] = item;
			}
		}
	}

	if count(related) > 0 {
		unset this->related[lowerProperty];

		let this->dirtyRelated[lowerProperty] = related,
			this->dirtyState = self::DIRTY_STATE_TRANSIENT;

		return value;
	}

	break;

I had also tried with this test:

$customer->invoices;
$customer->invoices = [$invoices];

And this case worked, so the problem does come from unset()

I think there's an error in zephir using "unset($obj->var);"

I will try to see if it is possible to get the problem in the "set" method of Filter.zep and "init" of Factory/AbstractFactory.zep
But I really recommend adding the isset condition to avoid any problems.

@niden niden added 4.0.6 and removed 4.0.5 labels Mar 8, 2020
@niden niden added 4.1.0 and removed 4.0.6 labels May 18, 2020
@Jeckerson Jeckerson added 4.1.1 The issues we want to solve in the 4.1.1 release and removed 4.1.0 labels Dec 14, 2020
@Jeckerson Jeckerson added 5.0 The issues we want to solve in the 5.0 release and removed 4.1.1 The issues we want to solve in the 4.1.1 release labels Mar 26, 2021
@Jeckerson Jeckerson self-assigned this Mar 26, 2021
@Jeckerson
Copy link
Member

This was fixed in 0.13.x Zephir. Closing.

@Jeckerson Jeckerson added the external dependency This issue depends on external issue to be resolved. label May 15, 2021
@niden niden moved this to Released in Phalcon v5 Aug 25, 2022
@niden niden added this to Phalcon v5 Aug 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
5.0 The issues we want to solve in the 5.0 release bug A bug report external dependency This issue depends on external issue to be resolved. status: medium Medium
Projects
Archived in project
Development

No branches or pull requests

4 participants