preg_replace not finding tags

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
mattcooper
Forum Contributor
Posts: 210
Joined: Thu Mar 17, 2005 5:51 am
Location: London, UK

preg_replace not finding tags

Post by mattcooper »

Hi all,

I'm using the following methods to look for instances of tags in a template. When I use them outside of object context, everything is fine.

Code: Select all

private function find_objects() {
		
			
			$templateData = file_get_contents($this->templateLocation);
			
			if(!empty($templateData)) {
				$templateData = '<% hello %>';
				/* @- find <% tags %> and load them into an array */
				$objectArr = array();
				preg_replace("/<\%[[]](.*)[[]]\%>/isUe", "$this->get_object_name('\\1')", $templateData);

			}
			
			else echo 'Error: template not found';		
		
		}	
	
		private function get_object_name($name) {
				
			global $objectArr;
			$objectArr[] = $name; print_r($objectArr);
			return true;
		
		}
		
		private function examineTemplate() {
			
			/* @- loop through the template data and find the tags */
			$this->find_objects();
			print_r($objectArr);
		}
However, when placed in the class, they do not work - ie, <% tags %> are not found. Here's the full class as it is at the moment:

Code: Select all

class templateParser {
		
		/* @- CLASS NOTES
		   This class is for parsing tags with the following structures:
		   <% simpletagname %> and <% tagname:function(optional-args) %>
		   We use output from a db table to populate and replace the simple tags,
		   and do the same for the complex tag, but apply any special treatment
		   requested in "function" before outputting it. This can currently be
		   any native PHP string function defined in $allowedFunctions plus date().
		   
		   Its purpose is to keep PHP out of templates but still allow some
		   basic formatting/parsing of text.
	
		/* @- the location of the template file to parse */
		var $templateLocation = null;
		
		/* @- OUTPUT MODE
		/* @- this will be either "single" or "loop".
		/* @- "single" produces one instance of a template
		/* @- "loop" produces multiple instances */
		var $outputMode = null;
		
		/* @- DEFINE ALLOWED FUNCTIONS
		/* @- This array stores the names of all of the functions
		/* @- that can be applied to the output of the tag */
		private $allowedFunctions = array('ucfirst','ucwords','strtolower','strtoupper','nl2br','str_replace','strlen','substr','date');		

		private function find_objects() {
		
			
			$templateData = file_get_contents($this->templateLocation);
			
			if(!empty($templateData)) {
				$templateData = '<% hello %>';
				/* @- find <% tags %> and load them into an array */
				$objectArr = array();
				preg_replace("/<\%[[]](.*)[[]]\%>/isUe", "$this->get_object_name('\\1')", $templateData);

			}
			
			else echo 'Error: template not found';		
		
		}	
	
		private function get_object_name($name) {
				
			global $objectArr;
			$objectArr[] = $name; print_r($objectArr);
			return true;
		
		}
		
		private function examineTemplate() {
			
			/* @- loop through the template data and find the tags */
			$this->find_objects();
			#print_r($objectArr);
		}
									  
		private function examineTag($tagData) {
			
			/* @- break up a tag and assess its structure
			/* @- return tag string that has had a function applied to it, or
			/* @- just the string if it is a simple tag */
			
			
			# temporary:
			# first, remove the tag wrappers <% %>
			$tagStr = ereg_replace('<% ','',$tagData);
			$tagStr = ereg_replace(' %>','',$tagStr);
			
			# we should now have raw data from inside the tags
			# and next we need to separate tagname and attributes
			# from any special functionality, if specified
			
			# look for an instance of the : (colon) symbol and break the
			# string on it if it is present
			if(ereg(':',$tagStr)) {
				
				$tagArray = explode(':',$tagStr);
				# we now have an array with $tagArray[0] holding name
				# and $tagArray[1] holding the special function and args				
				
				# now let's see what the function is, and check that it's in the functions array
				if($tagArray['1']) {
				
					$function = $tagArray['1'];
					# are there any args?
					# the quickest way to find out (but not the most elegant)
					# is to look for and break the string up on "(" and then ditch the 
					# closing paranthesis with substr()
					if(ereg(',',$function)) {
						$tempArgs = explode('(',$function);
						$function = $tempArgs['0'];
						$args = substr($tempArgs['1'],0,-1);
					}
					
					# now we can apply the function to the tag it came with
					# but first need to check that the function is allowed
					if(!in_array($function, $this->allowedFunctions)) {
						# return the text in the tag but also
						# warn the developer by adding a flag
						return $tagArray['0'] .'[E]';
					}
					# now we have our function and args (if any) as separate vars,
					# check for the existence of args
					if(!empty($args)) {

						# look at the args... bust them up on the commas
						$argsArray = explode(',',$args);
						/* @- The two functions that will have multiple args are str_replace and substr.
						/* @- The position of $tagStr is different in each, so let's tell the function what to do */
						if($function == 'substr') {
						
							# this one has the string on the first arg position
							$finalOP = substr($tagArray['0'],$argsArray['0'],$argsArray['1']);
						}
						if($function == 'str_replace') {
						
							# this one has the string in the final arg position
							$finalOP = str_replace($argsArray['0'],$argsArray['1'],$tagArray['0']);
						}
						if($function == 'date') { 
							
							/* @- various options for date exist, parse them according to specification
							/* @- IF WE WANT OT PARSE THE DATE INTO A READABLE FORMAT:::
							/* @- use the tag structure <% date_row:date(formatpattern,true) %>
							/* @- otherwise you can return the raw integer for processing by using
							/* @- the tag format <% date_row:date(format,false) */
							switch($argsArray['1']) {
								case 'format' : # parse the date into readable format
								switch($argsArray['0']) {
									case 'fulldatetime' : # return F j, Y, g:i a
									$finalOP = date("F j, Y, g:i a", $tagArray['0']);
									break;
									case 'day' : # return l
									$finalOP = date("l", $tagArray['0']);
									break;
									case 'basic' : # return F j, Y
									$finalOP = date("F j, Y", $tagArray['0']);
									break;
									case 'date' : # return current date F j, Y
									$finalOP = date("F j, Y");
									break;
								}
								break;
								case 'raw' : # return the raw data
								$finalOP = $tagArray['0'];
								break;
							}
						}
					}
					else {
						
						# if $args is empty, we're using a function that just requires the string to be passed to it
						$finalOP = $function($tagArray['0']);
					}
				}
				return $finalOP;
			}

			# if we've found that this is a simple tag, just return the data that was passed to the function
			else return $tagStr;
		}
		
		public function outputData() {
			
			global $objectArr;
			$this->examineTemplate();
			print_r($objectArr);
			#echo $this->examineTag($this->examineTemplate());
		}
	} # end object
	
	$op = new templateParser;
	$op->templateLocation = $_SERVER['DOCUMENT_ROOT'] .'podcast/templates/user/userProfile.php';	
	$op->outputData();
What is wrong with this? Any help would be very much appreciated!
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

So are you just getting the error, or not getting anything?
User avatar
mattcooper
Forum Contributor
Posts: 210
Joined: Thu Mar 17, 2005 5:51 am
Location: London, UK

Post by mattcooper »

Sorry, I should know better by now! I'm not getting anything at all: not even using the string I've put in the variable to bypass the template file (<% hello %>).

very baffling...
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

I think you can use a callback function so you can pass that data to your method. preg_replace_callback().
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

PS Here is a little cleaner version of your code:

Code: Select all

<?php
class templateParser {
    /* @- CLASS NOTES
       This class is for parsing tags with the following structures:
       <% simpletagname %> and <% tagname:function(optional-args) %>
       We use output from a db table to populate and replace the simple tags,
       and do the same for the complex tag, but apply any special treatment
       requested in "function" before outputting it. This can currently be
       any native PHP string function defined in $allowedFunctions plus date().
      
       Its purpose is to keep PHP out of templates but still allow some
       basic formatting/parsing of text.
    
    /* @- the location of the template file to parse */
    var $templateLocation = null;
    
    /* @- OUTPUT MODE
    /* @- this will be either "single" or "loop".
    /* @- "single" produces one instance of a template
    /* @- "loop" produces multiple instances */
    var $outputMode = null;
    
    /* @- DEFINE ALLOWED FUNCTIONS
    /* @- This array stores the names of all of the functions
    /* @- that can be applied to the output of the tag */
    private $allowedFunctions = array('ucfirst','ucwords','strtolower','strtoupper','nl2br','str_replace','strlen','substr','date');               
    
    private function find_objects() {
        $templateData = file_get_contents($this->templateLocation);
               
        if(!empty($templateData)) {
            $templateData = '<% hello %>';
            /* @- find <% tags %> and load them into an array */
            $objectArr = array();
            preg_replace("/<\%[[]](.*)[[]]\%>/isUe", "$this->get_object_name('\\1')", $templateData);
        } else echo 'Error: template not found';   
    }       

    private function get_object_name($name) {
        global $objectArr;
        $objectArr[] = $name; print_r($objectArr);
        return true;
    }
       
    private function examineTemplate() {
        /* @- loop through the template data and find the tags */
        $this->find_objects();
        #print_r($objectArr);
    }
                                                                  
    private function examineTag($tagData) {
        /* @- break up a tag and assess its structure
        /* @- return tag string that has had a function applied to it, or
        /* @- just the string if it is a simple tag */
        # temporary:
        # first, remove the tag wrappers <% %>
        $tagStr = ereg_replace('<% ','',$tagData);
        $tagStr = ereg_replace(' %>','',$tagStr);
               
        # we should now have raw data from inside the tags
        # and next we need to separate tagname and attributes
        # from any special functionality, if specified
        
        # look for an instance of the : (colon) symbol and break the
        # string on it if it is present
        if(ereg(':',$tagStr)) {
            $tagArray = explode(':',$tagStr);
            # we now have an array with $tagArray[0] holding name
            # and $tagArray[1] holding the special function and args                               
            # now let's see what the function is, and check that it's in the functions array
            if($tagArray['1']) {
                $function = $tagArray['1'];
                # are there any args?
                # the quickest way to find out (but not the most elegant)
                # is to look for and break the string up on "(" and then ditch the
                # closing paranthesis with substr()
                if(ereg(',',$function)) {
                    $tempArgs = explode('(',$function);
                    $function = $tempArgs['0'];
                    $args = substr($tempArgs['1'],0,-1);
                }
                               
                # now we can apply the function to the tag it came with
                # but first need to check that the function is allowed
                if(!in_array($function, $this->allowedFunctions)) {
                    # return the text in the tag but also
                    # warn the developer by adding a flag
                    return $tagArray['0'] .'[E]';
                }
                # now we have our function and args (if any) as separate vars,
                # check for the existence of args
                if(!empty($args)) {
                    # look at the args... bust them up on the commas
                    $argsArray = explode(',',$args);
                    /* @- The two functions that will have multiple args are str_replace and substr.
                    /* @- The position of $tagStr is different in each, so let's tell the function what to do */
                    if($function == 'substr') {
                        # this one has the string on the first arg position
                        $finalOP = substr($tagArray['0'],$argsArray['0'],$argsArray['1']);
                    }
                    if($function == 'str_replace') {
                        # this one has the string in the final arg position
                        $finalOP = str_replace($argsArray['0'],$argsArray['1'],$tagArray['0']);
                    }
                    if($function == 'date') {
                        /* @- various options for date exist, parse them according to specification
                        /* @- IF WE WANT OT PARSE THE DATE INTO A READABLE FORMAT:::
                        /* @- use the tag structure <% date_row:date(formatpattern,true) %>
                        /* @- otherwise you can return the raw integer for processing by using
                        /* @- the tag format <% date_row:date(format,false) */
                        switch($argsArray['1']) {
                            case 'format' : # parse the date into readable format
                                switch($argsArray['0']) {
                                    case 'fulldatetime' : # return F j, Y, g:i a
                                        $finalOP = date("F j, Y, g:i a", $tagArray['0']);
                                        break;
                                    
                                    case 'day' : # return l
                                        $finalOP = date("l", $tagArray['0']);
                                        break;
                                    
                                    case 'basic' : # return F j, Y
                                        $finalOP = date("F j, Y", $tagArray['0']);
                                        break;
                                        
                                    case 'date' : # return current date F j, Y
                                        $finalOP = date("F j, Y");
                                        break;
                                }
                                break;
                                
                            case 'raw' : # return the raw data
                                $finalOP = $tagArray['0'];
                                break;
                        }
                    }
                } else {
                    # if $args is empty, we're using a function that just requires the string to be passed to it
                    $finalOP = $function($tagArray['0']);
                }
            }
            return $finalOP;
        }

        # if we've found that this is a simple tag, just return the data that was passed to the function
        else return $tagStr;
    }
       
    public function outputData() {
        global $objectArr;
        $this->examineTemplate();
        print_r($objectArr);
        #echo $this->examineTag($this->examineTemplate());
    }
} # end object

$op = new templateParser;
$op->templateLocation = $_SERVER['DOCUMENT_ROOT'] .'podcast/templates/user/userProfile.php';   
$op->outputData();
?>
User avatar
mattcooper
Forum Contributor
Posts: 210
Joined: Thu Mar 17, 2005 5:51 am
Location: London, UK

Post by mattcooper »

Thank you for cleaning up, it was a bit of a pain to read! I'm using this principle in a PHP4 application that is running nicely: here's a snippet from one of the classes:

Code: Select all

class clientData {
	
		function templateParse($templateLocation,$dataSource,$dataField) {
			
			# vars - $templateLocation : where the template to be parsed lives
			# $dataSource : 'media','user' - used for the switch
			# $dataField : the key we're using for the query in whatever class method is called
			
			# deals with SINGLE ITEM OUTPUT ONLY at the moment [TODO] - erm, do we want multiple template parsing?
			
			global $object_arr,$configs;
			
			if(!function_exists('find_objects')) {
				function find_objects($data) {
				
				# hunt for navigation objects
				$object_arr = array();
					# for <% ASP-style %> tags
					preg_replace("/<\%[[]](.*)[[]]\%>/isUe", "get_object_name('\\1')", $data);
							
				}
			}
			
			if(!function_exists('get_object_name')) {
				function get_object_name($name) {
						
					# companion fuction to "find_objects()" 
					global $object_arr;
					$object_arr[] = $name;
					return true;
				
				}
			}
			# open a template and replace tags with live content
			# get the template
			$bufferData = file_get_contents($templateLocation);
			# look for instances of <% tags %>
			$arr = find_objects($bufferData);
			
			switch($dataSource) {
				
				case 'user' :
				# instantiate the person class to extract data
				$p = new person();
				$p->username = $dataField; # try usinng the session to debug this if there are errors	
				$data = $p->person(); # we should now have an array of user vars
				break;
				
				case 'media' :
				# instantiate the media class to extract data
				$m = new media();
				$m->mediaId = $dataField;
				$data = $m->getMediaData();
				break;
				
			}
			
			# replace tag contents with $p data
			foreach($object_arr as $tagVal) {

				$bufferData = str_replace("<% ". $tagVal ." %>",stripslashes($data[$tagVal]),$bufferData);
				
			}
			
			return eval('?>'. $bufferData .'<?');
		}
}
Apologies for the code format again, my editor chucks it out like this! Anyway, it does the job correctly - however, I'm using functions within the method in this case. How does this differ from separating the methods out as in the new code example? I'm still rather new to OOP, having been knocking out PHP4 apps for ages, and am really trying hard to convert up to v5 but facing such difficulties on rather a regular basis.

Not too sure that I need to use the callback function, bearing in mind what I've said... what do you reckon?

Cheers in advance!
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Usually in a case like that instead of passing a function name you pass an array containing $this and the method name. Though that is typical for callback function in which instance the method receives an array of matches to work with.
User avatar
mattcooper
Forum Contributor
Posts: 210
Joined: Thu Mar 17, 2005 5:51 am
Location: London, UK

Post by mattcooper »

I understand. However, the point I was trying to make was that the regex doesn't appear to be finding the instances of the tags at all: so, if I try to echo $name from the get_object_name() callback, there's nothing there while in object context, but it does work when used outsde of a class environment.

I'm going to experiment with doing it another way, keep you posted ;)

Thanks for your input!
Post Reply