I wanted to test a complex piece of code in a node. I did not look forward to copying it from my program editor to Drupal's content editing page each time I made a change, so I did this.
1. Defined a new content type which I named Test node.
2. Created an empty node of that type.
3. Created a URL alias for the empty node and added it to my menu as the function I wanted to test.
4. Put the code to be tested in Test node's node template.
When I'm done, I will have to create an ordinary page node, put my code in it, and make the alias refer to it. I will keep the empty node around, and if I need to make more changes, I will change the alias back.
I am posting this for two reasons; first, because I think it's a useful technique. Second, to ask whether anybody can suggest a better one.
Comments
Did you consider just
Did you consider just placing an include('custom_code.inc') call in your node content area?
I'd recommend that when developing code using your real editor, and also is a better place to keep your code than in the database.
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
Yup, that sure is simpler!
Yup, that sure is simpler! I guess I know just enough to be dangerous. :-)
Wait --
Uh, wait. To do that, I would have to enable and use the PHP evaluator filter. That idea makes me nervous!
I'd only have to make it available to a trusted user (myself), but I'd rather not have it enabled at all. I'm not convinced that this use justifies the risk.
Come to that, moving my code directly into the node content would involve the same risk. Perhaps I had best leave it where it is, and change the name of the content type from "Test node" to something like "Safe node."
Up to you. I wouldn't sweat
Up to you. I wouldn't sweat it.
I said - when developing
Me, once I've figured out what I actually wanted to do, I usually wrap it into a trivial module :)
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
Module?...
Since I posted the original question I've figured out how to create a trivial module, and it wasn't nearly as scary as I expected.
Please help me think about this, though: how do I make a URL invoke a module without putting PHP code in content?
hook_menu()
http://api.drupal.org/api/function/hook_menu/5
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
I'll study that
Thank you. I looked at that, and on the first reading it didn't make much sense; it says that the function returns an array containing all the values that I'd expect to have to pass in, and I'm not sure what that means. It's also not clear to me how to use the function if I don't want to put the module in a menu. Probably this will make more sense after I study it a while.
Menus
Bear in mind that in Drupal the menu system does a lot more than just the menus. For instance it is also used when using a template to theme the output of a module, and I think it is also in charge of node access; so don’t be confused by using it when you don’t need a menu.
___________________
It’s in the detaιls…
demonstration portfolio
Tried it...
I think I understand this now. But it's not working, so I might be wrong!
Once I got used to thinking about the function as a hook, the fact that it returns information instead of receiving information made sense.
I created a module to contain functions that implement static pages (so far, just one of them). I defined the hook like this:
When I enabled the module the print statement was executed, assuring me that the hook was being called. However, I also got this error message near the top of the "Modules" page:
(Line 18 is the line containing the function declaration.)
When I try to visit the path /EditDeleteItems, I get "The requested page could not be found."
That function would be for
That function would be for Drupal 5, the hook changed in Drupal 6, see http://api.drupal.org/api/function/hook_menu/6
Better, but no banana for me yet
I updated the callback per that page. I'm not getting the error message any more, but I'm still getting "The requested page could not be found" when I try to use the pathname.
I'm baffled by the fact that the documentation doesn't even mention the 'path' array element any more. It's not clear how the callback can possibly map a pathname to a function without it, but the text still describes the MENU_CALLBACK option, which makes Drupal do nothing else, and the code listing actually shows an array element that uses MENU_CALLBACK but not 'path'.
Maybe the omission of 'path' was accidental in spite of that, and something else is wrong. Or maybe 'path' has been renamed, as some of the other elements have, and we aren't being told.
Here's what I've got now:
Path is now the index to
Path is now the index to $items so you should have
A couple of notes, in general it is good practice when no extending existing paths to include your module name (or something) in the path to avoid path conflicts. And you will need to visit Administer > Site building > Menus after making the change so you hook is called (hook_menu is only called when the module is enabled or when visiting the menu admin page)
Thank you again -- but
Thank you again -- I see now that the documentation shows the pathname as an array index, but it does not say it, and I did not pick up on it.
My link now calls the function and loads the "page" -- but a couple of problems remain.
One is that my function is not writing into the content area of the page template, as I assumed, but is simply dumping text and tags onto an otherwise blank page. This makes sense, when I think about it; my function is not the node it replaced, and there is no reason why Drupal should treat it like a node. This is a problem for me, though, and I don't know how to correct it.
The other problem is that my function calls other functions which are defined in template.php, and it can't find them. I guess this makes sense too, since template.php is in my theme directory, and modules operate independently of any particular theme. Again, though, I don't know how to deal with it. I guess I could duplicate the necessary functions within the module itself, where I assume they would be invisible to the outside world just as the template is invisible inside the module, but I'd rather not have to do that.
As for the content, I am
As for the content, I am guessing your function uses print/echo statements. Instead gather the output in a variable and return that.
Actually template.php can see module code (in general) but since it is not loaded till theming starts functions there are hidden from template.php. So without all the details it might be more appropriate for functions in template.php to be in your modules if there are not really related to theming the output or your module code should maybe calling theme functions.
Catch-22
That's correct, and this part of the problem is now solved; thank you.
I think this is a matter of terminology rather than a misunderstanding, but that sounds backwards to me: the question is not whether template.php can see module code, but whether module code can see functions in template.php.
I cannot simply move the functions into the module because many of them are utility functions that are used throughout my project. As a temporary expedient, at least, I copied each of the functions that PHP complained were undefined into the module. As soon as I finished that, it started complaining about duplicate definitions. So I'm caught in a Catch-22: if I don't put the functions in the modules the module code can't find them, and if I do, they're flagged as duplicates.
I guess I can copy the functions into the module, rename them, and rename all the references the module makes to them. This is very, very ugly. There must be some way to define these functions once and make them available everywhere I need them. I wonder if this is an occasion for using include_once.
You are doing very well in
You are doing very well in getting your head around the process so far. It's a slog to try and get it all at once like you are having to do!
OK. If they are utility functions, they should not live in template.php
Template is only for funcs that take a data structure and return HTML. Over-rides of theme hooks. Broadly speaking.
Templates and themes are called very late in the game. Some modules (like taxonomy_theme, or user.module) may change the current template on-the-fly during page processing. Templates are ONLY invoked AFTER the unthemed page structure has been prepared. There are short-cuts, but I can't advise without knowing what you are actually doing.
If you are developing a small library of funcs - they probably deserve to live in your/a module. That's a place to put utilities. There are a few modules that exist ONLY to provide a function library to other modules.
BUT, I think the place you are looking for today is settings.php. That's loaded always and everywhere, and is relative to your site only, not core, theme or even distro. It's expected to be modified by you. Put your custom include at the bottom of that.
In the long run however - whatever it is that they are doing should probably eventually end up in a nice specific module devoted to the job.
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
First I've heard of that --
This is the first I've heard of using settings.php as anything other than a configuration file. I'll have to research that.
It's going to have to wait a while, though. Converting the functions in my template.php from mixed PHP and HTML to pure PHP will be a significant project, and I'm going to have to defer it until I have resolved some more urgent problems.
Thank you for your advice. I think I know what I need to solve this problem now; all I need is time to work on it.
Meanwhile, I'm happy to tell you more about what I'm doing, only I'm not sure what you need to know. This is my first Drupal project. I'm approaching it with about three months of experience in Drupal architecture and PHP, and... let's see... a little over 35 years of professional experience as a programmer. It's a combination that is hard for some people to understand. It makes me very knowledgeable in some areas that most Drupal developers don't even comprehend, and ignorant in other areas that they consider utterly basic.
The utility functions are nothing special. I'm not making a systematic effort to develop a library (I don't know enough about Drupal yet to do that intelligently). When I need a function I write it, and I try to make it as generalized as I reasonably can.
A typical example of utility function: given a vocabulary ID and the name of a term in the vocabulary, get the term object. Another example: create an attribute array for the l() function that describes one of a few standard sets of attributes my project uses.
You are right...
Broadly speaking again, yes, I'd try to keep settings.php for true configs, but without knowing whether your funcs are helpers to the template, or modules, or inline code ... that's one possible place you can include your customizations so they are always there. Sorta a quick-fix, and I wouldn't call it best practice though.
But there is no preferred drop-box to place totally arbitrary code. Almost everything that happens is an offshoot of some other module action, so funcs get bundled with their modules. Think OO "static methods" I guess. But there is no 'main' library. Due to multisite support ... your site directory is the closest place to start.
If your calls are always and only going to be 'helpers' to other theme functions, you can have them in the template.php . I do that for a few almost trivial ones ... but need to remember not to invoke directly them from real code. You CAN declare and use real theme hooks (you can make them up arbitrarily and they just start working) but that's week 2 work ;-)
This is all the more reason to start with a small custom module to put that stuff in. Soon as possible. I've written about that here
FYI, the api may have some things you can use... or at least get examples from
http://api.drupal.org/api/function/taxonomy_get_term_by_name/5
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
Debugging difficulty
Knowing how to make this work -- I thought -- I moved on to other problems, and returned to it when I had time for cleanup. But moving my code to the callback function flushed out some errors, which I then had to debug. The debugging process proved difficult. I kept getting pages like this one:
The problems were mostly simple, and the error log described them clearly, but it was awkward to have to examine the error log each time I got an error. And I can imagine problems for which the error log would be essentially useless. Also, print_r, my mainstay debugging tool, was made less useful by the fact that it prints its output, while the code I'm debugging accumulates its output in a string. I wanted a version of print_r which returns a string value, but as far as I know there is no such thing.
Apparently I need a different set of debugging techniques for this type of work. Have you any suggestions?
print_r()!
print_r( … , true); will return its output and not print it.
http://uk.php.net/manual/en/function.print-r.php
___________________
It’s in the detaιls…
demonstration portfolio
Another debugging difficulty
This one seems to be a catch 22.
I did not want to define a separate little module for each function (I've been down that path before, and had fits trying to remember which contained what), so I created one "utility" module to contain all of the functions that are generally useful in my project and have no other home.
That means that I must occasionally add new functions to the existing module. How to make Drupal aware when they have been added?
I believe I should be able to do this by disabling the module, then re-enabling it. But there are a couple problems with that.
The first problem is that some of the functions in the module are essential to the operation of my theme. In other words, after I disable The module I can't reenable it, or do anything else, because I can't display a page. (I did this to myself once, and had to restore my database to recover.) I can use a different administrative theme, but that makes a complete hash of my header and footer, making it difficult to reach the non-administrative part of the site without changing back.
The second problem is that it's not working. Even after I disable and reenable both the utility module (containing utility functions) and the page callback module (containing the function that calls them), the page callback function fails because PHP thinks the utility functions are undefined. Code in template files can find them; code in other modules (at least, code in the page callback module) can't.
You sound a little confused.
You sound a little confused. But it is confusing in places.
Adding new low-level functions to your library module does just make them available immediately.
Exceptions to this are menu-defined callbacks, some views widgets (which get cached) and in D6 most theme functions. Each of these can be refreshed by truncating any table with 'cache' in the name. This is a useful commandline trick to keep in the background when developing.
#1 You really should try to make your theme less fragile. Add a few 'if(function_exists("my_hoopy_utility")){' patches to your theme, or start using theme_* callbacks instead.
#2, that situation can probably only happen if you are somehow invoking the library functions before Drupal bootstrap runs. You may have to tweak the system table to change the weight of the module loads if you have other dependant modules trying to run code that hasn't been loaded yet, but usually it's only possible to break things that badly if you implement the hook_init() function or have code at the file level that's not inside a function.
Templates are loaded late in the game, so things are usually available to them. Conversely, modules cannot call template functions directly sometimes - so always filter such calls through the theme() function.
It's tricky, but it sounds like you've dug at least some of your own hole :-)
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
Probably am...
I think you've got the information I need, but you're going to have to pound a little harder to get it through my skull :-).
You said that "Adding new low-level functions to your library module does just make them available immediately," with a few exceptions that do not apply. (The functions in question are purely utility functions, not theme functions, callbacks, etc. For example, one of them takes a taxonomy ID as an argument and returns a string that, when printed, renders a list of node titles in a particular format.)
That's how I thought it should work, although I believed that I had to disable and reenable the module to make Drupal reload it. But that's not how it is working with respect to calls from other modules. It's still not clear to me why.
The thing about changing the weight of module loads may be the key to the problem, but I don't understand it yet. I once had a similar problem which I solved by giving the called function's module a name which alphabetically preceded the name of the calling function's module. I never understood exactly what that accomplished, but I thought of it as ensuring that the called function was loaded before the calling function was loaded.
In this case the called function's module name already does come before the calling function's module name. This must be a different problem, or at least a more complicated one.
Regarding the theme problem, I didn't explain clearly what was happening, and you made some assumptions that were reasonable but incorrect. First, I'm developing a site with a custom theme, and there is no reason to suspect that the client will ever want to run it with a different theme. In this case, I can't say that requiring the site to run with its own theme makes it "fragile." I can say that making it theme-independent would require a substantial amount of additional work, and the client would not pay for that work because it would add no value for him.
Here I've hit an unexpected situation which requires me, as a developer, to change the administrative theme for certain steps in my work, and that makes hash of the page headers. This is a darned nuisance, but no more. I could eliminate the nuisance by making the site look prettier when used with core themes, but when I weigh the time that would take against the time it would save, I find it hard to justify.
Second, given the fact that we're talking about utility functions here, coding alternate logic with function_exists() would not make my code degrade gracefully -- it would make my code stop making sense. If it came to that, duplicating all the utility functions in every module that needs them would be a cleaner solution, or at least a less ugly one.
I know what it's like to be
I know what it's like to be custom-coding things you 'know' will never be used out of context. I'm making suggestions based on Drupal best practices and lessons learnt here.
In the long run, it costs less in time and effort to do it right from the start. Hacks that push all sorts of features into the theme itself are easier, but there are often more pluggable solutions that are no more complex code-wise. And just feel better. Sorry if I'm making assumptions again, but I know that phase of coding and have tutored a few Drupalists through it. It's tempting to treat your page.tpl.php like an old index.php web page. :-}
You describe how disabling your library makes the entire site inaccessable. That's the behaviour I called fragile, it's bitten you, and it would give you less headaches if you could code to avoid it. At no point should a theme be totally dependent on a module. That's an order-of-operations design problem.
Yes, I do understand your "use once" scenario, but still.. It shouldn't hurt to do things right anyway.
If you think that too many bulletproofing checks would clutter your template, I can only speculate that your template is doing too much thinking. Templates shouldn't contain much more than "print" statements and an occasional "if". That's not a rule written down anywhere I know, but it's a guideline I'd suggest.
At this point I can't suggest why your library utils are not being loaded in time. Except for a small period during bootstrap, all module funcs should be there all the time.
You'd have to start adding some trace debugs into your code to find it.
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
At no point should a theme
I see the merit of that principle, but if I take it to heart, I'm left with the same problem I had before: where to keep functions that are useful in more than one module, template file, or whatever?
I don't know how to respond to that -- it just doesn't relate to the problem I'm facing.
The functions in question are utility functions. They don't implement dispensable features; they implement basic operations that are used to implement features. The site cannot operate without them, any more than it could operate without built-in utility functions like print() and array(). Bulletproofing would not change that. At best it would enable the site to display a pretty page that said, "I'm dead because function X is undefined" instead of letting the server display an ugly page that said, "Your site is dead, so look at the error log to find out why."
But this is a distraction from the problem I need to solve. I need to figure out why the calling module can't find these functions -- or put them someplace else where the calling module can find them.
Okay, but... what am I looking for? It's not clear to me how to diagnose a problem of this nature by tracing, unless I trace the part of the PHP engine that loads and interprets source files. (I would consider that unrealistic even if it is technically possible!)
Well, it doesn't address
Well, it doesn't address your structural issues at all (which is what I've been trying to do), but I guess for a quick fix you can put your library into an inc in your site setting folder file, and include() it from the bottom of your appropriate settings.php file.
That file is appropriate for you to add things to that are specific to your one site.
Or am I repeating myself?
However, I do think you were doing the right thing by making a library module. There are a few like that already, like token.module, imageapi.module and others that just provide utilities that other things are built on. It should be possible for yours to work, and I cannot tell from this end of the internet what's been going wrong for you.
Neither can I give a detailed course in debugging, but tracing which includes are being interpreted is one of the easiest first steps. I do it often, especially when unravelling legacy code dependancies! You may try adding
at the top of the files in your module, your library module, and your theme template.php file to see the order they get loaded. ... take it from there??
Re code structure,
If your theme does not at all, ever, invoke your user-defined functions, then no, my suggestion doesn't relate to your problem. In fact, you wouldn't be having this problem.
The theme is the very end of the pipeline. All lookups, callbacks, utility funcs or not should have been finished getting their content together before the system even looks for your page.tpl.php. You'll see that all the core themes just print() blocks of content that have been prepared earlier (mostly from within phptemplate.engine).
IF you are calling a code-specific function from within your theme file (indispensable utility or not), you are swimming against the stream, backtracking into the code when the system thought it was ready to output. Themes should not call functions.
This is not really as big a deal as I may seem to be making out, as usually you can just get away with it for small things, (format_date() for example is acceptable) but it's a style thing, a separation of form from function thing, a maintenance and scalability thing, and relates to caching as well. It's about understanding the job of the theme abstraction layer.
It also helps with error-catching, as by the time it hits page.tpl.php it's too late for message-setting or graceful recovery.
Sorry if that's too deep, and I'm lecturing you on theory and the Right Thingtm when all you want is something that Just Workstm. :-}
These aren't even rules, they are just ways of avoiding putting yourself into the problem you now have. And with the bonus of portability, upgradability and maintainability - which OK, you don't care about today.
But you are at where you are at, and it sounds like you won't be refactoring. Try just including that inc library as suggested above. That's the most sure-fire way I can think of to make your indispensables always available.
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
Puzzled
First, as of today, the undefined-function problem is gone. I don't know why. I've experienced (and reported) things like this before: a malfunction with no apparent cause, which disappears for no apparent reason after I struggle for some hours or days to diagnose it. I hope I get to the bottom of that someday. Meanwhile, all I can do is shrug and move on.
I can't help feeling that we're miscommunicating somehow, because when you carry on about portability, maintainability, separation of presentation from function, etc., you're taking my lines. I can't remember all the times I've been told to Give It Up™ when I tried to promote the Right Thing™ over something that Just Works™. It appears to me that we have been discussing several more or less distinct issues, and in some respects you have conflated them. But maybe I'm wrong, and the specifics you are arguing for will make more sense to me when I have more general experience with Drupal and PHP.
I acknowledge that I often do things in a straightforward or naïve way when the Drupal Way might be architecturally cleaner, simply because I don't know what the Drupal Way is, and I don't know how to find out. To understand the Drupal Way consistently I will need either a year or so of experience or a clear and detailed description of the Drupal processing cycle -- something I have given up hope of finding.
Perhaps you can help me along by explaining how you would deal with a situation I have encountered several times: putting variable content in a sidebar. I do it by creating a block and putting the block in the sidebar, and then putting the necessary logic in the block template. Either I put the logic in the block template itself, necessarily including quite a few function calls, or if it is reusable I put it in a function and make the block template call that function. Either way, the block template contains function calls, and that is the approach that Just Works™. What is the Right Way™?
For your block example, I
For your block example, I guess the Right Way is to get a small module to provide the functional block.
But I know, and sometimes use the approach you describe.
My blather about themes and functions is mostly stylistic, and mostly about page.tpl.php, not sub-templates.
After a bit you may see what I mean about Drupal code structure. Especially after porting functionality through a couple of re-designs, version upgrades, or trying to re-use some solutions on a new site. Or maybe I am steering you wrong. Or maybe you'll come up with a few rules of thumb yourself :-)
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
.dan. is the New Zealand Drupal Developer working on Government Web Standards
Largely in agreement
In that case we're largely in agreement. I never really thought about whether logic belongs in the page template, but without consciously formulating any rules, I too would be skeptical of putting elaborate logic there.
I checked my own page template, and did not find a lot of logic. There are nine if statements, five of which simply test globals to see if they are null before printing them with wrappers. There are four calls to theme-defined functions, three of which call a function that displays a custom menu bar -- something I would consider a "core" page template function.
So I think that we are much more in agreement than it appeared.
Devel module provides a
Devel module provides a block where you can paste php and execute it.
___________________
It’s in the detaιls…
demonstration portfolio