GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

TbJsonGridView   C
last analyzed

Complexity

Total Complexity 59

Size/Duplication

Total Lines 431
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 59
lcom 1
cbo 3
dl 0
loc 431
ccs 0
cts 280
cp 0
rs 6.1904
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 11 3
A run() 0 9 2
B renderPager() 0 28 6
A initColumns() 0 10 4
A renderSummary() 0 22 3
A renderTemplates() 0 20 1
A renderTemplate() 0 4 1
B renderTableBody() 0 23 4
B renderTableRow() 0 19 5
B renderTableRowJSON() 0 20 5
B createDataColumn() 0 22 5
B registerClientScript() 0 56 9
B renderItems() 0 23 5
B renderTableBodyJSON() 0 38 5
A getRenderedTableBody() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like TbJsonGridView often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TbJsonGridView, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 *## TbJsonGridView class file
4
 *
5
 * @author: antonio ramirez <[email protected]>
6
 * @copyright Copyright &copy; Clevertech 2012-
7
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
8
 */
9
10
Yii::import('booster.widgets.TbGridView');
11
Yii::import('booster.widgets.TbJsonDataColumn');
12
13
/**
14
 *## Class TbJsonGridView
15
 *
16
 * Converts TbGridView into a Json Javascript grid when using AJAX updates calls. This grid makes use of localStorage or
17
 * a custom in memory plugin to avoid repetitive ajax requests/responses and speed up data visualization.
18
 *
19
 * @package booster.widgets.grids
20
 */
21
class TbJsonGridView extends TbGridView
22
{
23
	/**
24
	 * @var boolean $json true when there is an AJAX request. TbJsonGridView expect a JSON response.
25
	 */
26
	public $json;
27
28
	/**
29
	 * @var string $template Overridden standard template to add second pager on top.
30
	 */
31
	public $template = "{pager}\n{items}\n{summary}\n{pager}";
32
33
	/**
34
	 * @var int $cacheTTL how long we keep the responses on cache? It will depend on cacheTTLType (seconds, minutes, hours)
35
	 */
36
	public $cacheTTL = 1;
37
38
	/**
39
	 * @var string the type of cache duration
40
	 *  s: seconds
41
	 *  m: minutes
42
	 *  h: hours
43
	 */
44
	public $cacheTTLType = 's';
45
46
	/**
47
	 * @var bool $localCache whether we use client ajax cache or not. True by default.
48
	 */
49
	public $localCache = true;
50
51
	/**
52
	 * @var array the configuration for the pager.
53
	 * Defaults to <code>array('class'=>'ext.booster.widgets.TbPager')</code>.
54
	 */
55
	public $pager = array('class' => 'booster.widgets.TbJsonPager');
56
57
	/**
58
	 * @var bool true when there is an AJAX request and having in template summary
59
	 */
60
	protected $_showSummary;
61
62
	/**
63
	 * Initializes $json property to find out whether ajax request or not
64
	 */
65
	public function init()
66
	{
67
		// parse request to find out whether is an ajax request or not, if so, then return $dataProvider JSON formatted
68
		$this->json = Yii::app()->getRequest()->getIsAjaxRequest();
69
		if ($this->json) {
70
			$this->_showSummary = (strpos($this->template, '{summary}')) ? true : false;
71
			$this->template = '{items}';
72
		} // going to render only items!
73
74
		parent::init();
75
	}
76
77
	/**
78
	 * Renders the view.
79
	 * This is the main entry of the whole view rendering.
80
	 * Child classes should mainly override {@link renderContent} method.
81
	 */
82
	public function run()
83
	{
84
		if (!$this->json) {
85
			parent::run();
86
		} else {
87
			$this->registerClientScript();
88
			$this->renderContent();
89
		}
90
	}
91
92
	/**
93
	 * Renders the pager.
94
	 */
95
	public function renderPager()
96
	{
97
		if (!$this->json) {
98
			parent::renderPager();
99
			return true;
100
		}
101
102
		$pager = array();
103
		if (is_string($this->pager)) {
104
			$class = $this->pager;
105
		} else if (is_array($this->pager)) {
106
			$pager = $this->pager;
107
			if (isset($pager['class'])) {
108
				$class = $pager['class'];
109
				unset($pager['class']);
110
			}
111
		}
112
		$pager['pages'] = $this->dataProvider->getPagination();
113
114
		if ($pager['pages']->getPageCount() > 1) {
115
			$pager['json'] = $this->json;
116
			$widget = $this->createWidget($class, $pager);
0 ignored issues
show
Bug introduced by
The variable $class does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
117
118
			return $widget->run();
119
		} else {
120
			return array();
121
		}
122
	}
123
124
	/**
125
	 * Creates column objects and initializes them.
126
	 */
127
	protected function initColumns()
128
	{
129
		foreach ($this->columns as $i => $column) {
130
			if (is_array($column) && !isset($column['class'])) {
131
				$this->columns[$i]['class'] = 'booster.widgets.TbJsonDataColumn';
132
			}
133
		}
134
135
		parent::initColumns();
136
	}
137
138
	/**
139
	 * Renders the data items for the grid view.
140
	 */
141
	public function renderItems()
142
	{
143
		if ($this->json)
144
		{
145
			$renderedTableBody = $this->getRenderedTableBody();
146
			echo function_exists('json_encode')
147
				? json_encode($renderedTableBody)
148
				: CJSON::encode($renderedTableBody);
149
		}
150
		elseif ($this->dataProvider->getItemCount() > 0 || $this->showTableOnEmpty)
151
		{
152
			echo "<table class=\"{$this->itemsCssClass}\">\n";
153
			$this->renderTableHeader();
154
			$this->renderTableFooter(); // TFOOT must appear before TBODY according to the standard.
155
			$this->renderTableBody();
156
			echo "</table>";
157
			$this->renderTemplates();
158
		}
159
		else
160
		{
161
			$this->renderEmptyText();
162
		}
163
	}
164
165
	public function renderSummary()
166
	{
167
		if (!$this->json)
168
		{
169
			parent::renderSummary();
170
			return true;
171
		}
172
173
		if (!$this->_showSummary)
174
		{
175
			return null;
176
		}
177
178
		ob_start();
179
		parent::renderSummary();
180
		$summary = ob_get_clean();
181
		return array(
182
			'class' => $this->summaryCssClass,
183
			'text' => $summary
184
		);
185
186
	}
187
	/**
188
	 * Renders the required templates for the client engine (jqote2 used)
189
	 */
190
	protected function renderTemplates()
191
	{
192
		echo $this->renderTemplate($this->id . '-col-template', '<td <%=this.attrs%>><%=this.content%></td>');
193
		echo $this->renderTemplate(
194
			$this->id . '-row-template',
195
			'<tr class="<%=this.class%>"><% var t = "#' . $this->id . '-col-template"; out += $.jqote(t, this.cols);%></tr>'
196
		);
197
		echo $this->renderTemplate($this->id . '-keys-template', '<span><%=this%></span>');
198
199
		echo $this->renderTemplate(
200
			$this->id . '-pager-template',
201
			'<li class="<%=this.class%>"><a href="<%=this.url%>"><%=this.text%></a></li>'
202
		);
203
204
		echo $this->renderTemplate(
205
			$this->id . '-summary-template',
206
			'<div class="<%=this.class%>"><%=this.text%></div>'
207
		);
208
209
	}
210
211
	/**
212
	 * Encloses the given JavaScript within a script tag.
213
	 *
214
	 * @param string $id
215
	 * @param string $text the JavaScript to be enclosed
216
	 *
217
	 * @return string the enclosed JavaScript
218
	 */
219
	public function renderTemplate($id, $text)
220
	{
221
		return "<script type=\"text/x-jqote-template\" id=\"{$id}\">\n<![CDATA[\n{$text}\n]]>\n</script>";
222
	}
223
224
	/**
225
	 * Renders the table body.
226
	 */
227
	public function renderTableBody()
228
	{
229
		$data = $this->dataProvider->getData();
230
		$n = count($data);
231
232
		if ($this->json) {
233
			return $this->renderTableBodyJSON($n);
234
		}
235
236
		echo CHtml::openTag('tbody');
237
238
		if ($n > 0) {
239
			for ($row = 0; $row < $n; ++$row) {
240
				$this->renderTableRow($row);
241
			}
242
		} else {
243
			echo '<tr><td colspan="' . count($this->columns) . '" class="empty">';
244
			$this->renderEmptyText();
245
			echo "</td></tr>\n";
246
		}
247
248
		echo CHtml::closeTag('tbody');
249
	}
250
251
	/**
252
	 * Renders the body table for JSON requests - assumed ajax is for JSON
253
	 *
254
	 * @param integer $rows
255
	 *
256
	 * @return array
257
	 */
258
	protected function renderTableBodyJSON($rows)
259
	{
260
		$tbody = array(
261
			'headers' => array(),
262
			'rows' => array(),
263
			'keys' => array(),
264
			'summary' => array()
265
		);
266
		foreach ($this->columns as $column) {
267
			$tbody['headers'][] = $column->renderHeaderCell();
268
		}
269
270
		if ($rows > 0) {
271
			for ($row = 0; $row < $rows; ++$row) {
272
				$tbody['rows'][] = $this->renderTableRowJSON($row);
273
			}
274
275
			foreach ($this->dataProvider->getKeys() as $key) {
276
				$tbody['keys'][] = CHtml::encode($key);
277
			}
278
279
		} else {
280
			ob_start();
281
			$this->renderEmptyText();
282
			$content = ob_get_contents();
283
			ob_end_clean();
284
285
			$tbody['rows'][0]['cols'][] = array(
286
				'attrs' => "colspan=\"" . count($this->columns) . "\"",
287
				'content' => $content
288
			);
289
			$tbody['rows'][0]['class'] = " ";
290
		}
291
		$tbody['pager'] = $this->renderPager();
292
		$tbody['url'] = Yii::app()->getRequest()->getUrl();
293
		$tbody['summary'] = $this->renderSummary();
294
		return $tbody;
295
	}
296
297
	/**
298
	 * Renders a table body row.
299
	 *
300
	 * @param integer $row the row number (zero-based).
301
	 */
302
	public function renderTableRow($row)
303
	{
304
		if ($this->rowCssClassExpression !== null) {
305
			$data = $this->dataProvider->data[$row];
306
			echo '<tr class="' . $this->evaluateExpression(
307
				$this->rowCssClassExpression,
308
				array('row' => $row, 'data' => $data)
309
			) . '">';
310
		} else if (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0) {
311
			echo '<tr class="' . $this->rowCssClass[$row % $n] . '">';
312
		} else {
313
			echo '<tr>';
314
		}
315
		foreach ($this->columns as $column) {
316
			/** @var CGridColumn $column */
317
			$column->renderDataCell($row);
318
		}
319
		echo "</tr>\n";
320
	}
321
322
	/**
323
	 * Renders a table body row for JSON requests  - assumed ajax is for JSON
324
	 *
325
	 * @param integer $row
326
	 *
327
	 * @return array
328
	 */
329
	protected function renderTableRowJSON($row)
330
	{
331
		$json = array();
332
		if ($this->rowCssClassExpression !== null) {
333
			$data = $this->dataProvider->data[$row];
334
			$json['class'] = $this->evaluateExpression(
335
				$this->rowCssClassExpression,
336
				array('row' => $row, 'data' => $data)
337
			);
338
		} else if (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0) {
339
			$json['class'] = $this->rowCssClass[$row % $n];
340
		} else {
341
			echo '<tr>';
342
		}
343
		foreach ($this->columns as $column) {
344
			$json['cols'][] = $column->renderDataCell($row);
345
		}
346
347
		return $json;
348
	}
349
350
	/**
351
	 * Creates a column based on a shortcut column specification string.
352
	 *
353
	 * @param mixed $text the column specification string
354
	 *
355
	 * @return \TbJSONDataColumn|\TbDataColumn|\CDataColumn the column instance
356
	 * @throws CException if the column format is incorrect
357
	 */
358
	protected function createDataColumn($text)
359
	{
360
		if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $text, $matches)) {
361
			throw new CException(Yii::t(
362
				'zii',
363
				'The column must be specified in the format of "Name:Type:Label", where "Type" and "Label" are optional.'
364
			));
365
		}
366
367
		$column = new TbJsonDataColumn($this);
368
		$column->name = $matches[1];
369
370
		if (isset($matches[3]) && $matches[3] !== '') {
371
			$column->type = $matches[3];
372
		}
373
374
		if (isset($matches[5])) {
375
			$column->header = $matches[5];
376
		}
377
378
		return $column;
379
	}
380
381
	/**
382
	 * Registers necessary client scripts.
383
	 */
384
	public function registerClientScript()
385
	{
386
		$id = $this->getId();
387
388
		if ($this->ajaxUpdate === false) {
389
			$ajaxUpdate = false;
390
		} else {
391
			$ajaxUpdate = array_unique(
392
				preg_split('/\s*,\s*/', $this->ajaxUpdate . ',' . $id, -1, PREG_SPLIT_NO_EMPTY)
393
			);
394
		}
395
		$options = array(
396
			'ajaxUpdate' => $ajaxUpdate,
397
			'ajaxVar' => $this->ajaxVar,
398
			'pagerClass' => $this->pagerCssClass,
399
			'summaryClass' => $this->summaryCssClass,
400
			'loadingClass' => $this->loadingCssClass,
401
			'filterClass' => $this->filterCssClass,
402
			'tableClass' => $this->itemsCssClass,
403
			'selectableRows' => $this->selectableRows,
404
			'enableHistory' => $this->enableHistory,
405
			'updateSelector' => $this->updateSelector,
406
			'cacheTTL' => $this->cacheTTL,
407
			'cacheTTLType' => $this->cacheTTLType,
408
			'localCache' => $this->localCache
409
		);
410
		if ($this->ajaxUrl !== null) {
411
			$options['url'] = CHtml::normalizeUrl($this->ajaxUrl);
412
		}
413
		if ($this->enablePagination) {
414
			$options['pageVar'] = $this->dataProvider->getPagination()->pageVar;
415
		}
416
417
		foreach (array('beforeAjaxUpdate', 'afterAjaxUpdate', 'ajaxUpdateError', 'selectionChanged') as $prop) {
418
			if ($this->{$prop} !== null) {
419
				if ((!$this->{$prop} instanceof CJavaScriptExpression) && strpos($this->{$prop}, 'js:') !== 0) {
0 ignored issues
show
Bug introduced by
The class CJavaScriptExpression does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
420
					$options[$prop] = new CJavaScriptExpression($this->{$prop});
421
				} else {
422
					$options[$prop] = $this->{$prop};
423
				}
424
			}
425
		}
426
427
		$options = CJavaScript::encode($options);
428
		/** @var $cs CClientScript */
429
		$cs = Yii::app()->getClientScript();
430
		$cs->registerCoreScript('jquery');
431
		$cs->registerCoreScript('bbq');
432
		if ($this->enableHistory) {
433
			$cs->registerCoreScript('history');
434
		}
435
436
		$cs->registerPackage('json-grid-view');
437
438
		$cs->registerScript(__CLASS__ . '#' . $id, "jQuery('#$id').yiiJsonGridView($options);");
439
	}
440
441
	/**
442
	 * @return string
443
	 */
444
	private function getRenderedTableBody()
445
	{
446
		ob_start();
447
		$this->renderTableBody();
448
		$body = ob_get_clean();
449
		return $body;
450
	}
451
}
452