Help a Habari beginner? Function name confusion

9 views
Skip to first unread message

Mister Hilder

unread,
Apr 11, 2008, 9:12:44 AM4/11/08
to habari-dev
Hello all,

Well, I finally dipped my toes into the world of off-the-shelf content
management systems, and Habari was my first choice due to SQLite
support. The first thing I'm going to do with it is write my own
theme, but I'm finding it extremely frustrating, and I think this may
come from my lack of previous OO-PHP experience.

What's got me confused is the use of predefined variables and their
member functions when writing the dynamic portions of templates (posts
and comments home.php, for example). I'm working from an existing
theme (charcoal, as it happens), and stripping this down into a bare-
bones HTML layout.

I came across a reference to $post->tags_out, and with the help of
this message board I've tracked this down to a function called
filter_post_tags_out($array) in user/themes/.../theme.php. Can someone
please explain how tags_out is 'translated' into
filter_post_tags_out()? Is this something that's common to OO-PHP, or
is it specific to Habari? What is a 'filter' in this context?

So, at first I was willing to ignore how this magically works, but the
next one I found was $post->title_out, and I have no idea where it
comes from. My understanding is that $post is an object created from
the Post class, but doesn't that mean that I should see a member
variable or function by the name 'title_out' in system/classes/
post.php? I know that if I change $post->title_out to $post->title,
the result is exactly the same; I'm just really confused how PHP and/
or Habari is parsing this code.

Thanks to all, in advance!

Scott Merrill

unread,
Apr 11, 2008, 9:32:00 AM4/11/08
to habar...@googlegroups.com
On Fri, Apr 11, 2008 at 9:12 AM, Mister Hilder
<mister...@googlemail.com> wrote:
> Hello all,

Aloha!

> Well, I finally dipped my toes into the world of off-the-shelf content
> management systems, and Habari was my first choice due to SQLite
> support. The first thing I'm going to do with it is write my own
> theme, but I'm finding it extremely frustrating, and I think this may
> come from my lack of previous OO-PHP experience.

Welcome to the community! We'll do the best we can to help you out.

> What's got me confused is the use of predefined variables and their
> member functions when writing the dynamic portions of templates (posts
> and comments home.php, for example). I'm working from an existing
> theme (charcoal, as it happens), and stripping this down into a bare-
> bones HTML layout.

Habari objects implement several "magic" functions like __get() and __set():
http://us3.php.net/__get

> So, at first I was willing to ignore how this magically works, but the
> next one I found was $post->title_out, and I have no idea where it
> comes from. My understanding is that $post is an object created from
> the Post class, but doesn't that mean that I should see a member
> variable or function by the name 'title_out' in system/classes/
> post.php? I know that if I change $post->title_out to $post->title,
> the result is exactly the same; I'm just really confused how PHP and/
> or Habari is parsing this code.

The __get() method intercepts requests for object properties and
allows us to execute methods to return things as we want them. One of
the benefits of this is the use of "virtual" properties. As you've
discovered, there is no title_out property defined in the Post class.
Instead, requests for title_out get sent to Post::__get(), which
implements the filter logic.

Take a look at the first couple lines of Post::__get(). Inside the
first if block we check if the requested property matches some portion
of an actual property, and then check for the existence of an
underscore on the requested property. If that's true, we know we're
asking for a filtered version of an actual property.

So we separate out the actual property name and the filter. In this
case, we want the "out" filter on the "title" property. We don't have
a specific case for "title" in the switch statement, so it falls
through to the default case, fetching the value of "title" from the
parent QueryRecord's method to return the field value of that name.

After invoking general plugins on the requested property ("title"), we
invoke specific filters for the specified property ("title_out"). If
no filters exist, then the return value of "title" and "title_out"
will be the same, as you've discovered.

Inside your theme.php file, you could create a filter called
"post_title_out" that could change the output of the title. You could
make it all uppercase, for example, or manipulate the title in some
other clever way.

Hopefully with this information you can figure out $post->tags_out on
your own. if not, let us know. :)

Cheers,
Scott

Mister Hilder

unread,
Apr 11, 2008, 1:58:47 PM4/11/08
to habari-dev
Thanks for your concise reply, Scott, that definitely clears a few
things up, although I can't say I like the design -- to me it seems
very opaque.

I'd just like to clarify one or your statements... "...you could
create a filter called post_title_out...".

Does that mean: public function post_title_out(){...}
or perhaps: public function filter_post_title_out(){...}
or something else?

I can't say I've had much luck in figuring out $post->tags_out (ie. I
know where the function is, and what it does, I just can't trace the
call-stack), but I've come across another issue that's confused me
even more :(

In user/themes/charcoal/home.php, there is a call to $theme-
>post_comments_link($post, 'No Comments', '%s Comment', '%s Comments')
which has got me stumped, and I really hope there's a simple answer to
this. The main query that started me off is that the call in home.php
isn't preceded by 'echo', implying that the function itself does the
echoing. I spotted charcoal::theme_post_comments_link($theme, $post,
$zero, $one, $more) in user/themes/charcoal/theme.php, and due to the
slightly different function name, I presume that it's made its way
here through one or two __get() handlers. So I had a look around, and
I figured that Theme::_get() redirects to RawPHPEngine::_get(), and
from there the "post_comments_link" string should match one of the
elements in the engine_vars[] array. I guess my assumptions are
incorrect, because I can't find any other reference to
"post_comments_link" in any files, and this doesn't quite explain how
we get back to charcoal::theme_post_comments_link().

So maybe I'm making a mountain out of a molehill -- regardless of how
the function is 'redirected' -- in theme.php it returns a string, and
somehow that string is sent to the output buffer. I tried replacing
the function's internal logic with a simple "return 'test';", and sure
enough, "test" appears on the page, but what's really crazy is that I
get exactly the same output when I replace the 'return' with 'echo'.

I've never heard of 'return' sending data to the output buffer... I'm
going mad here, please help!

Thanks again,
Hilder :)

Scott Merrill

unread,
Apr 11, 2008, 2:07:07 PM4/11/08
to habar...@googlegroups.com
> Thanks for your concise reply, Scott, that definitely clears a few
> things up, although I can't say I like the design -- to me it seems
> very opaque.

It's opaque in some situations, but provides a tremendous amount of
very useful functionality without overly bloating or obfuscating the
underlying code. It allows themes to leverage custom filters defined
in their theme.php file without requiring additional plugins, for
example.

> I'd just like to clarify one or your statements... "...you could
> create a filter called post_title_out...".
>
> Does that mean: public function post_title_out(){...}
> or perhaps: public function filter_post_title_out(){...}
> or something else?

public function filter_post_title_out(). Sorry about the confusing
reply on my part.

> In user/themes/charcoal/home.php, there is a call to $theme-
> >post_comments_link($post, 'No Comments', '%s Comment', '%s Comments')
> which has got me stumped, and I really hope there's a simple answer to
> this. The main query that started me off is that the call in home.php
> isn't preceded by 'echo', implying that the function itself does the
> echoing. I spotted charcoal::theme_post_comments_link($theme, $post,
> $zero, $one, $more) in user/themes/charcoal/theme.php, and due to the
> slightly different function name, I presume that it's made its way
> here through one or two __get() handlers. So I had a look around, and
> I figured that Theme::_get() redirects to RawPHPEngine::_get(), and
> from there the "post_comments_link" string should match one of the
> elements in the engine_vars[] array. I guess my assumptions are
> incorrect, because I can't find any other reference to
> "post_comments_link" in any files, and this doesn't quite explain how
> we get back to charcoal::theme_post_comments_link().

There also exists some magic theme functions which I haven't explored.
Themeing isn't my itch, so I don't scratch it. Hopefully someone
else will explain those bits. It might be that $theme-> methods
automatically output stuff for you.

Cheers,
Scott

Mister Hilder

unread,
Apr 11, 2008, 2:37:54 PM4/11/08
to habari-dev
>
> It might be that $theme-> methods automatically output stuff for you.
>

You're right, I've just looked in Theme::_call(), and the return of
overloaded functions does get echoed. I'm a little disappointed by
that; I feel that I can't comment-out the 'echo' lines without
breaking something elsewhere (maybe in the admin pages), and I doubt
this would be changed in the source because all the themes 'in the
wild' were built on this design. I'm extremely fussy when it comes to
code standards and conventions (I won't allow whitespace in HTML
output, for example), and I was hoping not to have re-write the entire
CMS just to match my fussy brain. Oh well.

Just referring back to $post->tags_out, could you please help me
figure out the call trace? Best pitch it at 'n00b' level, I'm really
struggling :(

Thanks again,
Hilder

ringmaster

unread,
Apr 11, 2008, 4:48:14 PM4/11/08
to habari-dev
I think it's going to be a lot more helpful to know the general
purpose of these "postfixes" before delving into the code that makes
them work.

Primarily, as you said, we'll have defined $post as an instance of the
Post class. In it each member roughly corresponds to a field in the
posts table. $post->title is the title, as string. $post->content is
the post content, as string. $post->author though, is actually a User
object, even though the database only contains a numeric index. This
is achieved by using the __get() and __set() magic methods Scott
described.

$post->tags is an array of tags, not a string. This is important to
know.

Postfixes on these fields allow functions to be applied to the field
values without calling those functions explicitly in the theme. For
example, you might use this to output the title of a post as all
uppercase:

<?php echo $post->title_out; ?>

The "_out" in this case is the suffix that I'm talking about, attached
to the regular field property. Note that you can use any text you
want as the postfix as long as it contains exactly one underscore at
the beginning of the postfix. So $post->title_uppercase would be
perfectly valid.

The default theme in Habari uses the _out postfix in most places it
makes sense. By standardizing on _out (most of Habari uses "out" and
"get" by convention to keep things similar) plugins can affect the
functionality of the _out postfix.

By applying the postfix, the return value behaves as though you've
passed the value of the field through a series of plugin filters, and
the result returned by those filters is the value of the field plus
the postfix.

So this:

echo $post->title_out;

Is more or less the same as this:

echo Plugins::filter('post_title_out', $post->title);

Note that the first one is considerably more friendly, and you didn't
really need to know that it was funneled through a bunch of plugins in
order to acquire the output.

Suffixes should really be used for display purposes only. If you want
the raw value of the field, then you should not use a postfix.

Postfix filters can be applied in a number of ways. One way would be
to write a plugin that implements a function named
filter_post_title_out(). It could look like this:

function filter_post_title_out($value) {
return strtoupper($value);
}

This would cause all instances of $post->title_out to be in upper
case, whereas $post->title would remain the original value.

Of course, there may be occasions where you want to apply multiple
filters to a single value, or turn on filters discretely from the
theme itself. Habari provides another way to do that. In the theme
file, this line could be executed:

Format::apply( 'ucase', 'post_title_out' );

This tells Habari to make the value of $post->title_out filter through
the ucase() formatting function that I just made up. Format functions
exist in the Format class or children of the Format class. (There
will soon be a way to define these easily in plugins without having a
separate class, but there is not yet a way to do this.) You would
need to create a class that extends Format and have ucase() be defined
in there, or add in core the function ucase() to the Format class
itself for the above Format::apply() call to work.

Using Format::apply is virtually the same thing as defining the full
plugin filter function, but it lets you define multiple filters and do
it from the theme itself. You'll notice these lines in the /system/k2/
theme.php in the core.

Where I think you're seeing trouble is with the $post->tags_out, so
I'll work through that with you here.

$post->tags is an array of tags. As a result, $post->tags_out is also
going to start out as an array of tags, but filters applied to the
_out prefix by the theme convert it to a string. Look at this line in
k2's theme.php:

Format::apply( 'tag_and_list', 'post_tags_out' );

This applies the tag_and_list() function to $post->tags_out.
tag_and_list() is a Format function that comes with core in the Format
class itself. There, you can see that it converts the array into a
string with links and commas and makes it look nice for output.

If you're building a theme, you can omit this call to Format::apply()
and output the tags manually in a loop. Or you could write your own
format function, which might be useful to reuse in a theme or plugin,
or even share with others. Totally up to you.

Hopefully that gives you an overview of what's going on. I think with
that much information, you can either decide to figure out how that
all works, put together some more specific questions (which we'll try
our best to answer), or just assume that the API works like it's
supposed to and make use of it as you see fit.


As far as your other issue goes, I'm going to need to look at the code
myself, and I don't have Charcoal set up locally. I'll have to get
back to you when I have a little bit more time and you've hopefully
had a chance to digest this.

Owen

ringmaster

unread,
Apr 12, 2008, 6:16:55 PM4/12/08
to habari-dev
On Apr 11, 2:37 pm, Mister Hilder <misterhil...@googlemail.com> wrote:
> > It might be that $theme-> methods automatically output stuff for you.
>
> You're right, I've just looked in Theme::_call(), and the return of
> overloaded functions does get echoed. I'm a little disappointed by
> that; I feel that I can't comment-out the 'echo' lines without
> breaking something elsewhere (maybe in the admin pages), and I doubt
> this would be changed in the source because all the themes 'in the
> wild' were built on this design. I'm extremely fussy when it comes to
> code standards and conventions (I won't allow whitespace in HTML
> output, for example), and I was hoping not to have re-write the entire
> CMS just to match my fussy brain. Oh well.

Ok, so it turns out that the answer for this is quite simple, although
understandably complex on the implementation side.

When a template in the active theme is executing, the variable $theme
is set to the instance of the Theme itself. You can call methods on
that object normally, as you would expect. Since most themes derive
from the core Theme class, they implement the magic __call()
function. This allows a theme to implement methods that aren't
explicitly declared in the class.

See the __call() function declared in system/classes/theme.php at the
end of the file, line 614. It's a tad complicated, so I'll try
explaining it first by what it's attempting to do.

First off, calling a method on a theme object that starts with "act_"
triggers plugin filters. This is so that plugins can implement new
ways of assembling data to send to a theme. For example, you might
want to build a plugin that adds a new URL to the site like /
incoming_links

To write a plugin to implement /incoming_links, you'd need to add a
new RewriteRule that tells Habari to execute a specific function when
that URL is encountered. These functions all start with "act_", so
you might use "act_incoming_links". The function is dispatched to the
current theme class. The current theme class doesn't have that
function, but since it starts with "act_" Habari executes the plugin
action "theme_action_incoming_links". If the plugin implements a
function named "action_theme_action" then the plugin will have a
chance to produce a page for that URL.

Obviously, this doesn't have much to do with themes. I can't think of
a reason you'd call $theme->act_whatever() from a template, but you
never know, someone might think of something.

Apart from the "act_" prefix, the overloaded function calls are
similar to the suffixes I mentioned in my prior reply. Each function
call gets assigned a "purpose" depending on the suffix it is called
with.

By default, any function called without a predefined suffix has the
purpose "output". This is why when you call $theme-
>post_comments_link() in Charcoal, it outputs. It is because it uses
no suffix and the default purpose is for output. You could change
that though.

If you instead called the function $theme-
>post_comments_link_return(), it would do exactly the same thing as
$theme->post_comments_link() but it would only return the value, not
output it. So in your case, that might be what you're looking for.

Finally there's one more purpose, which is "_end". "_end" is a handy
way to output and return the last element of an array that is normally
returned by a function. So if you would normally call $theme-
>get_my_list_return() to return an of elements, you could instead call
$theme->get_my_list_end() to output the last element of that array.
Sounds strange, but it's useful.

So, to review, the reason why that function outputs by default is
because for theme functions, the default "purpose" is to output the
return value from the plugin filters. If you want only to have the
value returned and not output, then apply the "_return" suffix to the
function in the template file.

You should not need to alter any core functions to get what you want.
The idea was to force these filters to all return their values (rather
than echo them themselves) so that you could manipulate them, but the
expectation is that you'd probably just want to output the returned
value anyway in most cases. Hence what seems like a sensible default
setting.

Hopefully that explains the issues you had. If you have any
questions, you know where to find us.

Owen


Reply all
Reply to author
Forward
0 new messages