Basically, I want to see if the two objects have matching properties (without hardcoding the properties themselves into the algorithm) and do some actions.
Yes, this helps. The assumption-based answer I started on wouldn't have sufficed. But KISS - keep it stupid simple - still applies here, IMO. Keep it short and to the point, no need to force it into a design pattern if it doesn't naturally fit.
Lets use a special language feature to our advantage. PHP allows you to call dynamic access to attributes, with names stored as strings in variables. It's often frowned upon in PHP, but is also sometimes the simplest solution. For example, in this pseudocode:
$attribs = array(
array('User' => 'reads_articles', 'Content' => 'is_article'),
array('User' => 'reads_books', 'Content' => 'is_book'),
);
class User {
function can_access(Content) {
foreach ($attribs as $reqs) {
if (!property_exists(self, $reqs['User'])) {
throw Exception("User is missing {$reqs['User']}");
}
if (!property_exists(Content, $reqs['Content'])) {
throw Exception("Content is missing {$reqs['Content']}");
}
if (self->{$reqs['User']} && Content->{$reqs['Content']}) {
return true;
}
}
return false;
}
reads_articles = true;
reads_books = true;
}
(Warning: Untested, and I know I have some of the syntax wrong (such as not actually naming the Content argument, and I doubt the scope is right).)
There's 3 possible sources for $attribs
that I can think of. Here's some further thoughts on each:
- Hardcoded in the source, as above
- Advantages: It's right there. For maintenance, very easy to see where it's coming from. Even in another file, you don't have to mentally switch context to get it.
- Disadvantages: It's static. All the
User
and Content
classes have to define every single one, unless can_access
is modified to accept that "missing" means "false".
- Loaded from a database table
- Advantages: Simply put, it's a lot more flexible and easier to extend in the future since the interface to use the database is already there.
- Disadvantages: Reading the code, you now have to also look at the database to figure out which attributes will be used together. It's also easier to overlook when adding a new one.
- Inferred through reflection, examining the class(es)
- Advantages: New attributes are automatically included, and the "rules" for determining which ones go together are pretty straightforward.
- Disadvantages: The rules for pairing attributes could just be skin-deep (
is_letter
/ sends_letters
does not fit, for example), and there's a chance they can match something they're not supposed to anyway. "Meta" programming like this is also less obvious than the other two, since it tends to be used less often, so it should be documented pretty well.
The disadvantages to using a database are so small that that's the way I'd go with it, unless I was very sure this set of attributes was unlikely to change or the project doesn't already have a database - then I might go with the first option, hardcoding somewhere. At least with it already in a semi-tabulated form like that, it would be simple to convert to a database table if needed.
Inferring it through pattern matching has to be really well-defined, so I'd avoid that as much as possible. It IMO falls into the "neat trick" category that's rarely more useful than other methods, do to the possibility of false positives and likely needing extra documentation.
Now, there is a halfway option that I think should be used instead of attributes directly on the class:
class User {
$permissions = array(
'reads_articles' => true,
'reads_books' => true,
);
}
This way, there's no overlap with the normal User attributes, and it's obvious which ones are important. The caveats about how to pair these with the ones on Content still applies, so one of the former 3 methods would still be necessary. However, from the question, I'm not sure if this would work for what you have in mind.
(Side note, since it may not be obvious - I'm trying to keep this slightly generic, since I'm not sure if the permissions you speak of are at the class level or instance level. Almost all of the above can be done either way.)
best solution to approach this
to do what? Are you trying to dump everything the User can see? Are you trying to check if a User has access to a given Content? Both? Something else? How many types of Content are there? Can they be added/removed, or is this set in the code?