Solved

Regex help needed to write placeholder replacement function

Posted on 2013-06-09
9
648 Views
Last Modified: 2013-06-13
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:

<!doctype html>
<html>
<head>
...
...
</head>
    <body>
        [[mod:user:login:home:echo]]
        ...
    </body>
</html>

Open in new window


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;
    }

Open in new window



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_advertising: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
0
Comment
Question by:FolkLore
  • 4
  • 4
9 Comments
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
I see you haven't used EE very much recently, FolkLore.  Here are some general guidelines to make your use of this site more enjoyable and productive for you...

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.
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_7830-A-Quick-Tour-of-Test-Driven-Development.html

Best regards, ~Ray
0
 
LVL 12

Expert Comment

by:adrian_brooks
Comment Utility
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
0
 

Author Comment

by:FolkLore
Comment Utility
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:

[[mod:header:load_header:echo]]
[[mod:footer:load_footer:echo]]

Open in new window


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]]

Open in new window


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
0
 
LVL 12

Accepted Solution

by:
adrian_brooks earned 500 total points
Comment Utility
Folklore,

Have you entertained the possibility of performing an explode on the string? Doing this would then allow you to have access to each element of the string and then you can create corresponding actions based on each element's value.

~AB
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:FolkLore
Comment Utility
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
0
 
LVL 12

Expert Comment

by:adrian_brooks
Comment Utility
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
0
 

Author Comment

by:FolkLore
Comment Utility
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
str_replace()

Open in new window

as opposed to
preg_replace()

Open in new window

, 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
0
 

Author Closing Comment

by:FolkLore
Comment Utility
Occam's razor. Always better to make things as simple as possible. Thanks AB.
0
 
LVL 12

Expert Comment

by:adrian_brooks
Comment Utility
It's been a pleasure assisting you, Folklore. I'm very glad this was helpful for you.

Happy coding. :)

~AB
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Recently I spent hours debugging an issue in a Rails project where ActiveRecord was causing MySQL errors trying to create a User object of a class at the top level of a Single Table Inheritance model structure.  It turns out `.create` behaves differ…
Since pre-biblical times, humans have sought ways to keep secrets, and share the secrets selectively.  This article explores the ways PHP can be used to hide and encrypt information.
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now