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

Ideas/considerations for GhostObject Proxies lazy loading #159

Closed
Ocramius opened this issue Mar 29, 2014 · 5 comments
Closed

Ideas/considerations for GhostObject Proxies lazy loading #159

Ocramius opened this issue Mar 29, 2014 · 5 comments
Assignees
Milestone

Comments

@Ocramius
Copy link
Owner

See #152

A couple of considerations to be applied:

  • the proxy does not need to re-implement any public API of the object at all

  • the only methods to be implemented are __get, __set, __isset and __unset. Maybe also __sleep and __wakeup, but only to clear/reset initializers

  • public, protected and private properties have to be stored separately in order to be able to allow or deny access from unprivileged scope. Here's an example data-structure to make it more clear (may not be the best solution):

    // randomized to prevent access to users that want to hack around proxies
    private static $propertyScopes3407230983409 = [
        'foo' => [
            'declaringClass' => 'Name\Of\The\DeclaringClass',
            'visibility' => 'private',
            'default' => 'defaultValueHere',
        ],
        'bar' => [
            'declaringClass' => 'Name\Of\The\Parent\DeclaringClass',
            'visibility' => 'protected',
            'default' => 'defaultValueHere',
        ],
        'baz' => [
            'declaringClass' => 'Name\Of\The\DeclaringClass',
            'visibility' => 'public',
            'default' => 'defaultValueHere',
        ],
    ]
  • initialization infinite recursion has to be prevented through a flag

  • method declaration class has to be cached in order to be able to discover scope and prevent access to private/protected properties when not allowed. Here's an example of how the data-structure may look like in order to minimize lookup complexity. Array structure may also be exploded into direct properties for further performance optimizations:

    private static $methodScopes12321323132 = [
        // indexed by class name
        'classes' => [
            'DeclaringClass' => [
                'someMethod' => 'public',
                'otherMethod' => 'protected',
                'anotherMethod' => 'private',
            ],
            // ...
        ],
        // indexed by method name
        'methods' => [
            'someMethod' => [
                'declaringClass' => 'DeclaringClass',
                'visibility' => 'public',
            ],
            'otherMethod' => [
                'declaringClass' => 'DeclaringClass',
                'visibility' => 'protected',
            ],
            'anotherMethod' => [
                'declaringClass' => 'DeclaringClass',
                'visibility' => 'private',
            ],
        ],
    ]
  • property access attempts via magic methods should be checked against the method map and the local accessors (reflection properties or closures). The check is to be applied to any protected/private properties, with additional checks for private properties. debug_backtrace() gives us the scope where the access was attempted.

  • checks may be avoided when a particular internal flag is set to false, so that lazy loading won't affect performance because of the number of checks

  • some properties may be skipped when generating the proxy: this allows for performance optimizations, such as avoiding lazy-loading for properties that are always eagerly loaded.

  • partial lazy loading should be possible. In order to do that, the initializer should just unset the properties that have to be initialized again, and it should provide a new initializer for those.

  • the constructor will be left un-touched, and instead, a static instantiatior will be used. The static instantiator will be responsible for calling ReflectionClass#newInstanceWithoutConstructor() as well as initialize the newly created proxy by un-setting its lazy-loaded properties and setting a given initializer. Reflectors as well as accessor closures will be cached statically.

@Ocramius Ocramius self-assigned this Mar 29, 2014
@Ocramius Ocramius added this to the 1.0.0 milestone Mar 29, 2014
@Ocramius Ocramius modified the milestones: 1.0.0, 2.0.0 Sep 16, 2014
@Ocramius
Copy link
Owner Author

Ocramius commented Oct 9, 2014

Provided a POC implementation at http://3v4l.org/aB4ll

Ping @malukenho if you want to check this, but no requirements/deadlines, as it is a VERY HARD issue.

@Pittiplatsch
Copy link

Very nice approach of intercepting actual access to properties instead of overwriting (almost) every method with all its labor and shortcomings:+1:
However, you explicitly stated not to touch the constructor, but in your POC you do need the constructor to start the LL magic. Granted, I don't have a better idea, but the caveats about using the constructor for internal LL functionality are well-known ;-) (#115)

@Ocramius
Copy link
Owner Author

Note that I have a WIP branch for the constructor issue at #175

@Pittiplatsch
Copy link

Pwned ;-) So you essentially move all initialization logic from "real" to static "constructor", which is auto-generated, right? However, I still don't completely understand which mechanism triggers this static constructor with its initialization logic (i.e. unsetting properties) when new ClassWithCounterConstructor(10) is called.
Will have to check out your branch in a spare minute...

@Ocramius
Copy link
Owner Author

Handled in #192, yay!

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

No branches or pull requests

2 participants