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

Fixed property visibility #2078

Merged
merged 28 commits into from
Apr 23, 2020
Merged

Fixed property visibility #2078

merged 28 commits into from
Apr 23, 2020

Conversation

sergeyklay
Copy link
Contributor

@sergeyklay sergeyklay commented Apr 16, 2020

Hello!

In raising this pull request, I confirm the following:

  • I have checked that another pull request for this purpose does not exist
  • I wrote some tests for this PR
  • I updated the CHANGELOG

Small description of change:

Zephir ignored property visibility and has not thrown error when setting private/protected properties in scope that shouldn't intended for it.

Thanks

We used the same function to change properties of an object:

  1. let object->property = expression
  2. class A { private object = []; }

And to be able to change object's property we had to use object's scope.
Thus, we have NEVER checked the scope in which we are actually located:

  // Global Scope

  class Bar {
      // Bar's Scope
      private prop;
  }

  class Foo {
      // Foo's Scope

      public function test (Bar bar) {
          // Here we replaced the scope by Bar's one
          let bar->prop = 42;
      }
  }

Fixes #2057

/cc @niden @Jurigag
@codecov
Copy link

codecov bot commented Apr 16, 2020

Codecov Report

Merging #2078 into development will increase coverage by 0.00%.
The diff coverage is 0.00%.

@@              Coverage Diff               @@
##             development    #2078   +/-   ##
==============================================
  Coverage          35.97%   35.98%           
- Complexity          8306     8307    +1     
==============================================
  Files                565      566    +1     
  Lines              46122    46219   +97     
==============================================
+ Hits               16593    16632   +39     
- Misses             29529    29587   +58     

@sergeyklay
Copy link
Contributor Author

@dreamsxin There are 4 failed and 14 passed tests. It seems I need help here. Could you please take a look what wrong with zephir_update_property_zval. For skipped tests see unit-tests/Extension/Oo/Scopes/PrivateScopeTest.php.

@sergeyklay
Copy link
Contributor Author

@dreamsxin Do you have any idea?

@dreamsxin
Copy link
Contributor

@sergeyklay Fatal error: Class 'TestScopePhp' not found

@sergeyklay
Copy link
Contributor Author

@dreamsxin Try composer dump-autoload from the project root

@dreamsxin
Copy link
Contributor

@sergeyklay
Commented code

	/* Use caller's scope */
	//zephir_set_scope(ce);

	/* Restore original scope */
	//zephir_set_scope(scope);

Will throw

Fatal error: Uncaught Error: Cannot access private property TestScopeExtending::$privateProperty in /home/myleft/work/test.php:27
Stack trace:
#0 /home/myleft/work/test.php(27): Test\Oo\Scopes\AbstractClass->setProperty('privateProperty', 'test')
#1 {main}
  thrown in /home/myleft/work/test.php on line 27

You want to throw an exception with read

@dreamsxin
Copy link
Contributor

dreamsxin commented Apr 20, 2020

Maybe can change
int zephir_update_property_zval(zval *object, const char *property_name, unsigned int property_length, zval *value) to
int zephir_update_property_zval(zval *object, zend_class_entry current_ce, const char *property_name, unsigned int property_length, zval *value)

Like:

PHP_METHOD(Test_Oo_Scopes_AbstractClass, setProperty) {

	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zval *name_param = NULL, *value, value_sub;
	zval name;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&name);
	ZVAL_UNDEF(&value_sub);

	ZEPHIR_MM_GROW();
	zephir_fetch_params(1, 2, 0, &name_param, &value);

	zephir_get_strval(&name, name_param);


	zephir_update_property_zval_zval(this_ptr, test_oo_scopes_abstractclass_ce, &name, value);
	RETURN_THIS();

}

@sergeyklay
Copy link
Contributor Author

@dreamsxin Yes, commenting does the trick. But if you run test without scope-related code you will see some failed tests. Do you suggest to modify zephir_update_property_zval_zval and to change scope inside zephir_update_property_zval_zval ?

@dreamsxin
Copy link
Contributor

dreamsxin commented Apr 21, 2020

If you want the scope of the variable to work, have to modify it.
Changes may affect some of the previous code and need to be fully tested.
I don't recommend any changes.

Zephir's scope doesn't have to be exactly the same as PHP's

@dreamsxin
Copy link
Contributor

@sergeyklay Test in 7.4

        $tester = new Test\Oo\Scopes\PrivateScopeTester();
        $tester->setPropertyObj(new TestScopePhp(), 'privateProperty', 'test');

Fatal error: Uncaught Error: Cannot access private property TestScopePhp::$privateProperty in /home/myleft/work/test.php:27

@sergeyklay
Copy link
Contributor Author

@dreamsxin Take a look at 3bbdc7a...b534cf9 As you can see tests for PHP 7.4 failed.

@sergeyklay
Copy link
Contributor Author

@dreamsxin Removing scope does not help :-/ Any idea?

@dreamsxin
Copy link
Contributor

dreamsxin commented Apr 23, 2020

@sergeyklay I test in local

<?php

class UserExample extends Test\Oo\PropertyAccess
{
}

$example = new UserExample();
$example->setPrivateVariable('test');

var_dump($example->getPrivateVariable());

class TestScopeExtending extends Test\Oo\Scopes\AbstractClass
{
    private $privateProperty = 'private';
    protected $protectedProperty = 'protected';
    public $publicProperty = 'public';

    /**
     * @return string
     */
    public function getProtectedProperty(): string
    {
        return $this->protectedProperty;
    }
}

$obj = new TestScopeExtending();
$obj->setProperty('privateProperty2', 'test');

var_dump($obj->getPrivateProperty2());


class TestScopeExtendingMagic extends Test\Oo\Scopes\AbstractClassMagic
{
    private $privateProperty2 = 'private';

    public function getProtectedProperty(): string
    {
        return $this->protectedProperty;
    }
}

$tester = new Test\Oo\Scopes\PrivateScopeTester();
$object = new TestScopeExtendingMagic();

$actual = $tester->setPropertyObj($object, 'privateProperty', 'test');
var_dump($actual);
var_dump($object->setCount);

$tester = new Test\Oo\Scopes\PrivateScopeTester();
$obj = $tester->setPropertyNew(TestScopeExtendingMagic::class, 'privateProperty', 'test');

var_dump($obj->privateProperty);
var_dump($obj->setCount);
string(4) "test"
string(4) "test"
string(4) "test"
int(1)
string(4) "test"
int(1)

See #2079

@sergeyklay
Copy link
Contributor Author

@dreamsxin Aha! It seems we don't need to change scope for zephir_read_property too. Good catch. Thank you!

@sergeyklay
Copy link
Contributor Author

sergeyklay commented Apr 23, 2020

@dreamsxin Removing scope-related code from zephir_update_property_zval as well as zephir_read_property works only for PHP 7.4. I've added new test (see 8da899b).

This related the way PHP < 7.4 handles object's properties when there
is a magic __set method present.

Actually we DO NOT change property here (fixed).  Only PHP 7.4 throws a
Fatal Error. All previous versions just out a Notice and continue execution.

/cc @nide @Jurigag @dreamsxin
@sergeyklay sergeyklay marked this pull request as ready for review April 23, 2020 18:33
Copy link
Contributor

@ruudboon ruudboon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job!

@sergeyklay sergeyklay mentioned this pull request Apr 23, 2020
@sergeyklay sergeyklay changed the title [WIP] Fixed property visibility Fixed property visibility Apr 23, 2020
@sergeyklay sergeyklay merged commit c770f3c into development Apr 23, 2020
@sergeyklay sergeyklay deleted the fix/2057 branch April 23, 2020 19:57
@sergeyklay
Copy link
Contributor Author

image

@dreamsxin
Copy link
Contributor

@sergeyklay PHP < 7.4 Can't be modified, but no exception is thrown

class TestScopeExtendingMagic
{
    private $privateProperty2 = 'private';

    public function getProtectedProperty(): string
    {
        return $this->protectedProperty;
    }

    public function getPrivateProperty2()
    {
        return $this->privateProperty2;
    }
}

class PrivateScopeTester 
{
    public function setPropertyObj($obj, $property, $value)
    {
        $obj->{$property} = $value;

        return $obj->{$property};
    }
}

$tester = new PrivateScopeTester();
$object = new TestScopeExtendingMagic();


$tester->setPropertyObj($object, 'privateProperty2', 'CHANGED');
var_dump($object->getPrivateProperty2());

@sergeyklay
Copy link
Contributor Author

@dreamsxin Yes, see my comments here f41d57f

Question: Can we modify this behavior so that Zephir will throw E_NOTICE for PHP 7.0 - 7.3 ?

@Jurigag
Copy link
Contributor

Jurigag commented May 5, 2020

Wow great job guys that you fixed this finally. It was really keeping me from updating to 4.0 and suggest it anywhere.

@sergeyklay
Copy link
Contributor Author

@dreamsxin

class PrivateScopeTester
{
    public function setPropertyObj($obj, string $property, $value)
    {
        $obj->{$property} = $value;
    }
}

class TestScopePhpMagic
{
    public function __set($name, $value)
    {
        $this->$name = $value;
    }
}

class TestScopePhpMagicExtending extends TestScopePhpMagic
{
    private $privateProperty2 = 'private2';
}

$obj = new TestScopePhpMagicExtending();
var_dump($obj);

$tester = new PrivateScopeTester();
$tester->setPropertyObj($obj, 'privateProperty2', 'CHANGED');
var_dump($obj);
object(TestScopePhpMagicExtending)#1 (5) {
  ["privateProperty2":"TestScopePhpMagicExtending":private]=>
  string(8) "private2"
}
object(TestScopePhpMagicExtending)#1 (1) {
  ["privateProperty2":"TestScopePhpMagicExtending":private]=>
  string(8) "private2"
}

PHP 7.0.33

Property is not changed. But there is no notice/error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants