Jump to content

PHP Gurus - I need thy aid!


Arthmoor

Recommended Posts

So yeah. After upgrading the server things run on here, I found that PHP is now crying about a function I use in my blog. Being the boob I apparently am, I have no idea how to fix the problem it's whining at me about (or was, until I slapped it silly so the blog would run).

 

Here's the problem:

 

preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead

 

Here's the code this is currently complaining in:

 

    private function pre_parse_links( $in )
    {
        $parse = array(
            'matches' => array('~(^|\s)([a-z0-9-_.]+@[a-z0-9-.]+\.[a-z0-9-_.]+)~i',
                '~(^|\s)(http|https|ftp)://(\w+[^\s\[\]]+)~ise'),
            'replacements' => array('\\1[email=\\2]\\2[/email]',
                '\'\\1[url=\\2://\\3]\\2://\\3[/url]\'')
        );

        return preg_replace($parse['matches'], $parse['replacements'], $in);
    }

 

Apparently the "/e modifier" is some kind of default to preg_replace(). I have no idea how to turn this into something preg_replace_callback() will like because I have never really gotten the hang of how crazy stuff like callback functions work.

 

If anyone here happens to know how to fix this I'd be quite grateful for some help. I really don't want to have to wade into the hell that is most PHP support forums over what they're sure to tell me is something I should be able to figure out on my own.

Link to comment
Share on other sites

But it doesn't appear to me that you're using the /e modifier in that call. Rather, from what I can tell, the option is set in an option file somewhere. Here's the page that discusses it. If you can find where that option is set, then you should be able to disable it and your function will work fine. It's pretty late for me, so I can't look up where the option is set atm. That page I linked also shows a good example of converting the replacement string to a callback function, should that be necessary, but I don't think it's needed in your case.

Link to comment
Share on other sites

Sorry, dunno PHP

$In is the string of interest that is modified and returned to you. The value of the function itself is presumably 0 = success, 1 = a wobbly

It looks like "Matches" and "Replacements" are each defined as a string array with two string elements. Does the comma need an escape sequence? \','

Link to comment
Share on other sites

return preg_replace_callback(
    $parse['matches'],
    function($parse) { return $parse['replacements']; },
    $in
    );

Caveat - I don't have php installed here at present so I can't test that (so please forgive any syntax errors), but from examining the page AndalayBay linked, and this page it looks sort of right. Basically just replace the 2nd parameter with a function call which returns the same info as the original code.

 

Even if there's a configuration option it's probably not a good idea to change it - this being a security issue (hence the deprecation of preg_replace).

Link to comment
Share on other sites

Erm. That got rid of the complaints about the /e modifier but now (after removing all the e modifiers) I'm getting this instead:

Undefined index: replacements

Notice [8]:

        The error was reported on line 185 of /home/****/public_html/lib/bbcode.php


        Code:
182         // return preg_replace($parse['matches'], $parse['replacements'], $in);
183         return preg_replace_callback(
184             $parse['matches'],
185             function($parse) { return $parse['replacements']; },
186             $in
187             );
188     }

 

'replacements' can't be an undefined index though, so what's it really complaining about?

Link to comment
Share on other sites

Sorry, I have no idea why that doesn't work - my PHP is a bit rusty ;) . Try the other format - a separately defined function (not an in-line one). Example 2 on the page I linked to shows how to do that.

 

Something like:-

function re_placements ($parse) { return $parse['replacements']; }

return preg_replace_callback(
   $parse['matches'],
   "re_placements",
   $in
   );
Link to comment
Share on other sites

I'll give it a shot, I saw their examples but they all deal with single issue cases and don't seem to cover array replacements at all and I have no idea how to go about that.

 

Frankly I'm not even sure whatever security issue this is about is worth the hassle it's putting a lot of folks through to adapt to.

Link to comment
Share on other sites

Sadly it's the world we live in. Hackers attack sites to get logins (and email addresses), many people use the same logins all over - including for their banks - which what they're really after.

 

Any loophole will be exploited, so it's understandable that a language like PHP, given its prevalence on the 'net, really has to be as secure as possible otherwise they'll get the blame for everything.

 

Sucks? Damn right it does, but rather that than letting the hackers/spammers/other_lowlifes win.

 

The actual issue is that preg_replace incorporates an eval (evaluate, i.e. execute, the contents of the string) which effectively gives the hacker control of the php to do database queries and such like. Pretty risky leaving a door like that wide open.

 

[edit] I guess you could pass the array index in to the function instead of the name (using the 1st form) - maybe? [/edit]

Link to comment
Share on other sites

Erm. That got rid of the complaints about the /e modifier but now (after removing all the e modifiers) I'm getting this instead:

 

 

 

'replacements' can't be an undefined index though, so what's it really complaining about?

 

I know I'm going blind, but I didn't see any e modifiers in that pre_parse_links function. Do you mean somewhere else?

 

Anyway, I think Screwball has it right. Add his code to your original function like this:

private function pre_parse_links( $in )
   {
        $parse = array(
            'matches' => array('~(^|\s)([a-z0-9-_.]+@[a-z0-9-.]+\.[a-z0-9-_.]+)~i',
                '~(^|\s)(http|https|ftp)://(\w+[^\s\[\]]+)~ise'),
            'replacements' => array('\\1[email=\\2]\\2[/email]',
                '\'\\1[url=\\2://\\3]\\2://\\3[/url]\'')
        );

       function re_placements ($parse) { return $parse['replacements']; }

return preg_replace_callback(
   $parse['matches'],
   "re_placements",
   $in
   );
    }

I'm not sure if you understood that or not, so I thought I'd clarify. :)

Link to comment
Share on other sites

Seems like progress made.... maybe. I have this now:

 

    private function pre_parse_links( $in )
    {
        $parse = array(
            'matches' => array('~(^|\s)([a-z0-9-_.]+@[a-z0-9-.]+\.[a-z0-9-_.]+)~i',
                '~(^|\s)(http|https|ftp)://(\w+[^\s\[\]]+)~is'),
            'replacements' => array('\\1[email=\\2]\\2[/email]',
                '\'\\1[url=\\2://\\3]\\2://\\3[/url]\'')
        );

        function re_placements ($parse) { return $parse['replacements']; }

        // return preg_replace($parse['matches'], $parse['replacements'], $in);
        return preg_replace_callback(
           $parse['matches'],
           "re_placements",
           $in
        );
    }

 

Which generates this error in my HTML logs:

 

[Mon Dec 23 16:55:36.471682 2013] [:error] [pid 24363] PHP Fatal error:  Cannot redeclare re_placements() (previously declared in xxxxxxxxxxx/public_html/lib/bbcode.php:182) in xxxxxxxxxx/public_html/lib/bbcode.php on line 182
 

Line 182: function re_placements ($parse) { return $parse['replacements']; }

 

This is code for a bbcode parser in the blog, which is open source - you can see what's there now here: http://code.google.com/p/sandbox/source/browse/sandbox/Dev/lib/bbcode.php

Link to comment
Share on other sites

Arthmoor

 

If I were you I would ask Fligg about how to fix this and I know he knows for sure since he is a PHP programmer plus he is well known in the Morrowind community for being accommodating to almost anyone.

Link to comment
Share on other sites

@lmstearn and AndalayBay - the problem is not the regex as it (presumably) worked fine with the preg_replace function. It's simply how to convert that call to use preg_replace_callback instead. Preferably without causing any errors ;)

If recursion wasn't needed before then it shouldn't be needed now.

 

How on earth it thinks it is redeclaring it I don't know - that's exactly how the example shows it being used.

 

[edit] Have another look at the page I linked earlier - especially the comments at the bottom. There are caveats about using preg_replace_callback with BBCode :( [/edit]

Link to comment
Share on other sites

Got me, but it clearly doesn't like it.

 

I saw the blurb about bbcode but rewriting the parser is beyond me at this point so I'll just have to live with leaving the error shut off for now. I know that's delaying the inevitable, but what the heck do they seriously expect people to do in situations like this?

Link to comment
Share on other sites

Kinda surprised nobody seems to have put out a nice simple library for this sort of thing after all this time.

Link to comment
Share on other sites

Ok, I would try getting rid of the re_placements function declaration altogether. Basically take your original function and replace the preg_replace call with preg_replace_callback. There seemed to be some variants of the callback version that would accept a string.

If that doesn't work, then I'd have to grab the entire code and play with it for a bit. It's been a while for me too. :P

Link to comment
Share on other sites

 A thought.... Maybe the problem isn't using preg_replace but the contents of the regex being interpreted as setting the "e" modifier (PREG_REPLACE_EVAL) unintentionally which is the cause of the initial problem. The following code has an explicit /e which could well be the cause (no guarantees mind you, just a guess).

'replacements' => array('\\1[email=\\2]\\2[/email]',

If you could escape that e somehow then you could circumvent the problem (possibly). Don't ask me how though, regular expressions look like pure gibberish to me  ;)

See this page noting the comments about the PREG_REPLACE_EVAL modifier and also the 5th comment at the bottom of the page.
 

 

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...