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

DependencyChecker: added callback support #173

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

DependencyChecker: added callback support #173

wants to merge 1 commit into from

Conversation

ghost
Copy link

@ghost ghost commented Aug 8, 2018

This PR adds possibility to invalidate the DI container using given callback.

  • feature
  • not aware of any BC break (but causes an error with cached container on package update)

Currently, you can make DependencyChecker track specific files (and classes or methods). Using callback, we could invalidate the container, for instance:

  • when there is a new file (like a presenter with routing annotation - this is actually real use case),
  • when something of environment changes.

I'm not sure about proper implementation - open to discussion. What concerns me is isExpired() method; it used to call calculateHash() only when a file was changed, but now it gets called always when there is a callback as well (which causes tracked files to be loaded and inspected unnecessarily). An optimization would require a separate hash for callbacks.

EDIT: This actually probably breaks when nette/di updates and there's a cached container regardless of the VERSION advance. Could be fixed using optional parameters in isExpired() signature, if that's an issue.

Usage

Dependency is supposed to be defined like this:

Array(string & callable $callback, mixed ...$arg)

First argument must be a callable and string, which limits the callback to a function or a static method. Remaining arguments are stored within the callback and are always passed to it (I can explain what's that good for if it's not obvious).

$compiler->addDependencies([

    // track for PHP version change
    ['phpversion'],

    // track for an extension
    ['extension_loaded', 'sockets'],

    // stupid simple file watcher
    ['glob', 'src/*/*Presenter.php'],

    // custom callback
    [[MyClass:class, 'getInstallationHash'], 123],

]);

@milo
Copy link
Member

milo commented Aug 9, 2018

I would consider sytanx for static method call as [MyClass::class, 'method']. It is easier to maintain in IDE. Sure, uglier version [MyClass::class . '::method'] would work ;)

@ghost
Copy link
Author

ghost commented Aug 9, 2018

Yup, here's what I have been thinking:

  • the array syntax is mostly used with an object as a first argument - we'd have to check and reject such callback,
  • if we use the array syntax, how arguments will be passed (can't think of an intuitive way)?

I don't really mind anyway, if you want, I'll edit the PR.

@milo
Copy link
Member

milo commented Aug 9, 2018

Probably [[MyClass::class, 'method'], 'arg', 'arg'] but save your time and don't edit it now. It's just note.

@JanTvrdik
Copy link
Contributor

(Also note that support for MyClass::class . '::method' MAY be removed in PHP 8)

@ghost
Copy link
Author

ghost commented Aug 9, 2018

I've allowed the callback to be specified using array syntax.

Something about performace - would it be OK to use part of hash for files and other part for callbacks? I think there's no need for entirely separated hash.

@dg
Copy link
Member

dg commented Aug 9, 2018

I think Callback::isStatic() may help https://api.nette.org/2.4/Nette.Utils.Callback.html

@dg
Copy link
Member

dg commented Aug 9, 2018

Something about performace - would it be OK to use part of hash for files and other part for callbacks? I think there's no need for entirely separated hash.

It must be separated. Hashes for callbacks will be generated for each request and it should not influence generating hashes for files.

@ghost
Copy link
Author

ghost commented Aug 9, 2018

Updates:

  • files and callbacks are checked individually using single concatenated hash,
  • no failure on package update with cached container (but I don't like the solution and considering 3.0 is not out yet, we could just leave it crash?),
  • using Nette\Utils\Callback::isStatic().

$files = @array_map('filemtime', array_combine($files, $files)); // @ - file may not exist
$phpFiles = @array_map('filemtime', array_combine($phpFiles, $phpFiles)); // @ - file may not exist
return [self::VERSION, $files, $phpFiles, $classes, $functions, $hash];
return [self::VERSION, $files, $phpFiles, $classes, $functions, $callbacks, $hash];
Copy link
Member

Choose a reason for hiding this comment

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

Maybe simpler solution will be better, ie. $reflectionHash = self::calculateReflectionHash(); $callbackHash = self::calculateCallbackHash(); and then return [self::VERSION, $files, $phpFiles, $classes, $functions, $callbacks, $reflectionHash, $callbackHash], what do you mean?

Copy link
Author

@ghost ghost Aug 9, 2018

Choose a reason for hiding this comment

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

Yes, that was exactly the original solution (linked above). I'll split them, if you wish. How about compatibility with cached container, should I drop it or keep it?

Copy link
Member

Choose a reason for hiding this comment

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

Compatibility is not needed, ie. it is enough to have compatible isExpired declaration:

public static function isExpired(int $version, array $files, array &$phpFiles, array $classes, array $functions, string $reflectionHash, array $callbacks = [], string $callbackHash = '')

@ghost
Copy link
Author

ghost commented Aug 9, 2018

Now I've been thinking about this - in practice, it'd be useful to be able alter cached data without rebuilding the container. For example, if a class file changes, but there's no interesting annotation in it, I don't want to discard the container, but next time, I would have to load and inspect the file again.

Not sure if done properly, but here's a patch which excludes parameters from hashing and when they change (using reference), they'll get stored without rebuilding, much like class files do.

@dg
Copy link
Member

dg commented Aug 10, 2018

That makes sense, but it must be done a little differently. I'll try write more later.

@dg dg force-pushed the master branch 3 times, most recently from 8224b32 to d51ca9e Compare September 16, 2018 21:16
@dg dg force-pushed the master branch 3 times, most recently from 1fc7645 to 485a875 Compare October 3, 2018 16:24
@dg dg added this to the v3.0 milestone Oct 3, 2018
@dg dg force-pushed the master branch 11 times, most recently from 41deac3 to 191506d Compare October 10, 2018 11:08
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.

None yet

4 participants