Support for the PHP syntax in patterns! Patch. Also some UI doc, a fix for DB prefix, and the user permissions pattern
| Project: | Patterns |
| Version: | 6.x-1.x-dev |
| Component: | Code |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | needs review |
I'm currently trying to knit together the deployment features I want from a combination of install profiles, macro,
Install Profile API, CRUD, and a hobnailed collection of my own pseudo-CRUD macro helpers.
I like the re-use and share vision I see in patterns, but I need logic in my macros, so YAML and XML won't do that for me. If I can't dynamically retrieve system variables and do a few limited lookups like variable_get('image_gallery_nav_vocabulary', ''); then my 'patterns' will not be portable enough for what I need to do.
So I'm keep to keep using the PHP syntax. Down that path I see convergence with macro.module. (my home-brew solution includes form submission arrays as part of an install profile, and a macro-runner that does drupal_execute() on them. )
But I can't see the examples of PHP patterns that I can build and tune.
Looking at the code, I'm sure they are basic, but...
I see patterns_load_yaml() and patterns_load_xml(), So I guess I can emulate that, but will it work? No patterns_load_php() ?
Is there somewhere I should be looking?

#1
I'm guessing it should go something like this:
///////////////////////////////////////
// Create a dummy page
///////////////////////////////////////
$pattern = array(
'info' => array(
'title' => 'Set up template contact page',
'description' => 'Set up template contact page',
'author' => 'dman',
'author_email' => 'dan@coders.co.nz',
'version' => '0.1',
'category' => 'Examples',
'core' => '6.x',
),
'modules' => array(
'node',
),
'actions' => array(
// Actions is an array of smaller acts
array(
'tag' => 'node', // 'tag'?
'type' => 'page',
'title' => 'Contact Us',
'body' => 'Put your contact details here',
),
),
);
.. and a bit of psychic coding gave me:
<?php
/**
* Read and evaluate a php file to return a 'pattern'
*/
function patterns_load_php($path, $local = TRUE) {
if ($local && !file_exists($path)) {
return FALSE;
}
$pattern = array();
if (!$php = file_get_contents($path)) {
trigger_error("Failed to read PHP pattern file $path", E_USER_WARNING);
return FALSE;
}
eval($php);
// That should have declared a 'pattern' into current scope.
if (!patterns_validate_pattern($pattern)) {
trigger_error("Failed to evaluate a useful pattern from the input file $path. Pattern did not validate. May have been invalid syntax. ", E_USER_WARNING);
return FALSE;
}
return $pattern;
}
?>
Am I right?
#2
Sorry for putting three things in one patch, but I didn't get any response to the first one, so needed to keep things moving forward.
OK.
This involved touching a few places to ensure that *.php files could be used the same as *.xml files. Does not yet save in php, but does read it.
(This involved a tiny refactoring of patterns_get_patterns() to abstract the search algorithm into a smaller function)
db_query('SHOW TABLES LIKE "cache_%"');and it threw errors when I tried on a prefixed site that had a different set of tables than the 'default' site.A small fix for this is attached.
<?php
function patterns_execute_action($form_id, &$form_state, $params) {
// Make sure we always have a clear cache for everything
// Beware - this direct database access needs to be db-prefix-safe!
global $db_prefix;
$result = db_query('SHOW TABLES LIKE "{cache}_%"');
while ($table = db_fetch_array($result)) {
// Remove the db prefix if any. cache_clear_all() will put it back again.
$table = substr(current($table), strlen($db_prefix));
cache_clear_all(null, $table);
}
...
?>
<?php$data[$data['rid']] = array_merge($perms, $data[$data['rid']]);
?>
when 'rid' was not an array. The code indicates that 'rid' is never expected to be an array, so I cast it into one to suppress the error. You'd only see it if running PHP_STRICT warnings - which I do.
#3
Hi dman, sorry for not getting around to look at these much earlier. The patch looks great and my initial tests were fine so I just committed it. Will leave it open for review for a bit longer.
Thanks!
#4
Cool, thanks.
I'm doing a bit of intensive cookie-cutter-site install-profile wizarding this week, so more tweaks may be forthcoming. I may even experiment with 'publishing' patterns...
The next one I need to do is one that auto-configures FCK Editor the way I want it ...
... after the one that creates a handful of boilerplate pages and content types...
Good fun, but slow going for me.
#5
Sounds fun! Just why we have all these various solutions for doing just that. Hope things work out for you :)
#6
Nice work dman! It would be nice to also expose a permission for "Use PHP patterns" or something to that effect. I guess it's generally not that important since most of the time Patterns are run as user 1 or a fully privileged user anyways.
Also, it would be interesting to support PHP snippets that could be evaluated in XML or YAMP patterns. I'm not sure if that's a good idea technically but if possible and security considerations taken into place, it would be useful.
#7
Well, there certainly is a potential security issue with PHP evaluation anywhere.
I once cracked a system that had inadvertently left cck import open :-)
OTOH, I am really not in favor of Yet Another configuration language, when clearly a PHP array is as obvious, flexible, dynamic, portable and powerful enough for any set of configs I need.
So yeah, the thing to do is tie the ability to use eval() to the php filter permission, or something like that.
I guess you can embed ?php? tags directly into XML ... and have the processor eval them on the fly? ... Nah, I want to be able to use the full power, maybe a foreach. "A pattern that creates a node for each registered user". I'm treating patterns as bundled PHP 'snippets' that perform admin actions I guess...
#8
One important aspect of using ?php? tags directly in XML/YAML ect is that you can have context available to you at that time. For instance if you're creating nodes, you won't know the node ids of what you're trying to create if you have a straight php pattern. If there was a tag, you could put that in the right points in the pattern where you'll have context and access to all info from all actions completed up until that point.
One thing on the list of todos. Should really be an easy one too :)
#9
I have encountered a problem with part 3 of the above patch when using patterns on a multi-site installation.
On our site, $db_prefix is an array, and so I need to hack line 1720 of patterns.module:
WAS:
$table = substr(current($table), strlen($db_prefix));
NOW:
$table = substr(current($table), strlen($db_prefix['default']));
I'm sure there would be a nice way to do this to support various configurations.
Here is our db_prefix array in the site settings file for reference:
$db_prefix = array(
'default' => 'mc_',
'users' => 'shared_',
'sessions' => 'shared_',
'role' => 'shared_',
'authmap' => 'shared_',
'sequences' => 'shared_',
'profile_fields' => 'shared_',
'profile_values' => 'shared_',
'blocks' => 'shared_',
'filters' => 'shared_',
'users_roles' => 'shared_',
);
#10
Ah.
I've not worked with shared tables and prefix arrays yet. Must remember to consider those.
I don't know if there is a quick fix, could be tricky.
#11
Well, thanks for reviewing. I'm glad it's on your radar at least. Let me know if you want any assistance when the time comes to revisit it.
Brian