Plugin Directory

Changeset 2912478


Ignore:
Timestamp:
05/15/2023 12:35:28 PM (3 years ago)
Author:
hexydec
Message:

Updated packages to latest versions.
Fixed bug where 304 headers were not set the Wordpress way, which caused Wordpress to overwrite it.
Fixed incorrectly loaded rebuild command.
Fixed minor PHP 8.1 data handling issues.

Location:
torque/trunk
Files:
25 edited

Legend:

Unmodified
Added
Removed
  • torque/trunk/app.php

    r2823176 r2912478  
    118118                if ($options['preloadstyle'] && $options['combinestyle']) {
    119119                    $file = $this->config['output'].\md5(\implode(',', $options['combinestyle'])).'.css';
    120                     $root = \dirname(\dirname(\dirname(__DIR__))).'/';
    121                     $options['preload'][] = \str_replace('\\', '/', \mb_substr($file, \mb_strlen($root)).'?'.\filemtime($file));
     120                    if (\file_exists($file)) {
     121                        $root = \dirname(\dirname(\dirname(__DIR__))).'/';
     122                        $options['preload'][] = \str_replace('\\', '/', \mb_substr($file, \mb_strlen($root)).'?'.\filemtime($file));
     123                    }
    122124                }
    123125
     
    167169                            if (\file_exists($file)) {
    168170                                foreach ($options['combinestyle'] AS $item) {
    169                                     $doc->remove('link[rel=stylesheet][href*="'.$item.'"]');
     171                                    $style = $doc->find('link[rel=stylesheet][href*="'.$item.'"]');
     172                                    if (($id = $style->attr("id")) !== null) {
     173                                        $inline = \substr($id, 0, -3).'inline-css';
     174                                        $doc->find('style[id="'.$inline.'"]')->remove();
     175                                    }
     176                                    $style->remove();
    170177                                }
    171178                                $url = \mb_substr($file, \mb_strlen($_SERVER['DOCUMENT_ROOT'])).'?'.\filemtime($file);
     
    225232                    // check etags
    226233                    if (($options['etags'] ?? null) !== null && empty($_POST) && $this->matchesEtag($html)) {
    227                         \http_response_code(304);
     234                        \status_header(304);
    228235                        return '';
    229236                    }
  • torque/trunk/assets.php

    r2823176 r2912478  
    244244        $dir = \dirname(\dirname(\dirname(__DIR__))).'/'; // can't use get_home_path() here
    245245        foreach ($files AS $item) {
    246             $item = $dir.$item;
    247             if (\file_exists($item) && ($file = \file_get_contents($item)) !== false) {
    248                 $css .= \preg_replace_callback('/url\\([\'"]?+([^\\)"\':]++)[\'"]?\\)/i', function (array $match) use ($item) {
    249                     \chdir(\dirname($item));
     246            $url = $dir.$item;
     247            if (\file_exists($url) && ($file = \file_get_contents($url)) !== false) {
     248
     249                // add before styles
     250                if (($styles = self::getInlineStyles($item)) !== null && $styles['type'] === 'before') {
     251                    $css .= $styles['content'];
     252                }
     253
     254                // extract and rework asset URLs
     255                $css .= \preg_replace_callback('/url\\([\'"]?+([^\\)"\':]++)[\'"]?\\)/i', function (array $match) use ($url) : string {
     256                    \chdir(\dirname($url));
    250257                    $path = \realpath(\dirname($match[1])).'/'.\basename($match[1]);
    251258                    return 'url('.\str_replace('\\', '/', \substr($path, \strlen($_SERVER['DOCUMENT_ROOT']))).')';
    252259                }, $file);
     260
     261                // add after styles
     262                if (($styles['type'] ?? '') === 'after') {
     263                    $css .= $styles['content'];
     264                }
    253265            }
    254266        }
     
    278290        }
    279291        return false;
     292    }
     293
     294    protected static function getStyleAssets() {
     295        static $style = null;
     296        if ($style === null) {
     297            $doc = new \hexydec\html\htmldoc();
     298            $url = \home_url().'/?notorque';
     299            if (($html = self::getPage($url)) !== false && $doc->load($html)) {
     300                $style = [];
     301                foreach ($doc->find('link[rel=stylesheet][id],style[id]') AS $item) {
     302                    $style[$item->attr('id')] = [
     303                        'href' => $item->attr('href'),
     304                        'content' => $item->html()
     305                    ];
     306                }
     307            }
     308        }
     309        return $style;
     310    }
     311
     312    protected static function getInlineStyles(string $url) : array|null {
     313        if (($style = self::getStyleAssets()) !== null) {
     314            $keys = \array_flip(\array_keys($style));
     315            foreach ($style AS $key => $item) {
     316                $inline = \substr($key, 0, -3).'inline-css';
     317                if (\mb_strpos($item['href'] ?? '', $url) !== false && isset($style[$inline])) {
     318                    return [
     319                        'content' => \mb_substr($style[$inline]['content'], \mb_strpos($style[$inline]['content'], '>') + 1, -9),
     320                        'type' => $keys[$key] > $keys[$inline] ? 'before' : 'after'
     321                    ];
     322                }
     323            }
     324        }
     325        return null;
    280326    }
    281327
     
    355401    }
    356402
    357     protected static function getExtraScript(string $url) {
     403    protected static function getExtraScript(string $url) : array|null {
    358404        if (($scripts = self::getScriptAssets()) !== null) {
    359405            $keys = \array_flip(\array_keys($scripts));
    360406            foreach ($scripts AS $key => $item) {
    361                 if (\mb_strpos($item['src'], $url) !== false && isset($scripts[$key.'-extra'])) {
     407                if (\mb_strpos($item['src'] ?? '', $url) !== false && isset($scripts[$key.'-extra'])) {
    362408                    return [
    363409                        'content' => \mb_substr($scripts[$key.'-extra']['content'], \mb_strpos($scripts[$key.'-extra']['content'], '>') + 1, -9),
  • torque/trunk/overview.php

    r2823176 r2912478  
    416416                        'title' => 'Prevent MIME Type Sniffing',
    417417                        'badge' => function (array $data, bool &$status = null) : string {
    418                             $status = $data['x-content-type-options'] === 'nosniff';
     418                            $status = ($data['x-content-type-options'] ?? '') === 'nosniff';
    419419                            return $status ? 'Enabled' : 'Not Enabled';
    420420                        },
     
    491491        $assets = [];
    492492        if (!empty($link)) {
    493             foreach (\explode(',', $link) AS $item) {
     493            foreach (\explode(',', \trim($link, ',; ')) AS $item) {
    494494                $props = [];
    495495                foreach (\explode(';', $item) AS $value) {
  • torque/trunk/packages.php

    r2823178 r2912478  
    1717     * @var string VERSION The version number of the application, this is used in the cache key for CSS/Javascript that is minified
    1818     */
    19     public const VERSION = '0.7.1';
     19    public const VERSION = '0.7.2';
    2020
    2121    /**
  • torque/trunk/packages/cssdoc/cssdoc.php

    r2817597 r2912478  
    77
    88    /**
    9      * @var array<string> $tokens Regexp components keyed by their corresponding codename for tokenising HTML
     9     * @var array<string> $tokens Regexp components keyed by their corresponding codename for tokenising CSS
    1010     */
    1111    protected static array $tokens = [
     
    3535    protected array $config = [
    3636        'nested' => ['@media', '@supports', '@keyframes', '@-webkit-keyframes', '@-moz-keyframes', '@-o-keyframes', '@document', '@-moz-document', '@container'], // directive that can have nested rules
    37         'spaced' => ['calc'], // values where spaces between operators must be retained
     37        'spaced' => ['calc', 'min', 'max', 'clamp'], // values where spaces between operators must be retained
    3838        'quoted' => ['content', 'format', 'counters', '@charset', 'syntax', 'font-feature-settings', '-webkit-font-feature-settings', '-moz-font-feature-settings', 'quotes', 'text-overflow'], // directives or properties where the contained values must be quoted
    3939        'casesensitive' => ['url'], // property values that should not be lowercased
     
    215215     */
    216216    public function __construct(array $config = []) {
    217         if ($config) {
     217        if (!empty($config)) {
    218218            $this->config = \array_replace_recursive($this->config, $config);
    219219        }
     
    226226     * @return mixed The number of children in the object for length, the output config, or null if the parameter doesn't exist
    227227     */
    228     #[\ReturnTypeWillChange]
    229     public function __get(string $var) {
     228    public function __get(string $var) : mixed {
    230229        if ($var === 'length') {
    231230            return \count($this->document->rules ?? []);
     
    251250     * @param mixed $value The value of the array key in the children array to be updated
    252251     */
    253     public function offsetSet($i, $value) : void {
     252    public function offsetSet(mixed $i, mixed $value) : void {
    254253        if (\is_null($i)) {
    255254            $this->document->rules[] = $value;
     
    262261     * Array access method allows you to check that a key exists in the configuration array
    263262     *
    264      * @param string|integer $i The key to be checked, can be a string or integer
     263     * @param mixed $i The key to be checked
    265264     * @return bool Whether the key exists in the config array
    266265     */
    267     public function offsetExists($i) : bool {
     266    public function offsetExists(mixed $i) : bool {
    268267        return isset($this->document->rules[$i]);
    269268    }
     
    272271     * Removes a key from the configuration array
    273272     *
    274      * @param string|integer $i The key to be removed, can be a string or integer
    275      */
    276     public function offsetUnset($i) : void {
     273     * @param mixed $i The key to be removed
     274     */
     275    public function offsetUnset(mixed $i) : void {
    277276        unset($this->document->rules[$i]);
    278277    }
     
    281280     * Retrieves a value from the configuration array with the specified key
    282281     *
    283      * @param string|integer $i The key to be accessed, can be a string or integer
     282     * @param mixed $i The key to be accessed, can be a string or integer
    284283     * @return mixed The requested value or null if the key doesn't exist
    285284     */
    286     #[\ReturnTypeWillChange]
    287     public function offsetGet($i) { // return reference so you can set it like an array
     285    public function offsetGet(mixed $i) : mixed { // return reference so you can set it like an array
    288286        return $this->document->rules[$i] ?? null;
    289287    }
     
    294292     * @return document|rule The child node at the current pointer position
    295293     */
    296     #[\ReturnTypeWillChange]
    297     public function current() {
     294    public function current() : mixed {
    298295        return $this->document->rules[$this->pointer] ?? null;
    299296    }
     
    304301     * @return mixed The current pointer position
    305302     */
    306     #[\ReturnTypeWillChange]
    307303    public function key() : mixed {
    308304        return $this->pointer;
     
    337333
    338334    /**
    339      * Open an HTML file from a URL
    340      *
    341      * @param string $url The address of the HTML file to retrieve
     335     * Open a CSS file from a URL
     336     *
     337     * @param string $url The address of the CSS file to retrieve
    342338     * @param resource $context A resource object made with stream_context_create()
    343339     * @param ?string &$error A reference to any user error that is generated
    344      * @return mixed The loaded HTML, or false on error
    345      */
    346     public function open(string $url, mixed $context = null, ?string &$error = null) {
     340     * @return string|false The loaded CSS, or false on error
     341     */
     342    public function open(string $url, $context = null, ?string &$error = null) : string|false {
    347343
    348344        // check resource
     
    355351
    356352        // retrieve the stream contents
    357         } elseif (($html = \stream_get_contents($handle)) === false) {
     353        } elseif (($css = \stream_get_contents($handle)) === false) {
    358354            $error = 'Could not read file "'.$url.'"';
    359355
     
    373369            }
    374370
    375             // load html
    376             if ($this->load($html, $charset, $error)) {
    377                 return $html;
     371            // load CSS
     372            if ($this->load($css, $charset, $error)) {
     373                return $css;
    378374            }
    379375        }
     
    382378
    383379    /**
    384      * Parse an HTML string into the object
     380     * Parse a CSS string into the object
    385381     *
    386382     * @param string $css A string containing valid CSS
    387383     * @param string $charset The charset of the document
    388384     * @param ?string &$error A reference to any user error that is generated
    389      * @return bool Whether the input HTML was parsed
     385     * @return bool Whether the input CSS was parsed
    390386     */
    391387    public function load(string $css, string $charset = null, ?string &$error = null) : bool {
     
    393389        // detect the charset
    394390        if ($charset || ($charset = $this->getCharsetFromCss($css)) !== null) {
    395             $css = \mb_convert_encoding($css, \mb_internal_encoding(), $charset);
     391            $css = \mb_convert_encoding($css, (string) \mb_internal_encoding(), $charset);
    396392        }
    397393
     
    410406
    411407    /**
    412      * Reads the charset defined in the Content-Type meta tag, or detects the charset from the HTML content
     408     * Reads the charset defined in the Content-Type meta tag, or detects the charset from the CSS content
    413409     *
    414410     * @param string $css A string containing valid CSS
     
    428424     *
    429425     * @param string $css A string containing valid CSS
    430      * @return document|bool A document object or false if the string could not be parsed
    431      */
    432     protected function parse(string $css) {
     426     * @return document|false A document object or false if the string could not be parsed
     427     */
     428    protected function parse(string $css) : document|false {
    433429
    434430        // tokenise the input CSS
     
    454450     */
    455451    public function minify(array $minify = []) : void {
    456         $minify = \array_merge($this->config['minify'], $minify);
    457         $this->document->minify($minify);
     452        if ($this->document !== null) {
     453            $minify = \array_merge($this->config['minify'], $minify);
     454            $this->document->minify($minify);
     455        }
    458456    }
    459457
     
    464462     * @return string The document as a string
    465463     */
    466     public function compile(array $options = []) : string {
    467         $options = \array_merge($this->config['output'], $options);
    468         return $this->document->compile($options);
     464    public function compile(array $options = []) : ?string {
     465        if ($this->document !== null) {
     466            $options = \array_merge($this->config['output'], $options);
     467            return $this->document->compile($options);
     468        }
     469        return null;
    469470    }
    470471
     
    476477     * @return string|false The compiled CSS, or false if the file could not be saved
    477478     */
    478     public function save(string $file = null, array $options = []) {
     479    public function save(string $file = null, array $options = []) : string|false {
    479480        $css = $this->compile($options);
    480481
     
    489490    }
    490491
    491     public function collection(array $rules) {
    492         $this->document = new document($this, $rules);
    493     }
    494 
    495     /**
    496     * Find rules in the document that match the specified criteria
    497     *
    498     * @param string $selector A string specifying the selectors to match, comma separate multiple selectors
    499     * @param array|string $hasProp A string or array specifying the properties that any rules must contain
    500     * @param array $media An array specifying how any media queries should be match, where the key is the property and the key the value. 'max-width' will match any rules where the value is lower that that specified, 'min-width' the value must be higher. Use 'media' to specify the media type
    501     * @param bool $exact Denotes whether to match selectors exactly, if false, selectors will be matched from the left
    502     * @return cssdoc A CSSdoc object
    503     */
    504     public function find(?string $selector, $hasProp = null, array $media = [], bool $exact = true) : cssdoc {
    505 
    506         // normalise selectors
    507         $selector = $selector === null ? null : \array_map('\\trim', \explode(',', $selector));
    508         if (!\is_array($hasProp)) {
    509             $hasProp = [$hasProp];
    510         }
    511 
    512         // find rules
    513         $rules = $this->document->find($selector, $hasProp, $media);
    514 
    515         // attach to a new document
    516         $obj = new cssdoc($this->config);
    517         $obj->collection($rules);
    518         return $obj;
    519     }
     492    // public function collection(array $rules) {
     493    // $this->document = new document($this, $rules);
     494    // }
     495
     496    // /**
     497    // * Find rules in the document that match the specified criteria
     498    // *
     499    // * @param string $selector A string specifying the selectors to match, comma separate multiple selectors
     500    // * @param array|string $hasProp A string or array specifying the properties that any rules must contain
     501    // * @param array $media An array specifying how any media queries should be match, where the key is the property and the key the value. 'max-width' will match any rules where the value is lower that that specified, 'min-width' the value must be higher. Use 'media' to specify the media type
     502    // * @param bool $exact Denotes whether to match selectors exactly, if false, selectors will be matched from the left
     503    // * @return cssdoc A CSSdoc object
     504    // */
     505    // public function find(?string $selector, $hasProp = null, array $media = [], bool $exact = true) : cssdoc {
     506
     507    // // normalise selectors
     508    // $selector = $selector === null ? null : \array_map('\\trim', \explode(',', $selector));
     509    // if (!\is_array($hasProp)) {
     510    //      $hasProp = [$hasProp];
     511    // }
     512
     513    // // find rules
     514    // $rules = $this->document->find($selector, $hasProp, $media);
     515
     516    // // attach to a new document
     517    // $obj = new cssdoc($this->config);
     518    // $obj->collection($rules);
     519    // return $obj;
     520    // }
    520521
    521522    // public function prop(string $prop, ?string $func = null) {
  • torque/trunk/packages/cssdoc/tokens/directive.php

    r2817597 r2912478  
    4848    public function parse(tokenise $tokens) : bool {
    4949        if (($token = $tokens->current()) !== null) {
    50             $directive = true;
    5150            $properties = false;
    5251            $root = $this->root;
     
    107106        // minify properties
    108107        $props = $this->properties;
    109         foreach ($props AS $key => $item) {
     108        foreach ($props AS $item) {
    110109            $item->minify($minify);
    111110        }
    112111
    113         if ($minify['semicolons'] && $props) {
     112        if ($minify['semicolons'] && !empty($props)) {
    114113            \end($props)->semicolon = false;
    115114        }
    116115
    117116        // minify document
    118         if ($this->document) {
     117        if ($this->document !== null) {
    119118            $this->document->minify($minify);
    120119            if ($minify['empty'] && !$this->document->rules) {
     
    153152            $join = $b ? ', ' : ',';
    154153        }
    155         if (!$this->properties && !$this->document) {
     154        if (empty($this->properties) && $this->document === null) {
    156155            $css .= ';';
    157156        }
    158157
    159158        // compile properties
    160         if ($this->properties) {
     159        if (!empty($this->properties)) {
    161160            $css .= $b ? ' {' : '{';
    162161
     
    170169
    171170        // compile document
    172         if ($this->document) {
     171        if ($this->document !== null) {
    173172            $css .= $b ? ' {' : '{';
    174173            $tab = $b ? "\n\t".$options['prefix'] : '';
  • torque/trunk/packages/cssdoc/tokens/document.php

    r2817597 r2912478  
    1919     * Constructs the comment object
    2020     *
    21      * @param cssdoc $root The parent htmldoc object
     21     * @param cssdoc $root The parent cssdoc object
    2222     */
    2323    public function __construct(cssdoc $root, array $rules = []) {
     
    102102     * @param array $media An array specifying how any media queries should be match, where the key is the property and the key the value. 'max-width' will match any rules where the value is lower that that specified, 'min-width' the value must be higher. Use 'media' to specify the media type
    103103     * @param bool $exact Denotes whether to match selectors exactly, if false, selectors will be matched from the left
    104      * @return array A CSSdoc object
     104     * @return array An array of rule objects
    105105     */
    106     public function find(?array $selectors, $hasProp = null, array $media = [], bool $exact = true) {
    107         $rules = [];
    108         foreach ($this->rules AS $item) {
    109             if (\get_class($item) === '\\hexydec\\css\\cssdoc' && $item->matches($selectors, $hasProp, $exact)) {
    110                 $rules[] = $item;
    111             }
    112         }
    113         return $rules;
    114     }
     106    // public function find(?array $selectors, array|string $hasProp = null, array $media = [], bool $exact = true) {
     107    // $rules = [];
     108    // foreach ($this->rules AS $item) {
     109    //      if (\get_class($item) === '\\hexydec\\css\\cssdoc' && $item->matches($selectors, $hasProp, $exact)) {
     110    //          $rules[] = $item;
     111    //      }
     112    // }
     113    // return $rules;
     114    // }
    115115}
  • torque/trunk/packages/cssdoc/tokens/property.php

    r2817597 r2912478  
    4949     *
    5050     * @param string $var The name of the property to retrieve
    51      * @return ?string The value of the requested property, or null if the porperty doesn't exist
     51     * @return mixed The value of the requested property, or null if the property doesn't exist
    5252     */
    53     public function __get(string $var) {
     53    public function __get(string $var) : mixed {
    5454        if ($var === 'name') {
    5555            return $this->name;
     
    7979                            case 'string':
    8080                            case 'colon':
    81                                 $this->name = $prop;
     81                                $this->name = $prop; // set name if colon
    8282                            case 'comma':
    8383                                $item = new value($this->root, $this->name);
  • torque/trunk/packages/cssdoc/tokens/rule.php

    r2817597 r2912478  
    2929     * Constructs the comment object
    3030     *
    31      * @param cssdoc $root The parent htmldoc object
     31     * @param cssdoc $root The parent cssdoc object
    3232     */
    3333    public function __construct(cssdoc $root) {
     
    5252                        break;
    5353                    case 'directive':
    54                         $tokens->prev();
     54                        $tokens->prev(); // rewind the token and return
    5555                    case 'curlyclose':
    5656                        break 2;
     
    7676            } while (($token = $tokens->next()) !== null);
    7777        }
    78         return $this->selectors && $this->properties;
     78        return !empty($this->selectors) && !empty($this->properties);
    7979    }
    8080
     
    9898
    9999        // remove last semi-colon
    100         if ($this->properties && $minify['semicolons']) {
     100        if (!empty($this->properties) && $minify['semicolons']) {
    101101            \end($this->properties)->semicolon = false;
    102102        }
    103103    }
    104104
    105     public function isEmpty() {
    106         return !$this->properties;
     105    public function isEmpty() : bool {
     106        return empty($this->properties);
    107107    }
    108108
     
    156156
    157157        // check props
    158         if ($matches && $hasProp) {
     158        if ($matches && !empty($hasProp)) {
    159159            foreach ($this->properties AS $item) {
    160160                if (!\in_array($item->name, $hasProp)) {
  • torque/trunk/packages/cssdoc/tokens/selector.php

    r2817597 r2912478  
    1919     * Constructs the comment object
    2020     *
    21      * @param cssdoc $root The parent htmldoc object
     21     * @param cssdoc $root The parent cssdoc object
    2222     */
    2323    public function __construct(cssdoc $root) {
     
    3737                switch ($token['type']) {
    3838                    case 'whitespace':
    39                         if (!$join && $this->selectors) {
     39                        if (!$join && !empty($this->selectors)) {
    4040                            $join = ' ';
    4141                        }
     
    4545                            $join = $token['value'];
    4646                            break;
    47                         }
     47                        } // fall-through if not *
    4848                    case 'string':
    4949                        $this->selectors[] = [
     
    5555                    case 'colon':
    5656                        $parts = ':';
    57                         $brackets = false;
    5857                        while (($token = $tokens->next()) !== null) {
    5958
  • torque/trunk/packages/cssdoc/tokens/value.php

    r2817597 r2912478  
    2424     * @var array Properties
    2525     */
    26     protected $properties = [];
     26    protected array $properties = [];
    2727
    2828    /**
     
    6969                    break;
    7070                case 'bracketopen':
    71                     $item = new value($this->root, !$this->brackets && $this->properties ? end($this->properties) : $this->name, true);
     71                    $name = !$this->brackets && !empty($this->properties) ? \end($this->properties) : $this->name;
     72                    $item = new value($this->root, $name, true);
    7273                    if ($item->parse($tokens)) {
    7374                        $this->properties[] = $item;
     
    7576                    break;
    7677                case 'comment':
    77                     $comment = $token['value'];
     78                    // $comment = $token['value'];
    7879                    break;
    7980                case 'semicolon':
     
    8182                        $this->properties[] = $token['value'];
    8283                    }
     84                    // rewind pointer below
    8385                case 'curlyopen':
    8486                case 'curlyclose':
    8587                case 'important':
    8688                    $tokens->prev();
     89                    break 2;
    8790                case 'bracketclose':
    8891                    break 2;
     
    123126
    124127                    // shorten hex values
    125                     if (\preg_match('/^#(([a-f0-9])\\2)(([a-f0-9])\\4)(([a-f0-9])\\6)/i', $item, $match)) {
     128                    if (\preg_match('/^#(([a-f0-9])\\2)(([a-f0-9])\\4)(([a-f0-9])\\6)$/i', $item, $match)) {
    126129                        $item = '#'.$match[2].$match[4].$match[6];
    127130                    }
     
    248251     */
    249252    public function compile(array $options) : string {
    250         $b = $options['style'] !== 'minify';
    251253        $css = $options['prefix'];
    252254        $join = '';
    253255        $last = null;
     256        $spaced = $this->root->config['spaced'];
    254257        foreach ($this->properties AS $item) {
    255258            if (\is_object($item)) {
     
    259262                $css .= '('.$item->compile($options).')';
    260263                $join = ' ';
    261             } elseif (\in_array($item, ['-', '+'], true) && !\in_array(\mb_strtolower($this->name), $this->root->config['spaced'], true)) {
     264            } elseif (\in_array($item, ['-', '+'], true) && !\in_array(\mb_strtolower($this->name ?? ''), $spaced, true)) {
    262265                $css .= $item;
    263266                $join = '';
  • torque/trunk/packages/htmldoc/helpers/selector.php

    r2817597 r2912478  
    110110                        $selectors[] = $parts;
    111111                        $parts = [];
    112                         break;
     112                        break 2;
    113113                }
    114114            } while (($token = $tokens->next()) !== null);
  • torque/trunk/packages/htmldoc/htmldoc.php

    r2817597 r2912478  
    6868     * @return mixed The number of children in the object for length, the output config, or null if the parameter doesn't exist
    6969     */
    70     #[\ReturnTypeWillChange]
    71     public function __get(string $var) {
     70    public function __get(string $var) : mixed {
    7271        if ($var === 'config') {
    7372            return $this->config;
    7473        } elseif ($var === 'length') {
    7574            return \count($this->children);
    76         }
    77         return null;
     75        } else {
     76            return $this->children[0]->{$var};
     77        }
    7878    }
    7979
     
    9393     * @param mixed $value The value of the array key in the children array to be updated
    9494     */
    95     public function offsetSet($i, $value) : void {
     95    public function offsetSet(mixed $i, mixed $value) : void {
    9696        $this->children[$i] = $value;
    9797    }
     
    103103     * @return bool Whether the key exists in the config array
    104104     */
    105     public function offsetExists($i) : bool {
     105    public function offsetExists(mixed $i) : bool {
    106106        return isset($this->children[$i]);
    107107    }
     
    112112     * @param mixed $i The key to be removed
    113113     */
    114     public function offsetUnset($i) : void {
     114    public function offsetUnset(mixed $i) : void {
    115115        unset($this->children[$i]);
    116116    }
     
    122122     * @return mixed An HTMLdoc object containing the child node at the requested position or null if there is no child at the requested position
    123123     */
    124     #[\ReturnTypeWillChange]
    125     public function offsetGet($i) { // return reference so you can set it like an array
     124    public function offsetGet(mixed $i) : mixed { // return reference so you can set it like an array
    126125        if (isset($this->children[$i])) {
    127126            $obj = new htmldoc($this->config);
    128             $obj->collection([$this->children[$i]]);
    129             return $obj;
     127            return $obj->collection([$this->children[$i]]);
    130128        }
    131129        return null;
     
    137135     * @return mixed An HTMLdoc object containing the child node at the current pointer position or null if there are no children
    138136     */
    139     #[\ReturnTypeWillChange]
    140     public function current() {
     137    public function current() : mixed {
    141138        if (isset($this->children[$this->pointer])) {
    142139            $obj = new htmldoc($this->config);
    143             $obj->collection([$this->children[$this->pointer]]);
    144             return $obj;
     140            return $obj->collection([$this->children[$this->pointer]]);
    145141        }
    146142        return null;
     
    152148     * @return mixed The current pointer position
    153149     */
    154     #[\ReturnTypeWillChange]
    155     public function key() {
     150    public function key() : mixed {
    156151        return $this->pointer;
    157152    }
     
    192187     * @return string|false The loaded HTML, or false on error
    193188     */
    194     public function open(string $url, $context = null, ?string &$error = null) {
     189    public function open(string $url, $context = null, ?string &$error = null) : string|false {
    195190
    196191        // check resource
     
    296291     *
    297292     * @param string|htmldoc $html A string of HTML, or an htmldoc object
    298      * @return bool|array An array of node objects or false on error
    299      */
    300     protected function parse($html) {
     293     * @return array|false An array of node objects or false on error
     294     */
     295    protected function parse(string|htmldoc $html) : array|false {
    301296
    302297        // convert string to nodes
     
    347342     *
    348343     * @param int $index The index of the child tag to retrieve
    349      * @return mixed A tag object if index is specified, or an array of tag objects, or null if the specified index doesn't exist or the object is empty
    350      */
    351     public function get(int $index = null) {
     344     * @return tag|array|null A tag object if index is specified, or an array of tag objects, or null if the specified index doesn't exist or the object is empty
     345     */
     346    public function get(int $index = null) : tag|array|null {
    352347
    353348        // build children that are tags
     
    362357        if ($index === null) {
    363358            return $children;
    364         }
    365 
    366         // check if index is minus
    367         if ($index < 0) {
    368             $index = \count($children) + $index;
    369         }
    370 
    371         // return index if set
    372         if (isset($children[$index])) {
    373             return $children[$index];
     359        } else {
     360
     361            // check if index is minus
     362            if ($index < 0) {
     363                $index = \count($children) + $index;
     364            }
     365
     366            // return index if set
     367            if (isset($children[$index])) {
     368                return $children[$index];
     369            }
    374370        }
    375371        return null;
     
    448444     */
    449445    public function children() : htmldoc {
    450         return $this->find('>*');
     446        return $this->find('*>*');
     447    }
     448
     449    /**
     450     * Generate a new htmldoc object containing all the child tags of the parents
     451     *
     452     * @return htmldoc A new htmldoc object
     453     */
     454    public function parent() : htmldoc {
     455        $doc = new htmldoc($this->config);
     456        $nodes = [];
     457        foreach ($this->children AS $item) {
     458            if (\get_class($item) === 'hexydec\\html\\tag' && ($parent = $this->children[0]->parent) !== null) {
     459                $nodes[] = $parent;
     460            }
     461        }
     462        return $doc->collection($nodes);
     463    }
     464
     465    /**
     466     * Retrieves the tag name from the first tag object in the collection
     467     *
     468     * @return ?string The name of the tag, or null if there are no tags in the collection
     469     */
     470    public function tag() : ?string {
     471        foreach ($this->children AS $item) {
     472            if (\get_class($item) === 'hexydec\\html\\tag') {
     473                return $item->tagName;
     474            }
     475        }
     476        return null;
    451477    }
    452478
     
    456482     * @param string $key The name of the attribute to retrieve
    457483     * @param string $value The value of the attribute to update
    458      * @return string The value of the attribute or null if the attribute doesn't exist
     484     * @return ?string The value of the attribute or null if the attribute doesn't exist
    459485     */
    460486    public function attr(string $key, ?string $value = null) : ?string {
     
    492518     *
    493519     * @param array $nodes An array of nodes to add to the collection
    494      * @return void
    495      */
    496     protected function collection(array $nodes) : void {
    497         $this->children = $nodes;
     520     * @return htmldoc The current instance of this object
     521     */
     522    protected function collection(array $nodes) : htmldoc {
     523
     524        // only store unique nodes as some find operations can produce the same node multiple times
     525        $unique = [];
     526        foreach ($nodes AS $item) {
     527            if (!\in_array($item, $unique, true)) {
     528                $unique[] = $item;
     529            }
     530        }
     531        $this->children = $unique;
     532        return $this;
    498533    }
    499534
     
    600635     * @return htmldoc The current htmldoc object with the nodes appended
    601636     */
    602     public function append($html) : htmldoc {
     637    public function append(string|htmldoc $html) : htmldoc {
    603638        if (($nodes = $this->parse($html)) !== false) {
    604639            foreach ($this->children AS $item) {
     
    617652     * @return htmldoc The current htmldoc object with the nodes appended
    618653     */
    619     public function prepend($html) : htmldoc {
     654    public function prepend(string|htmldoc $html) : htmldoc {
    620655        if (($nodes = $this->parse($html)) !== false) {
    621656            foreach ($this->children AS $item) {
     
    634669     * @return htmldoc The current htmldoc object with the nodes appended
    635670     */
    636     public function before($html) : htmldoc {
     671    public function before(string|htmldoc $html) : htmldoc {
    637672        if (($nodes = $this->parse($html)) !== false) {
    638673            foreach ($this->children AS $item) {
     
    651686     * @return htmldoc The current htmldoc object with the nodes appended
    652687     */
    653     public function after($html) : htmldoc {
     688    public function after(string|htmldoc $html) : htmldoc {
    654689        if (($nodes = $this->parse($html)) !== false) {
    655690            foreach ($this->children AS $item) {
     
    665700     * Removes all top level nodes, or if $selector is specified, the nodes matched by the selector
    666701     *
    667      * @param string $selector A CSS selector to refine the nodes to delete or null to delete top level nodes
     702     * @param ?string $selector A CSS selector to refine the nodes to delete or null to delete top level nodes
    668703     * @return htmldoc The current htmldoc object with the requested nodes deleted
    669704     */
    670     public function remove(string $selector = null) : htmldoc {
     705    public function remove(?string $selector = null) : htmldoc {
    671706        $obj = $selector ? $this->find($selector) : $this;
    672707        foreach ($obj->children AS $item) {
     
    683718     * @param string|null $file The file location to save the document to, or null to just return the compiled code
    684719     * @param array $options An array indicating output options, this is merged with htmldoc::$output
    685      * @return string|bool The compiled HTML, or false if the file could not be saved
    686      */
    687     public function save(string $file = null, array $options = []) {
     720     * @return string|false The compiled HTML, or false if the file could not be saved
     721     */
     722    public function save(?string $file = null, array $options = []) : string|false {
    688723
    689724        // compile html
  • torque/trunk/packages/htmldoc/tokens/tag.php

    r2817597 r2912478  
    9595     * @return void
    9696     */
    97     public function __clone() {
     97    public function __clone() : void {
    9898        foreach ($this->children AS &$item) {
    9999            $item = clone $item;
     
    288288     * Returns the parent of the current object
    289289     *
    290      * @return tag The parent tag
     290     * @return ?tag The parent tag
    291291     */
    292292    public function parent() : ?tag {
     
    480480
    481481                // use parent folders if it is shorter
    482                 if ($minify['urls']['parent'] && $dirs && \mb_strpos($attributes[$key], '/') === 0 && \mb_strpos($attributes[$key], '//') === false) {
     482                if ($minify['urls']['parent'] && $dirs && \mb_strpos($attributes[$key], '/') === 0 && !\str_contains($attributes[$key], '//')) {
    483483                    $isDir = \mb_strrpos($attributes[$key], '/') === \mb_strlen($attributes[$key])-1;
    484484                    $compare = \explode('/', \trim($isDir ? $attributes[$key] : \dirname($attributes[$key]), '/'));
     
    507507            if ($minify['attributes']) {
    508508
     509                // cache attributes
     510                $min = $minify['attributes'];
     511
    509512                // trim attribute
    510                 if ($minify['attributes']['trim'] && $attributes[$key]) {
     513                if ($min['trim'] && $attributes[$key]) {
    511514                    $attributes[$key] = \trim($attributes[$key], " \r\n\t");
    512515                }
    513516
    514517                // boolean attributes
    515                 if ($minify['attributes']['boolean'] && \in_array($key, $attr['boolean'], true)) {
     518                if ($min['boolean'] && \in_array($key, $attr['boolean'], true)) {
    516519                    $attributes[$key] = null;
    517520
     521                // remove empty attributes
     522                } elseif (\in_array($attributes[$key], ['', null], true) && $min['empty'] && \in_array($key, $attr['empty'], true)) {
     523                    unset($attributes[$key]);
     524
    518525                // minify style tag
    519                 } elseif ($key === 'style' && $minify['attributes']['style']) {
     526                } elseif ($key === 'style' && $min['style'] && $attributes[$key]) {
    520527                    $attributes[$key] = \trim(\str_replace(
    521                         ['  ', ' : ', ': ', ' :', ' ; ', ' ;', '; '],
    522                         [' ', ':', ':', ':', ';', ';', ';'],
     528                        ["\t", "\r", "\n", '  ', ' : ', ': ', ' :', ' ; ', ' ;', '; '],
     529                        [' ', '', ' ', ' ', ':', ':', ':', ';', ';', ';'],
    523530                        $attributes[$key]
    524531                    ), '; ');
    525532
    526533                // trim classes
    527                 } elseif ($key === 'class' && $minify['attributes']['class'] && \mb_strpos($attributes[$key], ' ') !== false) {
     534                } elseif ($key === 'class' && $min['class'] && \str_contains($attributes[$key] ?? '', ' ')) {
    528535                    $attributes[$key] = \trim(\preg_replace('/\s+/', ' ', $attributes[$key]));
    529536
    530537                // minify option tag, always capture the tag to prevent it being removed as a default
    531538                } elseif ($key === 'value' && $tag === 'option') {
    532                     if ($minify['attributes']['option'] && isset($this->children[0]) && $this->children[0]->text() === $attributes[$key]) {
     539                    if ($min['option'] && isset($this->children[0]) && $this->children[0]->text() === $attributes[$key]) {
    533540                        unset($attributes[$key]);
    534541                    }
    535                     continue;
    536542
    537543                // remove tag specific default attribute
    538                 } elseif ($minify['attributes']['default'] && isset($attr['default'][$tag][$key]) && ($attr['default'][$tag][$key] === true || $attr['default'][$tag][$key] === $attributes[$key])) {
     544                } elseif ($min['default'] && isset($attr['default'][$tag][$key]) && ($attr['default'][$tag][$key] === true || $attr['default'][$tag][$key] === $attributes[$key])) {
    539545                    unset($attributes[$key]);
    540                     continue;
    541                 }
    542 
    543                 // remove other attributes
    544                 if ($attributes[$key] === '' && $minify['attributes']['empty'] && \in_array($key, $attr['empty'], true)) {
    545                     unset($attributes[$key]);
    546                     continue;
    547546                }
    548547            }
     
    643642
    644643            // pass rest of selector to level below
    645             if ($item['join'] && $i) {
     644            if (\in_array($item['join'], [' ', '>'], true) && $i) {
    646645                $match = false;
     646                $childselector = \array_slice($selector, $i);
    647647                foreach ($this->children AS $child) {
    648648                    if (\get_class($child) === 'hexydec\\html\\tag') {
    649                         $found = \array_merge($found, $child->find(\array_slice($selector, $i)));
     649                        $found = \array_merge($found, $child->find($childselector));
     650                    }
     651                }
     652                break;
     653
     654            // find siblings
     655            } elseif (\in_array($item['join'], ['+', '~'], true) && $i) {
     656                $match = false;
     657                $siblingselector = \array_slice($selector, $i);
     658                $search = false;
     659                foreach ($this->parent->children AS $sibling) {
     660                    if (!$search && $sibling === $this) {
     661                        $search = true;
     662                    } elseif ($search && \get_class($sibling) === 'hexydec\\html\\tag') {
     663                        $found = \array_merge($found, $sibling->find($siblingselector));
     664                        if ($item['join'] === '+') {
     665                            break;
     666                        }
    650667                    }
    651668                }
     
    863880
    864881                // single quotes || swap when minimal and there are double quotes in the string
    865                 } elseif ($options['quotestyle'] === 'single' || ($options['quotestyle'] === 'minimal' && \mb_strpos($value, '"') !== false)) {
     882                } elseif ($options['quotestyle'] === 'single' || ($options['quotestyle'] === 'minimal' && \str_contains($value, '"'))) {
    866883                    $html .= "='".\str_replace(['&', "'", '<'], ['&amp;', '&#39;', '&lt;'], $value)."'";
    867884
     
    919936     * @return mixed The value of the requested property
    920937     */
    921     #[\ReturnTypeWillChange]
    922     public function __get(string $var) {
     938    public function __get(string $var) : mixed {
    923939        return $this->$var;
    924940    }
  • torque/trunk/packages/htmldoc/tokens/text.php

    r2817597 r2912478  
    3939     * @return void
    4040     */
    41     public function __set(string $name, $value) : void {
     41    public function __set(string $name, mixed $value) : void {
    4242        if ($name === 'parent' && \get_class($value) === 'hexydec\\html\\tag') {
    4343            $this->parent = $value;
     
    118118    }
    119119
    120     protected function getIndex($children) {
     120    protected function getIndex(array $children) : int|false {
    121121        foreach ($children AS $key => $value) {
    122122            if ($value === $this) {
  • torque/trunk/packages/jslite/jslite.php

    r2823176 r2912478  
    44use \hexydec\tokens\tokenise;
    55
     6/**
     7 * @property-read mixed Either the config array, or the length property
     8 */
     9
    610class jslite {
    711
    812    /**
    9      * @var array $tokens Regexp components keyed by their corresponding codename for tokenising HTML
     13     * @var array $tokens Regexp components keyed by their corresponding codename for tokenising Javascript
    1014     */
    1115    protected static array $tokens = [
     
    2327
    2428        // keywords number and variables
    25         'keyword' => '\\b(?:let|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|finally|for|function|if|import|in|of|instanceof|new|return|super|switch|this|throw|try|typeof|var|void|while|with|yield|null|async|await|true|false|undefined)\\b',
     29        'keyword' => '\\b(?:let|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|finally|for|function|if|import|in|of|instanceof|new|return|super|switch|this|throw|try|typeof|var|void|while|with|yield|null|async|await|true|false|undefined|static)\\b',
    2630        'variable' => '[\\p{L}\\p{Nl}$_][\\p{L}\\p{Nl}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}$_]*+',
    2731        'number' => '(?:0[bB][01_]++n?|0[oO][0-7_]++n?|0[xX][a-f0-9_]|[0-9][0-9_]*+(?:\\.[0-9_]++)?+(?:e[+-]?[1-9][0-9]*+)?+)',
     
    5963        ]
    6064    ];
    61     protected ?array $expressions = null;
     65    protected array $expressions = [];
    6266
    6367    public function __construct(array $config = []) {
    64         if ($config) {
     68        if (!empty($config)) {
    6569            $this->config = \array_replace_recursive($this->config, $config);
    6670        }
     
    7377     * @return mixed The number of children in the object for length, the output config, or null if the parameter doesn't exist
    7478     */
    75     #[\ReturnTypeWillChange]
    76     public function __get(string $var) {
     79    public function __get(string $var) : mixed {
    7780        if ($var === 'config') {
    7881            return $this->config;
     
    9194     * @return string|false The loaded Javascript, or false on error
    9295     */
    93     public function open(string $url, $context = null, ?string &$error = null) {
     96    public function open(string $url, $context = null, ?string &$error = null) : string|false {
    9497
    9598        // check resource
     
    113116     * @param string $js A string containing valid Javascript
    114117     * @param ?string &$error A reference to any user error that is generated
    115      * @return bool Whether the input HTML was parsed
     118     * @return bool Whether the input Javascript was parsed
    116119     */
    117120    public function load(string $js, ?string &$error = null) : bool {
     
    142145        // generate expressions
    143146        $expressions = [];
    144         while (($token = $tokens->next()) !== null) {
     147        while ($tokens->next() !== null) {
    145148            $obj = new expression();
    146149            if ($obj->parse($tokens)) {
     
    154157     * Minifies the internal representation of the document
    155158     *
    156      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     159     * @param array $minify An array indicating which minification operations to perform, this is merged with self::$config['minify']
    157160     * @return void
    158161     */
     
    164167        // minify expressions
    165168        $last = null;
    166         $not = ['whitespace', 'comment'];
    167169        foreach ($this->expressions AS $item) {
    168170            $item->minify($minify);
     
    204206     * @param string|null $file The file location to save the document to, or null to just return the compiled code
    205207     * @param array $options An array indicating output options
    206      * @return string|bool The compiled Javascript, or false if the file could not be saved
    207      */
    208     public function save(string $file = null, array $options = []) {
     208     * @return string|false The compiled Javascript, or false if the file could not be saved
     209     */
     210    public function save(string $file = null, array $options = []) : string|false {
    209211        $js = $this->compile($options);
    210212
  • torque/trunk/packages/jslite/tokens/brackets.php

    r2817597 r2912478  
    4444        if (($token = $tokens->current()) !== null) {
    4545            $bracket = $this->bracket = \mb_substr($token['type'], 4);
    46             while (($token = $tokens->next()) !== null) {
     46            while ($tokens->next() !== null) {
    4747                $obj = new expression($bracket);
    4848                if ($obj->parse($tokens)) {
     
    6060     * Minifies the internal representation of the document
    6161     *
    62      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     62     * @param array $minify An array indicating which minification operations to perform
    6363     * @return void
    6464     */
     
    8282        }
    8383
    84         // other checks before we remove semi-colon
    85         if ($last && $minify['semicolons']) {
    86             $key = __NAMESPACE__.'\\keyword';
    87             $bra = __NAMESPACE__.'\\brackets';
     84        // remove last eol if not keyword-bracket or in for loop
     85        if ($last !== null && !$this->isKeywordBracket($last) && !$this->isInForLoop($expressions)) {
     86            $last->eol = null;
     87        }
     88    }
    8889
    89             // don't remove semi-colon from keyword + brackets with no following commands
    90             if ($this->bracket === 'curly') {
    91                 $sigcomms = [];
    92                 foreach ($last->commands AS $comm) {
    93                     if ($comm::significant) {
    94                         $sigcomms[] = $comm;
     90    /**
     91     * Checks to see if the last expression is a keyword followed by brackets, with no other commands - semi-colon must not be removed
     92     *
     93     * @param object $last The last JSlite object that is being checked for semi-colon removal
     94     * @return bool Whether the object contains a keyword-bracket expression
     95     */
     96    protected function isKeywordBracket(object $last) : bool {
     97        $key = __NAMESPACE__.'\\keyword';
     98        $bra = __NAMESPACE__.'\\brackets';
     99
     100        // don't remove semi-colon from keyword + brackets with no following commands
     101        if ($this->bracket === 'curly') {
     102            $sigcomms = [];
     103            foreach ($last->commands AS $comm) {
     104                if ($comm::significant) {
     105                    $sigcomms[] = $comm;
     106                }
     107            }
     108            if (\count($sigcomms) === 2 && \get_class($sigcomms[0]) === $key && $sigcomms[0]->content !== 'return' && \get_class($sigcomms[1]) === $bra && $sigcomms[1]->bracket === 'bracket') {
     109                return true;
     110            }
     111        }
     112        return false;
     113    }
     114
     115    /**
     116     * Analyses the cirrent expression set to see if it is contained within a for loop with a specific pattern of semi-colons, where the final one should not be removed
     117     *
     118     * @param array $expressions An array containing the current expressesion set
     119     * @return bool WHether the current expresiion set is wrapped in a for loop
     120     */
     121    protected function isInForLoop(array $expressions) : bool {
     122        $key = __NAMESPACE__.'\\keyword';
     123
     124        // must not remove eol if for loop
     125        $prev = null;
     126        foreach ($this->root->commands AS $item) {
     127            if ($item === $this) {
     128                if (\is_object($prev) && \get_class($prev) === $key && $prev->content === 'for') {
     129
     130                    // count expressions where the EOL is ; (Could be comma)
     131                    $count = 0;
     132                    foreach ($expressions AS $expr) {
     133                        if ($expr->eol === ';') {
     134                            $count++;
     135                        }
     136                    }
     137                    if ($count !== 3) {
     138                        return true;
    95139                    }
    96140                }
    97                 if (count($sigcomms) === 2 && \get_class($sigcomms[0]) === $key && $sigcomms[0]->content !== 'return' && \get_class($sigcomms[1]) === $bra && $sigcomms[1]->bracket === 'bracket') {
    98                     return;
    99                 }
    100             }
    101 
    102             // must not remove eol if for loop
    103             $prev = null;
    104             foreach ($this->root->commands AS $i => $item) {
    105                 if ($item === $this) {
    106                     if ($prev && \get_class($prev) === $key && $prev->content === 'for') {
    107 
    108                         // count expressions where the EOL is ; (Could be comma)
    109                         $count = 0;
    110                         foreach ($expressions AS $expr) {
    111                             if ($expr->eol === ';') {
    112                                 $count++;
    113                             }
    114                         }
    115                         if ($count !== 3) {
    116                             $last = null;
    117                         }
    118                     }
    119                     break;
    120                 } elseif ($item::significant) {
    121                     $prev = $item;
    122                 }
    123             }
    124 
    125             // remove last eol
    126             if ($last) {
    127                 $last->eol = null;
     141                break;
     142            } elseif ($item::significant) {
     143                $prev = $item;
    128144            }
    129145        }
     146        return false;
    130147    }
    131148
     
    133150     * Compile as Javascript
    134151     *
    135      * @return string The compiled HTML
     152     * @return string The compiled Javascript
    136153     */
    137154    public function compile() : string {
  • torque/trunk/packages/jslite/tokens/expression.php

    r2817597 r2912478  
    4949            do {
    5050                switch ($token['type']) {
     51
     52                    // comments
    5153                    case 'commentsingle':
    5254                    case 'commentmulti':
    53                         $obj = new comment();
    54                         if ($obj->parse($tokens)) {
    55                             $commands[] = $obj;
    56                         }
    57                         break;
     55                        $commands = $this->getCommand('comment', $tokens, $commands);
     56                        break;
     57
     58                    // operators
    5859                    case 'operator':
    5960                        $obj = new operator();
     
    6566                        }
    6667                        break;
     68
     69                    // variables, numbers, and increment
     70                    case 'variable':
     71                    case 'number':
    6772                    case 'increment':
    68                         $obj = new increment();
    69                         if ($obj->parse($tokens)) {
    70                             $commands[] = $obj;
    71                         }
    72                         break;
     73                        $commands = $this->getCommand($token['type'], $tokens, $commands);
     74                        break;
     75
     76                    // keywords
    7377                    case 'keyword':
    74                         if ($this->isKeyword($last, $token, $tokens)) {
    75                             $obj = new keyword();
    76                             if ($obj->parse($tokens)) {
    77                                 $commands[] = $obj;
    78                             }
    79                             break;
    80                         }
    81                     case 'variable':
    82                         $obj = new variable();
    83                         if ($obj->parse($tokens)) {
    84                             $commands[] = $obj;
    85                         }
    86                         break;
    87                     case 'number':
    88                         $obj = new number();
    89                         if ($obj->parse($tokens)) {
    90                             $commands[] = $obj;
    91                         }
    92                         break;
     78                        if ($this->isKeyword($last, $tokens)) {
     79                            $commands = $this->getCommand('keyword', $tokens, $commands);
     80                            break; // if not keyword then variable
     81                        }
     82
     83                    // quoted strings
    9384                    case 'doublequotes':
    9485                    case 'singlequotes':
    9586                    case 'templateliterals':
    96                         $obj = new jsstring();
    97                         if ($obj->parse($tokens)) {
    98                             $commands[] = $obj;
    99                         }
    100                         break;
     87                        $commands = $this->getCommand('jsstring', $tokens, $commands);
     88                        break;
     89
     90                    // regular expressions
    10191                    case 'regexp':
    10292
    10393                        // regexp is extremely awkward to capture, and because we only look ahead in the regexp, sometimes it can get it wrong
    104                         if (!$last || $this->isRegexpAllowed($last, $beforelast)) {
     94                        if (!\is_object($last) || $this->isRegexpAllowed($last, $beforelast)) {
    10595
    10696                            // create regexp object
    107                             $obj = new regexp();
    108                             if ($obj->parse($tokens)) {
    109                                 $commands[] = $obj;
    110                             }
     97                            $commands = $this->getCommand('regexp', $tokens, $commands);
    11198
    11299                        // if we have got it wrong then the first character will be a divide
     
    118105                        }
    119106                        break;
     107
     108                    // whitespace
    120109                    case 'whitespace':
    121110                        $end = false;
    122111
    123112                        // catch un-terminated line endings
    124                         if ($last && \mb_strpos($token['value'], "\n") !== false) {
     113                        if (\is_object($last) && \str_contains($token['value'], "\n")) {
    125114                            $end = $this->isEol($tokens, $last, $beforelast, $assignment);
    126115                        }
    127116
    128117                        // create whitespace object
    129                         $obj = new whitespace($this);
    130                         if ($obj->parse($tokens)) {
    131                             $commands[] = $obj;
    132                         }
     118                        $commands = $this->getCommand('whitespace', $tokens, $commands);
    133119                        if ($end) {
    134120                            break 2;
     
    136122                            break;
    137123                        }
     124
     125                    // brackets
    138126                    case 'openbracket':
    139127                    case 'opensquare':
    140128                    case 'opencurly':
    141                         $obj = new brackets($this);
    142                         if ($obj->parse($tokens)) {
    143                             $commands[] = $obj;
    144                         }
    145                         break;
     129                        $commands = $this->getCommand('brackets', $tokens, $commands);
     130                        break;
     131
     132                    // end of lines
    146133                    case 'eol':
    147134                    case 'comma':
    148135                        $this->eol = $token['value'];
     136
     137                    // close brackets
    149138                    case 'closebracket':
    150139                    case 'closesquare':
     
    165154
    166155    /**
     156     * Create a token object and parse some tokens
     157     *
     158     * @param string $obj The name of the token object to create
     159     * @param tokenise $tokens A tokenise object conaining th etokens to ve parsed
     160     * @param array $commands The current array of commands
     161     * @return array The input $commands, with the command object pushed on if anything was parsed
     162     */
     163    protected function getCommand(string $obj, tokenise $tokens, array $commands) : array {
     164        $cls = __NAMESPACE__.'\\'.$obj;
     165        $obj = new $cls($this);
     166        if ($obj->parse($tokens)) {
     167            $commands[] = $obj;
     168        }
     169        return $commands;
     170    }
     171
     172    /**
    167173     * Works out whether a keyword is legal in the current context
    168174     */
    169     protected function isKeyword($prev, array $current, tokenise $tokens) {
     175    protected function isKeyword(?object $prev, tokenise $tokens) {
    170176        if (($next = $this->getNextSignificantToken($tokens)) !== null) {
    171177
    172178            // property name
    173             if (\mb_strpos($next['value'], ':') === 0 || $next['value'] === '.') {
    174                 return false;
    175 
    176             // var undefined
    177             // } elseif ($current['value'] === 'undefined') {
    178             //
    179             //  // is a variable definition
    180             //  if ($prev && \in_array($prev->content, ['const', 'let', 'var'])) {
    181             //      return false;
    182             //
    183             //  // followed by an assignment, comma, or EOL
    184             //  } elseif (!$prev && \in_array($next['value'], ['=', ',', ';'])) {
    185             //      return false;
    186             //  }
    187             }
    188         } elseif ($prev && \get_class($prev) === __NAMESPACE__.'\\operator' && $prev->content === '.') {
     179            return \mb_strpos($next['value'], ':') !== 0 && $next['value'] !== '.';
     180
     181        // previous object is .
     182        } elseif (\is_object($prev) && \get_class($prev) === __NAMESPACE__.'\\operator' && $prev->content === '.') {
    189183            return false;
    190184        }
     
    194188    /**
    195189     * Works out whether a regular expression is legal in the current context
    196      */
    197     protected function isRegexpAllowed($prev, $beforeprev = null) : bool {
     190     *
     191     * @param object $prev The previous object
     192     * @param ?object $beforeprev The bject before the previous object
     193     * @return bool Whether RegExp is allowed in the current position
     194     */
     195    protected function isRegexpAllowed(object $prev, ?object $beforeprev = null) : bool {
    198196        $key = __NAMESPACE__.'\\keyword';
    199197        $bra = __NAMESPACE__.'\\brackets';
     
    216214     *
    217215     * @param tokenise $tokens A tokenise object to get the next tokens from
    218      * @param mixed $prev The previous command object
    219      * @param mixed $beforeprev The command object before the previous command object
     216     * @param object $prev The previous command object
     217     * @param object $beforeprev The command object before the previous command object
    220218     * @return bool Whether the expression should end at the previous command
    221219     */
    222     protected function isEol(tokenise $tokens, $prev = null, $beforeprev = null, bool $assignment = false) : bool {
     220    protected function isEol(tokenise $tokens, object $prev, ?object $beforeprev = null, bool $assignment = false) : bool {
    223221        $prevtype = \get_class($prev);
    224222        $beforeprevtype = $beforeprev ? \get_class($beforeprev) : null;
     
    236234        // special case for keyword followed by bracket
    237235        } elseif ($prevtype === $bra && $beforeprevtype === $key && !\in_array($beforeprev->content, $keywords, true)) {
    238             return false;
    239236
    240237        // if prev is curly then expression will have already ended
     
    244241        // get next token
    245242        } elseif (($next = $this->getNextSignificantToken($tokens)) === null) {
    246             return false;
    247243
    248244        // if the previous expression is an operator, like + or =, then the expression must end if next not an operator
    249         } elseif ($beforeprevtype === $op && !\in_array($next['type'], ['operator', 'openbracket', 'eol'])) {
     245        } elseif ($beforeprevtype === $op && $prevtype !== $op && !\in_array($next['type'], ['operator', 'openbracket', 'eol'])) {
    250246            return true;
    251247
     
    264260        // see if the statement needs to be terminated
    265261        } else {
    266             $end = [ // previous type => [next types]
    267                 'brackets' => ['variable', 'number', 'string', 'increment'],
    268                 'variable' => ['variable', 'string', 'number', 'regexp', 'opencurly', 'increment'],
    269                 'number' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment'],
    270                 'string' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment'],
    271                 'regexp' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment'],
    272                 'increment' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment']
    273             ];
    274             foreach ($end AS $key => $item) {
    275                 if ('hexydec\\jslite\\'.$key === $prevtype && \in_array($next['type'], $item, true)) {
    276                     return true;
    277                 }
     262            return $this->matchesEolPattern($prevtype, $next['type']);
     263        }
     264        return false;
     265    }
     266
     267    protected function matchesEolPattern(string $prevtype, string $nexttype) : bool {
     268        $end = [ // previous type => [next types]
     269            'brackets' => ['variable', 'number', 'string', 'increment'],
     270            'variable' => ['variable', 'string', 'number', 'regexp', 'opencurly', 'increment'],
     271            'number' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment'],
     272            'string' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment'],
     273            'regexp' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment'],
     274            'increment' => ['variable', 'number', 'string', 'regexp', 'openbracket', 'opensquare', 'opencurly', 'increment']
     275        ];
     276        foreach ($end AS $key => $item) {
     277            if ('hexydec\\jslite\\'.$key === $prevtype && \in_array($nexttype, $item, true)) {
     278                return true;
    278279            }
    279280        }
     
    309310     * Minifies the internal representation of the document
    310311     *
    311      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     312     * @param array $minify An array indicating which minification operations to perform
    312313     * @return void
    313314     */
     
    323324     * Compile as Javascript
    324325     *
    325      * @return string The compiled HTML
     326     * @return string The compiled Javascript
    326327     */
    327328    public function compile() : string {
  • torque/trunk/packages/jslite/tokens/keyword.php

    r2817597 r2912478  
    3333     * Minifies the internal representation of the document
    3434     *
    35      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     35     * @param array $minify An array indicating which minification operations to perform
    3636     * @return void
    3737     */
     
    5959     * Compile as Javascript
    6060     *
    61      * @return string The compiled HTML
     61     * @return string The compiled Javascript
    6262     */
    6363    public function compile() : string {
  • torque/trunk/packages/jslite/tokens/number.php

    r2817597 r2912478  
    3333     * Minifies the internal representation of the document
    3434     *
    35      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     35     * @param array $minify An array indicating which minification operations to perform, this is merged with self::$config['minify']
    3636     * @return void
    3737     */
     
    4545     * Compile as Javascript
    4646     *
    47      * @return string The compiled HTML
     47     * @return string The compiled Javascript
    4848     */
    4949    public function compile() : string {
  • torque/trunk/packages/jslite/tokens/regexp.php

    r2817597 r2912478  
    3333     * Minifies the internal representation of the document
    3434     *
    35      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     35     * @param array $minify An array indicating which minification operations to perform
    3636     * @return void
    3737     */
     
    4747     * Compile as Javascript
    4848     *
    49      * @return string The compiled HTML
     49     * @return string The compiled Javascript
    5050     */
    5151    public function compile() : string {
  • torque/trunk/packages/jslite/tokens/string.php

    r2817597 r2912478  
    4848     * Minifies the internal representation of the document
    4949     *
    50      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     50     * @param array $minify An array indicating which minification operations to perform
    5151     * @return void
    5252     */
     
    6060     * Compile as Javascript
    6161     *
    62      * @return string The compiled HTML
     62     * @return string The compiled Javascript
    6363     */
    6464    public function compile() : string {
  • torque/trunk/packages/jslite/tokens/whitespace.php

    r2817597 r2912478  
    4747     * Minifies the internal representation of the document
    4848     *
    49      * @param array $minify An array indicating which minification operations to perform, this is merged with htmldoc::$config['minify']
     49     * @param array $minify An array indicating which minification operations to perform
    5050     * @return void
    5151     */
     
    5555            $eol = $this->parent->eol;
    5656            $prev = null;
    57             $beforeprev = null;
    5857            $count = \count($commands);
    59             $not = ['whitespace', 'comment'];
    6058
    6159            // specify class names
     
    7371
    7472                    // first item
    75                     if (!$i || !$prev) {
     73                    if (!$i || $prev === null) {
    7674                        $this->content = '';
    7775
     
    9189
    9290                        // remove whitespace if last in the parent expression
    93                         if (!$next) {
     91                        if ($next === null) {
    9492
    9593                            // terminate any statements that are not terminated
    96                             if (!$eol && \mb_strpos($this->content, "\n") !== false) {
     94                            if (!$eol && \str_contains($this->content, "\n")) {
    9795
    9896                                // always terminate return
     
    108106
    109107                        // handle operators next to an increment
    110                         } elseif ($prevtype === $op && $nexttype === $inc && \mb_strpos($next->compile(), $prev->compile()) !== false) {
     108                        } elseif ($prevtype === $op && $nexttype === $inc && \str_contains($next->compile(), $prev->compile())) {
    111109                            $this->content = ' ';
    112110
     
    136134                // record the previous significant objects
    137135                } elseif ($item::significant) {
    138                     $beforeprev = $prev;
    139136                    $prev = $item;
    140137
     
    159156     * Compile as Javascript
    160157     *
    161      * @param array $options An array indicating output options
    162      * @return string The compiled HTML
     158     * @return string The compiled Javascript
    163159     */
    164     public function compile(array $options = []) : string {
     160    public function compile() : string {
    165161        return $this->content;
    166162    }
  • torque/trunk/readme.txt

    r2823178 r2912478  
    22Contributors: hexydec
    33Tags: minify,minification,performance,security,optimization
    4 Requires at least: 5.9
    5 Tested up to: 6.1
    6 Requires PHP: 7.4
    7 Stable tag: 0.7.1
     4Requires at least:
     5Tested up to: 6.2
     6Requires PHP: 8.0
     7Stable tag: 0.7.2
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
     
    4646    * Preload combined stylesheets
    4747* Administration panel to control all features, including all minification optimisations
     48* Print minification stats in the console
    4849
    4950See the [Torque Github homepage](https://github.com/hexydec/torque) for more information.
     
    128129== Changelog ==
    129130
     131= Version 0.7.2 =
     132
     133* Fixed issue when combining CSS where any inline CSS attached to an external stylesheet was not combined, sometimes causing ordering issues
     134* Updated packages to latest versions
     135* Fixed bug where 304 headers were not set the Wordpress way, which caused Wordpress to overwrite it
     136* Fixed incorrectly loaded rebuild command
     137* Fixed minor PHP 8.1 data handling issues
     138
    130139= Version 0.7.1 =
    131140
  • torque/trunk/torque.php

    r2823178 r2912478  
    1010Plugin URI:     https://github.com/hexydec/torque
    1111Description:    Make your Wordpress website noticably faster by optimising how it is delivered. Analyse your website's performance and security, minify and combine your assets, and configure an array of performance and security settings quickly and easily with this comprehensive plugin. Achieves the best compression of any minification plugin.
    12 Version:        0.7.1
    13 Requires PHP:   7.4
     12Version:        0.7.2
     13Requires PHP:   8.0
    1414Author:         Hexydec
    1515Author URI:     https://github.com/hexydec/
     
    5757// add rebuild command
    5858if (\class_exists('WP_CLI')) {
    59     \WP_CLI::add_command('torque rebuild', '\\hexydec\\torque\\packages::rebuildAssets', [
     59    \WP_CLI::add_command('torque rebuild', function () {
     60        return \hexydec\torque\assets::rebuildAssets();
     61    }, [
    6062        'shortdesc' => 'Rebuild the configured combined CSS and Javascript files'
    6163    ]);
Note: See TracChangeset for help on using the changeset viewer.