• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / application / 20561245963

28 Dec 2025 11:47PM UTC coverage: 82.276% (-0.2%) from 82.434%
20561245963

push

github

dg
composer: calls Tester with the -C parameter

A pragmatic decision - I do not know how to set extension_dir in a cross-platform way

1959 of 2381 relevant lines covered (82.28%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.1
/src/Application/UI/Component.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Nette\Application\UI;
11

12
use Nette;
13
use function array_key_exists, array_slice, class_exists, func_get_arg, func_get_args, func_num_args, get_debug_type, is_array, link, method_exists, sprintf, trigger_error;
14

15

16
/**
17
 * Component is the base class for all Presenter components.
18
 *
19
 * Components are persistent objects located on a presenter. They have ability to own
20
 * other child components, and interact with user. Components have properties
21
 * for storing their status, and responds to user command.
22
 *
23
 * @property-read Presenter $presenter
24
 * @property-read bool $linkCurrent
25
 */
26
abstract class Component extends Nette\ComponentModel\Container implements SignalReceiver, StatePersistent, \ArrayAccess
27
{
28
        use Nette\ComponentModel\ArrayAccess;
29

30
        /** @var array<callable(self): void>  Occurs when component is attached to presenter */
31
        public array $onAnchor = [];
32
        protected array $params = [];
33

34

35
        /**
36
         * Returns the presenter where this component belongs to.
37
         */
38
        public function getPresenter(): ?Presenter
39
        {
40
                if (func_num_args()) {
1✔
41
                        trigger_error(__METHOD__ . '() parameter $throw is deprecated, use getPresenterIfExists()', E_USER_DEPRECATED);
×
42
                        $throw = func_get_arg(0);
×
43
                }
44

45
                return $this->lookup(Presenter::class, throw: $throw ?? true);
1✔
46
        }
47

48

49
        /**
50
         * Returns the presenter where this component belongs to.
51
         */
52
        public function getPresenterIfExists(): ?Presenter
53
        {
54
                return $this->lookup(Presenter::class, throw: false);
1✔
55
        }
56

57

58
        /** @deprecated */
59
        public function hasPresenter(): bool
60
        {
61
                return (bool) $this->lookup(Presenter::class, throw: false);
×
62
        }
63

64

65
        /**
66
         * Returns a fully-qualified name that uniquely identifies the component
67
         * within the presenter hierarchy.
68
         */
69
        public function getUniqueId(): string
70
        {
71
                return $this->lookupPath(Presenter::class);
1✔
72
        }
73

74

75
        protected function createComponent(string $name): ?Nette\ComponentModel\IComponent
1✔
76
        {
77
                if (method_exists($this, $method = 'createComponent' . $name)) {
1✔
78
                        (new AccessPolicy(new \ReflectionMethod($this, $method)))->checkAccess($this);
1✔
79
                }
80
                $res = parent::createComponent($name);
1✔
81
                if ($res && !$res instanceof SignalReceiver && !$res instanceof StatePersistent) {
1✔
82
                        $type = $res::class;
×
83
                        trigger_error("It seems that component '$name' of type $type is not intended to be used in the Presenter.");
×
84
                }
85

86
                return $res;
1✔
87
        }
88

89

90
        protected function validateParent(Nette\ComponentModel\IContainer $parent): void
1✔
91
        {
92
                parent::validateParent($parent);
1✔
93
                $this->monitor(Presenter::class, function (Presenter $presenter): void {
1✔
94
                        $this->loadState($presenter->popGlobalParameters($this->getUniqueId()));
1✔
95
                        Nette\Utils\Arrays::invoke($this->onAnchor, $this);
1✔
96
                });
1✔
97
        }
1✔
98

99

100
        /**
101
         * Calls public method if exists.
102
         */
103
        protected function tryCall(string $method, array $params): bool
1✔
104
        {
105
                $rc = static::getReflection();
1✔
106
                if (!$rc->hasMethod($method)) {
1✔
107
                        return false;
1✔
108
                } elseif (!$rc->hasCallableMethod($method)) {
1✔
109
                        $this->error('Method ' . Nette\Utils\Reflection::toString($rc->getMethod($method)) . ' is not callable.');
×
110
                }
111

112
                $rm = $rc->getMethod($method);
1✔
113
                (new AccessPolicy($rm))->checkAccess($this);
1✔
114
                $this->checkRequirements($rm);
1✔
115
                try {
116
                        $args = ParameterConverter::toArguments($rm, $params);
1✔
117
                } catch (Nette\InvalidArgumentException $e) {
1✔
118
                        $this->error($e->getMessage());
1✔
119
                }
120

121
                $rm->invokeArgs($this, $args);
1✔
122
                return true;
1✔
123
        }
124

125

126
        /**
127
         * Descendant can override this method to check for permissions.
128
         * It is called with the presenter class and the render*(), action*(), and handle*() methods.
129
         */
130
        public function checkRequirements(\ReflectionClass|\ReflectionMethod $element): void
1✔
131
        {
132
        }
1✔
133

134

135
        /**
136
         * Access to reflection.
137
         */
138
        public static function getReflection(): ComponentReflection
139
        {
140
                return new ComponentReflection(static::class);
1✔
141
        }
142

143

144
        /********************* interface StatePersistent ****************d*g**/
145

146

147
        /**
148
         * Loads state information.
149
         */
150
        public function loadState(array $params): void
1✔
151
        {
152
                $reflection = static::getReflection();
1✔
153
                foreach ($reflection->getParameters() as $name => $meta) {
1✔
154
                        if (isset($params[$name])) { // nulls are ignored
1✔
155
                                if (!ParameterConverter::convertType($params[$name], $meta['type'])) {
1✔
156
                                        $this->error(sprintf(
1✔
157
                                                "Value passed to persistent parameter '%s' in %s must be %s, %s given.",
1✔
158
                                                $name,
1✔
159
                                                $this instanceof Presenter ? 'presenter ' . $this->getName() : "component '{$this->getUniqueId()}'",
1✔
160
                                                $meta['type'],
1✔
161
                                                get_debug_type($params[$name]),
1✔
162
                                        ));
163
                                }
164

165
                                $this->$name = $params[$name];
×
166
                        } else {
167
                                $params[$name] = $this->$name ?? null;
1✔
168
                        }
169
                }
170

171
                $this->params = $params;
1✔
172
        }
1✔
173

174

175
        /**
176
         * Saves state information for next request.
177
         */
178
        public function saveState(array &$params): void
1✔
179
        {
180
                $this->saveStatePartial($params, static::getReflection());
1✔
181
        }
1✔
182

183

184
        /**
185
         * @internal used by presenter
186
         * @param  array<string, mixed>  $params
187
         */
188
        public function saveStatePartial(array &$params, ComponentReflection $reflection): void
1✔
189
        {
190
                $tree = Nette\Application\Helpers::getClassesAndTraits(static::class);
1✔
191

192
                foreach ($reflection->getPersistentParams() as $name => $meta) {
1✔
193
                        if (isset($params[$name])) {
1✔
194
                                // injected value
195

196
                        } elseif (
197
                                array_key_exists($name, $params) // nulls are skipped
1✔
198
                                || (isset($meta['since']) && !isset($tree[$meta['since']])) // not related
1✔
199
                                || !isset($this->$name)
1✔
200
                        ) {
201
                                continue;
1✔
202

203
                        } else {
204
                                $params[$name] = $this->$name; // object property value
1✔
205
                        }
206

207
                        if (!ParameterConverter::convertType($params[$name], $meta['type'])) {
1✔
208
                                throw new InvalidLinkException(sprintf(
1✔
209
                                        "Value passed to persistent parameter '%s' in %s must be %s, %s given.",
1✔
210
                                        $name,
1✔
211
                                        $this instanceof Presenter ? 'presenter ' . $this->getName() : "component '{$this->getUniqueId()}'",
1✔
212
                                        $meta['type'],
1✔
213
                                        get_debug_type($params[$name]),
1✔
214
                                ));
215
                        }
216

217
                        if ($params[$name] === $meta['def'] || ($meta['def'] === null && $params[$name] === '')) {
1✔
218
                                $params[$name] = null; // value transmit is unnecessary
1✔
219
                        }
220
                }
221
        }
1✔
222

223

224
        /**
225
         * Returns component param.
226
         */
227
        final public function getParameter(string $name): mixed
1✔
228
        {
229
                if (func_num_args() > 1) {
1✔
230
                        $default = func_get_arg(1);
×
231
                }
232
                return $this->params[$name] ?? $default ?? null;
1✔
233
        }
234

235

236
        /**
237
         * Returns component parameters.
238
         */
239
        final public function getParameters(): array
240
        {
241
                return $this->params;
1✔
242
        }
243

244

245
        /**
246
         * Returns a fully-qualified name that uniquely identifies the parameter.
247
         */
248
        final public function getParameterId(string $name): string
1✔
249
        {
250
                $uid = $this->getUniqueId();
1✔
251
                return $uid === '' ? $name : $uid . self::NameSeparator . $name;
1✔
252
        }
253

254

255
        /********************* interface SignalReceiver ****************d*g**/
256

257

258
        /**
259
         * Calls signal handler method.
260
         * @throws BadSignalException if there is not handler method
261
         */
262
        public function signalReceived(string $signal): void
1✔
263
        {
264
                if (!$this->tryCall(static::formatSignalMethod($signal), $this->params)) {
1✔
265
                        $class = static::class;
×
266
                        throw new BadSignalException("There is no handler for signal '$signal' in class $class.");
×
267
                }
268
        }
269

270

271
        /**
272
         * Formats signal handler method name -> case sensitivity doesn't matter.
273
         */
274
        public static function formatSignalMethod(string $signal): string
1✔
275
        {
276
                return 'handle' . $signal;
1✔
277
        }
278

279

280
        /********************* navigation ****************d*g**/
281

282

283
        /**
284
         * Generates URL to presenter, action or signal.
285
         * @param  string   $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
286
         * @param  array|mixed  $args
287
         * @throws InvalidLinkException
288
         */
289
        public function link(string $destination, $args = []): string
1✔
290
        {
291
                try {
292
                        $args = func_num_args() < 3 && is_array($args)
1✔
293
                                ? $args
1✔
294
                                : array_slice(func_get_args(), 1);
1✔
295
                        return $this->getPresenter()->getLinkGenerator()->link($destination, $args, $this, 'link');
1✔
296

297
                } catch (InvalidLinkException $e) {
1✔
298
                        return $this->getPresenter()->handleInvalidLink($e);
1✔
299
                }
300
        }
301

302

303
        /**
304
         * Returns destination as Link object.
305
         * @param  string   $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
306
         * @param  array|mixed  $args
307
         */
308
        public function lazyLink(string $destination, $args = []): Link
309
        {
310
                $args = func_num_args() < 3 && is_array($args)
×
311
                        ? $args
×
312
                        : array_slice(func_get_args(), 1);
×
313
                return new Link($this, $destination, $args);
×
314
        }
315

316

317
        /**
318
         * Determines whether it links to the current page.
319
         * @param  ?string   $destination in format "[[[module:]presenter:]action | signal! | this]"
320
         * @param  array|mixed  $args
321
         * @throws InvalidLinkException
322
         */
323
        public function isLinkCurrent(?string $destination = null, $args = []): bool
1✔
324
        {
325
                if ($destination !== null) {
1✔
326
                        $args = func_num_args() < 3 && is_array($args)
1✔
327
                                ? $args
1✔
328
                                : array_slice(func_get_args(), 1);
×
329
                        $this->getPresenter()->getLinkGenerator()->createRequest($this, $destination, $args, 'test');
1✔
330
                }
331

332
                return $this->getPresenter()->getLastCreatedRequestFlag('current');
1✔
333
        }
334

335

336
        /**
337
         * Redirect to another presenter, action or signal.
338
         * @param  string   $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
339
         * @param  array|mixed  $args
340
         * @return never
341
         * @throws Nette\Application\AbortException
342
         */
343
        public function redirect(string $destination, $args = []): void
1✔
344
        {
345
                $args = func_num_args() < 3 && is_array($args)
1✔
346
                        ? $args
1✔
347
                        : array_slice(func_get_args(), 1);
1✔
348
                $presenter = $this->getPresenter();
1✔
349
                $presenter->saveGlobalState();
1✔
350
                $presenter->redirectUrl($presenter->getLinkGenerator()->link($destination, $args, $this, 'redirect'));
1✔
351
        }
352

353

354
        /**
355
         * Permanently redirects to presenter, action or signal.
356
         * @param  string   $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
357
         * @param  array|mixed  $args
358
         * @return never
359
         * @throws Nette\Application\AbortException
360
         */
361
        public function redirectPermanent(string $destination, $args = []): void
1✔
362
        {
363
                $args = func_num_args() < 3 && is_array($args)
1✔
364
                        ? $args
1✔
365
                        : array_slice(func_get_args(), 1);
1✔
366
                $presenter = $this->getPresenter();
1✔
367
                $presenter->redirectUrl(
1✔
368
                        $presenter->getLinkGenerator()->link($destination, $args, $this, 'redirect'),
1✔
369
                        Nette\Http\IResponse::S301_MovedPermanently,
1✔
370
                );
371
        }
372

373

374
        /**
375
         * Throws HTTP error.
376
         * @throws Nette\Application\BadRequestException
377
         */
378
        public function error(string $message = '', int $httpCode = Nette\Http\IResponse::S404_NotFound): void
1✔
379
        {
380
                throw new Nette\Application\BadRequestException($message, $httpCode);
1✔
381
        }
382
}
383

384

385
class_exists(PresenterComponent::class);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc