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

nette / application / 20834855301

08 Jan 2026 10:54PM UTC coverage: 84.605% (+1.7%) from 82.856%
20834855301

push

github

dg
added CLAUDE.md

1940 of 2293 relevant lines covered (84.61%)

0.85 hits per line

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

91.13
/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, 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-deprecated Presenter $presenter
24
 * @property-deprecated 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

33
        /** @var array<string, mixed> */
34
        protected array $params = [];
35

36

37
        /**
38
         * Returns the presenter where this component belongs to.
39
         */
40
        public function getPresenter(): Presenter
41
        {
42
                return $this->lookup(Presenter::class);
1✔
43
        }
44

45

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

54

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

61

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

71

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

83
                return $res;
1✔
84
        }
85

86

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

96

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

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

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

123

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

132

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

141

142
        /********************* interface StatePersistent ****************d*g**/
143

144

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

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

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

173

174
        /**
175
         * Saves state information for next request.
176
         * @param  array<string, mixed>  $params
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
                        trigger_error(__METHOD__ . '() parameter $default is deprecated, use operator ??', E_USER_DEPRECATED);
×
231
                        $default = func_get_arg(1);
×
232
                }
233
                return $this->params[$name] ?? $default ?? null;
1✔
234
        }
235

236

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

246

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

256

257
        /********************* interface SignalReceiver ****************d*g**/
258

259

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

272

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

281

282
        /********************* navigation ****************d*g**/
283

284

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

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

304

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

318

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

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

337

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

355

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

375

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