[2 days left] What’s wrong with your cloud strategy? Learn why multicloud solutions matter with Nimble Storage.Register Now

x
?
Solved

Regex help needed to write placeholder replacement function

Posted on 2013-06-09
9
Medium Priority
?
681 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 4
9 Comments
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 39234490
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:Richard Davis
ID: 39234745
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
ID: 39238110
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
Moving data to the cloud? Find out if you’re ready

Before moving to the cloud, it is important to carefully define your db needs, plan for the migration & understand prod. environment. This wp explains how to define what you need from a cloud provider, plan for the migration & what putting a cloud solution into practice entails.

 
LVL 12

Accepted Solution

by:
Richard Davis earned 2000 total points
ID: 39238729
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
 

Author Comment

by:FolkLore
ID: 39240263
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:Richard Davis
ID: 39241183
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
ID: 39244899
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
ID: 39244905
Occam's razor. Always better to make things as simple as possible. Thanks AB.
0
 
LVL 12

Expert Comment

by:Richard Davis
ID: 39245262
It's been a pleasure assisting you, Folklore. I'm very glad this was helpful for you.

Happy coding. :)

~AB
0

Featured Post

Will your db performance match your db growth?

In Percona’s white paper “Performance at Scale: Keeping Your Database on Its Toes,” we take a high-level approach to what you need to think about when planning for database scalability.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this article you'll learn how to use Ajax calls within your CodeIgniter application. To explain this, I'll illustrate how to implement a simple contact form to allow visitors to send you an email through your web site.
Password hashing is better than message digests or encryption, and you should be using it instead of message digests or encryption.  Find out why and how in this article, which supplements the original article on PHP Client Registration, Login, Logo…
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…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …
Suggested Courses

649 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