<commit_before><?php
/**
 * Copyright (c) 2010 <NAME>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *   * Neither the name of <NAME> nor the names of his
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @package     Phix_Project
 * @subpackage  Phix
 * @author      <NAME> <<EMAIL>>
 * @copyright   2010 <NAME>
 * @license     http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @link        http://github.com/m4rw3r
 * @version     @@PACKAGE_VERSION@@
 */

namespace Phix_Project\Phix;

/**
 * Searches trough a set of paths for classes, returns a list of classes
 * and in which file they are located, does NOT include the files.
 * 
 * Raises a PHP NOTICE if duplicate class identifiers are found.
 */
class ClassFinder
{
	/**
	 * Tells the ClassFinder to raise a PHP NOTICE error when a duplicate class
	 * is found.
	 * 
	 * @var int
	 */
	const TRIGGER_NOTICE_ON_DUPLICATE = 1;
	
	/**
	 * Tells the ClassFinder to raise a PHP WARNING error when a duplicate class
	 * is found.
	 * 
	 * @var int
	 */
	const TRIGGER_WARNING_ON_DUPLICATE = 2;
	
	/**
	 * Tells the ClassFinder to raise a PHP ERROR error when a duplicate class
	 * is found.
	 * 
	 * @var int
	 */
	const TRIGGER_ERROR_ON_DUPLICATE = 4;
	
	/**
	 * The list containing classes and their filenames.
	 * 
	 * @var array(string => string)  Class => filename
	 */
	protected $list = array();
	
	/**
	 * The regex determining which files to search.
	 * 
	 * @var string
	 */
	protected $file_regex;
	
	/**
	 * A list of paths to search.
	 * 
	 * @var array(string)
	 */
	protected $paths = array();
	
	/**
	 * The operation flags for this ClassFinder, consists of binary flags
	 * from class constants.
	 * 
	 * @var int
	 */
	protected $flags = self::TRIGGER_NOTICE_ON_DUPLICATE;
	
	// ------------------------------------------------------------------------
	
	/**
	 * @param  string|array
	 * @param  string
	 * @param  int
	 */
	function __construct($paths = '.', $file_regex = '/\.php$/', $flags = self::TRIGGER_NOTICE_ON_DUPLICATE)
	{
		foreach((Array) $paths as $p)
		{
			$this->paths[] = realpath($p);
		}
		
		// array_unique() will fix problems with eg. PHP's include path
		$this->paths      = array_unique($this->paths);
		$this->file_regex = $file_regex;
		$this->flags      = $flags;
	}
	
	// ------------------------------------------------------------------------

	/**
	 * Searches the supplied paths for the files and returns a list of classes
	 * and in which file they're located.
	 * 
	 * @return array(class => file)
	 */
	public function getClassFiles()
	{
		if( ! empty($this->list))
		{
			return $this->list;
		}
		
		foreach($this->paths as $path)
		{
			$path = realpath($path);
			
			// Search the folder
			$diriter  = new \RecursiveDirectoryIterator($path);
			$iteriter = new \RecursiveIteratorIterator($diriter, \RecursiveIteratorIterator::LEAVES_ONLY);
			$files    = new \RegexIterator($iteriter, $this->file_regex);
			
			foreach($files as $name => $file)
			{
				foreach($this->getClasses($name) as $class)
				{
					if(isset($this->list[$class]))
					{
						$msg = sprintf('ClassFinder: Conflicting class name found: %s in file %s, first found in %s', $class, $name, $this->list[$class]);
						
						if($this->flags & self::TRIGGER_ERROR_ON_DUPLICATE)
						{
							\trigger_error($msg, \E_ERROR_NOTICE);
						}
						elseif($this->flags & self::TRIGGER_WARNING_ON_DUPLICATE)
						{
							\trigger_error($msg, \E_USER_WARNING);
						}
						elseif($this->flags & self::TRIGGER_NOTICE_ON_DUPLICATE)
						{
							\trigger_error($msg, \E_USER_NOTICE);
						}
					}
					
					$this->list[$class] = $name;
				}
			}
		}
		
		return $this->list;
	}
	
	// ------------------------------------------------------------------------

	/**
	 * Tokenizes the file and iterates all tokens in search of classes.
	 * 
	 * @param  string         Path to file
	 * @return array(string)  List of classnames
	 */
	protected function getClasses($filepath)
	{
		$tokens = token_get_all(file_get_contents($filepath));
		
		$classes          = array();
		$is_classname     = false;
		$is_namespace     = false;
		$inside_namespace = false;
		$indentation      = 0;
		$current_ns       = null;
		
		foreach($tokens as $token)
		{
			if( ! is_array($token))
			{
				// we're only interested in brackets
				switch($token)
				{
					case '{':
						$indentation++;
						break;
						
					case '}':
						$indentation--;<commit_msg>Adjusted comments in ClassFinder class<commit_after><?php
/**
 * Copyright (c) 2010 <NAME>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *   * Neither the name of <NAME> nor the names of his
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @package     Phix_Project
 * @subpackage  Phix
 * @author      <NAME> <<EMAIL>>
 * @copyright   2010 <NAME>
 * @license     http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @link        http://github.com/m4rw3r
 * @version     @@PACKAGE_VERSION@@
 */

namespace Phix_Project\Phix;

/**
 * Searches through a set of paths for classes, returns a list of classes
 * and in which file they are located, does NOT include the files.
 * 
 * Raises a PHP NOTICE if duplicate class identifiers are found.
 */
class ClassFinder
{
	/**
	 * Tells the ClassFinder to raise a PHP NOTICE error when a
	 * duplicate class is found.
	 * 
	 * @var int
	 */
	const TRIGGER_NOTICE_ON_DUPLICATE = 1;
	
	/**
	 * Tells the ClassFinder to raise a PHP WARNING error when a
	 * duplicate class is found.
	 * 
	 * @var int
	 */
	const TRIGGER_WARNING_ON_DUPLICATE = 2;
	
	/**
	 * Tells the ClassFinder to raise a PHP ERROR error when a
	 * duplicate class is found.
	 * 
	 * @var int
	 */
	const TRIGGER_ERROR_ON_DUPLICATE = 4;
	
	/**
	 * The list containing classes and their filenames.
	 * 
	 * @var array(string => string)  Class => filename
	 */
	protected $list = array();
	
	/**
	 * The regex determining which files to search.
	 * 
	 * @var string
	 */
	protected $file_regex;
	
	/**
	 * A list of paths to search.
	 * 
	 * @var array(string)
	 */
	protected $paths = array();
	
	/**
	 * The operation flags for this ClassFinder, consists of binary
	 * flags from class constants.
	 * 
	 * @var int
	 */
	protected $flags = self::TRIGGER_NOTICE_ON_DUPLICATE;
	
	// -----------------------------------------------------------------
	
	/**
	 * @param  string|array
	 * @param  string
	 * @param  int
	 */
	function __construct($paths = '.', $file_regex = '/\.php$/', $flags = self::TRIGGER_NOTICE_ON_DUPLICATE)
	{
		foreach((Array) $paths as $p)
		{
			$this->paths[] = realpath($p);
		}
		
		// array_unique() will fix problems with eg. PHP's include path
		$this->paths      = array_unique($this->paths);
		$this->file_regex = $file_regex;
		$this->flags      = $flags;
	}
	
	// -----------------------------------------------------------------

	/**
	 * Searches the supplied paths for the files and returns a list 
	 * of classes and in which file they're located.
	 * 
	 * @return array(class => file)
	 */
	public function getClassFiles()
	{
		if( ! empty($this->list))
		{
			return $this->list;
		}
		
		foreach($this->paths as $path)
		{
			$path = realpath($path);
			
			// Search the folder
			$diriter  = new \RecursiveDirectoryIterator($path);
			$iteriter = new \RecursiveIteratorIterator($diriter, \RecursiveIteratorIterator::LEAVES_ONLY);
			$files    = new \RegexIterator($iteriter, $this->file_regex);
			
			foreach($files as $name => $file)
			{
				foreach($this->getClasses($name) as $class)
				{
					if(isset($this->list[$class]))
					{
						$msg = sprintf('ClassFinder: Conflicting class name found: %s in file %s, first found in %s', $class, $name, $this->list[$class]);
						
						if($this->flags & self::TRIGGER_ERROR_ON_DUPLICATE)
						{
							\trigger_error($msg, \E_ERROR_NOTICE);
						}
						elseif($this->flags & self::TRIGGER_WARNING_ON_DUPLICATE)
						{
							\trigger_error($msg, \E_USER_WARNING);
						}
						elseif($this->flags & self::TRIGGER_NOTICE_ON_DUPLICATE)
						{
							\trigger_error($msg, \E_USER_NOTICE);
						}
					}
					
					$this->list[$class] = $name;
				}
			}
		}
		
		return $this->list;
	}
	
	// -----------------------------------------------------------------

	/**
	 * Tokenizes the file and iterates all tokens in search of classes.
	 * 
	 * @param  string         Path to file
	 * @return array(string)  List of classnames
	 */
	protected function getClasses($filepath)
	{
		$tokens = token_get_all(file_get_contents($filepath));
		
		$classes          = array();
		$is_classname     = false;
		$is_namespace     = false;
		$inside_namespace = false;
		$indentation      = 0;
		$current_ns       = null;
		
		foreach($tokens as $token)
		{
			if( ! is_array($token))
			{
				// we're only interested in brackets
				switch($token)
				{
					case '{':
						$indentation++;
						break;
						
					case '}':
						$indentation--;
{'fraction_non_alphanumeric': 0.14089200273410799, 'fraction_numerical': 0.0023923444976076554, 'mean_word_length': 2.5501971489232638, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7379704', 'n_tokens_mistral': 4059, 'n_tokens_neox': 3359, 'n_words': 1137}