Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
optional parameters of "component"
#1
Now component is like an un parameterised micro-template. I wish I have an ability to use it in a manner of function, like

Code:
<?
$paramValue2 = 'test';
get_component('article', 'paramValue1', $paramValue2);
?>

This could be usefull when component contains PHP code that should change it's behavior due to parameters.
Reply
#2
You need the EXEC-PHP plugin

http://get-simple.info/extend/plugin/exec-php/17/
My Github Repos: Github
Website: DigiMute
Reply
#3
Hm... It seems you've got me wrong. PHP code can be used in components without any plugins - just out of box. It would be cool, if those components should have parameters.
Reply
#4
bugman Wrote:Now component is like an un parameterised micro-template. I wish I have an ability to use it in a manner of function, like

Code:
<?
$paramValue2 = 'test';
get_component('article', 'paramValue1', $paramValue2);
?>

This could be usefull when component contains PHP code that should change it's behavior due to parameters.

Using the DynPages plugin you have this possibility, when including the component on a page.

If you want to use it from the template, use a function like this:
Code:
function get_component_with_params($name, $params=array()) {
  global $args;
  if (isset($args)) $saved_args = $args;
  $args = $params;
  get_component($name);
  if (isset($saved_args)) $args = $saved_args; else unset($args);
}

In the component use the global variable $args, e.g.
Code:
global $args;
echo "Parameter 1 = $args[0]";

Call with
Code:
get_component_with_params('article', array('paramValue1','paramValue2'));
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#5
Thanks, mvlcek. I've thought about implementation just in that way. But I've also hoped, that there's no cons to have this ability right in GS without any plugins Smile I think of components like of function with frendly web interface to develop Smile
Reply
#6
<<< is very tired today.. and totally misunderstood 8)
My Github Repos: Github
Website: DigiMute
Reply
#7
n00dles101 Wrote:<<< is very tired today.. and totally misunderstood 8)

Anyway thanks for rapid response. I've seen your cast of processWire - great one! It seems that it has anything i ever need of cms/cmf.
Reply
#8
bugman Wrote:Thanks, mvlcek. I've thought about implementation just in that way. But I've also hoped, that there's no cons to have this ability right in GS without any plugins Smile I think of components like of function with frendly web interface to develop Smile

I18N version 1.7 now supports calling components with parameters, set here.
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#9
@mvlcek:

I see you are using a global variable inside your implementation, why? You can do this without leaking any data into the global scoop, that way you don’t have to bother with back-upping any already existing variables either.

This works just as well: (large bit of code directly from the GS core)
Code:
function get_component_args($id) {
    if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
    if (file_exists(GSDATAOTHERPATH.'components.xml')) {
        $data = getXML(GSDATAOTHERPATH.'components.xml');
        $components = $data->item;
        if (count($components) != 0) {
            foreach ($components as $component) {
                if ($id == $component->slug) {
                    eval("?>" . strip_decode($component->value) . "<?php ");
                }
            }
        }
    }
}
And can be used like this, in a template:
Code:
get_component_args('credits','Zegnåt','http://zegnat.net/');
and with a component called credits:
Code:
<p>Build by <a href="<?php echo $args[1]; ?>"><?php echo $args[0]; ?></a></p>
Please stay out of the global scoop unless you need to communicate with global data.

--- --- --- --- ---

The big question for this topic to answer is: should this be implemented in the core?
“Don’t forget the important ˚ (not °) on the a,” says the Unicode lover.
Help us test a key change for the core! ¶ Problems with GetSimple? Be sure to enable debug mode!
Reply
#10
Zegnåt Wrote:@mvlcek:

I see you are using a global variable inside your implementation, why? You can do this without leaking any data into the global scoop, that way you don’t have to bother with back-upping any already existing variables either.

Yes, I know this. But I want to stay compatible with the DynPages plugin, which just calls get_component() and thus has to pass the parameters as globals.
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#11
Zegnåt Wrote:The big question for this topic to answer is: should this be implemented in the core?
can someone explain what needs to change in the core to me so I don't have to try and decipher this whole thread to decide if it should be or not? Thanks
- Chris
Thanks for using GetSimple! - Download

Please do not email me directly for help regarding GetSimple. Please post all your questions/problems in the forum!
Reply
#12
tl;dr for Chris:

The proposed solution has the original get_component() code in it. The one big addition is:
Code:
if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
This puts all arguments other than the first (which we assume is the component name) in an array $args.

Now the component code can use $args[n] to make use of arguments passed from the template to the get_component function:
Code:
get_component('name','argument0','argument1', …)
“Don’t forget the important ˚ (not °) on the a,” says the Unicode lover.
Help us test a key change for the core! ¶ Problems with GetSimple? Be sure to enable debug mode!
Reply
#13
Zegnåt Wrote:tl;dr for Chris:

The proposed solution has the original get_component() code in it. The one big addition is:
Code:
if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
This puts all arguments other than the first (which we assume is the component name) in an array $args.

Now the component code can use $args[n] to make use of arguments passed from the template to the get_component function:
Code:
get_component('name','argument0','argument1', …)

Yes, that's it.
BTW: a return after the eval would be beneficial (because of speed and because the component could set the variable $id - which could lead to unwanted results).
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#14
mvlcek Wrote:BTW: a return after the eval would be beneficial
What kind of return would you suggest? Something like this? (Modifying get_component() from the SVN.)

Code:
function get_component($id) {
    global $components;
    if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
    if (!$components) {
         if (file_exists(GSDATAOTHERPATH.'components.xml')) {
            $data = getXML(GSDATAOTHERPATH.'components.xml');
            $components = $data->item;
        } else {
            $components = array();
        }
    }
    if (count($components) > 0) {
        foreach ($components as $component) {
            if ($id == $component->slug) {
                $eval = eval("?>" . strip_decode($component->value) . "<?php ");
            }
        }
    }
    return $eval===NULL||$eval!==FALSE;
}
“Don’t forget the important ˚ (not °) on the a,” says the Unicode lover.
Help us test a key change for the core! ¶ Problems with GetSimple? Be sure to enable debug mode!
Reply
#15
Zegnåt Wrote:
mvlcek Wrote:BTW: a return after the eval would be beneficial
What kind of return would you suggest?

I just meant a return directly after the eval. But as you mention it, in the I18N plugin I return, if the component exists or not:

Code:
function get_component($id) {
    global $components;
    if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
    if (!$components) {
         if (file_exists(GSDATAOTHERPATH.'components.xml')) {
            $data = getXML(GSDATAOTHERPATH.'components.xml');
            $components = $data->item;
        } else {
            $components = array();
        }
    }
    if (count($components) > 0) {
        foreach ($components as $component) {
            if ($id == $component->slug) {
                eval("?>" . strip_decode($component->value) . "<?php ");
                return true;
            }
        }
    }
    return false;
}

This way you can use code like this in your template (custom sidebar for specific pages):
Code:
<?php get_component('sidebar-'.return_page_slug()) || get_component('sidebar'); ?>
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#16
mvlcek Wrote:
Zegnåt Wrote:
mvlcek Wrote:BTW: a return after the eval would be beneficial
What kind of return would you suggest?
I just meant a return directly after the eval.
I guess this would be better performance wise, I just don’t like returns inside loops. Maybe put a break there instead.

mvlcek Wrote:But as you mention it, in the I18N plugin I return, if the component exists or not
I have one issues with your code as it stands: it doesn’t return whether the component actually works or not. eval() is no magic and the PHP inside might fail. My proposed return is this: (Where $eval holds the return value of eval().)
Code:
$eval===NULL||$eval!==FALSE;
Any eval() that just runs code will return NULL on success, it will return FALSE if it encounters a PHP error or it might return any arbitrary value if someone used “return”. The above code will return TRUE if the component has produced a non-FALSE response.

Let me know what you think of that.

Combining:

Code:
function get_component($id) {
    global $components;
    if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
    if (!$components) {
         if (file_exists(GSDATAOTHERPATH.'components.xml')) {
            $data = getXML(GSDATAOTHERPATH.'components.xml');
            $components = $data->item;
        } else {
            $components = array();
        }
    }
    if (count($components) > 0) {
        foreach ($components as $component) {
            if ($id == $component->slug) {
                $eval = eval("?>" . strip_decode($component->value) . "<?php ");
                break;
            }
        }
    }
    return $eval===NULL||$eval!==FALSE;
}

If you can agree with this as a solution I could put it in the core right now.
“Don’t forget the important ˚ (not °) on the a,” says the Unicode lover.
Help us test a key change for the core! ¶ Problems with GetSimple? Be sure to enable debug mode!
Reply
#17
Zegnåt Wrote:
mvlcek Wrote:
Zegnåt Wrote:What kind of return would you suggest?
I just meant a return directly after the eval.
I guess this would be better performance wise, I just don’t like returns inside loops. Maybe put a break there instead.

Not just for better performance, but to prevent that variables set within the component influence the function.
And I don't like breaks, as they are basically a goto ;-)

Zegnåt Wrote:I have one issues with your code as it stands: it doesn’t return whether the component actually works or not.

And this is good IMHO.
As developer of components (with PHP code) I have to test the component (in debug mode) and will get error messages.

The result of get_component is for the user = caller of a component. As user I assume that the component works and I am (only) concerned about the functional logic: if component x does not exist, use
component y or display some static text. And it's not: if component x does not exist or is erroneous, ...

And components returning values would add additional complexity, the user would have to look at the component to know what it returns and would not be able to do something as described above.

I see components as snippets to include in the template or (via the DynPages plugin) in a page. In most cases it will contain HTML and simple code like <?php get_something(params); ?>. There is no need for return values. If I need to return something, a function is a better way to do it.

Zegnåt Wrote:My proposed return is this: (Where $eval holds the return value of eval().)
Code:
$eval===NULL||$eval!==FALSE;

BTW: I think this will output a warning (at least in debug mode), if the component does not exist, as $eval is not set :-(

So, I think, it's more confusing to "return true, if the component exists and works and does return nothing or at least does not return null or false" than to "return true, if the component exists".

But I can live with it, if with a non-faulty component it will return true, if it exists and false otherwise (given that the component itself does not return anything), and does so in debug mode, too.
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#18
mvlcek Wrote:And I don't like breaks, as they are basically a goto ;-)
We’ll let Chris decide over the multiple-returns or break usage then ;-)

mvlcek Wrote:As developer of components (with PHP code) I have to test the component (in debug mode) and will get error messages.
You still get error messages my way. Because the code is still eval()’d before I return anything. It’s just that the function will return FALSE if an error occurs, that way you can use it inside the template to offer an alternative when the component were to break.

mvlcek Wrote:And components returning values would add additional complexity, the user would have to look at the component to know what it returns and would not be able to do something as described above.
That’s something up to the “user” (as you call them) though. You might never want to use a return inside eval()’d code, I surely never want that, but someone might. It’s just a case I had to include in the check.

mvlcek Wrote:
Zegnåt Wrote:My proposed return is this: (Where $eval holds the return value of eval().)
Code:
$eval===NULL||$eval!==FALSE;

BTW: I think this will output a warning (at least in debug mode), if the component does not exist, as $eval is not set :-(

You are right, that’s my mistake, I will try to figure out what to do about that. The problem is that I can’t use isset(), because a working eval() will return NULL and isset(NULL)==FALSE.

I’m going to see what I can do.
“Don’t forget the important ˚ (not °) on the a,” says the Unicode lover.
Help us test a key change for the core! ¶ Problems with GetSimple? Be sure to enable debug mode!
Reply
#19
Zegnåt Wrote:That’s something up to the “user” (as you call them) though. You might never want to use a return inside eval()’d code, I surely never want that, but someone might. It’s just a case I had to include in the check.

"a case I had to include" is the wrong expression, as it's not necessary in my approach, where return values of the component are ignored (not supported) and the meaning of the return value of get_component is always: "true, if the component exists" ;-)

"a case I want to support" - with that I can agree ;-)

And I still think my approach is the cleaner one...
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#20
After a little testing, this is the correct way to find out whether a variable ($eval) is NULL or non-FALSE:
Code:
!isset($eval)&&array_key_exists('eval',get_defined_vars())||isset($eval)&&$eval!==FALSE
This is way too confusing, so it’s much easier to just set $eval to FALSE and take it from there.

We now have two proposed solutions to this problem, both have two alternative ways of writing—since they can even be made to work with a break or with two seperate returns. The only real difference between the solutions is mvlcek’s returns TRUE when a component is found, no matter what happens with eval() while mine will return TRUE only when a component is found and was correctly eval()’d.

Should this be put up for vote or something? :p

mvlcek:
Code:
function get_component($id) {
    global $components;
    if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
    if (!$components) {
         if (file_exists(GSDATAOTHERPATH.'components.xml')) {
            $data = getXML(GSDATAOTHERPATH.'components.xml');
            $components = $data->item;
        } else {
            $components = array();
        }
    }
    if (count($components) > 0) {
        foreach ($components as $component) {
            if ($id == $component->slug) {
                eval("?>" . strip_decode($component->value) . "<?php ");
                return true;
            }
        }
    }
    return false;
}
Uses 2 separate returns (as preferred by mvlcek over breaks). The function will return TRUE when a component exists with the provided $id and FALSE if there doesn’t.

Zegnåt:
Code:
function get_component($id) {
    global $components;
    $eval = FALSE;
    if (func_num_args() > 1) { $args = func_get_args(); array_shift($args); }
    if (!$components) {
         if (file_exists(GSDATAOTHERPATH.'components.xml')) {
            $data = getXML(GSDATAOTHERPATH.'components.xml');
            $components = $data->item;
        } else {
            $components = array();
        }
    }
    if (count($components) > 0) {
        foreach ($components as $component) {
            if ($id == $component->slug) {
                $eval = eval("?>" . strip_decode($component->value) . "<?php ");
                break;
            }
        }
    }
    return $eval!==FALSE;
}
Uses a break to stop the loop after a component has been found. The function will return TRUE when the component eval()’d correctly and FALSE if no component has been found to match the provided $id or the component produced PHP errors when being eval()’d. (In debug mode, these errors are still shown, nothing is hidden!)
“Don’t forget the important ˚ (not °) on the a,” says the Unicode lover.
Help us test a key change for the core! ¶ Problems with GetSimple? Be sure to enable debug mode!
Reply
#21
Comparison of the variants:
Code:
Component   Component    Component        Result    Result
exists     has errors    returns         mvlcek    ZegnÃ¥t
===============================================================
  yes         yes          false           true      false  <=
  yes         yes     nothing/not false    true      false  <=
  yes         no           false           true      false  <=
  yes         no      nothing/not false    true      true
  no          yes          false           false     false
  no          yes     nothing/not false    false     false
  no          no           false           false     false
  no          no      nothing/not false    false     false

In the important case - Component has no errors and returns nothing, both variants return the same.

So my only issues with Zegnåt's approach is that it is overly complex:
  • It tests, if the component has errors. But will users really check the return value for this? Is anybody - except in special cases - writing code to test, if some other code compiles???
  • On the other hand, in a realistic call, for which the return code of get_component was intended (by me, at least, based on feature requests in the forum) - like get_component('sidebar-'.return_page_slug()) || get_component('sidebar') - ZegnÃ¥t's approach has no advantages for testing/debugging. See below.

I'm assuming that component 'sidebar' has no errors, as it is the default and was already tested.
My approach:
  • sidebar is displayed, but shouldn't => sidebar-xxx does not exist
  • nothing or an error message is displayed => there is an error in sidebar-xxx
Zegnåt's approach:
  • sidebar is displayed, but shouldn't => sidebar-xxx does not exist or sidebar-xxx has an error (or sidebar-xxx returns false)
  • nothing or an error message is displayed => can't happen

Having said this and trying to keep it simple, I prefer my simple approach, because support for it is much simpler - it's easier to ask "does the component exist?" instead of "Does the component exist and compile and not return anything or at least not false?" - but I'm happy with either way.

BTW: I don't care if break or return is used - if the code works ;-)
I18N, I18N Search, I18N Gallery, I18N Special Pages - essential plugins for multi-language sites.
Reply
#22
So as I got it from the above you decided not to put it to core, ehm okay Smile
Reply
#23
no but i am glad you brought this up, since i added 2 arguments already to this function and that might cause issues if I want to add this.

however i am not entirely sure what this thread was attempting to solve, seems most of the discussion is on eval return values ???

components are eval'd, essentially anonymous functions
meaning there is no parameterization possible

I would simply pass func_args to the eval using a static variable for now.
Ideally components would be class methods that can have objects passed into them, since we still require php 5.2 and I dont feel like implementing janky old class constructs, I would just hack this in with an object and let you get it via $self or $this, instead of an actual reflection class or whatever the best solution would actually be in 5.3+

another solution would be to see if we can slip an extract into the code, and simply extract your argument array into the scope of the component.

extract is dangerous but shit we are already inside an eval, soooooo..

solutions welcome
NEW: SA Admin Toolbar Plugin | View All My Plugins
- Shawn A aka Tablatronix
Reply
#24
I like the approach with passing parameters via $args variable available to the evaluated code of component - for my own purpose I hacked my admin/inc/theme_functions.php to fix get_component function the way was shown, but it's up to you to decide on the best approach (if any at all).
Reply
#25
I would prefer if users do not have to do args[0]
Other that that
NEW: SA Admin Toolbar Plugin | View All My Plugins
- Shawn A aka Tablatronix
Reply




Users browsing this thread: 1 Guest(s)