Simon Lloyd Posted April 30, 2012 Share Posted April 30, 2012 Hi all, this is my first post here AND i'm a novice so please be gentle. I own a forum and my members can post code (much like here) but i used a modified version of CODE tags, some of my members are either new to forums or just dont use the tags, what i'd like is, via php, to check to see if my KODE tags are in place if they are do nothing, if CODE tags are in place replace them with the KODE tags, if neither KODE or Code tags are in place look for Sub anytext(parameters) where anytext can be anything (there's always a space between Sub and anytext) and parameters may or may not be present so those parenthesis could simply look like () . When found replace Sub anytext(parameters) for [KODE]Sub anytext(parameters) then find End Sub and replace with End Sub[KODE] The only other anomalies would be the word Sub could appear as Function I can replace CODE tags if they exist, but cannot do the rest, so it would be great if it was all done in one go, i've heard that str_replace is quicker than preg_replace which of course would be preferrable, here's my effort at replacing the tags one way but it doesn't work. $pattern[0] = "Sub "; $pattern[1] = "End Sub"; $replace[0] = "[KODE]Sub\s"; $replace[1] = "End\sSub\[\/KODE\]"; $mystring = preg_replace($pattern, $replace, $mystring); The spaces are intentional, but i didnt know if i should have the whitespace character \s in the patterns. Update: This works to a fashion, however it doesn't check if any tags already exist and doubles up, not good! $pattern1 = "/Sub\s/"; $pattern2 = "/End Sub/"; $pattern3 = "/Function\s/"; $pattern4 = "/End Function/"; $replace1 = "[KODE]Sub "; $replace2 = "End Sub[/KODE]"; $replace3 = "[KODE]Function "; $replace4 = "End Function[/KODE]"; $MyString= preg_replace($pattern1, $replace1, $post['message']); $MyString= preg_replace($pattern2, $replace2, $post['message']); $MyString= preg_replace($pattern3, $replace3, $post['message']); $MyString= preg_replace($pattern4, $replace4, $post['message']); $MyString= preg_replace('/\\[code\\](.*)\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']); I'm sure this can be combined and made more efficient! Regards, Simon P.S I should mention im using PHP5.2.17 Update 2:I have wrapped the whole thing in this if (strpos($post['message'],'/[KODE]/') !== true AND strpos($post['message'],'/[/KODE]/') !== true ) { //All the preg_replace code } but now the first replacement for "Sub " doesn't seem to take place? Quote Link to comment Share on other sites More sharing options...
xyph Posted April 30, 2012 Share Posted April 30, 2012 What did you want to happen when a user enters Sub anytext(parameters) but no matching End Sub? How did you want the script to handle nested Sub/End Subs? Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted April 30, 2012 Author Share Posted April 30, 2012 Hey thanks for the reply, there will never be a Sub anytext(parameters) without End Sub and they are never nested within each other, however they may appear after each other like Sub Test() 'some code End Sub Sub Test2(MyTest by Val) 'some code End Sub Function TestFunc() 'some code End Function Function TestFunc2(Rng As Range) 'some code End Function the code should skip replacing Sub or Function if the [KODE] tag is present before it the same with End Sub or End Function [/KODE] tag appearing after them should cause it to be skipped from replacing, there would only be one strange anomaly and that is the word Function may appear within a sub like Sub Mytest() 'some code Function...etc End Sub so if the word function appears between Sub... and End Sub then it should not be replaced, it never happens the other way around like Sub appearing in the Function routine. Lol, have i made any sense? ask me anything else you need, i appreciate anything you can help with Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 1, 2012 Author Share Posted May 1, 2012 Just a short note, i've tried using the boundary limit "/\bSub (*)\b/".....etc but still get the tags doubling or tripling up! Quote Link to comment Share on other sites More sharing options...
xyph Posted May 1, 2012 Share Posted May 1, 2012 This should give you a good start <?php $expr = '/(\[kode\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2/si'; $data = "Sub Test() 'some code End Sub [KODE]Sub Test2(MyTest by Val) 'some code End Sub[/KODE] Function TestFunc() 'some code End Function Function TestFunc2(Rng As Range) 'some code End Function [KODE] Function preventsUs(from using lookaround) blah End Function [/KODE]"; preg_match_all( $expr, $data, $matches, PREG_SET_ORDER ); $parsed = array(); foreach( $matches as $match ) { if( !empty($match[1]) ) // check if [kode] was found continue; $parsed[] = array( 'name' => $match[3], 'arguments' => $match[4], 'body' => trim($match[5]) ); } print_r( $parsed ); ?> Output Array ( [0] => Array ( [name] => Test [arguments] => [body] => 'some code ) [1] => Array ( [name] => TestFunc [arguments] => [body] => 'some code ) [2] => Array ( [name] => TestFunc2 [arguments] => Rng As Range [body] => 'some code ) ) RegEx explained (\[kode\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2 Options: dot matches newline; case insensitive Match the regular expression below and capture its match into backreference number 1 «(\[kode\])?» Between zero and one times, as many times as possible, giving back as needed (greedy) «?» Match the character “[” literally «\[» Match the characters “kode” literally «kode» Match the character “]” literally «\]» Match a single character that is a “whitespace character” (spaces, tabs, and line breaks) «\s*» Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» Match the regular expression below and capture its match into backreference number 2 «(sub|function)» Match either the regular expression below (attempting the next alternative only if this one fails) «sub» Match the characters “sub” literally «sub» Or match regular expression number 2 below (the entire group fails if this one fails to match) «function» Match the characters “function” literally «function» Match a single character that is a “whitespace character” (spaces, tabs, and line breaks) «\s+» Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+» Match the regular expression below and capture its match into backreference number 3 «([a-z0-9]+)» Match a single character present in the list below «[a-z0-9]+» Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+» A character in the range between “a” and “z” «a-z» A character in the range between “0” and “9” «0-9» Match the character “(” literally «\(» Match the regular expression below and capture its match into backreference number 4 «([a-z0-9 ]*)» Match a single character present in the list below «[a-z0-9 ]*» Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» A character in the range between “a” and “z” «a-z» A character in the range between “0” and “9” «0-9» The character “ ” « » Match the character “)” literally «\)» Match the regular expression below and capture its match into backreference number 5 «(.*?)» Match any single character «.*?» Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?» Match the characters “end ” literally «end » Match the same text as most recently matched by capturing group number 2 «\2» Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 1, 2012 Author Share Posted May 1, 2012 Thanks for replying again, i used the code $expr = '/(\[KODE\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2/si'; preg_match_all( $expr, $post['message'], $matches, PREG_SET_ORDER ); $parsed = array(); foreach( $matches as $match ) { if( !empty($match[1]) ) // check if [kode] was found continue; $parsed[] = array( 'name' => $match[3], 'arguments' => $match[4], 'body' => trim($match[5]) ); } //print_r( $parsed ); I replaced $data with $post['message'] because thats the source of the data, i had to comment out the //print_r($parsed); as it was giving an error "headers already sent" but thats just down to the way vbulletin works, even with that fault top left of the screen it displayed "Array()" and no replacements were made, even after commenting out the the print command and running it no replacemnets were made. Am i using it wrong? did i need something from the Output code? Quote Link to comment Share on other sites More sharing options...
xyph Posted May 1, 2012 Share Posted May 1, 2012 This wasn't meant to be a plug-in solution, more of a guideline to help you accomplish what you're trying to do. If you need help integrating the RegEx into 3rd-party code, you could try the 3rd Party PHP Scripts forum. My example was more for educational purposes. Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 1, 2012 Author Share Posted May 1, 2012 Lol, i understand you we're trying to teach me (and im no young pup!) and vbulletin has a plugin area designed specifically for php where you just add the php code without the start and end <?. I dont understand REGEX or the scenarios of where we'd use look forward, look back, look around...etc, so i have to admit i was looking for a solution which i could then later try and decipher as to why it works and what it's doing. Still, ido appreciate everything you've done so far, thanks Regards, Simon Quote Link to comment Share on other sites More sharing options...
xyph Posted May 1, 2012 Share Posted May 1, 2012 There's no 'lookaround' in my expression. I took it out because you can't have variable-length lookarounds. I prefer to check if [kode] is there using a capturing group and ?, and then allow PHP's logic to handle if the data should be kept or discarded. Doing this, I can check for [kode] followed by 0 or more white-space characters, followed by sub/function. If [kode] is found, the logic later discards the results of that match. Is there any part of my solution that needs clarification? Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 1, 2012 Author Share Posted May 1, 2012 It didnt make any replacements at all, why would that be do you think? in the previous preg_replace it worked too well and doubled sometimes tripled up so im wondering why your latest solution didnt replace anything? even if i tested with only one section of data to look at Sub Test() If This = "" Then Msgbox "This is that" End If End Sub it didnt replace anything in that string, did i need the print command uncommented? i thought that may have just been to show the results on screen? Regards, Simon P.S When i said i'm no young pup i meant you cant teach an old dog new tricks well not quickly anyway! Quote Link to comment Share on other sites More sharing options...
xyph Posted May 1, 2012 Share Posted May 1, 2012 You're testing this code in an environment I can't easily replicate, so it's very hard to tell... I don't know what kind of values or returns vBulletin expects. This might help you further (uses the snippet above, with this change to the foreach) foreach( $matches as $match ) { if( !empty($match[1]) ) // check if [kode] was found continue; echo "[KODE]$match[2] $match[3]($match[4]) $match[5] End $match[2][/KODE]\n"; } Outputs [KODE]Sub Test() 'some code End Sub[/KODE] [KODE]Function TestFunc() 'some code End Function[/KODE] [KODE]Function TestFunc2(Rng As Range) 'some code End Function[/KODE] Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 1, 2012 Author Share Posted May 1, 2012 Ok changed the entire for each like this //foreach( $matches as $match ) { //if( !empty($match[1]) ) // check if [kode] was found //continue; //$parsed[] = array( //'name' => $match[3], //'arguments' => $match[4], //'body' => trim($match[5]) //); //} foreach( $matches as $match ) { if( !empty($match[1]) ) // check if [kode] was found continue; echo "[KODE]$match[2] $match[3]($match[4]) $match[5] End $match[2][/KODE]\n"; } There wasn't a replacement if no tags existed and no echo and there wasn't the echo if tags existed, i even replaced echo with print but nothing appeared? Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 2, 2012 Author Share Posted May 2, 2012 Would it be easier to go back to pattern1 = "/Sub\s/"; $pattern2 = "/End Sub/"; $pattern3 = "/Function\s/"; $pattern4 = "/End Function/"; $replace1 = "[KODE]Sub "; $replace2 = "End Sub[/KODE]"; $replace3 = "[KODE]Function "; $replace4 = "End Function[/KODE]"; $MyString= preg_replace($pattern1, $replace1, $post['message']); $MyString= preg_replace($pattern2, $replace2, $post['message']); $MyString= preg_replace($pattern3, $replace3, $post['message']); $MyString= preg_replace($pattern4, $replace4, $post['message']); $MyString= preg_replace('/\\[code\\](.*)\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']); and maybe add /(?<![KODE])Sub/ will only match "Sub" if it is not preceded by "[KODE]". I found that on the net but i don't know how to implement it for each of the expressions and i especially have no idea how to implement it for following "End Sub"...etc Regards, Simon Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 2, 2012 Author Share Posted May 2, 2012 Sorry $MyString should read differently $pattern1 = "/(?<![KODE])Sub\s/"; $pattern2 = "/(?![/KODE])End Sub/"; $pattern3 = "/Function\s/"; $pattern4 = "/End Function/"; $replace1 = "[KODE]Sub "; $replace2 = "End Sub[/KODE]"; $replace3 = "[KODE]Function "; $replace4 = "End Function[/KODE]"; $post['message']= preg_replace($pattern1, $replace1, $post['message']); $post['message']= preg_replace($pattern2, $replace2, $post['message']); $post['message']= preg_replace($pattern3, $replace3, $post['message']); $post['message']= preg_replace($pattern4, $replace4, $post['message']); $post['message']= preg_replace('/\\[code\\](.*)\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']); I tried adding the negative assertion to check the beginnig and end, i got an error back "..Unknown qualifier "K"...", so i tried to escape it like this \\[KODE\\]..etc but still an error, im just shooting in the dark as i don't have the foggiest what im doing. Regards, Simon Quote Link to comment Share on other sites More sharing options...
xyph Posted May 2, 2012 Share Posted May 2, 2012 I really would love to help you further, but I just don't know how the vBulletin BBParser works. It seems like matching the opening and closing tags separately is a bad idea in this case, because extra processing is needed to make sure there are as many opening tags and closing. If we were wanting to check for nested values, though, we'd definitely want to check separately. Perhaps if you elaborated how the system worked, or provided me with a link or tutorial on how I should format my help. I'm still not going to give you the copy+paste solution, but I might be able to guide you, rather than throwing code at you blindly Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 2, 2012 Author Share Posted May 2, 2012 Hi would it help to have test forum access (although you'd have to replace [ code ] tags there) or would it help to have the bbcode.php?, to be honest with you all that needs checking is, is there already a [KODE] tag there before SUB or FUNCTION if yes skip END SUB or END FUNCTION and look for next SUB or FUNCTION do the check again if no [KODE] tag is present then add [KODE]just before it, find the next END SUB or END FUNCTION and after it add [/KODE]. Like i said above the one where i do each seperately works but too well as it doubles or tripples up depending on how many instances of code there are in the post being made, thats why i tried adding the negative assertion code to one of them to try it but couldn't get the syntax correct. I appreciate this is probably frustrating to you and really appreciate you sticking with it. Regards, Simon Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 3, 2012 Author Share Posted May 3, 2012 Ok, this works perfect but only if i remove the: Function MyTest() blah End Function The word Function makes it nuts!, any ideas? $post['message'] = preg_replace('/\\[code\\](.*)(?<!\[\/KODE\])\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']); $post['message'] = preg_replace("/(?<!\[KODE\]|End\s)Sub(?<!\s\[\/KODE\])(.*)End Sub/siU", '[KODE]Sub \\1 End Sub[/KODE]', $post['message']); $post['message'] = preg_replace("/(?<!\[KODE\]|End\s)Function(?<!\s\[\/KODE\])(.*)End Function/siU", '[KODE]Function \\1 End Function[/KODE]', $post['message']); So how can we change the last preg_replace to ignore the word FUNCTION as a command word? Regards, Simon Quote Link to comment Share on other sites More sharing options...
xyph Posted May 3, 2012 Share Posted May 3, 2012 <?php $expr = '/(\[kode\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2/si'; $data = "Sub Test() 'some code End Sub [KODE]Sub Test2(MyTest by Val) 'some code End Sub[/KODE] Function TestFunc() 'some code End Function Function TestFunc2(Rng As Range) 'some code End Function [KODE] Function preventsUs(from using lookaround) blah End Function [/KODE]"; $data = preg_replace_callback($expr, 'replace_function', $data); echo $data; function replace_function( $match ) { if( empty($match[1]) ) // check if [kode] was found return "[KODE]$match[0][/KODE]"; else return $match[0]; } ?> If you use >= PHP5.3, you can use an anonymous function instead. Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 3, 2012 Author Share Posted May 3, 2012 Im on php 5.2.17, a lot of my add-ons..etc wont function in php5.3.x unfortunately, thanks for that i'll give it a try Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 3, 2012 Author Share Posted May 3, 2012 I tried that and it gave an error relating to preg_replace, didnt catch it because it was up briefly, it didnt echo the $data! Quote Link to comment Share on other sites More sharing options...
xyph Posted May 3, 2012 Share Posted May 3, 2012 I can't really help you unless you show me how you implemented it, and what the error message was. Try <?php $data = preg_replace_callback($expr, create_function('$match', 'if( empty($match[1]) ) return "[KODE]$match[0][/KODE]"; else return $match[0];'), $data); ?> Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 3, 2012 Author Share Posted May 3, 2012 I implemented it by replacing all the code for that supplied, running it last time and with the revised code gave this error Warning preg_replace_callback() [function preg-replace-callback] Delimieter must not be alphanumeric or backslash in [path]/includes/functions_newpost.php(414):eval()'d code on line 7i know you were expecting an error and for it to give you some clue but other than a delimiter issue i cant help you understand my issue Quote Link to comment Share on other sites More sharing options...
xyph Posted May 3, 2012 Share Posted May 3, 2012 What expression are you putting in to the function? The expression I provided above will not throw that error Quote Link to comment Share on other sites More sharing options...
Simon Lloyd Posted May 3, 2012 Author Share Posted May 3, 2012 I simply copied it verbatim, however i removed what seemed to be an erroneous ' just before IF and the error i now get is: Parse error: syntax error, unexpected T_IF in....etc so i guess it needed the ' Quote Link to comment Share on other sites More sharing options...
xyph Posted May 4, 2012 Share Posted May 4, 2012 The code I pasted runs on it's own without any errors. It can't be an error in my code. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.