Completed
Push — prado-3.3 ( e90646...0b76d5 )
by Fabio
23:37 queued 03:01
created

ClassTreeBuilder::saveAsDWExtension()   C

Complexity

Conditions 14
Paths 16

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
nc 16
nop 1
dl 0
loc 44
rs 6.2666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
$basePath=dirname(__FILE__);
4
$frameworkPath=realpath($basePath.'/../../framework');
5
require_once($frameworkPath.'/prado.php');
6
require_once($basePath.'/DWExtension.php');
7
8
//the manager class sets up some dependency paths
9
Prado::using('System.Data.SqlMap.TSqlMapManager');
10
11
$exclusions=array(
12
	'pradolite.php',
13
	'prado-cli.php',
14
	'JSMin.php',
15
	'/I18N/core',
16
	'/3rdParty',
17
	'/Testing',
18
	'/Web/UI/WebControls/assets',
19
	);
20
$a=new ClassTreeBuilder($frameworkPath,$exclusions);
21
$a->buildTree();
22
$a->saveToFile($basePath.'/classes.data');
23
$a->saveAsDWExtension($basePath);
24
25
class ClassTreeBuilder
26
{
27
	const REGEX_RULES='/^\s*(abstract\s+)?class\s+(\w+)(\s+extends\s+(\w+)\s*|\s*)/msS';
28
	private $_frameworkPath;
29
	private $_exclusions;
30
	private $_classes=array();
31
32
	public function __construct($frameworkPath,$exclusions)
33
	{
34
		$this->_frameworkPath=realpath($frameworkPath);
35
		$this->_exclusions=array();
36
		foreach($exclusions as $exclusion)
37
		{
38
			if($exclusion[0]==='/')
39
				$this->_exclusions[realpath($frameworkPath.'/'.$exclusion)]=true;
40
			else
41
				$this->_exclusions[$exclusion]=true;
42
		}
43
	}
44
45
	public function buildTree()
46
	{
47
		$sourceFiles=$this->getSourceFiles($this->_frameworkPath);
48
		foreach($sourceFiles as $sourceFile)
49
			$this->parseFile($sourceFile);
50
		ksort($this->_classes);
51
		foreach(array_keys($this->_classes) as $className)
52
		{
53
			$parentClass=$this->_classes[$className]['ParentClass'];
54
			if(isset($this->_classes[$parentClass]))
55
				$this->_classes[$parentClass]['ChildClasses'][]=$className;
56
		}
57
		echo "\nClass tree built successfully. Total ".count($this->_classes)." classes found.\n";
58
	}
59
60
	public function saveToFile($fileName)
61
	{
62
		file_put_contents($fileName,serialize($this->_classes));
63
	}
64
65
	public function displayTree()
66
	{
67
		$this->displayTreeInternal(array_keys($this->_baseClasses),0);
0 ignored issues
show
Bug introduced by
The property _baseClasses does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
68
	}
69
70
	public function displayTreeInternal($classNames,$level)
71
	{
72
		foreach($classNames as $className)
73
		{
74
			echo str_repeat(' ',$level*4);
75
			echo $className.':'.$this->_classes[$className]->Package."\n";
76
			$this->displayTreeInternal(array_keys($this->_classes[$className]->ChildClasses),$level+1);
77
		}
78
	}
79
80
	protected function parseFile($sourceFile)
81
	{
82
		include_once($sourceFile);
83
		$classFile=strtr(substr($sourceFile,strlen($this->_frameworkPath)),'\\','/');
84
		echo "Parsing $classFile...\n";
85
		$content=file_get_contents($sourceFile);
86
		if(preg_match('/@package\s+([\w\.]+)\s*/msS',$content,$matches)>0)
87
			$package=$matches[1];
88
		else
89
			$package='';
90
		$n=preg_match_all(self::REGEX_RULES,$content,$matches,PREG_SET_ORDER);
91
		for($i=0;$i<$n;++$i)
92
		{
93
			$className=$matches[$i][2];
94
			if(isset($this->_classes[$className]))
95
				throw new Exception("Class $className is defined in both $sourceFile and ".$this->_classes[$className]->ClassFile);
96
			$c=new TComponentReflection($className);
97
			$properties=$c->getProperties();
98
			$this->parseMethodComments($properties);
99
			$events=$c->getEvents();
100
			$this->parseMethodComments($events);
101
			$methods=$c->getMethods();
102
			$this->parseMethodComments($methods);
103
			$this->_classes[$className]=array(
104
				'ClassFile'=>$classFile,
105
				'Package'=>$package,
106
				'ParentClass'=>isset($matches[$i][4])?$matches[$i][4]:'',
107
				'ChildClasses'=>array(),
108
				'Properties'=>$properties,
109
				'Events'=>$events,
110
				'Methods'=>$methods);
111
		}
112
	}
113
114
	protected function parseMethodComments(&$methods)
115
	{
116
		foreach(array_keys($methods) as $key)
117
		{
118
			$method=&$methods[$key];
119
			$comments=$method['comments'];
120
			$s='';
121
			foreach(explode("\n",$comments) as $line)
122
			{
123
				$line=trim($line);
124
				$line=trim($line,'/*');
125
				$s.=' '.$line;
126
			}
127
			$s=trim($s);
128
			$s=preg_replace('/\{@link.*?([\w\(\)]+)\}/i','$1',$s);
129
			$pos1=strpos($s,'@');
130
			$pos2=strpos($s,'.');
131
			if($pos1===false)
132
			{
133
				if($pos2!==false)
134
					$method['comments']=substr($s,0,$pos2);
135
				else
136
					$method['comments']=$s;
137
			}
138
			else if($pos1>0)
139
			{
140
				if($pos2 && $pos2<$pos1)	// use the first line as comment
141
					$method['comments']=substr($s,0,$pos2);
142
				else
143
					$method['comments']=substr($s,0,$pos1);
144
			}
145
			else
146
			{
147
				$matches=array();
148
				if(preg_match('/@return\s+[\w\|]+\s+([^\.]*)/',$s,$matches)>0)
149
					$method['comments']=$matches[1];
150
				else
151
					$method['comments']='';
152
			}
153
		}
154
	}
155
156
	protected function isValidPath($path)
157
	{
158
		if(is_dir($path))
159
			return !isset($this->_exclusions[basename($path)]) && !isset($this->_exclusions[$path]);
160
		else
161
			return basename($path)!==basename($path,'.php') && !isset($this->_exclusions[basename($path)]);
162
	}
163
164
	public function getSourceFiles($path)
165
	{
166
		$files=array();
167
		$folder=opendir($path);
168
		while($file=readdir($folder))
169
		{
170
			if($file==='.' || $file==='..')
171
				continue;
172
			$fullPath=realpath($path.'/'.$file);
173
			if($this->isValidPath($fullPath))
174
			{
175
				if(is_file($fullPath))
176
					$files[]=$fullPath;
177
				else
178
					$files=array_merge($files,$this->getSourceFiles($fullPath));
179
			}
180
		}
181
		closedir($folder);
182
		return $files;
183
	}
184
185
	public function saveAsDWExtension($basePath)
186
	{
187
		$tagPath=$basePath.'/Configuration/TagLibraries/PRADO';
188
189
		// prepare the directory to save tag lib
190
		@mkdir($basePath.'/Configuration');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
191
		@mkdir($basePath.'/Configuration/TagLibraries');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
192
		@mkdir($basePath.'/Configuration/TagLibraries/PRADO');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
193
194
		$docMXI = new PradoMXIDocument(Prado::getVersion());
195
		$tagChooser = new PradoTagChooser;
196
197
		$controlClass = new ReflectionClass('TControl');
198
199
		foreach($this->_classes as $className=>$classInfo)
200
		{
201
			$class = new ReflectionClass($className);
202
			if($class->isInstantiable() && ($className==='TControl' || $class->isSubclassOf($controlClass)))
203
			{
204
				$docMXI->addTag($className);
205
				$tagChooser->addElement($className);
206
				$docVTM = new PradoVTMDocument($className);
207
				foreach($classInfo['Properties'] as $name=>$property)
208
				{
209
					$type=$property['type'];
210
					if(isset($this->_classes[$type]) && ($type==='TFont' || strrpos($type,'Style')===strlen($type)-5 && $type!=='TStyle'))
211
						$this->processObjectType($type,$this->_classes[$type],$name,$docVTM);
212
					if($property['readonly'] || $property['protected'])
213
						continue;
214
					if(($type=$this->checkType($className,$name,$property['type']))!=='')
215
						$docVTM->addAttribute($name,$type);
216
				}
217
				foreach($classInfo['Events'] as $name=>$event)
218
				{
219
					$docVTM->addEvent($name);
220
				}
221
				file_put_contents($tagPath.'/'.$className.'.vtm',$docVTM->getXML());
222
			}
223
		}
224
225
		file_put_contents($basePath.'/PRADO.mxi',$docMXI->getXML());
226
		file_put_contents($tagPath.'/TagChooser.xml',$tagChooser->getXML());
227
228
	}
229
230
	private	function processObjectType($objectType,$objectInfo,$prefix,$doc)
231
	{
232
		foreach($objectInfo['Properties'] as $name=>$property)
233
		{
234
			if($property['type']==='TFont')
235
				$this->processObjectType('TFont',$this->_classes['TFont'],$prefix.'.'.$name,$doc);
236
			if($property['readonly'] || $property['protected'])
237
				continue;
238
			if(($type=$this->checkType($objectType,$name,$property['type']))!=='')
239
				$doc->addAttribute($prefix.'.'.$name,$type);
240
		}
241
	}
242
243
	private	function checkType($className,$propertyName,$type)
244
	{
245
		if(strrpos($propertyName,'Color')===strlen($propertyName)-5)
246
			return 'color';
247
		if($propertyName==='Style')
248
			return 'style';
249
		if($type==='boolean')
250
			return array('true','false');
251
		if($type==='string' || $type==='integer' || $type==='ITemplate')
252
			return 'text';
253
		return '';
254
	}
255
}
256