<commit_before><?php

/**
 * 
 * @author clue
 * @link http://www.ffnn.nl/pages/articles/media/uml-diagrams-using-graphviz-dot.php
 * @link http://www.holub.com/goodies/uml/
 */
class LoaderUmlClassDiagram extends Loader{
    private $graph;
    
    private $options = array(
		'only-self'   => true, // whether to only show methods/properties that are actually defined in this class (and not those merely inherited from base)
		'only-public' => false, // whether to only show public methods/properties (or also include private/protected ones)
        'show-constants' => true, // whether to show class constants as readonly static variables (or just omit them completely)
    );
    
    public function __construct(){
        $this->graph = new Graph();
    }
    
    public function setOption($name,$flag){
    	if(!isset($this->options[$name])){
    		throw new Exception('Invalid option name "'.$name.'"');
    	}
    	$this->options[$name] = !!$flag;
    	return $this;
    }
    
    public function hasClass($class){
        try{
            $this->graph->getVertex($class);
            return true;
        }
        catch(Exception $ignroe){}
        return false;
    }
    
    public function createVertexClass($class){
        $vertex = $this->graph->createVertex($class);
        
        $reflection = new ReflectionClass($class);
        
        $parent = $reflection->getParentClass();
        if($parent){
            try{
                $parentVertex = $this->graph->getVertex($parent->getName());
            }
            catch(Exception $ignore){
                $parentVertex = $this->createVertexClass($parent->getName());
            }
            $vertex->createEdgeTo($parentVertex)->setLayout('arrowhead','empty');
        }
        
        foreach($reflection->getInterfaceNames() as $interface){
            try{
            	$parentVertex = $this->graph->getVertex($interface);
            }
            catch(Exception $ignore){
            	$parentVertex = $this->createVertexClass($interface);
            }
            $vertex->createEdgeTo($parentVertex)->setLayout('arrowhead','empty')->setLayout('style','dashed');
        }
        
        $label = '"{';
        
        $isInterface = false;
        if($reflection->isInterface()){
            $label .= '«interface»\\n';
            $isInterface = true;
        }else if($reflection->isAbstract()){
            $label .= '«abstract»\\n';
        }
        
        $label .= $this->escape($class).'|';
        
        if($this->options['show-constants']){
        	foreach($reflection->getConstants() as $name=>$value){
        		if($this->options['only-self'] && $parent && $parent->getConstant($name) === $value) continue;
        
        		$label .= '+ «static» '.self::escape($name).' : '.$this->escape($this->getType(gettype($value))).' = '.$this->getCasted($value).' \\{readOnly\\}\\l';
        	}
        }
        
        $defaults = $reflection->getDefaultProperties();
        foreach($reflection->getProperties() as $property){
            if($this->options['only-self'] && $property->getDeclaringClass()->getName() !== $class) continue;
            
            if($this->options['only-public'] && !$property->isPublic()) continue;
            
            $label .= $this->visibility($property);
            if($property->isStatic()){
            	$label .= ' «static»';
            }
            $label .= ' ' . $this->escape($property->getName());
            
            $type = NULL; // TODO: parse docblock for parameter type
            if($type !== NULL){
            	$label .= ' : '.$type;
            }
            
            if(isset($defaults[$property->getName()])){ // only show non-NULL values
                $label .= ' = '.$this->getCasted($defaults[$property->getName()]);
            }
            
            $label .= '\\l';
        }
        
        $label .= '|';
        
        foreach($reflection->getMethods() as $method){
            if($this->options['only-self'] && $method->getDeclaringClass()->getName() !== $class) continue; // method not defined in this class (inherited from parent), so skip
            
            if($this->options['only-public'] && !$method->isPublic()) continue;
            
//             $ref = preg_replace('/[^a-z0-9]/i','',$method->getName());
//             $label .= '<"'.$ref.'">';
            
            $label .= $this->visibility($method);
            
            if(!$isInterface && $method->isAbstract()){
            	$label .= ' «abstract»';
            }
            if($method->isStatic()){
            	$label .= ' «static»';
            }
            $label .= ' ' . $this->escape($method->getName()).'(';
            
            $firstParam = true;
            foreach($method->getParameters() as $parameter){
                if($firstParam){
                    $firstParam = false;
                }else{
                    $label .= ', ';
                }
                
                $label .= $this->escape($parameter->getName());
                
                $type = NULL; // TODO: parse docblock for parameter type
                if($type !== NULL){
                    $label .= ' : '.$type;
                }
                
                if($parameter->isOptional()){
                    try{
                        $label .= ' = '.$this->getCasted($parameter->getDefaultValue());
                    }
                    catch(Exception $ignore){
                        $label .= ' = «unknown»';
                    }
                }
            }
            $label .= ')';
            
            $type = NULL; // TODO: parse docblock for return value
            if($type !== NULL){
                $label .= ' : '.$type;
            }
            
            $label .= '\\l'; // align this line to the left
        }
        
        $label .= '}"';
        
        $vertex->setLayout('shape','record');
        $vertex->setLayoutRaw('label',$label);
        return $vertex;
    }
    
    public function getGraph(){
        return $this->graph;
    }
    
    private function getType($ret){<commit_msg>Parse docblocks for return/parameter types<commit_after><?php

/**
 * 
 * @author clue
 * @link http://www.ffnn.nl/pages/articles/media/uml-diagrams-using-graphviz-dot.php
 * @link http://www.holub.com/goodies/uml/
 */
class LoaderUmlClassDiagram extends Loader{
    private $graph;
    
    private $options = array(
		'only-self'   => true, // whether to only show methods/properties that are actually defined in this class (and not those merely inherited from base)
		'only-public' => false, // whether to only show public methods/properties (or also include private/protected ones)
        'show-constants' => true, // whether to show class constants as readonly static variables (or just omit them completely)
    );
    
    public function __construct(){
        $this->graph = new Graph();
    }
    
    public function setOption($name,$flag){
    	if(!isset($this->options[$name])){
    		throw new Exception('Invalid option name "'.$name.'"');
    	}
    	$this->options[$name] = !!$flag;
    	return $this;
    }
    
    public function hasClass($class){
        try{
            $this->graph->getVertex($class);
            return true;
        }
        catch(Exception $ignroe){}
        return false;
    }
    
    public function createVertexClass($class){
        $vertex = $this->graph->createVertex($class);
        
        $reflection = new ReflectionClass($class);
        
        $parent = $reflection->getParentClass();
        if($parent){
            try{
                $parentVertex = $this->graph->getVertex($parent->getName());
            }
            catch(Exception $ignore){
                $parentVertex = $this->createVertexClass($parent->getName());
            }
            $vertex->createEdgeTo($parentVertex)->setLayout('arrowhead','empty');
        }
        
        foreach($reflection->getInterfaceNames() as $interface){
            try{
            	$parentVertex = $this->graph->getVertex($interface);
            }
            catch(Exception $ignore){
            	$parentVertex = $this->createVertexClass($interface);
            }
            $vertex->createEdgeTo($parentVertex)->setLayout('arrowhead','empty')->setLayout('style','dashed');
        }
        
        $label = '"{';
        
        $isInterface = false;
        if($reflection->isInterface()){
            $label .= '«interface»\\n';
            $isInterface = true;
        }else if($reflection->isAbstract()){
            $label .= '«abstract»\\n';
        }
        
        $label .= $this->escape($class).'|';
        
        if($this->options['show-constants']){
        	foreach($reflection->getConstants() as $name=>$value){
        		if($this->options['only-self'] && $parent && $parent->getConstant($name) === $value) continue;
        
        		$label .= '+ «static» '.self::escape($name).' : '.$this->escape($this->getType(gettype($value))).' = '.$this->getCasted($value).' \\{readOnly\\}\\l';
        	}
        }
        
        $defaults = $reflection->getDefaultProperties();
        foreach($reflection->getProperties() as $property){
            if($this->options['only-self'] && $property->getDeclaringClass()->getName() !== $class) continue;
            
            if($this->options['only-public'] && !$property->isPublic()) continue;
            
            $label .= $this->visibility($property);
            if($property->isStatic()){
            	$label .= ' «static»';
            }
            $label .= ' ' . $this->escape($property->getName());
            
            $type = $this->getDocBlockVar($property);
            if($type !== NULL){
            	$label .= ' : '.$this->escape($type);
            }
            
            if(isset($defaults[$property->getName()])){ // only show non-NULL values
                $label .= ' = '.$this->getCasted($defaults[$property->getName()]);
            }
            
            $label .= '\\l';
        }
        
        $label .= '|';
        
        foreach($reflection->getMethods() as $method){
            if($this->options['only-self'] && $method->getDeclaringClass()->getName() !== $class) continue; // method not defined in this class (inherited from parent), so skip
            
            if($this->options['only-public'] && !$method->isPublic()) continue;
            
//             $ref = preg_replace('/[^a-z0-9]/i','',$method->getName());
//             $label .= '<"'.$ref.'">';
            
            $label .= $this->visibility($method);
            
            if(!$isInterface && $method->isAbstract()){
            	$label .= ' «abstract»';
            }
            if($method->isStatic()){
            	$label .= ' «static»';
            }
            $label .= ' ' . $this->escape($method->getName()).'(';
            
            $firstParam = true;
            foreach($method->getParameters() as $parameter){
                if($firstParam){
                    $firstParam = false;
                }else{
                    $label .= ', ';
                }
                
                $label .= $this->escape($parameter->getName());
                
                $type = $this->getParameterType($parameter);
                if($type !== NULL){
                    $label .= ' : '.$this->escape($type);
                }
                
                if($parameter->isOptional()){
                    try{
                        $label .= ' = '.$this->getCasted($parameter->getDefaultValue());
                    }
                    catch(Exception $ignore){
                        $label .= ' = «unknown»';
                    }
                }
            }
            $label .= ')';
            
            $type = $this->getDocBlockReturn($method);
            if($type !== NULL){
                $label .= ' : '.$this->escape($type);
            }
            
            $label .= '\\l'; // align this line to the left
        }
        
        $label .= '}"';
        
        $vertex->setLayout('shape','record');
        $vertex->setLayoutRaw('label',$label);
        return $vertex;
    }
    
    public function getGraph(){
        return $this->graph;
    }
    
    private function getDocBlock($ref){
    	$doc = $ref->getDocComment();
    	if($doc !== false){
    		return trim(preg_replace('/(^(?:\h*\*)\h*|\h+$)/m','',substr($doc,3,-2)));
    	}
    	return NULL;
    }
    
    private function getDocBlockVar($ref){
    	return $this->getType($this->getDocBlockSingle($ref,'var'));
    }
    
    private function getDocBlockReturn($ref){
    	return $this->getType($this->getDocBlockSingle($ref,'return'));
    }
    
    private function getParameterType(ReflectionParameter $parameter){
    	$class = $parameter->getClass();
    	if($class !== NULL){
    		return $class->getName();
    	}
    
    	$pos = $parameter->getPosition();
    	$refFn = $parameter->getDeclaringFunction();
    	$params = $this->getDocBlockMulti($refFn,'param');
    	if(count($params) === $refFn->getNumberOfParameters()){
    		return $this->getType($params[$pos]);
    	}
    	return NULL;
    }
    
    private function getDocBlockMulti($ref,$what){
    	$doc = $this->getDocBlock($ref);
    	if($doc === NULL){
    		//return 'nah';
    		return NULL;
    	}
    	preg_match_all('/^@'.$what.' ([^\s]+)/m',$doc,$matches,PREG_SET_ORDER);
    	$ret = array();
    	foreach($matches as $match){
    		$ret []= trim($match[1]);
    	}
    	return $ret;
    }
    
    private function getDocBlockSingle($ref,$what){
    	$multi = $this->getDocBlockMulti($ref, $what);
    	if(count($multi) !== 1){
    		//return json_encode($matches);
    		return NULL;
    	}
    	return $multi[0];
    }
    
    private function getType($ret){
{'fraction_non_alphanumeric': 0.17128827267475447, 'fraction_numerical': 0.0006499133448873484, 'mean_word_length': 1.6859968968192398, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 167, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, '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': '913208', 'n_tokens_mistral': 3785, 'n_tokens_neox': 3739, 'n_words': 694}