| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 | <?php$pomo = dirname( dirname( dirname( __FILE__ ) ) ) . '/src/wp-includes/pomo';require_once "$pomo/entry.php";require_once "$pomo/translations.php";/** * Responsible for extracting translatable strings from PHP source files * in the form of Translations instances */class StringExtractor {	var $rules = array(		'__' => array( 'string' ),		'_e' => array( 'string' ),		'_n' => array( 'singular', 'plural' ),	);	var $comment_prefix = 'translators:';	function __construct( $rules = array() ) {		$this->rules = $rules;	}	function extract_from_directory( $dir, $excludes = array(), $includes = array(), $prefix = '' ) {		$old_cwd = getcwd();		chdir( $dir );		$translations = new Translations;		$file_names = (array) scandir( '.' );		foreach ( $file_names as $file_name ) {			if ( '.' == $file_name || '..' == $file_name ) continue;			if ( preg_match( '/\.php$/', $file_name ) && $this->does_file_name_match( $prefix . $file_name, $excludes, $includes ) ) {				$translations->merge_originals_with( $this->extract_from_file( $file_name, $prefix ) );			}			if ( is_dir( $file_name ) ) {				$translations->merge_originals_with( $this->extract_from_directory( $file_name, $excludes, $includes, $prefix . $file_name . '/' ) );			}		}		chdir( $old_cwd );		return $translations;	}	function extract_from_file( $file_name, $prefix ) {		$code = file_get_contents( $file_name );		return $this->extract_from_code( $code, $prefix . $file_name );	}	function does_file_name_match( $path, $excludes, $includes ) {		if ( $includes ) {			$matched_any_include = false;			foreach( $includes as $include ) {				if ( preg_match( '|^'.$include.'$|', $path ) ) {					$matched_any_include = true;					break;				}			}			if ( !$matched_any_include ) return false;		}		if ( $excludes ) {			foreach( $excludes as $exclude ) {				if ( preg_match( '|^'.$exclude.'$|', $path ) ) {					return false;				}			}		}		return true;	}	function entry_from_call( $call, $file_name ) {		$rule = isset( $this->rules[$call['name']] )? $this->rules[$call['name']] : null;		if ( !$rule ) return null;		$entry = new Translation_Entry;		$multiple = array();		$complete = false;		for( $i = 0; $i < count( $rule ); ++$i ) {			if ( $rule[$i] && ( !isset( $call['args'][$i] ) || !is_string( $call['args'][$i] ) || '' == $call['args'][$i] ) ) return false;			switch( $rule[$i] ) {			case 'string':				if ( $complete ) {					$multiple[] = $entry;					$entry = new Translation_Entry;					$complete = false;				}				$entry->singular = $call['args'][$i];				$complete = true;				break;			case 'singular':				if ( $complete ) {					$multiple[] = $entry;					$entry = new Translation_Entry;					$complete = false;				}				$entry->singular = $call['args'][$i];				$entry->is_plural = true;				break;			case 'plural':				$entry->plural = $call['args'][$i];				$entry->is_plural = true;				$complete = true;				break;			case 'context':				$entry->context = $call['args'][$i];				foreach( $multiple as &$single_entry ) {					$single_entry->context = $entry->context;				}				break;			}		}		if ( isset( $call['line'] ) && $call['line'] ) {			$references = array( $file_name . ':' . $call['line'] );			$entry->references = $references;			foreach( $multiple as &$single_entry ) {				$single_entry->references = $references;			}		}		if ( isset( $call['comment'] ) && $call['comment'] ) {			$comments = rtrim( $call['comment'] ) . "\n";			$entry->extracted_comments = $comments;			foreach( $multiple as &$single_entry ) {				$single_entry->extracted_comments = $comments;			}		}		if ( $multiple && $entry ) {			$multiple[] = $entry;			return $multiple;		}		return $entry;	}	function extract_from_code( $code, $file_name ) {		$translations = new Translations;		$function_calls = $this->find_function_calls( array_keys( $this->rules ), $code );		foreach( $function_calls as $call ) {			$entry = $this->entry_from_call( $call, $file_name );			if ( is_array( $entry ) )				foreach( $entry as $single_entry )					$translations->add_entry_or_merge( $single_entry );			elseif ( $entry)				$translations->add_entry_or_merge( $entry );		}		return $translations;	}	/**	 * Finds all function calls in $code and returns an array with an associative array for each function:	 *	- name - name of the function	 *	- args - array for the function arguments. Each string literal is represented by itself, other arguments are represented by null.	 *  - line - line number	 */	function find_function_calls( $function_names, $code ) {		$tokens = token_get_all( $code );		$function_calls = array();		$latest_comment = false;		$in_func = false;		foreach( $tokens as $token ) {			$id = $text = null;			if ( is_array( $token ) ) list( $id, $text, $line ) = $token;			if ( T_WHITESPACE == $id ) continue;			if ( T_STRING == $id && in_array( $text, $function_names ) && !$in_func ) {				$in_func = true;				$paren_level = -1;				$args = array();				$func_name = $text;				$func_line = $line;				$func_comment = $latest_comment? $latest_comment : '';				$just_got_into_func = true;				$latest_comment = false;				continue;			}			if ( T_COMMENT == $id ) {				$text = trim( preg_replace( '%^/\*|//%', '', preg_replace( '%\*/$%', '', $text ) ) );				if ( 0 === stripos( $text, $this->comment_prefix ) ) {					$latest_comment = $text;				}			}			if ( !$in_func ) continue;			if ( '(' == $token ) {				$paren_level++;				if ( 0 == $paren_level ) { // start of first argument					$just_got_into_func = false;					$current_argument = null;					$current_argument_is_just_literal = true;				}				continue;			}			if ( $just_got_into_func ) {				// there wasn't a opening paren just after the function name -- this means it is not a function				$in_func = false;				$just_got_into_func = false;			}			if ( ')' == $token ) {				if ( 0 == $paren_level ) {					$in_func = false;					$args[] = $current_argument;					$call = array( 'name' => $func_name, 'args' => $args, 'line' => $func_line );					if ( $func_comment ) $call['comment'] = $func_comment;					$function_calls[] = $call;				}				$paren_level--;				continue;			}			if ( ',' == $token && 0 == $paren_level ) {				$args[] = $current_argument;				$current_argument = null;				$current_argument_is_just_literal = true;				continue;			}			if ( T_CONSTANT_ENCAPSED_STRING == $id && $current_argument_is_just_literal ) {				// we can use eval safely, because we are sure $text is just a string literal				eval('$current_argument = '.$text.';' );				continue;			}			$current_argument_is_just_literal = false;			$current_argument = null;		}		return $function_calls;	}}
 |