Completed
Push — master ( 0eb774...254a9f )
by Romans
02:34 queued 02:29
created

lib/AbstractView.php (15 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * A base class for all Visual objects in Agile Toolkit. The
4
 * important distinctive property of all Views is abiltiy
5
 * to render themselves (produce HTML) automatically and
6
 * recursively.
7
 */
8
abstract class AbstractView extends AbstractObject
9
{
10
    /**
11
     * $template is an object containing indexed HTML template.
12
     *
13
     * Example:
14
     *
15
     * $view->template->set('title', $my_title);
16
     *
17
     * Assuming you have tag <?$title?> in template file associated
18
     * with this view - will insert text into this tag.
19
     *
20
     * @see AbstractObject::add();
21
     * @see AbstractView::defaultTemplate();
22
     *
23
     * @var Template
24
     */
25
    public $template = false;
26
27
    /**
28
     * @internal
29
     *
30
     * $template_flush is set to a spot on the template, which
31
     * should be flushed out. When using AJAX we want to show
32
     * only certain region from our template. However several
33
     * childs may want to put their data. This property will
34
     * be set to region's name my call_ajax_render and if it's
35
     * set, call_ajax_render will echo it and return false.
36
     *
37
     * @var string
38
     */
39
    public $template_flush = false;
40
41
    /**
42
     * $spot defines a place on a parent's template where render() will
43
     * output() resulting HTML.
44
     *
45
     * @see output()
46
     * @see render()
47
     * @see AbstractObject::add();
48
     * @see defaultSpot();
49
     *
50
     * @var string
51
     */
52
    public $spot;
53
54
    /**
55
     * When using setModel() with Views some views will want to populate
56
     * fields, columns etc corresponding to models meta-data. That is the
57
     * job of Controller. When you create a custom controller for your view
58
     * set this property to point at your controller and it will be used.
59
     * automatically.
60
     *
61
     * @var string
62
     */
63
    public $default_controller = null;
64
65
    /**
66
     * @var boolean
67
     */
68
    public $auto_track_element = true;
69
70
71
72
    // {{{ Basic Operations
73
74
    /**
75
     * For safety, you can't clone views. Use $view->newInstance instead.
76
     */
77
    public function __clone()
78
    {
79
        throw $this->exception('Can\'t clone Views');
80
    }
81
    /**
82
     * Associate view with a model. Additionally may initialize a controller
83
     * which would copy fields from the model into the View.
84
     *
85
     * @param object|string $model         Class without "Model_" prefix or object
86
     * @param array|string|null  $actual_fields List of fields in order to populate
87
     *
88
     * @return AbstractModel object
89
     */
90
    public function setModel($model, $actual_fields = UNDEFINED)
91
    {
92
        parent::setModel($model);
93
94
        // Some models will want default controller to be associated
95
        if ($this->model->default_controller) {
96
            $this->controller
97
                = $this->model->setController($this->model->default_controller);
98
        }
99
100
        // Use our default controller if present
101
        if ($this->default_controller) {
102
            $this->controller = $this->setController($this->default_controller);
103
        }
104
105
        if ($this->controller) {
106
            if ($this->controller->hasMethod('setActualFields')) {
107
                $this->controller->setActualFields($actual_fields);
108
            }
109
            if ($this->controller->hasMethod('_bindView')) {
110
                $this->controller->_bindView();
111
            }
112
        }
113
114
        if ($this->model instanceof SQL_Model) {
115
            $this->dq = $this->model->_dsql();    // compatibility
0 ignored issues
show
The property dq 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...
116
        }
117
118
        return $this->model;
119
    }
120
121
    /** @internal  used by getHTML */
122
    public $_tsBuffer = '';
123
    /** @internal accumulates output for getHTML */
124
    public function _tsBuffer($t, $data)
125
    {
126
        $this->_tsBuffer .= $data;
127
    }
128
129
    /**
130
     * Converting View into string will render recursively and produce HTML.
131
     * If argument is passed, JavaScript will be added into on_ready section
132
     * of your document like when rendered normally. Note that you might
133
     * require to destroy object if you don't want it's HTML to appear normally.
134
     *
135
     * @param bool $destroy    Destroy object preventing it from rendering
136
     * @param bool $execute_js Also capture JavaScript chains of object
137
     *
138
     * @return string HTML
139
     */
140
    public function getHTML($destroy = true, $execute_js = true)
141
    {
142
        $this->addHook('output', array($this, '_tsBuffer'));
143
        $this->recursiveRender();
144
        $this->removeHook('output', array($this, '_tsBuffer'));
145
        $ret = $this->_tsBuffer;
146
        $this->_tsBuffer = '';
147
        if ($execute_js && @$this->app->jquery) {
148
            $this->app->jquery->getJS($this);
149
        }
150
        if ($destroy) {
151
            $this->destroy();
152
        }
153
154
        return $ret;
155
    }
156
    // }}}
157
158
    // {{{ Template Setup
159
160
    /**
161
     * Called automatically during init for template initalization.
162
     *
163
     * @param string       $template_spot   Where object's output goes
164
     * @param string|array $template_branch Where objects gets it's template
165
     *
166
     * @return AbstractView $this
167
     *
168
     * @internal
169
     */
170
    public function initializeTemplate($template_spot = null, $template_branch = null)
171
    {
172
        if (!$template_spot) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $template_spot of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
173
            $template_spot = $this->defaultSpot();
174
        }
175
        $this->spot = $template_spot;
176
        if (@$this->owner->template
177
            && !$this->owner->template->is_set($this->spot)
178
        ) {
179
            throw $this->owner->template->exception(
180
                'Spot is not found in owner\'s template'
181
            )->addMoreInfo('spot', $this->spot);
182
        }
183
        if (!isset($template_branch)) {
184
            $template_branch = $this->defaultTemplate();
185
        }
186
        if (isset($template_branch)) {
187
            // template branch would tell us what kind of template we have to
188
            // use. Let's look at several cases:
189
190
            if (is_object($template_branch)) {
191
                // it might be already template instance (object)
192
                $this->template = $template_branch;
193
            } elseif (is_array($template_branch)) {
194
                // it might be array with [0]=template, [1]=tag
195
                if (is_object($template_branch[0])) {
196
                    // if [0] is object, we'll use that
197
                    $this->template = $template_branch[0];
198
                } else {
199
                    $this->template = $this->app->add('Template');
200
                    $this->template->loadTemplate($template_branch[0]);
201
                }
202
                // Now that we loaded it, let's see which tag we need to cut out
203
                $this->template = $this->template->cloneRegion(
204
                    isset($template_branch[1]) ? $template_branch[1] : '_top'
205
                );
206
            } else {
207
                // brach could be just a string - a region to clone off parent
208
                if (isset($this->owner->template)) {
209
                    $this->template
210
                        = $this->owner->template->cloneRegion($template_branch);
211
                } else {
212
                    $this->template = $this->add('Template');
213
                }
214
            }
215
            $this->template->owner = $this;
216
        }
217
218
        // Now that the template is loaded, let's take care of parent's template
219
        if ($this->owner
220
            && (isset($this->owner->template))
221
            && (!empty($this->owner->template))
222
        ) {
223
            $this->owner->template->del($this->spot);
224
        }
225
226
        // Cool, now let's set _name of this template
227
        if ($this->template) {
228
            $this->template->trySet('_name', $this->getJSID());
229
        }
230
    }
231
232
    /**
233
     * This method is called to automatically fill in some of the tags in this
234
     * view. Normally the call is bassed to $app->setTags(), however you can
235
     * extend and add more tags to fill.
236
     */
237
    public function initTemplateTags()
238
    {
239
        if ($this->template
240
            && $this->app->hasMethod('setTags')
241
        ) {
242
            $this->app->setTags($this->template);
243
        }
244
    }
245
246
    /**
247
     * This method is commonly redefined to set a default template for an object.
248
     * If you return string, object will try to clone specified region off the
249
     * parent. If you specify array, it will load and parse a separate template.
250
     *
251
     * This is overriden by 4th argument in add() method
252
     *
253
     * @return array|string Template definition
254
     */
255
    public function defaultTemplate()
256
    {
257
        return $this->spot;
258
    }
259
260
    /**
261
     * Normally when you add a view, it's output is placed inside <?$Content?>
262
     * tag of its parent view. You can specify a different tag as 3rd argument
263
     * for the add() method. If you wish for object to use different tag by
264
     * default, you can override this method.
265
     *
266
     * @return string Tag / Spot in $this->owner->template
267
     */
268
    public function defaultSpot()
269
    {
270
        return 'Content';
271
    }
272
    // }}}
273
274
    // {{{ Rendering, see http://agiletoolkit.org/learn/understand/api/exec
275
    /**
276
     * Recursively renders all views. Calls render() for all or for the one
277
     * being cut. In some cases you may want to redefine this function instead
278
     * of render(). The difference is that this function is called before
279
     * sub-views are rendered, but render() is called after.
280
     *
281
     * function recursiveRender(){
282
     *   $this->add('Text')->set('test');
283
     *   return parent::recursiveRender(); // will render Text also
284
     * }
285
     *
286
     * When cut_object is specified in the GET arguments, then output
287
     * of HTML would be limited to object with matching $name or $short_name.
288
     *
289
     * This method will be called instead of default render() and it will
290
     * stop rendering process and output object's HTML once it finds
291
     * a suitable object. Exception_StopRender is used to terminate
292
     * rendering process and bubble up to the APP. This exception is
293
     * not an error.
294
     */
295
    public function recursiveRender()
296
    {
297
        if ($this->hook('pre-recursive-render')) {
298
            return;
299
        }
300
301
        $cutting_here = false;
302
        $cutting_output = '';
303
304
        $this->initTemplateTags();
305
306
        if (isset($_GET['cut_object'])
307
            && ($_GET['cut_object'] == $this->name
308
            || $_GET['cut_object'] == $this->short_name)
309
        ) {
310
            // If we are cutting here, render childs and then we are done
311
            unset($_GET['cut_object']);
312
            $cutting_here = true;
313
314
            $this->addHook('output', function ($self, $output) use (&$cutting_output) {
315
                $cutting_output .= $output;
316
            });
317
        }
318
319
        if ($this->model
320
            && is_object($this->model)
321
            && $this->model->loaded()
0 ignored issues
show
Documentation Bug introduced by
The method loaded does not exist on object<AbstractModel>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
322
        ) {
323
            $this->modelRender();
324
        }
325
326
        foreach ($this->elements as $key => $obj) {
327
            if ($obj instanceof self) {
328
                $obj->recursiveRender();
329
                $obj->moveJStoParent();
330
            }
331
        }
332
333
        if (!isset($_GET['cut_object'])) {
334
            if (isset($_GET['cut_region'])) {
335
                $this->region_render();
336
            } else {
337
                $this->render();
338
            }
339
        }
340
341
        if ($cutting_here) {
342
            //$result=$this->owner->template->cloneRegion($this->spot)->render();
343
            if ($this->app->jquery) {
344
                $this->app->jquery->getJS($this);
345
            }
346
            throw new Exception_StopRender($cutting_output);
347
        }
348
        // if template wasn't cut, we move all JS chains to parent
349
    }
350
351
    /**
352
     * When model is specified for a view, values of the model is
353
     * inserted inside the template if corresponding tags exist.
354
     * This is used as default values and filled out before
355
     * the actual render kicks in.
356
     */
357
    public function modelRender()
358
    {
359
        $this->template->set($this->model->get());
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<AbstractModel>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
360
    }
361
    /**
362
     * Append our chains to owner's chains. JS chains bubble up to
363
     * app, which plugs them into template. If the object is being
364
     * "cut" then only relevant chains will be outputed.
365
     */
366
    public function moveJStoParent()
367
    {
368
        $this->owner->js = array_merge_recursive($this->owner->js, $this->js);
0 ignored issues
show
The property js does not seem to exist in AbstractObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
369
    }
370
371
    /**
372
     * Default rendering method. Generates HTML presentation of $this view.
373
     * For most views, rendering the $this->template would be sufficient.
374
     *
375
     * If your view requires to do some heavy-duty work, please be sure to do
376
     * it inside render() method. This way would save some performance in cases
377
     * when your object is not being rendered.
378
     *
379
     * render method relies on method output(), which appeends HTML chunks
380
     * to the parent's template.
381
     */
382
    public function render()
383
    {
384
        if (!($this->template)) {
385
            throw $this->exception('You should specify template for this object')
386
                ->addMoreInfo('object', $this->name)
387
                ->addMoreInfo('spot', $this->spot);
388
        }
389
        $this->output(($render = $this->template->render()));
390
        if (@$this->debug) {
391
            echo '<font color="blue">'.htmlspecialchars($render).'</font>';
392
        }
393
    }
394
395
    /**
396
     * Low level output function which append's to the parent object's
397
     * template. For normal objects, you simply need to specify a suitable
398
     * template.
399
     *
400
     * @param string $txt HTML chunk
401
     */
402
    public function output($txt)
403
    {
404
        if (!is_null($this->hook('output', array($txt)))) {
405
            if (isset($this->owner->template)
406
                && !empty($this->owner->template)
407
            ) {
408
                $this->owner->template->append($this->spot, $txt, false);
409
            } elseif ($this->owner instanceof App_CLI) {
410
                echo $txt;
411
            }
412
        }
413
    }
414
415
    /**
416
     * When "cut"-ing using cut_region we need to output only a specified
417
     * tag. This method of cutting is mostly un-used now, and should be
418
     * considered obsolete.
419
     *
420
     * @obsolete
421
     */
422
    public function region_render()
423
    {
424
        throw $this->exception('cut_region is now obsolete');
425
426
        if ($this->template_flush) {
0 ignored issues
show
if ($this->template_flus...te_flush)->render()); } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
The variable $this seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
427
            if ($this->app->jquery) {
0 ignored issues
show
The variable $this seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
428
                $this->app->jquery->getJS($this);
0 ignored issues
show
The property jquery does not seem to exist in App_CLI.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
429
            }
430
            throw new Exception_StopRender(
431
                $this->template->cloneRegion($this->template_flush)->render()
0 ignored issues
show
Documentation Bug introduced by
The method render does not exist on object<AbstractObject>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
432
            );
433
        }
434
        $this->render();
435
        if ($this->spot == $_GET['cut_region']) {
0 ignored issues
show
The variable $this seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
436
            $this->owner->template_flush = $_GET['cut_region'];
0 ignored issues
show
The property template_flush does not seem to exist. Did you mean template?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
437
        }
438
    }
439
    // }}}
440
441
    // {{{ Object JavaScript Interface
442
    public $js = array();
443
    /**
444
     * Views in Agile Toolkit can assign javascript actions to themselves. This
445
     * is done by calling $view->js() method.
446
     *
447
     * Method js() will return jQuery_Chain object which would record all calls
448
     * to it's non-existant methods and convert them into jQuery call chain.
449
     *
450
     * js([action], [other_chain]);
451
     *
452
     * Action can represent javascript event, such as "click" or "mouseenter".
453
     * If you specify action = true, then the event will ALWAYS be executed on
454
     * pageload. It will also be executed if respective view is being reloaded
455
     * by js()->reload()
456
     *
457
     * (Do not make mistake by specifying "true" instead of true)
458
     *
459
     * action = false will still return jQuery chain but will not bind it.
460
     * You can bind it by passing to a different object's js() call as 2nd
461
     * argument or output the chain in response to AJAX-ec call by calling
462
     * execute() method.
463
     *
464
     * 1. Calling with arguments:
465
     *
466
     * $view->js();                   // does nothing
467
     * $a = $view->js()->hide();      // creates chain for hiding $view but does not
468
     *                                // bind to event yet.
469
     *
470
     * 2. Binding existing chains
471
     * $img->js('mouseenter', $a);    // binds previously defined chain to event on
472
     *                                // event of $img.
473
     *
474
     * Produced code: $('#img_id').click(function(ev){ ev.preventDefault();
475
     *    $('view1').hide(); });
476
     *
477
     * 3. $button->js('click',$form->js()->submit());
478
     *                                // clicking button will result in form submit
479
     *
480
     * 4. $view->js(true)->find('.current')->text($text);
481
     *
482
     * Will convert calls to jQuery chain into JavaScript string:
483
     *  $('#view').find('.current').text('abc');    // The $text will be json-encoded
484
     *                                              // to avoid JS injection.
485
     *
486
     * 5. ON YOUR OWN RISK
487
     *
488
     *  $view->js(true,'alert(123)');
489
     *
490
     * Will inject javascript un-escaped portion of javascript into chain.
491
     * If you need to have a custom script then put it into file instead,
492
     * save into templates/js/myfile.js and then  include:
493
     *
494
     *  $view->js()->_load('myfile');
495
     *
496
     * It's highly suggested to bind your libraries with jQuery namespace by
497
     * registered them as plugins, this way you can call your function easily:
498
     *
499
     *  $view->js(true)->_load('myfile')->myplugin('myfunc',array($arg,$arg));
500
     *
501
     * This approach is compatible with jQuery UI Widget factory and will keep
502
     * your code clean
503
     *
504
     * @param string|true|null   $when     Event when chain will be executed
505
     * @param array|chain|string $code     JavaScript chain(s) or code
506
     * @param string             $instance Obsolete
507
     *
508
     * @link http://agiletoolkit.org/doc/js
509
     *
510
     * @return jQuery_Chain
511
     */
512
    public function js($when = null, $code = null, $instance = null)
513
    {
514
        // Create new jQuery_Chain object
515
        if (!isset($this->app->jquery)) {
516
            throw new BaseException('requires jQuery or jUI support');
517
        }
518
519
        // Substitute $when to make it better work as a array key
520
        if ($when === true) {
521
            $when = 'always';
522
        }
523
        if ($when === false || $when === null) {
524
            $when = 'never';
525
        }
526
527
        if ($instance && isset($this->js[$when][$instance])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $instance of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
528
            $js = $this->js[$when][$instance];
529
        } else {
530
            $js = $this->app->jquery->chain($this);
531
        }
532
533
        if ($code) {
534
            $js->_prepend($code);
535
        }
536
537
        if ($instance) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $instance of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
538
            $this->js[$when][$instance] = $js;
539
        } else {
540
            $this->js[$when][] = $js;
541
        }
542
543
        return $js;
544
    }
545
546
    public function getJSID()
547
    {
548
        return str_replace('/', '_', $this->name);
549
    }
550
    /**
551
     * Views in Agile Toolkit can assign javascript actions to themselves. This
552
     * is done by calling $view->js() or $view->on().
553
     *
554
     * on() method implements implementation of jQuery on() method.
555
     *
556
     * on(event, [selector], [other_chain])
557
     *
558
     * Returned is a javascript chain wich is executed when event is triggered
559
     * on specified selector (or all of the view if selector is ommitted).
560
     * Optional other_chain argument can contain one or more chains (in array)
561
     * which will also be executed.
562
     *
563
     * The chain returned by on() will properly select affected element. For
564
     * example if the following view would contain multiple <a> elements, then
565
     * only the clicked-one will be hidden.
566
     *
567
     * on('click','a')->hide();
568
     *
569
     *
570
     * Other_chain can also be specified as a Callable. In this case the
571
     * executable code you have specified here will be called with several
572
     * arguments:
573
     *
574
     * function($js, $data){
575
     *   $js->hide();
576
     * }
577
     *
578
     *
579
     * In this case javascript method is executed on a clicked event but
580
     * in a more AJAX-way
581
     *
582
     * If your method returns a javascript chain, it will be executed
583
     * instead. You can execute both if you embed $js inside returned
584
     * chain.
585
     *
586
     * The third argument passed to your method contains
587
     */
588
    public function on($event, $selector = null, $js = null)
589
    {
590
        if (!is_string($selector) && is_null($js)) {
591
            $js = $selector;
592
            $selector = null;
593
        }
594
595
        if (is_callable($js)) {
596
            $p = $this->add('VirtualPage');
597
598
            $p->set(function ($p) use ($js) {
599
                // $js is an actual callable
600
                $js2 = $p->js()->_selectorRegion();
601
602
                $js3 = call_user_func($js, $js2, $_POST);
603
604
                // If method returns something, execute that instead
605
                if ($js3) {
606
                    $p->js(null, $js3)->execute();
607
                } else {
608
                    $js2->execute();
609
                }
610
            });
611
612
            $js = $this->js()->_selectorThis()->univ()->ajaxec($p->getURL(), true);
613
        }
614
615
        if ($js) {
616
            $ret_js = $this->js(null, $js)->_selectorThis();
617
        } else {
618
            $ret_js = $this->js()->_selectorThis();
619
        }
620
621
        $on_chain = $this->js(true);
0 ignored issues
show
true is of type boolean, but the function expects a string|object<true>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
622
        $fired = false;
623
624
        $this->app->jui->addHook(
625
            'pre-getJS',
626
            function ($app) use ($event, $selector, $ret_js, $on_chain, &$fired) {
627
                if ($fired) {
628
                    return;
629
                }
630
                $fired = true;
631
632
                $on_chain->on($event, $selector, $ret_js->_enclose(null, true));
633
            }
634
        );
635
636
        return $ret_js;
637
    }
638
    // }}}
639
}
640