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

cypht-org / cypht / 26230827274

21 May 2026 02:01PM UTC coverage: 72.803% (-5.4%) from 78.201%
26230827274

push

travis-ci

web-flow
Merge pull request #1972 from IrAlfred/remove-duplicate-testdox-flag-in-coverage-report

fix(workflow): remove duplicate --testdox flag causing coverage job exit code 1

4797 of 6589 relevant lines covered (72.8%)

7.74 hits per line

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

91.67
/lib/module.php
1
<?php
2

3
/**
4
 * Module classes
5
 * @package framework
6
 * @subpackage module
7
 */
8

9
/**
10
 * Module data management. These functions provide an interface for modules (both handler and output)
11
 * to fetch data set by other modules and to return their own output. Handler modules must use these
12
 * methods to set a response, output modules must if the format is AJAX, otherwise they should return
13
 * an HTML5 string
14
 */
15
trait Hm_Module_Output {
16

17
    /* module output */
18
    protected $output = [];
19

20
    /* protected output keys */
21
    protected $protected = [];
22

23
    /* list of appendable keys */
24
    protected $appendable = [];
25

26
    /**
27
     * @param string $name name to check for
28
     * @param array $list array to look for name in
29
     * @param string $type
30
     * @param mixed $value value
31
     * @return bool
32
     */
33
    protected function check_overwrite($name, $list, $type, $value) {
60✔
34
        if (in_array($name, $list, true)) {
60✔
35
            Hm_Debug::add(sprintf('MODULES: Cannot overwrite %s %s with %s', $type, $name, print_r($value,true)));
2✔
36
            return false;
2✔
37
        }
38
        return true;
60✔
39
    }
40

41
    /**
42
     * Add a name value pair to the output array
43
     * @param string $name name of value to store
44
     * @param mixed $value value
45
     * @param bool $protected true disallows overwriting
46
     * @return bool true on success
47
     */
48
    public function out($name, $value, $protected = true) {
60✔
49
        if (!$this->check_overwrite($name, $this->protected, 'protected', $value)) {
60✔
50
            return false;
1✔
51
        }
52
        if (!$this->check_overwrite($name, $this->appendable, 'protected', $value)) {
60✔
53
            return false;
1✔
54
        }
55
        if ($protected) {
60✔
56
            $this->protected[] = $name;
32✔
57
        }
58
        $this->output[$name] = $value;
60✔
59
        return true;
60✔
60
    }
61

62
    /**
63
     * append a value to an array, create it if does not exist
64
     * @param string $name array name
65
     * @param string $value value to add
66
     * @return bool true on success
67
     */
68
    public function append($name, $value) {
2✔
69
        if (!$this->check_overwrite($name, $this->protected, 'protected', $value)) {
2✔
70
            return false;
1✔
71
        }
72
        if (array_key_exists($name, $this->output)) {
2✔
73
            if (is_array($this->output[$name])) {
1✔
74
                $this->output[$name][] = $value;
1✔
75
                return true;
1✔
76
            } else {
77
                Hm_Debug::add(sprintf('Tried to append %s to scaler %s', $value, $name));
1✔
78
                return false;
1✔
79
            }
80
        } else {
81
            $this->output[$name] = [$value];
2✔
82
            $this->appendable[] = $name;
2✔
83
            return true;
2✔
84
        }
85
    }
86

87
    /**
88
     * Sanitize input string
89
     * @param string $string text to sanitize
90
     * @param bool $special_only only use htmlspecialchars not htmlentities
91
     * @return string sanitized value
92
     */
93
    public function html_safe($string, $special_only = false) {
35✔
94
        if ($special_only) {
35✔
95
            return htmlspecialchars((string) $string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
2✔
96
        }
97
        return htmlentities((string) $string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
35✔
98
    }
99

100
    /**
101
     * Concatenate a value
102
     * @param string $name name to add to
103
     * @param string $value value to add
104
     * @return bool true on success
105
     */
106
    public function concat($name, $value) {
13✔
107
        if (array_key_exists($name, $this->output)) {
13✔
108
            if (is_string($this->output[$name])) {
1✔
109
                $this->output[$name] .= $value;
1✔
110
                return true;
1✔
111
            } else {
112
                Hm_Debug::add(sprintf('Could not append %s to %s', print_r($value,true), $name));
1✔
113
                return false;
1✔
114
            }
115
        } else {
116
            $this->output[$name] = $value;
13✔
117
            return true;
13✔
118
        }
119
    }
120

121
    /**
122
     * Return module output from process()
123
     * @return array
124
     */
125
    public function module_output() {
159✔
126
        return $this->output;
159✔
127
    }
128

129
    /**
130
     * Return protected output field list
131
     * @return array
132
     */
133
    public function output_protected() {
78✔
134
        return $this->protected;
78✔
135
    }
136

137
    /**
138
     * Fetch an output value
139
     * @param string $name key to fetch the value for
140
     * @param mixed $default default return value if not found
141
     * @param string $typed if a default value is given, typecast the result to it's type
142
     * @return mixed value if found or default
143
     */
144
    public function get($name, $default = NULL, $typed = true) {
104✔
145
        if (array_key_exists($name, $this->output)) {
104✔
146
            $val = $this->output[$name];
63✔
147
            if (!is_null($default) && $typed) {
63✔
148
                if (gettype($default) != gettype($val)) {
44✔
149
                    Hm_Debug::add(sprintf('TYPE CONVERSION: %s to %s for %s', gettype($val), gettype($default), $name), 'info');
4✔
150
                    settype($val, gettype($default));
4✔
151
                }
152
            }
153
            return $val;
63✔
154
        }
155
        return $default;
88✔
156
    }
157

158
    /**
159
     * Check for a key
160
     * @param string $name key name
161
     * @return bool true if found
162
     */
163
    public function exists($name) {
2✔
164
        return array_key_exists($name, $this->output);
2✔
165
    }
166

167
    /**
168
     * Check to see if a value matches a list
169
     * @param string $name name to check
170
     * @param array $values list to check against
171
     * @return bool true if found
172
     */
173
    public function in($name, $values) {
2✔
174
        if (array_key_exists($name, $this->output) && in_array($this->output[$name], $values, true)) {
2✔
175
            return true;
2✔
176
        }
177
        return false;
2✔
178
    }
179
}
180

181
/**
182
 * Methods used to validate handler module operations, like the HTTP request
183
 * type and target/origin values
184
 */
185
trait Hm_Handler_Validate {
186

187
    /**
188
     * Validate HTTP request type, only GET and POST are allowed
189
     * @param object $session
190
     * @param object $request
191
     * @return bool
192
     */
193
    public function validate_method($session, $request) {
2✔
194
        if (!empty($request->method) && is_string($request->method)) {
2✔
195
            if (!in_array(mb_strtolower($request->method), ['get', 'post'], true)) {
2✔
196
                if ($session->loaded) {
1✔
197
                    $session->destroy($request);
1✔
198
                    Hm_Debug::add(sprintf('LOGGED OUT: invalid method %s', $request->method));
1✔
199
                }
200
                return false;
1✔
201
            }
202
            return true;
2✔
203
        }
204
        // Handle the case where method is null or invalid
205
        if ($session->loaded) {
×
206
            $session->destroy($request);
×
207
            Hm_Debug::add('LOGGED OUT: missing or invalid request method', 'warning');
×
208
        }
209
        return false;
×
210
    }
211

212
    /**
213
     * Validate that the request has matching source and target origins
214
     * @return bool
215
     */
216
    public function validate_origin($session, $request, $config) {
2✔
217
        if (!$session->loaded) {
2✔
218
            return true;
1✔
219
        }
220
        list($source, $target) = $this->source_and_target($request, $config);
2✔
221
        if (!$this->validate_target($target, $source, $session, $request) ||
2✔
222
            !$this->validate_source($target, $source, $session, $request)) {
2✔
223
            return false;
2✔
224
        }
225
        return true;
1✔
226
    }
227

228
    /**
229
     * Find source and target values for validate_origin
230
     * @return string[]
231
     */
232
    private function source_and_target($request, $config) {
2✔
233
        $source = false;
2✔
234
        $target = $config->get('cookie_domain', false);
2✔
235
        if ($target == 'none') {
2✔
236
            $target = false;
1✔
237
        }
238
        $server_vars = [
2✔
239
            'HTTP_REFERER' => 'source',
2✔
240
            'HTTP_ORIGIN' => 'source',
2✔
241
            'HTTP_HOST' => 'target',
2✔
242
            'HTTP_X_FORWARDED_HOST' => 'target'
2✔
243
        ];
2✔
244
        foreach ($server_vars as $header => $type) {
2✔
245
            if (!empty($request->server[$header])) {
2✔
246
                $$type = $request->server[$header];
1✔
247
            }
248
        }
249
        return [$source, $target];
2✔
250
    }
251

252
    /**
253
     * @param string $target
254
     * @param string $source
255
     * @return boolean
256
     */
257
    private function validate_target($target, $source, $session, $request) {
2✔
258
        if (!$target || !$source) {
2✔
259
            $session->destroy($request);
2✔
260
            Hm_Debug::add('LOGGED OUT: missing target origin', 'warning');
2✔
261
            return false;
2✔
262
        }
263
        return true;
1✔
264
    }
265

266
    /**
267
     * @param string $target
268
     * @param string $source
269
     * @return boolean
270
     */
271
    private function validate_source($target, $source, $session, $request) {
1✔
272
        $source = parse_url($source);
1✔
273
        if (!is_array($source) || !array_key_exists('host', $source)) {
1✔
274
            $session->destroy($request);
1✔
275
            Hm_Debug::add('LOGGED OUT: invalid source origin', 'warning');
1✔
276
            return false;
1✔
277
        }
278
        if (array_key_exists('port', $source)) {
1✔
279
            $source['host'] .= ':'.$source['port'];
1✔
280
        }
281
        if ($source['host'] !== $target) {
1✔
282
            $session->destroy($request);
1✔
283
            Hm_Debug::add('LOGGED OUT: invalid source origin', 'warning');
1✔
284
            return false;
1✔
285
        }
286
        return true;
1✔
287
    }
288
}
289

290
/**
291
 * Base class for data input processing modules, called "handler modules"
292
 *
293
 * All modules that deal with processing input data extend from this class.
294
 * It provides access to input and state through the following member variables:
295
 *
296
 * $session      The session interface object
297
 * $request      The HTTP request details object
298
 * $config       The site config object
299
 * $user_config  The user settings object for the current user
300
 *
301
 * Modules that extend this class need to override the process function
302
 * Modules can pass information to the output modules using the out() and append() methods,
303
 * and see data from other modules with the get() method
304
 * @abstract
305
 */
306
abstract class Hm_Handler_Module {
307

308
    use Hm_Module_Output;
309
    use Hm_Handler_Validate;
310

311
    /* session object */
312
    public $session;
313

314
    /* request object */
315
    public $request;
316

317
    /* site configuration object */
318
    public $config;
319

320
    /* current request id */
321
    protected $page = '';
322

323
    /* user settings */
324
    public $user_config;
325

326
    public $cache;
327

328
    /**
329
     * Assign input and state sources
330
     * @param object $parent instance of the Hm_Request_Handler class
331
     * @param string $page page id
332
     * @param array $output data from handler modules
333
     * @param array $protected list of protected output names
334
     */
335
    public function __construct($parent, $page, $output = [], $protected = []) {
83✔
336
        $this->session = $parent->session;
83✔
337
        $this->request = $parent->request;
83✔
338
        $this->cache = $parent->cache;
83✔
339
        $this->page = $page;
83✔
340
        $this->config = $parent->site_config;
83✔
341
        $this->user_config = $parent->user_config;
83✔
342
        $this->output = $output;
83✔
343
        $this->protected = $protected;
83✔
344
    }
345

346
    /**
347
     * @return string
348
     */
349
    private function invalid_ajax_key() {
2✔
350
        if (DEBUG_MODE) {
2✔
351
            Hm_Debug::add('REQUEST KEY check failed', 'warning');
1✔
352
            Hm_Debug::load_page_stats();
1✔
353
            Hm_Debug::show();
1✔
354
        }
355
        Hm_Functions::cease(json_encode(['status' => 'not callable']));
2✔
356
        return 'exit';
2✔
357
    }
358

359
    /**
360
     * @return string
361
     */
362
    private function invalid_http_key() {
2✔
363
        if ($this->session->loaded) {
2✔
364
            $this->session->destroy($this->request);
2✔
365
            Hm_Debug::add('LOGGED OUT: request key check failed', 'warning');
2✔
366
        }
367
        Hm_Dispatch::page_redirect($this->build_page_url('home'));
2✔
368
        return 'redirect';
2✔
369
    }
370

371
    /**
372
     * Validate a form key. If this is a non-empty POST form from an
373
     * HTTP request or AJAX update, it will take the user to the home
374
     * page if the page_key value is either not present or not valid
375
     * @return false|string
376
     */
377
    public function process_key() {
3✔
378
        if (empty($this->request->post)) {
3✔
379
            return false;
2✔
380
        }
381
        $key = array_key_exists('hm_page_key', $this->request->post) ? $this->request->post['hm_page_key'] : false;
3✔
382
        $valid = Hm_Request_Key::validate($key);
3✔
383
        if ($valid) {
3✔
384
            return false;
1✔
385
        }
386
        if ($this->request->type == 'AJAX') {
3✔
387
            return $this->invalid_ajax_key();
2✔
388
        } else {
389
            return $this->invalid_http_key();
2✔
390
        }
391
    }
392

393
    /**
394
     * Validate a value in a HTTP POST form
395
     * @param mixed $val
396
     * @return mixed
397
     */
398
    private function check_field($val) {
31✔
399
        switch (true) {
400
            case is_array($val):
31✔
401
            case is_string($val):
31✔
402
            case is_int($val):
26✔
403
            case is_float($val):
24✔
404
            case is_bool($val):
24✔
405
            case $val === '0':
1✔
406
            case $val === 0:
1✔
407
                return $val;
31✔
408
            default:
409
                return NULL;
1✔
410
        }
411
    }
412

413
    /**
414
     * Process an HTTP POST form
415
     * @param array $form list of required field names in the form
416
     * @return array tuple with a bool indicating success, and an array of valid form values
417
     */
418
    public function process_form($form) {
31✔
419
        $new_form = [];
31✔
420
        foreach($form as $name) {
31✔
421
            if (!array_key_exists($name, $this->request->post)) {
31✔
422
                continue;
9✔
423
            }
424
            $val = $this->check_field($this->request->post[$name]);
31✔
425
            if ($val !== NULL) {
31✔
426
                $new_form[$name] = $val;
31✔
427
            }
428
        }
429
        return [(count($form) === count($new_form)), $new_form];
31✔
430
    }
431

432
    /**
433
     * Determine if a module set is enabled
434
     * @param string $name the module set name to check for
435
     * @return bool
436
     */
437
    public function module_is_supported($name) {
13✔
438
        return in_array(mb_strtolower($name), $this->config->get_modules(true), true);
13✔
439
    }
440

441
   /**
442
     * Checks if a config setting is disabled and signals whether to skip further execution.
443
     *
444
     * @param string $setting_key  The configuration key to check.
445
     * @param mixed  $default      The default value to use if the key is not set.
446
     * @return bool  True if the feature is disabled and should be skipped.
447
     */
448
    public function should_skip_execution($setting_key, $default = false) {
12✔
449
        return !$this->user_config->get($setting_key, $default);
12✔
450
    }
451

452
    public function save_hm_msgs() {
×
453
        $msgs = Hm_Msgs::getRaw();
×
454
        if (!empty($msgs)) {
×
455
            Hm_Msgs::flush();
×
456
            $this->session->secure_cookie($this->request, 'hm_msgs', base64_encode(json_encode($msgs)));
×
457
        }
458
    }
459

460
    public function build_page_url($page, $params = []) {
2✔
461
        $url = '?'.$this->config->get('page_param_name').'='.$page;
2✔
462
        foreach ($params as $key => $value) {
2✔
463
            $url .= '&'.urlencode($key).'='.urlencode($value);
×
464
        }
465
        return $url;
2✔
466
    }
467

468
    /**
469
     * Handler modules need to override this method to do work
470
     */
471
    abstract public function process();
472
}
473

474
/**
475
 * Base class for output modules
476
 * All modules that output data to a request must extend this class and define
477
 * an output() method. It provides form validation, html sanitizing,
478
 * and string translation services to modules
479
 * @abstract
480
 */
481
abstract class Hm_Output_Module {
482

483
    use Hm_Module_Output;
484

485
    /* translated language strings */
486
    protected $lstr = [];
487

488
    /* langauge name */
489
    protected $lang = false;
490

491
    /* UI layout direction */
492
    protected $dir = 'ltr';
493

494
    /* Output format (AJAX or HTML5) */
495
    protected $format = '';
496

497
    /**
498
     * Constructor
499
     * @param array $input data from handler modules
500
     * @param array $protected list of protected keys
501
     */
502
    public function __construct($input, $protected) {
112✔
503
        $this->output = $input;
112✔
504
        $this->protected = $protected;
112✔
505
    }
506

507
    /**
508
     * Return a translated string if possible
509
     * @param string $string the string to be translated
510
     * @return string translated string
511
     */
512
    public function trans($string) {
69✔
513
        if (array_key_exists($string, $this->lstr)) {
69✔
514
            if ($this->lstr[$string] === false) {
58✔
515
                return strip_tags($string);
58✔
516
            } else {
517
                return strip_tags($this->lstr[$string]);
1✔
518
            }
519
        }
520
        else {
521
            Hm_Debug::add(sprintf('TRANSLATION NOT FOUND :%s:', $string), 'warning');
15✔
522
        }
523
        return str_replace('\n', '<br />', strip_tags($string));
15✔
524
    }
525

526
    /**
527
     * Return all translations for earch supported language
528
     * @return array translations
529
     */
530
    public function all_trans() {
1✔
531
        // Get all files in the language directory
532
        $language_files = glob(APP_PATH.'language/'. '*.php');
1✔
533
        $translations = [];
1✔
534

535
        foreach ($language_files as $file) {
1✔
536
            // Extract the language code from the file name
537
            $language_code = pathinfo($file, PATHINFO_FILENAME);
1✔
538

539
            // Read the content of the file
540
            $content = include $file;
1✔
541

542
            // Store the content in the translations array
543
            $translations[$language_code] = $content;
1✔
544
        }
545

546
        return $translations;
1✔
547
    }
548

549

550
    /**
551
     * Return a translated string of numbers if possible and if language is farsi
552
     * @param string $string the string to be translated which has to be numbers
553
     * @return string translated string
554
     */
555
    public function translate_number($number) {
×
556
        if (!is_numeric($number) || !in_array($this->lang, ['fa'])) {
×
557
            return $number;
×
558
        }
559
        $number_splitted = mb_str_split($number);
×
560
        $translated_number = "";
×
561
        foreach ($number_splitted as $number_splitted) {
×
562
            $translated_number .= $this->trans($number_splitted);
×
563
        }
564
        return $translated_number;
×
565
    }
566

567
    /**
568
     * Build output by calling module specific output functions
569
     * @param string $format output type, either HTML5 or AJAX
570
     * @param array $lang_str list of language translation strings
571
     * @return string
572
     */
573
    public function output_content($format, $lang_str) {
93✔
574
        $this->lstr = $lang_str;
93✔
575
        $this->format = str_replace('Hm_Format_', '', $format);
93✔
576
        if (array_key_exists('interface_lang', $lang_str)) {
93✔
577
            $this->lang = $lang_str['interface_lang'];
92✔
578
        }
579
        if (array_key_exists('interface_direction', $lang_str)) {
93✔
580
            $this->dir = $lang_str['interface_direction'];
92✔
581
        }
582
        return $this->output();
93✔
583
    }
584

585
    public function build_page_url($page, $params = [], $htm_context = false) {
16✔
586
        $url = '?'.$this->get('page_param_name', 'page').'='.$page;
16✔
587
        foreach ($params as $key => $value) {
16✔
588
            $url .= ($htm_context ? '&amp;': '&').$key.'='.$value;
3✔
589
        }
590
        return $url;
16✔
591
    }
592

593
    /**
594
     * Output modules need to override this method to add to a page or AJAX response
595
     * @return string
596
     */
597
    abstract protected function output();
598
}
599

600
/**
601
 * Placeholder classes for disabling a module in a set. These allow a module set
602
 * to replace another module set's assignments with "false" to disable them
603
 */
604
class Hm_Output_ extends Hm_Output_Module { protected function output() {} }
605
class Hm_Handler_ extends Hm_Handler_Module { public function process() {} }
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