-
Notifications
You must be signed in to change notification settings - Fork 174
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
Split off metadata #21
Conversation
This will make it a lot easier to add additional information to the metadata for a package. All you need to do is extend the Metadata class and overload the $cacheTime $headerMap, $readmeMap and/or constructor to add additional data. You will of course also need to overload the Package::fromArchive() method to use your extended class rather than the original, but as it's now a small method, it doesn't break the upgrade path as much as before.
… an extended constructor
… split-off-metadata Conflicts: includes/Wpup/Package.php
@YahnisElsts Just wondering whether you'd had a chance to have a look at this one yet. Hope all is well ;-) |
Thanks for the reminder!
That's a worthy goal, but I'm not convinced that introducing another class is the best way to achieve it. Some thoughts:
I would propose something like this instead:
What do you think? |
Hi @YahnisElsts, Sorry for my slow reply, I was away at a conference. I had to dig back into my memory why I choose this implementation. From looking at the code, I believe I choose this implementation for three reasons:
The main thing is that the Package object in this implementation stays the same as before (methods, properties), just how it retrieves the metadata changes.
True, forgot to mention that. We're overloading that already as we do version negotiation there ;-)
True, but the methods would still be static, i.e. the properties would also all need to be static for the static method to have access to them and then you'd get in quite a complicated mess of static vs self in accessing those properties. The other alternative would of course be to make the relevant methods non-static within the What do you think ? Shall I code out the alternative and send it in as a separate PR for review ? |
@YahnisElsts So... what do you think ? |
Huh, I could swear I replied to this thread a week ago. Fortunately, I still remember most of what I had (or thought I had) written.
There is that. I guess this particular case just doesn't hit my personal threshold for "needs better separation of concern".
The alternative I mentioned would provide a similar level of backwards compatibility. The
It seems I'm missing something here. What is the problem with overloading static methods? The only scenario that I can think of where it would complicate things is if you were doing some kind of dependency injection, and even then you could probably inject a class instead of an instance. As for static vs self and making a mess, the current implementation uses
Sure. |
Sorry about not doing anything with this, but I keep struggling with what you propose. The choice for sticking with static methods and even adding static properties in this case is IMHO just wrong. The split into two classes is justified as - as documented in the original class - metadata can be retrieved from other sources (db/config file). The implementation I propose(d) in this PR makes it simple to either swap out the Your suggested implementation on the other hand would make it more complicated rather than simpler. Sorry, but I stand by my original PR. |
Nitpick: Still, what's the problem with static methods and static properties? Is it a style issue? Why is it better to use instance methods in this case? |
Static methods and properties have their place in programming, just not in this situation. Here are some more articles which argue in a reasonably balanced manner when to use static and when not (even though the titles are misleading): |
Note: This ended up being pretty long. Regarding static methods Thanks for the links. Those articles make some good points. It looks like the first blogpost says that the problem with static methods is shared global state and side effects. The author mentions that static methods are appropriate for implementing stateless and/or purely functional algorithms. I can agree with that. Personally, I'd even go as far as to argue that any stateless task should be implemented statically because it makes code easier to read. Given a well-written static method, you can determine its inputs and outputs just by looking at the method signature. You don't have to worry that its behaviour might change in some unexpected way based on some hidden variable. For an instance method, you need to actually read the entire function body to see how it interacts with instance properties. Both "Static Methods Will Shock You" and "static considered harmful" mention another case where using static methods is valid: factory methods. I can agree with that, too. This is one of the reasons why "static considered harmful" also spends a lot of time explaining how static methods introduce dependencies that are hard to change without changing the original source code. It suggests dependency injection as a better alternative. I'd say this is generally a good idea, though one should be careful to avoid overengineering. Not every project needs an Then there's the testing/mocking argument, which is basically a special case of "dependency injection is better". How that applies to metadata I think my main cause for disagreement on this PR could be summarized like this: extracting ZIP metadata is
On the other hand, it's true that the above points might not apply to other implementations (loading metadata from the database, etc). And maybe some users that need to make complex changes to the metadata would benefit from a non-static implementation. All right, lets make a separate metadata class. Practical suggestions Should Ideas/options:
Regarding the current implementation:
|
@YahnisElsts Thank you for taking the time and your detailed feedback.
In which case that custom implementation is now easier to call what with the
Done.
I considered it, but as you say it would be extremely bare, so I left that out for now until there turns out to be a call for it. Would be easy enough to implement anyway.
Artefact from the original static implementation. Fixed.
As it's now a class and the data gets send to the I've also abstracted out the code a bit more. The original implementation stayed very close to the original methods. I've now refactored it to be more like a proper class ;-) Hope you approve of the changes. |
… split-off-metadata Conflicts: includes/Wpup/Package.php
Some thoughts: Since this is now a class and not a public method, there's no need for extractMetadata() to report an error by setting On a related note, I disagree with the idea of splitting the cache-related stuff into Also, in my experience, this is a fairly common pattern when working with cache:
These are all closely related operations. Why split off the last two? Another thing I noticed is that
Finally, a minor style issue: in |
f199462
to
153a3ec
Compare
Thanks for your feedback again. I agree with most of your points and probably would have done those things already if I wasn't working on ten things at the same time ;-)
I've looked at combining that into the new method as well, but with there being three differences between how things would need to be handled, it would actually result in a lot more code, rather than less.
Hope we got everything now. |
* @param array $map The key mapping for that sub-array where the key is the key as used in the | ||
* input array and the value is the key to use for the output array | ||
*/ | ||
protected function setMappedFields( $input, $map ){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No spaces around method parameters, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, sorry, too used to using the WP coding standards.
In theory, you could make the mapping function handle the differences by making it more general and extracting filtering/key mapping logic to a separate method or closure: protected function mapFields($input, $map, $transform){
$output = array();
foreach($input as $fieldKey => $value) {
$metaKey = call_user_func($transform, $fieldKey, $map);
if ( $metaKey !== null ) {
$output[$metaKey] = $value;
}
}
return $output;
}
/* This is the default $transform */
protected function getMappedKey($fieldKey, $map) {
return isset($map[$fieldKey]) ? $map[$fieldKey] : null;
}
/*...*/
$this->metadata['sections'] = $this->mapFields(
$this->packageInfo['readme']['sections'],
array(),
function($sectionName, $dummy) {
return str_replace(' ', '_', strtolower($sectionName));
}
); ... but it is indeed more code. |
@YahnisElsts So are we good to go now ? |
Split off metadata parsing as a separate class
Thank you! |
As - unlike with the
Headers
class -, the static methods in the Package class can not easily be overloaded, I'd suggest splitting them off into a separateMetadata
class which can be overloaded.This will make it a lot easier to add additional information to the metadata for a package.
All you need to do is extend the Metadata class and overload the $cacheTime $headerMap, $readmeMap and/or constructor to add additional data.
You will of course also need to overload the Package::fromArchive() method to use your extended class rather than the original, but as it's now a small method, it doesn't break the upgrade path as much as before.
Simple example code for how this could now be overloaded: