Skip to content
This repository
Browse code

ENHANCEMENT: move code introspection functions to the silverstripe-co…

…deviz module
  • Loading branch information...
commit e262a291933636b32b5133e28ceb0f01c7ba85b6 1 parent 69c066c
Mateusz U authored April 17, 2012
3  css/CodeViewer.css
... ...
@@ -1,3 +0,0 @@
1  
-pre { border: 1px #777 solid; background-color: #CCC; color: #333; margin: 0.5em 2em; padding: 1em; line-height: 120%; }
2  
-
3  
-pre strong { background-color: #FFC; color: #000; padding: 2px; }
3  css/TestViewer.css
... ...
@@ -1,3 +0,0 @@
1  
-pre { border: 1px #777 solid; background-color: #CCC; color: #333; margin: 0.5em 2em; padding: 1em; line-height: 120%; }
2  
-
3  
-pre strong { background-color: #FFC; color: #000; padding: 2px; }
351  dev/CodeViewer.php
... ...
@@ -1,351 +0,0 @@
1  
-<?php
2  
-/**
3  
- * Allows human reading of a test in a format suitable for agile documentation
4  
- * 
5  
- * @package framework
6  
- * @subpackage tools
7  
- */
8  
-class CodeViewer extends Controller {
9  
-	
10  
-	public static $url_handlers = array(
11  
-		''       => 'browse',
12  
-		'$Class' => 'viewClass'
13  
-	);
14  
-	
15  
-	static $allowed_actions = array(
16  
-		'index',
17  
-		'browse',
18  
-		'viewClass'
19  
-	);
20  
-	
21  
-	/**
22  
-	 * Define a simple finite state machine.
23  
-	 * Top keys are the state names.  'start' is the first state, and 'die' is the error state.
24  
-	 * Inner keys are token names/codes.  The values are either a string, new state, or an array(new state, handler method).
25  
-	 * The handler method will be passed the PHP token as an argument, and is expected to populate a property of the object.
26  
-	 */
27  
-	static $fsm = array(
28  
-		'start' => array(
29  
-			T_CLASS => array('className','createClass'),
30  
-			T_DOC_COMMENT => array('', 'saveClassComment'),
31  
-		),
32  
-		'className' => array(
33  
-			T_STRING => array('classSpec', 'setClassName'),
34  
-		),
35  
-		'classSpec' => array(
36  
-			'{' => 'classBody',
37  
-		),
38  
-		'classBody' => array(
39  
-			T_FUNCTION => array('methodName','createBodyMethod'),
40  
-			'}' => array('start', 'completeClass'),
41  
-			T_DOC_COMMENT => array('', 'saveMethodComment'),
42  
-		),
43  
-		'methodName' => array(
44  
-			T_STRING => array('methodSpec', 'setMethodName'),
45  
-		),
46  
-		'methodSpec' => array(
47  
-			'{' => 'methodBody',
48  
-		),
49  
-		'methodBody' => array(
50  
-			'{' => array('!push','appendMethodContent'),
51  
-			'}' => array(
52  
-				'hasstack' => array('!pop', 'appendMethodContent'),
53  
-				'nostack' => array('classBody', 'completeMethod'),
54  
-			),
55  
-			T_VARIABLE => array('variable', 'potentialMethodCall'),
56  
-			T_COMMENT => array('', 'appendMethodComment'),
57  
-			T_DOC_COMMENT => array('', 'appendMethodComment'),
58  
-			'*' => array('', 'appendMethodContent'),
59  
-		),
60  
-		'variable' => array(
61  
-			T_OBJECT_OPERATOR => array('variableArrow', 'potentialMethodCall'),
62  
-			'*' => array('methodBody', 'appendMethodContent'),
63  
-		),
64  
-		'variableArrow' => array(
65  
-			T_STRING => array('methodOrProperty', 'potentialMethodCall'),
66  
-			T_WHITESPACE => array('', 'potentialMethodCall'),
67  
-			'*' => array('methodBody', 'appendMethodContent'),
68  
-		),
69  
-		'methodOrProperty' => array(
70  
-			'(' => array('methodCall', 'potentialMethodCall'),
71  
-			T_WHITESPACE => array('', 'potentialMethodCall'),
72  
-			'*' => array('methodBody', 'appendMethodContent'),
73  
-		),
74  
-		'methodCall' => array(
75  
-			'(' => array('!push/nestedInMethodCall', 'potentialMethodCall'),
76  
-			')' => array('methodBody', 'completeMethodCall'),
77  
-			'*' => array('', 'potentialMethodCall'),
78  
-		),
79  
-		'nestedInMethodCall' => array(
80  
-			'(' => array('!push', 'potentialMethodCall'),
81  
-			')' => array('!pop', 'potentialMethodCall'),
82  
-			'*' => array('', 'potentialMethodCall'),
83  
-		),
84  
-	);
85  
-	
86  
-	function init() {
87  
-		parent::init();
88  
-		
89  
-		if(!Permission::check('ADMIN')) return Security::permissionFailure();
90  
-		TestRunner::use_test_manifest();
91  
-	}
92  
-	
93  
-	public function browse() {
94  
-		$classes = ClassInfo::subclassesFor('SapphireTest');
95  
-		
96  
-		array_shift($classes);
97  
-		ksort($classes);
98  
-		
99  
-		$result  ='<h1>View any of the following test classes</h1>';
100  
-		$result .='<ul>';
101  
-		foreach($classes as $class) {
102  
-			$result .="<li><a href=\"{$this->Link($class)}\">$class</a></li>";
103  
-		}
104  
-		$result .='</ul>';
105  
-		
106  
-		$result .='<h1>View any of the following other classes</h1>';
107  
-		
108  
-		$classes = array_keys(ClassInfo::allClasses());
109  
-		sort($classes);
110  
-		
111  
-		$result .='<ul>';
112  
-		foreach($classes as $class) {
113  
-			$result .="<li><a href=\"{$this->Link($class)}\">$class</a></li>";
114  
-		}
115  
-		$result .='</ul>';
116  
-		
117  
-		return $this->customise(array (
118  
-			'Content' => $result
119  
-		))->renderWith('CodeViewer');
120  
-	}
121  
-	
122  
-	public function viewClass(SS_HTTPRequest $request) {
123  
-		$class = $request->param('Class');
124  
-		
125  
-		if(!class_exists($class)) {
126  
-			throw new Exception('CodeViewer->viewClass(): not passed a valid class to view (does the class exist?)');
127  
-		}
128  
-		
129  
-		return $this->customise(array (
130  
-			'Content' => $this->testAnalysis(getClassFile($class))
131  
-		))->renderWith('CodeViewer');
132  
-	}
133  
-	
134  
-	public function Link($action = null) {
135  
-		return Controller::join_links(Director::absoluteBaseURL(), 'dev/viewcode/', $action);
136  
-	}
137  
-	
138  
-	protected $classComment, $methodComment;
139  
-
140  
-	function saveClassComment($token) {
141  
-		$this->classComment = $this->parseComment($token);
142  
-	}
143  
-	function saveMethodComment($token) {
144  
-		$this->methodComment = $this->parseComment($token);
145  
-	}
146  
-
147  
-	function createClass($token) {
148  
-		$this->currentClass = array(
149  
-			"description" => $this->classComment['pretty'],
150  
-			"heading" => isset($this->classComment['heading']) ? $this->classComment['heading'] : null,
151  
-		);
152  
-		$ths->classComment = null;
153  
-	}
154  
-	function setClassName($token) {
155  
-		$this->currentClass['name'] = $token[1];
156  
-		if(!$this->currentClass['heading']) $this->currentClass['heading'] = $token[1];
157  
-	}
158  
-	function completeClass($token) {
159  
-		$this->classes[] = $this->currentClass;
160  
-	}
161  
-	
162  
-	function createBodyMethod($token) {
163  
-		$this->currentMethod = array();
164  
-		$this->currentMethod['content'] = "<pre>";
165  
-		$this->currentMethod['description'] = $this->methodComment['pretty'];
166  
-		$this->currentMethod['heading'] = isset($this->methodComment['heading']) ? $this->methodComment['heading'] : null;
167  
-		$this->methodComment = null;
168  
-
169  
-	}
170  
-	function setMethodName($token) {
171  
-		$this->currentMethod['name'] = $token[1];
172  
-		if(!$this->currentMethod['heading']) $this->currentMethod['heading'] = $token[1];
173  
-	}
174  
-	function appendMethodComment($token) {
175  
-		if(substr($token[1],0,2) == '/*') {
176  
-			$this->closeOffMethodContentPre();
177  
-			$this->currentMethod['content'] .= $this->prettyComment($token) . "<pre>";
178  
-		} else {
179  
-			$this->currentMethod['content'] .= $this->renderToken($token);
180  
-		}
181  
-	} 
182  
-
183  
-	function prettyComment($token) {
184  
-		$comment = preg_replace('/^\/\*/','',$token[1]);
185  
-		$comment = preg_replace('/\*\/$/','',$comment);
186  
-		$comment = preg_replace('/(^|\n)[\t ]*\* */m',"\n",$comment);
187  
-		$comment = htmlentities($comment, ENT_COMPAT, 'UTF-8');
188  
-		$comment = str_replace("\n\n", "</p><p>", $comment);
189  
-		return "<p>$comment</p>";
190  
-	}
191  
-
192  
-	function parseComment($token) {
193  
-		$parsed = array();		
194  
-
195  
-		$comment = preg_replace('/^\/\*/','',$token[1]);
196  
-		$comment = preg_replace('/\*\/$/','',$comment);
197  
-		$comment = preg_replace('/(^|\n)[\t ]*\* */m',"\n",$comment);
198  
-		
199  
-		foreach(array('heading','nav') as $var) {
200  
-			if(preg_match('/@' . $var . '\s+([^\n]+)\n/', $comment, $matches)) {
201  
-				$parsed[$var] = $matches[1];
202  
-				$comment = preg_replace('/@' . $var . '\s+([^\n]+)\n/','', $comment);
203  
-			}
204  
-		}
205  
-		
206  
-		$parsed['pretty'] = "<p>" . str_replace("\n\n", "</p><p>", htmlentities($comment, ENT_COMPAT, 'UTF-8')). "</p>";
207  
-		return $parsed;
208  
-	}
209  
-	
210  
-	protected $isNewLine = true;
211  
-
212  
-	function appendMethodContent($token) {
213  
-		if($this->potentialMethodCall) {
214  
-			$this->currentMethod['content'] .= $this->potentialMethodCall;
215  
-			$this->potentialMethodCall = "";
216  
-		}
217  
-		//if($this->isNewLine && isset($token[2])) $this->currentMethod['content'] .= $token[2] . ": ";
218  
-		$this->isNewLine = false;
219  
-		$this->currentMethod['content'] .= $this->renderToken($token);
220  
-	}
221  
-	function completeMethod($token) {
222  
-		$this->closeOffMethodContentPre();
223  
-		$this->currentMethod['content'] = str_replace("\n\t\t","\n",$this->currentMethod['content']);
224  
-		$this->currentClass['methods'][] = $this->currentMethod;
225  
-	}
226  
-	
227  
-	protected $potentialMethodCall = "";
228  
-	function potentialMethodCall($token) {
229  
-		$this->potentialMethodCall .= $this->renderToken($token);
230  
-	}
231  
-	function completeMethodCall($token) {
232  
-		$this->potentialMethodCall .= $this->renderToken($token);
233  
-		if(strpos($this->potentialMethodCall, '-&gt;</span><span class="T_STRING">assert') !== false) {
234  
-			$this->currentMethod['content'] .= "<strong>" . $this->potentialMethodCall . "</strong>";
235  
-		} else {
236  
-			$this->currentMethod['content'] .= $this->potentialMethodCall;
237  
-		}
238  
-		$this->potentialMethodCall = "";
239  
-	}
240  
-	
241  
-	/**
242  
-	 * Finish the "pre" block in method content.
243  
-	 * Will remove whitespace and empty "pre" blocks
244  
-	 */
245  
-	function closeOffMethodContentPre() {
246  
-		$this->currentMethod['content'] = trim($this->currentMethod['content']);
247  
-		if(substr($this->currentMethod['content'],-5) == '<pre>') $this->currentMethod['content'] = substr($this->currentMethod['content'], 0,-5);
248  
-		else $this->currentMethod['content'] .= '</pre>';
249  
-	}
250  
-		
251  
-	/**
252  
-	 * Render the given token as HTML
253  
-	 */
254  
-	function renderToken($token) {
255  
-		$tokenContent = htmlentities(
256  
-			is_array($token) ? $token[1] : $token, 
257  
-			ENT_COMPAT, 
258  
-			'UTF-8'
259  
-		);
260  
-		$tokenName = is_array($token) ? token_name($token[0]) : 'T_PUNCTUATION';
261  
-
262  
-		switch($tokenName) {
263  
-			case "T_WHITESPACE":
264  
-				if(strpos($tokenContent, "\n") !== false) $this->isNewLine = true;
265  
-				return $tokenContent;
266  
-			default:
267  
-				return "<span class=\"$tokenName\">$tokenContent</span>";
268  
-		}
269  
-	}
270  
-	
271  
-	protected $classes = array();
272  
-	protected $currentMethod, $currentClass;
273  
-	
274  
-	function testAnalysis($file) {
275  
-		$content = file_get_contents($file);
276  
-		$tokens = token_get_all($content);
277  
-		
278  
-		// Execute a finite-state-machine with a built-in state stack
279  
-		// This FSM+stack gives us enough expressive power for simple PHP parsing
280  
-		$state = "start";
281  
-		$stateStack = array();
282  
-		
283  
-		//echo "<li>state $state";
284  
-		foreach($tokens as $token) {
285  
-			// Get token name - some tokens are arrays, some arent'
286  
-			if(is_array($token)) $tokenName = $token[0]; else $tokenName = $token;
287  
-			//echo "<li>token '$tokenName'";
288  
-			
289  
-			// Find the rule for that token in the current state
290  
-			if(isset(self::$fsm[$state][$tokenName])) $rule = self::$fsm[$state][$tokenName];
291  
-			else if(isset(self::$fsm[$state]['*'])) $rule = self::$fsm[$state]['*'];
292  
-			else $rule = null;
293  
-			
294  
-			// Check to see if we have specified multiple rules depending on whether the stack is populated	
295  
-			if(is_array($rule) && array_keys($rule) == array('hasstack', 'nostack')) {
296  
-				if($stateStack) $rule = $rule['hasstack'];
297  
-				else $rule = $rule = $rule['nostack'];
298  
-			}
299  
-			
300  
-			if(is_array($rule)) {
301  
-				list($destState, $methodName) = $rule;
302  
-				$this->$methodName($token);
303  
-			} else if($rule) {
304  
-				$destState = $rule;
305  
-			} else {
306  
-				$destState = null;
307  
-			}
308  
-			//echo "<li>->state $destState";
309  
-
310  
-			if(preg_match('/!(push|pop)(\/[a-zA-Z0-9]+)?/', $destState, $parts)) {
311  
-				$action = $parts[1];
312  
-				$argument = isset($parts[2]) ? substr($parts[2],1) : null;
313  
-				$destState = null;
314  
-				
315  
-				switch($action) {
316  
-					case "push":
317  
-						$stateStack[] = $state;
318  
-						if($argument) $destState = $argument;
319  
-						break;
320  
-					
321  
-					case "pop":
322  
-						if($stateStack) $destState = array_pop($stateStack);
323  
-						else if($argument) $destState = $argument;
324  
-						else user_error("State transition '!pop' was attempted with an empty state-stack and no default option specified.", E_USER_ERROR);
325  
-				}
326  
-			}
327  
-			
328  
-			if($destState) $state = $destState;
329  
-			if(!isset(self::$fsm[$state])) user_error("Transition to unrecognised state '$state'", E_USER_ERROR);
330  
-		}
331  
-		
332  
-		$subclasses = ClassInfo::subclassesFor('SapphireTest');
333  
-		foreach($this->classes as $classDef) {
334  
-			if(true ||in_array($classDef['name'], $subclasses)) {
335  
-				echo "<h1>$classDef[heading]</h1>";
336  
-				echo "<div style=\"font-weight: bold\">$classDef[description]</div>";
337  
-				if(isset($classDef['methods'])) foreach($classDef['methods'] as $method) {
338  
-					if(true || substr($method['name'],0,4) == 'test') {
339  
-						//$title = ucfirst(strtolower(preg_replace('/([a-z])([A-Z])/', '$1 $2', substr($method['name'], 4))));
340  
-						$title = $method['heading'];
341  
-
342  
-						echo "<h2>$title</h2>";
343  
-						echo "<div style=\"font-weight: bold\">$method[description]</div>";
344  
-						echo $method['content'];
345  
-					}
346  
-				}
347  
-			}
348  
-			
349  
-		}
350  
-	}
351  
-}
11  dev/DevelopmentAdmin.php
@@ -83,8 +83,7 @@ function index() {
83 83
 			"tests/endsession" => "Ends a test session",
84 84
 			"jstests" => "See a list of JavaScript tests to run",
85 85
 			"jstests/all" => "Run all JavaScript tests",
86  
-			"tasks" => "See a list of build tasks to run",
87  
-			"viewcode" => "Read source code in a literate programming style",
  86
+			"tasks" => "See a list of build tasks to run"
88 87
 		);
89 88
 		
90 89
 		// Web mode
@@ -127,10 +126,6 @@ function tasks() {
127 126
 		return TaskRunner::create();
128 127
 	}
129 128
 	
130  
-	function viewmodel() {
131  
-		return ModelViewer::create();
132  
-	}
133  
-	
134 129
 	function build($request) {
135 130
 		if(Director::is_cli()) {
136 131
 			$da = DatabaseAdmin::create();
@@ -187,8 +182,4 @@ function reset() {
187 182
 	function errors() {
188 183
 		Director::redirect("Debug_");
189 184
 	}
190  
-	
191  
-	function viewcode($request) {
192  
-		return CodeViewer::create();
193  
-	}
194 185
 }
213  dev/ModelViewer.php
... ...
@@ -1,213 +0,0 @@
1  
-<?php
2  
-/**
3  
- * Gives you a nice way of viewing your data model.
4  
- * Access at dev/viewmodel.
5  
- *
6  
- * Requirements: http://graphviz.org/
7  
- * 
8  
- * @package framework
9  
- * @subpackage tools
10  
- */
11  
-class ModelViewer extends Controller {
12  
-	static $url_handlers = array(
13  
-		'$Module!' => 'handleModule',
14  
-	);
15  
-
16  
-	protected $module = null;
17  
-	
18  
-	function handleModule($request) {
19  
-		return new ModelViewer_Module($request->param('Module'));
20  
-	}
21  
-	
22  
-	function init() {
23  
-		parent::init();
24  
-
25  
-		$canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN"));
26  
-		if(!$canAccess) return Security::permissionFailure($this);
27  
-
28  
-		// check for graphviz dependencies
29  
-		$returnCode = 0;
30  
-		$output = array();
31  
-		exec("which neato", $output, $returnCode);
32  
-		if($returnCode != 0) {
33  
-			user_error(
34  
-				'You don\'t seem to have the GraphViz library (http://graphviz.org/) and the "neato" command-line utility available',
35  
-				E_USER_ERROR
36  
-			);
37  
-		}
38  
-	}
39  
-
40  
-	/**
41  
-	 * Model classes
42  
-	 */
43  
-	function Models() {
44  
-		$classes = ClassInfo::subclassesFor('DataObject');
45  
-		array_shift($classes);
46  
-		$output = new ArrayList();
47  
-		foreach($classes as $class) {
48  
-			$output->push(new ModelViewer_Model($class));
49  
-		}
50  
-		return $output;
51  
-	}
52  
-
53  
-	/**
54  
-	 * Model classes, grouped by Module
55  
-	 */
56  
-	function Modules() {
57  
-		$classes = ClassInfo::subclassesFor('DataObject');
58  
-		array_shift($classes);
59  
-		
60  
-		$modules = array();
61  
-		foreach($classes as $class) {
62  
-			$model = new ModelViewer_Model($class);
63  
-			if(!isset($modules[$model->Module])) $modules[$model->Module] = new ArrayList();
64  
-			$modules[$model->Module]->push($model);
65  
-		}
66  
-		ksort($modules);
67  
-		unset($modules['userforms']);
68  
-		
69  
-		if($this->module) {
70  
-			$modules = array($this->module => $modules[$this->module]);
71  
-		}
72  
-
73  
-		$output = new ArrayList();
74  
-		foreach($modules as $moduleName => $models) {
75  
-			$output->push(new ArrayData(array(
76  
-				'Link' => 'dev/viewmodel/' . $moduleName,
77  
-				'Name' => $moduleName,
78  
-				'Models' => $models,
79  
-			)));
80  
-		}
81  
-		
82  
-		return $output;
83  
-	}
84  
-}
85  
-
86  
-/**
87  
- * @package framework
88  
- * @subpackage tools
89  
- */
90  
-class ModelViewer_Module extends ModelViewer {
91  
-	static $url_handlers = array(
92  
-		'graph' => 'graph',
93  
-	);
94  
-
95  
-	/**
96  
-	 * ModelViewer can be optionally constructed to restrict its output to a specific module
97  
-	 */
98  
-	function __construct($module = null) {
99  
-		$this->module = $module;
100  
-		
101  
-		parent::__construct();
102  
-	}
103  
-	
104  
-	function graph() {
105  
-		SSViewer::set_source_file_comments(false);
106  
-		$dotContent = $this->renderWith("ModelViewer_dotsrc");
107  
-		$CLI_dotContent = escapeshellarg($dotContent);
108  
-
109  
-		$output= `echo $CLI_dotContent | neato -Tpng:gd &> /dev/stdout`;
110  
-		if(substr($output,1,3) == 'PNG') header("Content-type: image/png");
111  
-		else header("Content-type: text/plain");
112  
-		echo $output;
113  
-	}
114  
-}
115  
-
116  
-/**
117  
- * Represents a single model in the model viewer 
118  
- * 
119  
- * @package framework
120  
- * @subpackage tools
121  
- */
122  
-class ModelViewer_Model extends ViewableData {
123  
-	protected $className;
124  
-	
125  
-	function __construct($className) {
126  
-		$this->className = $className;
127  
-		parent::__construct();
128  
-	}
129  
-	
130  
-	function getModule() {
131  
-		$classes   = SS_ClassLoader::instance()->getManifest()->getClasses();
132  
-		$className = strtolower($this->className);
133  
-
134  
-		if(($pos = strpos($className,'_')) !== false) $className = substr($className,0,$pos);
135  
-		if(isset($classes[$className])) {
136  
-			if(preg_match('/^'.str_replace('/','\/',preg_quote(BASE_PATH)).'\/([^\/]+)\//', $classes[$className], $matches)) {
137  
-				return $matches[1];
138  
-			}
139  
-		}
140  
-	}
141  
-	
142  
-	function getName() {
143  
-		return $this->className;
144  
-	}
145  
-	
146  
-	function getParentModel() {
147  
-		$parentClass = get_parent_class($this->className);
148  
-		if($parentClass != "DataObject") return $parentClass;
149  
-	}
150  
-	
151  
-	function Fields() {
152  
-		$output = new ArrayList();
153  
-		
154  
-		$output->push(new ModelViewer_Field($this,'ID', 'PrimaryKey'));
155  
-		if(!$this->ParentModel) {
156  
-			$output->push(new ModelViewer_Field($this,'Created', 'Datetime'));
157  
-			$output->push(new ModelViewer_Field($this,'LastEdited', 'Datetime'));
158  
-		}
159  
-
160  
-		$db = singleton($this->className)->uninherited('db',true);
161  
-		if($db) foreach($db as $k => $v) {
162  
-			$output->push(new ModelViewer_Field($this, $k, $v));
163  
-		}
164  
-		return $output;		
165  
-	}
166  
-	
167  
-	function Relations() {
168  
-		$output = new ArrayList();
169  
-		
170  
-		foreach(array('has_one','has_many','many_many') as $relType) {
171  
-			$items = singleton($this->className)->uninherited($relType,true);
172  
-			if($items) foreach($items as $k => $v) {
173  
-				$output->push(new ModelViewer_Relation($this, $k, $v, $relType));
174  
-			}
175  
-		}
176  
-		return $output;
177  
-	}
178  
-}
179  
-
180  
-/**
181  
- * @package framework
182  
- * @subpackage tools
183  
- */
184  
-class ModelViewer_Field extends ViewableData {
185  
-	public $Model, $Name, $Type;
186  
-	
187  
-	function __construct($model, $name, $type) {
188  
-		$this->Model = $model;
189  
-		$this->Name = $name;
190  
-		$this->Type = $type;
191  
-		
192  
-		parent::__construct();
193  
-	}
194  
-}
195  
-
196  
-/**
197  
- * @package framework
198  
- * @subpackage tools
199  
- */
200  
-class ModelViewer_Relation extends ViewableData {
201  
-	public $Model, $Name, $RelationType, $RelatedClass;
202  
-	
203  
-	function __construct($model, $name, $relatedClass, $relationType) {
204  
-		$this->Model = $model;
205  
-		$this->Name = $name;
206  
-		$this->RelatedClass = $relatedClass;
207  
-		$this->RelationType = $relationType;
208  
-		
209  
-		parent::__construct();
210  
-	}
211  
-	
212  
-}
213  
-
257  dev/TestViewer.php
... ...
@@ -1,257 +0,0 @@
1  
-<?php
2  
-/**
3  
- * Allows human reading of a test in a format suitable for agile documentation
4  
- * @package framework
5  
- * @subpackage testing
6  
- */
7  
-class TestViewer extends Controller {
8  
-	/**
9  
-	 * Define a simple finite state machine.
10  
-	 * Top keys are the state names.  'start' is the first state, and 'die' is the error state.
11  
-	 * Inner keys are token names/codes.  The values are either a string, new state, or an array(new state, handler method).
12  
-	 * The handler method will be passed the PHP token as an argument, and is expected to populate a property of the object.
13  
-	 */
14  
-	static $fsm = array(
15  
-		'start' => array(
16  
-			T_CLASS => array('className','createClass'),
17  
-		),
18  
-		'className' => array(
19  
-			T_STRING => array('classSpec', 'setClassName'),
20  
-		),
21  
-		'classSpec' => array(
22  
-			'{' => 'classBody',
23  
-		),
24  
-		'classBody' => array(
25  
-			T_FUNCTION => array('methodName','createBodyMethod'),
26  
-			'}' => array('start', 'completeClass'),
27  
-		),
28  
-		'methodName' => array(
29  
-			T_STRING => array('methodSpec', 'setMethodName'),
30  
-		),
31  
-		'methodSpec' => array(
32  
-			'{' => 'methodBody',
33  
-		),
34  
-		'methodBody' => array(
35  
-			'{' => array('!push','appendMethodContent'),
36  
-			'}' => array(
37  
-				'hasstack' => array('!pop', 'appendMethodContent'),
38  
-				'nostack' => array('classBody', 'completeMethod'),
39  
-			),
40  
-			T_VARIABLE => array('variable', 'potentialMethodCall'),
41  
-			T_COMMENT => array('', 'appendMethodComment'),
42  
-			'*' => array('', 'appendMethodContent'),
43  
-		),
44  
-		'variable' => array(
45  
-			T_OBJECT_OPERATOR => array('variableArrow', 'potentialMethodCall'),
46  
-			'*' => array('methodBody', 'appendMethodContent'),
47  
-		),
48  
-		'variableArrow' => array(
49  
-			T_STRING => array('methodOrProperty', 'potentialMethodCall'),
50  
-			T_WHITESPACE => array('', 'potentialMethodCall'),
51  
-			'*' => array('methodBody', 'appendMethodContent'),
52  
-		),
53  
-		'methodOrProperty' => array(
54  
-			'(' => array('methodCall', 'potentialMethodCall'),
55  
-			T_WHITESPACE => array('', 'potentialMethodCall'),
56  
-			'*' => array('methodBody', 'appendMethodContent'),
57  
-		),
58  
-		'methodCall' => array(
59  
-			'(' => array('!push/nestedInMethodCall', 'potentialMethodCall'),
60  
-			')' => array('methodBody', 'completeMethodCall'),
61  
-			'*' => array('', 'potentialMethodCall'),
62  
-		),
63  
-		'nestedInMethodCall' => array(
64  
-			'(' => array('!push', 'potentialMethodCall'),
65  
-			')' => array('!pop', 'potentialMethodCall'),
66  
-			'*' => array('', 'potentialMethodCall'),
67  
-		),
68  
-	);
69  
-	
70  
-	function init() {
71  
-		parent::init();
72  
-		
73  
-		$canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN"));
74  
-		if(!$canAccess) return Security::permissionFailure($this);
75  
-	}
76  
-
77  
-	function createClass($token) {
78  
-		$this->currentClass = array();
79  
-	}
80  
-	function setClassName($token) {
81  
-		$this->currentClass['name'] = $token[1];
82  
-	}
83  
-	function completeClass($token) {
84  
-		$this->classes[] = $this->currentClass;
85  
-	}
86  
-	
87  
-	function createBodyMethod($token) {
88  
-		$this->currentMethod = array();
89  
-		$this->currentMethod['content'] = "<pre>";
90  
-	}
91  
-	function setMethodName($token) {
92  
-		$this->currentMethod['name'] = $token[1];
93  
-	}
94  
-	function appendMethodComment($token) {
95  
-		if(substr($token[1],0,2) == '/*') {
96  
-			$comment = preg_replace('/^\/\*/','',$token[1]);
97  
-			$comment = preg_replace('/\*\/$/','',$comment);
98  
-			$comment = preg_replace('/\n[\t ]*\* */m',"\n",$comment);
99  
-			
100  
-			$this->closeOffMethodContentPre();
101  
-			$this->currentMethod['content'] .= "<p>$comment</p><pre>";
102  
-		} else {
103  
-			$this->currentMethod['content'] .= $this->renderToken($token);
104  
-		}
105  
-		
106  
-	} 
107  
-	function appendMethodContent($token) {
108  
-		if($this->potentialMethodCall) {
109  
-			$this->currentMethod['content'] .= $this->potentialMethodCall;
110  
-			$this->potentialMethodCall = "";
111  
-		}
112  
-		$this->currentMethod['content'] .= $this->renderToken($token);
113  
-	}
114  
-	function completeMethod($token) {
115  
-		$this->closeOffMethodContentPre();
116  
-		$this->currentMethod['content'] = str_replace("\n\t\t","\n",$this->currentMethod['content']);
117  
-		$this->currentClass['methods'][] = $this->currentMethod;
118  
-	}
119  
-	
120  
-	protected $potentialMethodCall = "";
121  
-	function potentialMethodCall($token) {
122  
-		$this->potentialMethodCall .= $this->renderToken($token);
123  
-	}
124  
-	function completeMethodCall($token) {
125  
-		$this->potentialMethodCall .= $this->renderToken($token);
126  
-		if(strpos($this->potentialMethodCall, '-&gt;</span><span class="T_STRING">assert') !== false) {
127  
-			$this->currentMethod['content'] .= "<strong>" . $this->potentialMethodCall . "</strong>";
128  
-		} else {
129  
-			$this->currentMethod['content'] .= $this->potentialMethodCall;
130  
-		}
131  
-		$this->potentialMethodCall = "";
132  
-	}
133  
-	
134  
-	/**
135  
-	 * Finish the "pre" block in method content.
136  
-	 * Will remove whitespace and empty "pre" blocks
137  
-	 */
138  
-	function closeOffMethodContentPre() {
139  
-		$this->currentMethod['content'] = trim($this->currentMethod['content']);
140  
-		if(substr($this->currentMethod['content'],-5) == '<pre>') $this->currentMethod['content'] = substr($this->currentMethod['content'], 0,-5);
141  
-		else $this->currentMethod['content'] .= '</pre>';
142  
-	}
143  
-		
144  
-	/**
145  
-	 * Render the given token as HTML
146  
-	 */
147  
-	function renderToken($token) {
148  
-		$tokenContent = htmlentities(
149  
-			is_array($token) ? $token[1] : $token, 
150  
-			ENT_COMPAT, 
151  
-			'UTF-8'
152  
-		);
153  
-		$tokenName = is_array($token) ? token_name($token[0]) : 'T_PUNCTUATION';
154  
-
155  
-		switch($tokenName) {
156  
-			case "T_WHITESPACE":
157  
-				return $tokenContent;
158  
-			default:
159  
-				return "<span class=\"$tokenName\">$tokenContent</span>";
160  
-		}
161  
-	}
162  
-	
163  
-	protected $classes = array();
164  
-	protected $currentMethod, $currentClass;
165  
-	
166  
-	function Content() {
167  
-		$className = $this->urlParams['ID'];
168  
-		if($className && ClassInfo::exists($className)) {
169  
-			return $this->testAnalysis(getClassFile($className));
170  
-		} else {
171  
-			$result = "<h1>View any of the following test classes</h1>";
172  
-			$classes = ClassInfo::subclassesFor('SapphireTest');
173  
-			ksort($classes);
174  
-			foreach($classes as $className) {
175  
-				if($className == 'SapphireTest') continue;
176  
-				$result .= "<li><a href=\"TestViewer/show/$className\">$className</a></li>";
177  
-			}
178  
-			return $result;
179  
-		}
180  
-	}
181  
-		
182  
-	function testAnalysis($file) {
183  
-		$content = file_get_contents($file);
184  
-		$tokens = token_get_all($content);
185  
-		
186  
-		// Execute a finite-state-machine with a built-in state stack
187  
-		// This FSM+stack gives us enough expressive power for simple PHP parsing
188  
-		$state = "start";
189  
-		$stateStack = array();
190  
-		
191  
-		//echo "<li>state $state";
192  
-		foreach($tokens as $token) {
193  
-			// Get token name - some tokens are arrays, some arent'
194  
-			if(is_array($token)) $tokenName = $token[0]; else $tokenName = $token;
195  
-			//echo "<li>token '$tokenName'";
196  
-			
197  
-			// Find the rule for that token in the current state
198  
-			if(isset(self::$fsm[$state][$tokenName])) $rule = self::$fsm[$state][$tokenName];
199  
-			else if(isset(self::$fsm[$state]['*'])) $rule = self::$fsm[$state]['*'];
200  
-			else $rule = null;
201  
-			
202  
-			// Check to see if we have specified multiple rules depending on whether the stack is populated	
203  
-			if(is_array($rule) && array_keys($rule) == array('hasstack', 'nostack')) {
204  
-				if($stateStack) $rule = $rule['hasstack'];
205  
-				else $rule = $rule = $rule['nostack'];
206  
-			}
207  
-			
208  
-			if(is_array($rule)) {
209  
-				list($destState, $methodName) = $rule;
210  
-				$this->$methodName($token);
211  
-			} else if($rule) {
212  
-				$destState = $rule;
213  
-			} else {
214  
-				$destState = null;
215  
-			}
216  
-			//echo "<li>->state $destState";
217  
-
218  
-			if(preg_match('/!(push|pop)(\/[a-zA-Z0-9]+)?/', $destState, $parts)) {
219  
-				$action = $parts[1];
220  
-				$argument = isset($parts[2]) ? substr($parts[2],1) : null;
221  
-				$destState = null;
222  
-				
223  
-				switch($action) {
224  
-					case "push":
225  
-						$stateStack[] = $state;
226  
-						if($argument) $destState = $argument;
227  
-						break;
228  
-					
229  
-					case "pop":
230  
-						if($stateStack) $destState = array_pop($stateStack);
231  
-						else if($argument) $destState = $argument;
232  
-						else user_error("State transition '!pop' was attempted with an empty state-stack and no default option specified.", E_USER_ERROR);
233  
-				}
234  
-			}
235  
-			
236  
-			if($destState) $state = $destState;
237  
-			if(!isset(self::$fsm[$state])) user_error("Transition to unrecognised state '$state'", E_USER_ERROR);
238  
-		}
239  
-		
240  
-		$subclasses = ClassInfo::subclassesFor('SapphireTest');
241  
-		foreach($this->classes as $classDef) {
242  
-			if(in_array($classDef['name'], $subclasses)) {
243  
-				echo "<h1>$classDef[name]</h1>";
244  
-				if($classDef['methods']) foreach($classDef['methods'] as $method) {
245  
-					if(substr($method['name'],0,4) == 'test') {
246  
-						//$title = ucfirst(strtolower(preg_replace('/([a-z])([A-Z])/', '$1 $2', substr($method['name'], 4))));
247  
-						$title = $method['name'];
248  
-
249  
-						echo "<h2>$title</h2>";
250  
-						echo $method['content'];
251  
-					}
252  
-				}
253  
-			}
254  
-			
255  
-		}
256  
-	}
257  
-}
14  scss/CodeViewer.scss
... ...
@@ -1,14 +0,0 @@
1  
-pre {
2  
-	border: 1px #777 solid;
3  
-	background-color: #CCC;
4  
-	color: #333;
5  
-	margin: 0.5em 2em;
6  
-	padding: 1em;
7  
-	line-height: 120%;
8  
-}
9  
-
10  
-pre strong {
11  
-	background-color: #FFC;
12  
-	color: #000;
13  
-	padding: 2px;
14  
-}
14  scss/TestViewer.scss
... ...
@@ -1,14 +0,0 @@
1  
-pre {
2  
-	border: 1px #777 solid;
3  
-	background-color: #CCC;
4  
-	color: #333;
5  
-	margin: 0.5em 2em;
6  
-	padding: 1em;
7  
-	line-height: 120%;
8  
-}
9  
-
10  
-pre strong {
11  
-	background-color: #FFC;
12  
-	color: #000;
13  
-	padding: 2px;
14  
-}
9  templates/CodeViewer.ss
... ...
@@ -1,9 +0,0 @@
1  
-<html>
2  
-<head>
3  
-<% base_tag %>
4  
-<link rel="stylesheet" href="$ModulePath(framework)/css/CodeViewer.css" />
5  
-</head>
6  
-<body>
7  
-	$Content
8  
-</body>
9  
-</html>
34  templates/ModelViewer.ss
... ...
@@ -1,34 +0,0 @@
1  
-<html>
2  
-    <head>
3  
-	<% base_tag %>
4  
-        <title>Data Model</title>
5  
-    </head>
6  
-
7  
-    <body>
8  
-        <h1>Data Model for your project</h1>
9  
-
10  
-		<% control Modules %>
11  
-		<h1>Module $Name</h1>
12  
-		
13  
-		<img src="$Link/graph" />
14  
-        
15  
-        <% control Models %>
16  
-        <h2>$Name <% if ParentModel %> (subclass of $ParentModel)<% end_if %></h2>
17  
-        <h4>Fields</h4>
18  
-        <ul>
19  
-        <% control Fields %>
20  
-        <li>$Name - $Type</li>
21  
-        <% end_control %>
22  
-        </ul>
23  
-
24  
-        <h4>Relations</h4>
25  
-        <ul>
26  
-        <% control Relations %>
27  
-        <li>$Name $RelationType $RelatedClass</li>
28  
-        <% end_control %>
29  
-        </ul>
30  
-        <% end_control %>
31  
-		<% end_control %>
32  
-    </body>
33  
-</html>
34  
-    
20  templates/ModelViewer_dotsrc.ss
... ...
@@ -1,20 +0,0 @@
1  
-digraph g {
2  
-	orientation=portrait;
3  
-	overlap=false;
4  
-	splines=true;
5  
-
6  
-	edge[fontsize=8,len=1.5]; 
7  
-	node[fontsize=10,shape=box];
8  
-
9  
-    <% control Modules %>
10  
-    <% control Models %>
11  
-        $Name [shape=record,label="{$Name|<% control Fields %>$Name\\n<% end_control %>}"];
12  
-	<% if ParentModel %>
13  
-		$Name -> $ParentModel [style=dotted];
14  
-	<% end_if %>
15  
-       <% control Relations %>
16  
-            $Model.Name -> $RelatedClass [label="$Name\\n$RelationType"];
17  
-        <% end_control %>
18  
-    <% end_control %>
19  
-    <% end_control %>
20  
-}
9  templates/TestViewer.ss
... ...
@@ -1,9 +0,0 @@
1  
-<html>
2  
-<head>
3  
-<% base_tag %>
4  
-<link rel="stylesheet" href="$ModulePath(framework)/css/TestViewer.css" />
5  
-</head>
6  
-<body>
7  
-	$Content
8  
-</body>
9  
-</html>

0 notes on commit e262a29

Please sign in to comment.
Something went wrong with that request. Please try again.