FolkLore
asked on
Regex help needed to write placeholder replacement function
Hi all,
I am busy writing a placeholder replacement system for my CodeIgniter CMS. The premise of my CMS is to make it completely customizable by non-developers for modules that are already developed, by allowing the non-developer to put a placeholder anywhere in his/her template to replace the placeholder with an existing module on the fly.
Example:
The regex that I need will have to:
1. Understand that I try to load a module ([[mod:) (could also be an image, box or variable)
2. The module I want to load is "user" and the function is called "login"
3. The remainder of matches (up to 5 parameters) are for parameters for the function. In this example, I am telling the login function to redirect to the home page, and echo out any errors, if found.
I have no idea how to get this working. I have been struggling for days on this.
Herewith my entire code for this section. I guess it would be best to show the whole code section to make it easier for someone to understand.
So:
1. I set the regex (CORRECT).
2. Do a preg_match_all (CORRECT).
3. I check the number of parameters via $gets variable (CORRECT).
4. Checks if the class is loaded, if not, then load it (CORRECT).
5. Checks if the method in the class exists (CORRECT).
6. ob_start(); (CORRECT, I think).
7. Execute the function based on the number of parameters (CORRECT).
8. ob_end_clean(); (CORRECT, I think).
Now here is where things are becoming hairy:
9. Replacements are not done correctly. Here is an example:
Template contains
[[mod:header:load_header:1 :echo]]
[[mod:footer:load_footer:1 :echo]]
When replacing the header with load_header, this introduces another placeholder:
[[mod:advertising:load_adv ertising:1 :home:hero :echo]]
So, the preg_replace(...) function replaces the header. Then, for some reason, it replaces the advertising one with the footer module. WTF? I think this has to do with the fact that the footer placeholder is the "original" second item, but now becomes the third item because the header placeholder introduced the advertising placeholder.
I tried setting and not setting the "limit" variable, and neither give me the correct result...
In short, it should replace ALL occurrences of the particular match, but only on exact match.
How do I approach this problem? How do I fix it?
Thanks!
Kobus
I am busy writing a placeholder replacement system for my CodeIgniter CMS. The premise of my CMS is to make it completely customizable by non-developers for modules that are already developed, by allowing the non-developer to put a placeholder anywhere in his/her template to replace the placeholder with an existing module on the fly.
Example:
<!doctype html>
<html>
<head>
...
...
</head>
<body>
[[mod:user:login:home:echo]]
...
</body>
</html>
The regex that I need will have to:
1. Understand that I try to load a module ([[mod:) (could also be an image, box or variable)
2. The module I want to load is "user" and the function is called "login"
3. The remainder of matches (up to 5 parameters) are for parameters for the function. In this example, I am telling the login function to redirect to the home page, and echo out any errors, if found.
I have no idea how to get this working. I have been struggling for days on this.
Herewith my entire code for this section. I guess it would be best to show the whole code section to make it easier for someone to understand.
function replace_mod($h)
{
$ci =& get_instance();
$gets = explode('/', uri_string());
$holder = $h;
$backup = $h;
$module_pattern = "/\[\[mod:(.*?):(.*?)(?::(.*?))?(?::(.*?))?(?::(.*?))?\]\]/i";
preg_match_all($module_pattern, $holder, $module_matches);
// Bring all modules called inside content into play on the page
if (is_array($module_matches[0]) && count($module_matches[0]) > 0)
{
foreach ($module_matches[0] as $key => $val)
{
if (file_exists(BASEPATH . '../application/modules/' . $module_matches[1][$key] . '/controllers/' . $module_matches[1][$key] . '.php'))
{
if (!class_exists($module_matches[1][$key]))
{
$ci->load->module($module_matches[1][$key]);
}
if (method_exists($module_matches[1][$key], $module_matches[2][$key]))
{
$is_active = $ci->core->get_module_active($module_always_matches[1][$key]);
if (isset($is_active[0]['active']) && $is_active[0]['active'] == 'Yes')
{
if (!isset($gets[3]) || (isset($gets[3]) && $gets[3] !== 'edit'))
{
ob_start();
if (isset($module_matches[7][$key]) && trim($module_matches[7][$key]) !== '')
{
$ci->$module_matches[1][$key]->$module_matches[2][$key]($module_matches[3][$key], $module_matches[4][$key], $module_matches[5][$key], $module_matches[6][$key], $module_matches[7][$key]);
}
elseif (isset($module_matches[6][$key]) && trim($module_matches[6][$key]) !== '')
{
$ci->$module_matches[1][$key]->$module_matches[2][$key]($module_matches[3][$key], $module_matches[4][$key], $module_matches[5][$key], $module_matches[6][$key]);
}
elseif (isset($module_matches[5][$key]) && trim($module_matches[5][$key]) !== '')
{
$ci->$module_matches[1][$key]->$module_matches[2][$key]($module_matches[3][$key], $module_matches[4][$key], $module_matches[5][$key]);
}
elseif (isset($module_matches[4][$key]) && trim($module_matches[4][$key]) !== '')
{
$ci->$module_matches[1][$key]->$module_matches[2][$key]($module_matches[3][$key], $module_matches[4][$key]);
}
elseif (isset($module_matches[3][$key]) && trim($module_matches[3][$key]) !== '')
{
$ci->$module_matches[1][$key]->$module_matches[2][$key]($module_matches[3][$key]);
}
else
{
$ci->$module_matches[1][$key]->$module_matches[2][$key]();
}
$dats = ob_get_clean();
$holder = preg_replace($module_pattern, $dats, $holder, 1);
// The change was unsuccessful for some reason - therefore, replaces the placeholder with nothing.
if ($holder === $backup)
{
$holder = preg_replace($module_pattern, '', $holder, 1);
}
}
}
}
}
else
{
$holder = preg_replace($module_pattern, '', $holder, 1);
}
}
}
return $holder;
}
So:
1. I set the regex (CORRECT).
2. Do a preg_match_all (CORRECT).
3. I check the number of parameters via $gets variable (CORRECT).
4. Checks if the class is loaded, if not, then load it (CORRECT).
5. Checks if the method in the class exists (CORRECT).
6. ob_start(); (CORRECT, I think).
7. Execute the function based on the number of parameters (CORRECT).
8. ob_end_clean(); (CORRECT, I think).
Now here is where things are becoming hairy:
9. Replacements are not done correctly. Here is an example:
Template contains
[[mod:header:load_header:1
[[mod:footer:load_footer:1
When replacing the header with load_header, this introduces another placeholder:
[[mod:advertising:load_adv
So, the preg_replace(...) function replaces the header. Then, for some reason, it replaces the advertising one with the footer module. WTF? I think this has to do with the fact that the footer placeholder is the "original" second item, but now becomes the third item because the header placeholder introduced the advertising placeholder.
I tried setting and not setting the "limit" variable, and neither give me the correct result...
In short, it should replace ALL occurrences of the particular match, but only on exact match.
How do I approach this problem? How do I fix it?
Thanks!
Kobus
Ray Paseur makes some extremely valuable points, but in a follow-up, I would like to interject that from what you have presented, it looks very much like you're attempting to write a RESTful parser and I naturally have to ask, without any intention of insulting you, why you are wishing to re-invent an already well established wheel?
~AB
~AB
ASKER
Hi guys,
Thanks for the comments. Yes, I have indeed not been here for a long time. I have now bought a premium subscription to get this question answered, because I have been searching for a long time, and people at Stack Overflow consider my question "too localized" and therefore I can not ask it there.
I am not sure how the points assigned to a question where if I am a premium member. How many points do I have available? I have upped this one to 500 points, let's see if it takes.
To answer your other comments about not knowing what the inputs are and what outputs are desired, I thought I had it nailed when I posted my question, but let me try again with a simpler example:
I have a file called template.php, which contains among other things, the following:
This instructs my CMS to look for the module "header" and call function "load_header" and echo the results out on screen. The same happens to the footer. But the problem comes in the fact that the header's view, called "header.php" also has a placeholder in it.
So, at the start of execution of the script (that grabs the CodeIgniter's output via a hook), the regex that I have does a preg_match_all() and thus finds the header and footer replacement items (2 total items).
Now that the header is being replaced, a new placeholder is introduced, namely the advertising one. This is expected (will never have more than a second tier place holder, meaning, advertising module will not have another placeholder itself).
I understand that I will have to run the "filter" a second time, because I now need to replace the advertising placeholder, but for some reason, the ADVERTISING placeholder is now replaced with the footer content, and when running the filter a second time, the FOOTER placeholder now gets replaced by the footer. This results in me getting the header and TWO footers, but what I needed was one header, one advert and one footer.
In short:
Expected:
1. Pass 1 of filter: replace header and footer placeholders with header and footer content respectively.
2. Pass 2 of filter: replace advertisement placeholder with advertisement content.
But what happens:
1. Pass 1 of filter: replace header and advertisement placeholders with header and footer content respectively.
2. Pass 2 of filter: replace footer placeholder with footer content.
I hope this makes more sense now. Using the code above (in the original question), I hope you can solve this problem for me.
Thanks!
Kobus
Thanks for the comments. Yes, I have indeed not been here for a long time. I have now bought a premium subscription to get this question answered, because I have been searching for a long time, and people at Stack Overflow consider my question "too localized" and therefore I can not ask it there.
I am not sure how the points assigned to a question where if I am a premium member. How many points do I have available? I have upped this one to 500 points, let's see if it takes.
To answer your other comments about not knowing what the inputs are and what outputs are desired, I thought I had it nailed when I posted my question, but let me try again with a simpler example:
I have a file called template.php, which contains among other things, the following:
[[mod:header:load_header:echo]]
[[mod:footer:load_footer:echo]]
This instructs my CMS to look for the module "header" and call function "load_header" and echo the results out on screen. The same happens to the footer. But the problem comes in the fact that the header's view, called "header.php" also has a placeholder in it.
[[mod:advertising:load_advert:home:hero:echo]]
So, at the start of execution of the script (that grabs the CodeIgniter's output via a hook), the regex that I have does a preg_match_all() and thus finds the header and footer replacement items (2 total items).
Now that the header is being replaced, a new placeholder is introduced, namely the advertising one. This is expected (will never have more than a second tier place holder, meaning, advertising module will not have another placeholder itself).
I understand that I will have to run the "filter" a second time, because I now need to replace the advertising placeholder, but for some reason, the ADVERTISING placeholder is now replaced with the footer content, and when running the filter a second time, the FOOTER placeholder now gets replaced by the footer. This results in me getting the header and TWO footers, but what I needed was one header, one advert and one footer.
In short:
Expected:
1. Pass 1 of filter: replace header and footer placeholders with header and footer content respectively.
2. Pass 2 of filter: replace advertisement placeholder with advertisement content.
But what happens:
1. Pass 1 of filter: replace header and advertisement placeholders with header and footer content respectively.
2. Pass 2 of filter: replace footer placeholder with footer content.
I hope this makes more sense now. Using the code above (in the original question), I hope you can solve this problem for me.
Thanks!
Kobus
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hi Adrian,
I could, I am sure! I only need the preg_match_all to find the string in the output, but never considered that a normal old explode could actually work a lot better. it will probably be faster as well, as preg* functions are quite intensive... Hmmm... I will try and provide feedback. Thanks for the answer!
Kobus
I could, I am sure! I only need the preg_match_all to find the string in the output, but never considered that a normal old explode could actually work a lot better. it will probably be faster as well, as preg* functions are quite intensive... Hmmm... I will try and provide feedback. Thanks for the answer!
Kobus
You're quite welcome. :)
I'm a firm believer in and avid user of Occam's razor when it comes to programming/problem solving.
I will be more than glad to offer more assistance in light of your additional research.
~AB
I'm a firm believer in and avid user of Occam's razor when it comes to programming/problem solving.
I will be more than glad to offer more assistance in light of your additional research.
~AB
ASKER
Hi Adrian,
Occam's razor. Yes, it is amazing how often we make things more complex than they need to be.
I have redeveloped the piece of code, using
In my case the following is true:
1. Using str_replace() as opposed to preg_replace() makes -0.001 difference :-)
2. One pass vs two passes makes 0.02s difference (Not bad for running it a second time)
You have put me on the right track, therefore, I will accept your answer as solution :-)
Thanks for your help!
Kobus
Occam's razor. Yes, it is amazing how often we make things more complex than they need to be.
I have redeveloped the piece of code, using
str_replace()
as opposed to
preg_replace()
, and used the profiler to determine whether it has a speed implication.In my case the following is true:
1. Using str_replace() as opposed to preg_replace() makes -0.001 difference :-)
2. One pass vs two passes makes 0.02s difference (Not bad for running it a second time)
You have put me on the right track, therefore, I will accept your answer as solution :-)
Thanks for your help!
Kobus
ASKER
Occam's razor. Always better to make things as simple as possible. Thanks AB.
It's been a pleasure assisting you, Folklore. I'm very glad this was helpful for you.
Happy coding. :)
~AB
Happy coding. :)
~AB
At EE, the experts exchange answers and advice for points. If you look at the questions awaiting answers in this zone, you will see a lot of 500 point questions. Your question is competing for the experts' attention among those high-point questions. So as a matter of simple economics you might be able to envision which questions will get the experts' attention first. Just a thought.
I'm not sure how to help you here because I really do not understand the inputs you have and the outputs you want. But I can say for sure that computer programming is only about one thing: transforming input data into output data. If you can give us a collection of inputs and show us the corresponding outputs, we can probably help with a regular expression that bridges the gap.
This article may be helpful in guiding your thinking about how to set up the examples.
https://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_7830-A-Quick-Tour-of-Test-Driven-Development.html
Best regards, ~Ray