Drupal Pipes
chx - February 7, 2008 - 03:29
| Project: | Drupal |
| Version: | 8.x-dev |
| Component: | base system |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | needs work |
Description
The basic idea is to execute a chain of callbacks with the output of one as the input of the next. For example, node_load gets 123 as an argument (from the URL), returns a $node. Pass this to node_access which either returns the $node or FALSE (needs patching but if (node_access($node)) wont change). Pass this to node_show. Pass the return value of node_show to theme('page').
<?php
callbacks => array(
'menu_load' => array('callback' => 'node_load', 'arguments' => array(1), FALSE => MENU_NOT_FOUND),
'menu_access' => array('callback' => 'node_access', FALSE => MENU_ACCESS_DENIED),
'menu_page' => array('callback' => 'node_show'),
'menu_render' => array('callback' => 'theme', 'arguments' => 'page'),
);
function drupal_run_callbacks($callbacks, $last = NULL) {
$callback = reset($callbacks);
while ($callback) {
$function = $callback['callback'];
if (isset($callback['arguments'])) {
$arguments = array_merge(menu_unserialize($callback['arguments']), array($input));
$input = call_user_function_array($function, $arguments);
}
else {
$input = $function($input);
}
unset($transition);
// TRUE or FALSE.
$transition_key = (bool)$output;
if (isset($callback[$transition_key])) {
$transition = $callback[$transition_key];
if (isset($callbacks[$transition])) {
reset($callbacks)
while ((list($key, $callback) = each($callbacks)) && $key != $transition);
}
else {
return $transition;
}
}
else {
$callback = next($callbacks);
}
}
}
?>
#1
Note that the function would need a return $input to make it really useful, also the keys of callbacks array are totally arbitrary and not even used in the above.
#2
<?php
function hook_callbacks() {
return array(
'node_load' => array('input' => array('integer'), 'output' => 'node', FALSE => MENU_NOT_FOUND),
'node_access' => array('input' => array('string', 'node'), 'output' => 'boolean', FALSE => MENU_ACCESS_DENIED),
'node_show' => array('input' => array('node'), 'output' => 'html'),
'theme' => array('input' => array('string', 'args'), 'output' => 'html', 'defaults' => array('string' => 'page')),
);
}
function drupal_run_callbacks($callbacks, $outputs = array()) {
$callback = reset($callbacks);
while ($callback) {
$function = key($callbacks);
if (isset($callback['input'])) {
$args = array();
foreach ($callback['input'] as $argument) {
if (isset($callback['args'][$argument])) {
$args[$argument] = menu_unserialize($callback['args'][$argument]);
}
elseif (isset($callback['defaults'][$argument])) {
$args[$argument] = menu_unserialize($callback['defaults'][$argument]);
}
else {
return FALSE;
}
}
$outputs[$callback['output']][$callback['destination']] = call_user_function_array($function, $args);
}
else {
$outputs[$callback['output']][$callback['destination']] = $function();
}
// TRUE or FALSE.
$transition_key = (bool)$output;
if (isset($callback[$transition_key])) {
return $callback[$transition_key];
}
else {
$callback = next($callbacks);
foreach ($callback['input'] as $argument) {
if (isset($outputs[$argument][key($callback)])) {
$callback['args'][$argument] = $outputs[$argument][key($callback)];
}
else {
return FALSE;
}
}
}
}
}
?>
Another very rough draft. This one assumes some nonexistent code that lets the administrator define which callback gives its output to which input(s).
#3
chx, I'm sure you overthought this very well, and there are plenty of use cases for this.. Unfortunatly, I can't come up with one.. Can you share some use cases?
#4
Want to get back the results in some other format than the full themed page? Just alter the last callback. Want to restrict access more? Piece of cake. We can do tricks like execute a query and then just call node_feed with said result as the next callback. If you want, say, an Atom feed, change that step. Reusability and hackability at its best.
#5
As a conceptual model, this is terrific. We'll work some more on how this actually gets patched into drupal.
callback is a bit of a loaded word in drupal already. perhaps we create a new word. i pondered a bit and i think 'steps' works for me. the array element that refers to a function can still be called callback. just rename to hook_steps and drupal_run_steps()
#6
Here's an initial patch, just for an update. The framework is pretty much there, may need some tweaking. What's needed next is implementations of the hooks, and then we can cut maybe 10% of all code from core modules ;). Any feedback would be appreciated. Note: this has no UI yet, but it will (and hopefully an excellent one, too).
Sorry that the .inc file is attached as a txt, I was having patching issues. It just needs to be placed in the includes directory.
#7
Subscribing - I am keeping an eye on this in case it can provide a clean foundation for #218830: Popups in Drupal 7: Plugable renderers for generating content
#8
Subscribe.
#9
Subscribing.
#10
Subscribing.
#11
me too.
#12
Subscribing too.
#13
track
#14
yep subing
#15
Subscribing.
#16
Subscribing.
#17
Subscribe.
#18
Could this be used to create views or rss feeds with much more complex logic than is now possible, for instance to output 10 nodes, teasers, list, whatever, containing 3 nodes from one source, 2 from another, and 5 from a third, but all sorted together according to whatever sort criteria one specifies?
Meaning it would take the output of three views as input for a fourth view.
#19
Subscribing.
#20
cwgordon7... any progress?
#21
chx and I discussed this and decided that we have fundamentally different ideas. He will continue to work on this thread, and I will take mine into contrib.
#22
Minor code style comment - I see a couple of if ifs there where && would be clearer.
#23
Subscribing.
#24
There is, in fact, a patch.
#25
Uh, no, there's not.
#26
Interesting concept, but I'd wonder how would this perform. The themes are already pluggable, so renderer changes are possible without this "If you want, say, an Atom feed, change that step" is not technically new.
#27
Two words. One dimension.
#28
Subscribe.
#29
Interesting... subscribing.
#30
So this basically becomes a filter chain, right? That way you can branch off at any given link in the chain? It would be interesting to be able to branch off, then reconnect further down the chain, effectively by-passing only the one section of the chain that you want to bypass. Wow, I said chain a lot.
#31
subscribing
#32
subscribe
#33
subscribing
#34
Looks like workflow-rules and in conjunction with triggers can be very usefull
#35
Recording progress. http://www.drupalbin.com/4621 is some new code. Writeup (which wont likely to make sense without checking that pastebin):
Now, the menu definitions would not change that much because most of the magic (esp the transitions) will be added as defaults. I might move back the path into a key as I want more to make possible to have several steps array defined on node/% .
#36
subscribin.
#37
#38
We just discussed this with pwolanin and I was afraid of unserializing a huge array for every single link. So we now think that if the steps are only the known defaults which defines a menu entry with pretty much the same features as like now, then the first two steps, for example we can call them menu_load and menu_access, these will be stored like they are now so that the common case will be fast.
#39
subscribing
#40
subscribin
#41
subsub
#42
Another step :p hopefully in the right direction. Still not integrated into Drupal but I implemented the transition part and set to CNR because I would like indeed the code to be reviewed and agreed upon before I continue. Writeup in #35. transition, for now, is just a simple compare.
#43
subscribe
#44
Subscribing.
#45
subscribe
#46
Good stuff. Subscribing.
#47
discussed this with chx at the DCDC code sprint. maybe i'm just missing stuff, so i'm going to post so i can be corrected.
i like the idea of chainable menu callbacks, but i don't know if building a general purpose pipe system is the way to go. do we really want to build a system that can just arbitrarily chain together functions? functions in php don't just read from STDIN and return a string to STDOUT :-(
how do we decide which step to take next if we are building up the chain ahead of time? aren't we going to want to do that at runtime?
how about a simpler approach that just allows for multiple callbacks?
here's a way to do that that is simpler, that chx doesn't like, but i'll let him explain why.
- it requires a set api for callback functions:
<?php/**
* $page - you add bits to this, and its to be passed to drupal_render_page($page)
* $context - info about the request, and anything else liked loaded nodes etc via. the menu system
* can load stuff in here magically like it does now so modules don't need to. we can also make a
* something like hook_load_context so modules can get in on this.
*/
function foo_page_callback(&$page, $context) {
}
?>
- it makes these functions not reusable as general purpose functions. in my opinion, this is not a problem, or at least, the solution to this (a system that can arbitrarily chain together functions with any signature and return values to any other function) is worse
- drupal_get_form, and anything else that is a callback and also something else, wouldn't work. i don't think this is a bad thing, but chx disagrees, not the first or last time i'm sure :-)
we can take the same approach to adding multiple access callbacks, much smaller scope patch, less powerful.
anyway, my US$0.02.
attached is a patch that shows a possible way to do the multiple page callbacks stuff. not complete, but i hope it makes some of what i'm saying clearer, even if to make it easier to explain why this approach wont fly. don't want to hijack this issue, so i'm happy to take this elsewhere.
#48
The last submitted patch failed testing.
#49
Would this concept of multiple page callbacks (I'm thinking especially of the patch in #47) do anything that could help fix my issue #501372: RDF module conflicts with overridden taxonomy_term_page() implementations?
Essentially my problem is that various contrib modules (Panels 3 via the Delegator module, RDF, Taxonomy Translation (i18ntaxonomy), and Taxonomy Manager) want to do various things to alter taxonomy_term_page(), but ultimately want to return a display of nodes which have a particular taxonomy term.
I would love to be able to chain these implementations together, essentially altering the taxonomy_term_page() but not overriding it 100%. But I don't know enough of what these does to know if it's actually related, or if that's just a pipe dream.
#50
Moving to D8.
#51
note that Drupal have abrupt
exitcalls which might attempt against this featurefor instead, one callback ending in a
drupal_goto(or cron) would halt remaining callbacksI think Drupal should avoid using
exitcalls and let every function end naturally #592664: function calls should end naturally (avoid exit calls)