Plugin Directory

Changeset 3196166


Ignore:
Timestamp:
11/25/2024 07:44:33 AM (16 months ago)
Author:
vanush10web
Message:

Fixed: Bug on Cloudflare cache flush

Location:
tenweb-speed-optimizer/trunk
Files:
22 edited

Legend:

Unmodified
Added
Removed
  • tenweb-speed-optimizer/trunk/config.php

    r3170042 r3196166  
    11<?php
    22
    3 define('TENWEB_SO_VERSION', '2.30.5');
     3define('TENWEB_SO_VERSION', '2.30.7');
    44define('TENWEB_SO_CONNECTED_FROM', 'speed_optimizer');
    55define('TENWEB_SO_DIR', plugin_dir_path(TWO_PLUGIN_FILE));
  • tenweb-speed-optimizer/trunk/env.php

    r3111518 r3196166  
    9797    "us-west2" => "Los Angeles, California, USA"
    9898);
     99
     100global $tenweb_firebaseConfig;
     101
     102$tenweb_firebaseConfig = [
     103    'apiKey' => "AIzaSyADDO7CWkI1jd8HUXFVs7AwSt4z52i27Tc",
     104    'authDomain'=> "copilote-prod-2096c.firebaseapp.com",
     105    'projectId'=> "copilote-prod-2096c",
     106    'storageBucket'=> "copilote-prod-2096c.appspot.com",
     107    'messagingSenderId'=> "698503034199",
     108    'appId'=> "1:698503034199:web:2282f40a461d9d95217a12",
     109    'databaseURL' => "https://copilote-prod-2096c.firebaseio.com",
     110];
     111
  • tenweb-speed-optimizer/trunk/includes/OptimizerMain.php

    r3158582 r3196166  
    10911091        $delete_cache_db = OptimizerUtils::delete_all_cache_db();
    10921092        $delete_cache_file = OptimizerUtils::delete_all_cache_file($dir, [$dir, $dir . '/css', $dir . '/js']);
     1093        OptimizerUtils::flushCloudflareCache();
    10931094        do_action('tenweb_purge_all_caches');
    10941095
  • tenweb-speed-optimizer/trunk/readme.txt

    r3187477 r3196166  
    55Tested up to: 6.7
    66Requires PHP: 7.4
    7 Stable tag: 2.30.5
     7Stable tag: 2.30.7
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    273273== Changelog ==
    274274
     275= 2.30.7 =
     276Fixed: Bug on Cloudflare cache flush
     277
    275278= 2.30.5 =
    276279Improved: Integration with Breakdance builder
  • tenweb-speed-optimizer/trunk/tenweb_speed_optimizer.php

    r3170042 r3196166  
    44 * Plugin URI: https://10web.io/page-speed-booster/
    55 * Description: Optimize your website speed and performance with 10Web Booster by compressing CSS and JavaScript.
    6  * Version: 2.30.5
     6 * Version: 2.30.7
    77 * Author: 10Web - Website speed optimization team
    88 * Author URI: https://10web.io/
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/.gitignore

    r2753365 r3196166  
    11/.idea/
    22/vendor/
     3.aider*
     4.env
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/src/Helper.php

    r2911090 r3196166  
    1313    private static $instance;
    1414    private static $network_domain_id;
     15    private static $auto_update_plugins;
    1516    private $login_instance;
    1617    public static $products_raw_data = array();
     
    719720        self::$addons_state = array();
    720721        self::$themes_state = array();
     722        self::$auto_update_plugins = get_site_option('auto_update_plugins', array());
    721723
    722724        $site_installed_plugins = get_plugins();
     
    783785                $state->set_tenweb_product(false);
    784786                $state->set_other_wp_info($file_name, $installed_plugin, self::get_installed_plugins_wp_info());
     787                //$state->set_is_autoupdate_enabled(in_array($file_name, self::$auto_update_plugins, true));
     788
    785789
    786790                self::$plugins_state[] = $state;
     
    924928                $state->set_is_paid($plugin_data['current_version']);
    925929                $state->set_other_wp_info($plugin_slug, $installed_plugin, $installed_plugins_info);
     930                //$state->set_is_autoupdate_enabled(in_array($plugin_slug, self::$auto_update_plugins, true));
    926931
    927932
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/src/ProductState.php

    r2944643 r3196166  
    3131    public $theme_errors = array();//@type array
    3232
     33    public $is_autoupdate_enabled = 0;//@type int [0,1]
     34
    3335    /**
    3436     * ProductState constructor.
     
    154156    }
    155157
     158    public function set_is_autoupdate_enabled($is_autoupdate_enabled)
     159    {
     160        $this->is_autoupdate_enabled = (int)$is_autoupdate_enabled;
     161    }
     162
    156163    public function get_info()
    157164    {
    158165        $state = array(
    159             'product_id'     => $this->product_id,
    160             'slug'           => $this->slug,
    161             'title'          => $this->title,
    162             'description'    => $this->description,
    163             'version'        => $this->version,
    164             'installed'      => $this->installed ? 1 : 0,
    165             'active'         => $this->active ? 1 : 0,
    166             'network_active' => $this->network_active ? 1 : 0,
    167             'is_paid'        => $this->is_paid ? 1 : 0,
    168             'screenshot'     => $this->screenshot,
    169             'tenweb_product' => $this->tenweb_product ? 1 : 0,
    170             'author'         => $this->author,
    171             'repo_version'   => $this->repo_version
     166            'product_id'         => $this->product_id,
     167            'slug'               => $this->slug,
     168            'title'              => $this->title,
     169            'description'        => $this->description,
     170            'version'            => $this->version,
     171            'installed'          => $this->installed ? 1 : 0,
     172            'active'             => $this->active ? 1 : 0,
     173            'network_active'     => $this->network_active ? 1 : 0,
     174            'is_paid'            => $this->is_paid ? 1 : 0,
     175            'screenshot'         => $this->screenshot,
     176            'tenweb_product'     => $this->tenweb_product ? 1 : 0,
     177            'author'             => $this->author,
     178            'repo_version'       => $this->repo_version,
     179            'autoupdate_enabled' => $this->is_autoupdate_enabled
    172180        );
    173181
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/src/config/autoconnect_production.env.php

    r3111518 r3196166  
    9797    "us-west2" => "Los Angeles, California, USA"
    9898);
     99
     100global $tenweb_firebaseConfig;
     101
     102$tenweb_firebaseConfig = [
     103    'apiKey' => "AIzaSyBP3zPfSTMZxUqJsfmHMd6EJV2aITZ-3FY",
     104    'authDomain'=> "copilot-dev-64f1d.firebaseapp.com",
     105    'projectId'=> "copilot-dev-64f1d",
     106    'storageBucket'=> "copilot-dev-64f1d.appspot.com",
     107    'messagingSenderId'=> "101114320744",
     108    'appId'=> "1:101114320744:web:3a98c2010c93b6e7a0a8dd",
     109    'databaseURL' => "https://copilot-dev-64f1d.firebaseio.com",
     110];
     111
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/src/config/local.env.php

    r3111518 r3196166  
    9797    "us-west2" => "Los Angeles, California, USA"
    9898);
     99
     100global $tenweb_firebaseConfig;
     101
     102$tenweb_firebaseConfig = [
     103    'apiKey' => "AIzaSyBP3zPfSTMZxUqJsfmHMd6EJV2aITZ-3FY",
     104    'authDomain'=> "copilot-dev-64f1d.firebaseapp.com",
     105    'projectId'=> "copilot-dev-64f1d",
     106    'storageBucket'=> "copilot-dev-64f1d.appspot.com",
     107    'messagingSenderId'=> "101114320744",
     108    'appId'=> "1:101114320744:web:3a98c2010c93b6e7a0a8dd",
     109    'databaseURL' => "https://copilot-dev-64f1d.firebaseio.com",
     110];
     111
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/src/config/production.env.php

    r3111518 r3196166  
    9797    "us-west2" => "Los Angeles, California, USA"
    9898);
     99
     100global $tenweb_firebaseConfig;
     101
     102$tenweb_firebaseConfig = [
     103    'apiKey' => "AIzaSyADDO7CWkI1jd8HUXFVs7AwSt4z52i27Tc",
     104    'authDomain'=> "copilote-prod-2096c.firebaseapp.com",
     105    'projectId'=> "copilote-prod-2096c",
     106    'storageBucket'=> "copilote-prod-2096c.appspot.com",
     107    'messagingSenderId'=> "698503034199",
     108    'appId'=> "1:698503034199:web:2282f40a461d9d95217a12",
     109    'databaseURL' => "https://copilote-prod-2096c.firebaseio.com",
     110];
     111
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/src/config/staging.env.php

    r3111518 r3196166  
    9696    "us-west2" => "Los Angeles, California, USA"
    9797);
     98
     99global $tenweb_firebaseConfig;
     100
     101$tenweb_firebaseConfig = [
     102    'apiKey' => "AIzaSyBP3zPfSTMZxUqJsfmHMd6EJV2aITZ-3FY",
     103    'authDomain'=> "copilot-dev-64f1d.firebaseapp.com",
     104    'projectId'=> "copilot-dev-64f1d",
     105    'storageBucket'=> "copilot-dev-64f1d.appspot.com",
     106    'messagingSenderId'=> "101114320744",
     107    'appId'=> "1:101114320744:web:3a98c2010c93b6e7a0a8dd",
     108    'databaseURL' => "https://copilot-dev-64f1d.firebaseio.com",
     109];
     110
  • tenweb-speed-optimizer/trunk/vendor/10web/authorization/src/config/testing.env.php

    r3111518 r3196166  
    9797    "us-west2" => "Los Angeles, California, USA"
    9898);
     99
     100global $tenweb_firebaseConfig;
     101
     102$tenweb_firebaseConfig = [
     103    'apiKey' => "AIzaSyBP3zPfSTMZxUqJsfmHMd6EJV2aITZ-3FY",
     104    'authDomain'=> "copilot-dev-64f1d.firebaseapp.com",
     105    'projectId'=> "copilot-dev-64f1d",
     106    'storageBucket'=> "copilot-dev-64f1d.appspot.com",
     107    'messagingSenderId'=> "101114320744",
     108    'appId'=> "1:101114320744:web:3a98c2010c93b6e7a0a8dd",
     109    'databaseURL' => "https://copilot-dev-64f1d.firebaseio.com",
     110];
  • tenweb-speed-optimizer/trunk/vendor/autoload.php

    r3170042 r3196166  
    2323require_once __DIR__ . '/composer/autoload_real.php';
    2424
    25 return ComposerAutoloaderInit124893b7c1f538d2a2a547c796c35cc0::getLoader();
     25return ComposerAutoloaderInit32d2a2df551fda474f04e8446c5df1d0::getLoader();
  • tenweb-speed-optimizer/trunk/vendor/composer/autoload_real.php

    r3170042 r3196166  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit124893b7c1f538d2a2a547c796c35cc0
     5class ComposerAutoloaderInit32d2a2df551fda474f04e8446c5df1d0
    66{
    77    private static $loader;
     
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInit124893b7c1f538d2a2a547c796c35cc0', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInit32d2a2df551fda474f04e8446c5df1d0', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
    29         spl_autoload_unregister(array('ComposerAutoloaderInit124893b7c1f538d2a2a547c796c35cc0', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInit32d2a2df551fda474f04e8446c5df1d0', 'loadClassLoader'));
    3030
    3131        require __DIR__ . '/autoload_static.php';
    32         call_user_func(\Composer\Autoload\ComposerStaticInit124893b7c1f538d2a2a547c796c35cc0::getInitializer($loader));
     32        call_user_func(\Composer\Autoload\ComposerStaticInit32d2a2df551fda474f04e8446c5df1d0::getInitializer($loader));
    3333
    3434        $loader->register(true);
    3535
    36         $filesToLoad = \Composer\Autoload\ComposerStaticInit124893b7c1f538d2a2a547c796c35cc0::$files;
     36        $filesToLoad = \Composer\Autoload\ComposerStaticInit32d2a2df551fda474f04e8446c5df1d0::$files;
    3737        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
    3838            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
  • tenweb-speed-optimizer/trunk/vendor/composer/autoload_static.php

    r3170042 r3196166  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit124893b7c1f538d2a2a547c796c35cc0
     7class ComposerStaticInit32d2a2df551fda474f04e8446c5df1d0
    88{
    99    public static $files = array (
     
    301301    {
    302302        return \Closure::bind(function () use ($loader) {
    303             $loader->prefixLengthsPsr4 = ComposerStaticInit124893b7c1f538d2a2a547c796c35cc0::$prefixLengthsPsr4;
    304             $loader->prefixDirsPsr4 = ComposerStaticInit124893b7c1f538d2a2a547c796c35cc0::$prefixDirsPsr4;
    305             $loader->prefixesPsr0 = ComposerStaticInit124893b7c1f538d2a2a547c796c35cc0::$prefixesPsr0;
    306             $loader->classMap = ComposerStaticInit124893b7c1f538d2a2a547c796c35cc0::$classMap;
     303            $loader->prefixLengthsPsr4 = ComposerStaticInit32d2a2df551fda474f04e8446c5df1d0::$prefixLengthsPsr4;
     304            $loader->prefixDirsPsr4 = ComposerStaticInit32d2a2df551fda474f04e8446c5df1d0::$prefixDirsPsr4;
     305            $loader->prefixesPsr0 = ComposerStaticInit32d2a2df551fda474f04e8446c5df1d0::$prefixesPsr0;
     306            $loader->classMap = ComposerStaticInit32d2a2df551fda474f04e8446c5df1d0::$classMap;
    307307
    308308        }, null, ClassLoader::class);
  • tenweb-speed-optimizer/trunk/vendor/composer/installed.json

    r3158582 r3196166  
    148148                "type": "git",
    149149                "url": "ssh://git@gitlab.10web.io:6202/10web-utils/tenweb-authorization.git",
    150                 "reference": "7993a20e971e04412f6c11ac81339197fa51bfd5"
    151             },
    152             "time": "2024-05-07T11:49:37+00:00",
     150                "reference": "16a820e0be9f95255404101d2d057394a5ae9d0d"
     151            },
     152            "time": "2024-10-25T11:48:14+00:00",
    153153            "default-branch": true,
    154154            "type": "library",
     
    537537                "type": "git",
    538538                "url": "https://github.com/monperrus/crawler-user-agents.git",
    539                 "reference": "6c0133b66cc2a01dc582a0efdcdddab6626b6f48"
    540             },
    541             "dist": {
    542                 "type": "zip",
    543                 "url": "https://api.github.com/repos/monperrus/crawler-user-agents/zipball/6c0133b66cc2a01dc582a0efdcdddab6626b6f48",
    544                 "reference": "6c0133b66cc2a01dc582a0efdcdddab6626b6f48",
    545                 "shasum": ""
    546             },
    547             "time": "2024-09-25T08:31:49+00:00",
     539                "reference": "e464fa7cb22920f3927cd85d7de32aeedfbf6c85"
     540            },
     541            "dist": {
     542                "type": "zip",
     543                "url": "https://api.github.com/repos/monperrus/crawler-user-agents/zipball/e464fa7cb22920f3927cd85d7de32aeedfbf6c85",
     544                "reference": "e464fa7cb22920f3927cd85d7de32aeedfbf6c85",
     545                "shasum": ""
     546            },
     547            "time": "2024-10-23T05:56:48+00:00",
    548548            "default-branch": true,
    549549            "type": "library",
     
    748748        {
    749749            "name": "symfony/filesystem",
    750             "version": "v5.4.44",
    751             "version_normalized": "5.4.44.0",
     750            "version": "v5.4.45",
     751            "version_normalized": "5.4.45.0",
    752752            "source": {
    753753                "type": "git",
    754754                "url": "https://github.com/symfony/filesystem.git",
    755                 "reference": "76c3818964e9d32be3862c9318ae3ba9aa280ddc"
    756             },
    757             "dist": {
    758                 "type": "zip",
    759                 "url": "https://api.github.com/repos/symfony/filesystem/zipball/76c3818964e9d32be3862c9318ae3ba9aa280ddc",
    760                 "reference": "76c3818964e9d32be3862c9318ae3ba9aa280ddc",
     755                "reference": "57c8294ed37d4a055b77057827c67f9558c95c54"
     756            },
     757            "dist": {
     758                "type": "zip",
     759                "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54",
     760                "reference": "57c8294ed37d4a055b77057827c67f9558c95c54",
    761761                "shasum": ""
    762762            },
     
    770770                "symfony/process": "^5.4|^6.4"
    771771            },
    772             "time": "2024-09-16T14:52:48+00:00",
     772            "time": "2024-10-22T13:05:35+00:00",
    773773            "type": "library",
    774774            "installation-source": "dist",
     
    798798            "homepage": "https://symfony.com",
    799799            "support": {
    800                 "source": "https://github.com/symfony/filesystem/tree/v5.4.44"
     800                "source": "https://github.com/symfony/filesystem/tree/v5.4.45"
    801801            },
    802802            "funding": [
  • tenweb-speed-optimizer/trunk/vendor/composer/installed.php

    r3170042 r3196166  
    44        'pretty_version' => 'dev-master',
    55        'version' => 'dev-master',
    6         'reference' => 'dffc8d926b4028a66a9756d7f78e4d5f2a040e18',
     6        'reference' => '827ab9bc91d2cf6f4db1233d0b4587c3adf317e9',
    77        'type' => 'project',
    88        'install_path' => __DIR__ . '/../../',
     
    6969            'pretty_version' => 'dev-master',
    7070            'version' => 'dev-master',
    71             'reference' => '7993a20e971e04412f6c11ac81339197fa51bfd5',
     71            'reference' => '16a820e0be9f95255404101d2d057394a5ae9d0d',
    7272            'type' => 'library',
    7373            'install_path' => __DIR__ . '/../10web/authorization',
     
    134134            'pretty_version' => 'dev-master',
    135135            'version' => 'dev-master',
    136             'reference' => '6c0133b66cc2a01dc582a0efdcdddab6626b6f48',
     136            'reference' => 'e464fa7cb22920f3927cd85d7de32aeedfbf6c85',
    137137            'type' => 'library',
    138138            'install_path' => __DIR__ . '/../monperrus/crawler-user-agents',
     
    163163            'pretty_version' => 'dev-master',
    164164            'version' => 'dev-master',
    165             'reference' => 'dffc8d926b4028a66a9756d7f78e4d5f2a040e18',
     165            'reference' => '827ab9bc91d2cf6f4db1233d0b4587c3adf317e9',
    166166            'type' => 'project',
    167167            'install_path' => __DIR__ . '/../../',
     
    179179        ),
    180180        'symfony/filesystem' => array(
    181             'pretty_version' => 'v5.4.44',
    182             'version' => '5.4.44.0',
    183             'reference' => '76c3818964e9d32be3862c9318ae3ba9aa280ddc',
     181            'pretty_version' => 'v5.4.45',
     182            'version' => '5.4.45.0',
     183            'reference' => '57c8294ed37d4a055b77057827c67f9558c95c54',
    184184            'type' => 'library',
    185185            'install_path' => __DIR__ . '/../symfony/filesystem',
  • tenweb-speed-optimizer/trunk/vendor/monperrus/crawler-user-agents/crawler-user-agents.json

    r3158582 r3196166  
    49474947    "addition_date": "2024/08/30",
    49484948    "instances": []
     4949  },
     4950  {
     4951    "pattern": "facebookcatalog\\/",
     4952    "addition_date": "2024/10/03",
     4953    "instances": [
     4954      "facebookcatalog/1.0"
     4955    ],
     4956    "url": "https://developers.facebook.com/docs/sharing/webmasters/web-crawlers"
     4957  },
     4958  {
     4959    "pattern": "meta-externalagent\\/",
     4960    "addition_date": "2024/10/03",
     4961    "instances": [
     4962      "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)",
     4963      "meta-externalagent/1.1"
     4964    ],
     4965    "url": "https://developers.facebook.com/docs/sharing/webmasters/web-crawlers"
     4966  },
     4967  {
     4968    "pattern": "meta-externalfetcher\\/",
     4969    "addition_date": "2024/10/03",
     4970    "instances": [
     4971      "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)",
     4972      "meta-externalfetcher/1.1"
     4973    ],
     4974    "url": "https://developers.facebook.com/docs/sharing/webmasters/web-crawlers"
     4975  },
     4976  {
     4977    "pattern": "AcademicBotRTU",
     4978    "addition_date": "2024/10/17",
     4979    "instances": [
     4980      "AcademicBotRTU (https://academicbot.rtu.lv; mailto:caps@rtu.lv)"
     4981    ],
     4982    "url": "https://academicbot.rtu.lv"
     4983  },
     4984  {
     4985    "pattern": "KeybaseBot",
     4986    "addition_date": "2024/10/21",
     4987    "url": "https://book.keybase.io/docs/chat/link-previews",
     4988    "instances": [
     4989      "Mozilla/5.0 (compatible; KeybaseBot; +https://keybase.io)"
     4990    ]
    49494991  }
    49504992]
  • tenweb-speed-optimizer/trunk/vendor/monperrus/crawler-user-agents/validate.go

    r3111518 r3196166  
    33import (
    44    _ "embed"
     5    "encoding/hex"
    56    "encoding/json"
    67    "fmt"
     8    "hash/maphash"
    79    "regexp"
     10    "regexp/syntax"
     11    "strconv"
     12    "strings"
    813    "time"
     14    "unicode"
    915)
    1016
     
    2733}
    2834
    29 // Private time needed to convert addition_date from/to the format used in JSON.
     35// Private type needed to convert addition_date from/to the format used in JSON.
    3036type jsonCrawler struct {
    3137    Pattern      string   `json:"pattern"`
     
    8187}()
    8288
    83 var regexps = func() []*regexp.Regexp {
    84     regexps := make([]*regexp.Regexp, len(Crawlers))
     89// analyzePattern expands a regular expression to the list of matching texts
     90// for plain search. The list is complete, i.e. iff a text matches the input
     91// pattern, then it contains at least one of the returned texts. If such a list
     92// can't be built, then the resulting list contains one element (main literal),
     93// it also returns built regexp object to run in this case. The main literal is
     94// a text that is contained in any matching text and is used to optimize search
     95// (pre-filter with this main literal before running a regexp). In the case such
     96// a main literal can't be found or the regexp is invalid, an error is returned.
     97func analyzePattern(pattern string) ([]string, *regexp.Regexp, error) {
     98    re, err := syntax.Parse(pattern, syntax.Perl)
     99    if err != nil {
     100        return nil, nil, fmt.Errorf("re %q does not compile: %w", pattern, err)
     101    }
     102    re = re.Simplify()
     103
     104    // Try to convert it to the list of literals.
     105    const maxLiterals = 100
     106    literals, ok := literalizeRegexp(re, maxLiterals)
     107    if ok {
     108        return literals, nil, nil
     109    }
     110
     111    // Fallback to using a regexp, but we need some string serving as
     112    // an indicator of its possible presence.
     113    mainLiteral := findLongestCommonLiteral(re)
     114    const minLiteralLen = 3
     115    if len(mainLiteral) < minLiteralLen {
     116        return nil, nil, fmt.Errorf("re %q does not contain sufficiently long literal to serve an indicator. The longest literal is %q", pattern, mainLiteral)
     117    }
     118
     119    return []string{mainLiteral}, regexp.MustCompile(pattern), nil
     120}
     121
     122// literalizeRegexp expands a regexp to the list of matching sub-strings.
     123// Iff a text matches the regexp, it contains at least one of the returned
     124// texts. Argument maxLiterals regulates the maximum number of patterns to
     125// return. In case of an overflow or if it is impossible to build such a list
     126// from the regexp, false is returned.
     127func literalizeRegexp(re *syntax.Regexp, maxLiterals int) (literals []string, ok bool) {
     128    switch re.Op {
     129    case syntax.OpNoMatch:
     130        return nil, true
     131
     132    case syntax.OpEmptyMatch:
     133        return []string{""}, true
     134
     135    case syntax.OpLiteral:
     136        return unwrapCase(re, []string{string(re.Rune)}, maxLiterals)
     137
     138    case syntax.OpCharClass:
     139        count := 0
     140        for i := 0; i < len(re.Rune); i += 2 {
     141            first := re.Rune[i]
     142            last := re.Rune[i+1]
     143            count += int(last - first + 1)
     144        }
     145
     146        if count > maxLiterals {
     147            return nil, false
     148        }
     149
     150        patterns := make([]string, 0, count)
     151        for i := 0; i < len(re.Rune); i += 2 {
     152            first := re.Rune[i]
     153            last := re.Rune[i+1]
     154            for r := first; r <= last; r++ {
     155                patterns = append(patterns, string([]rune{r}))
     156            }
     157        }
     158
     159        return unwrapCase(re, patterns, maxLiterals)
     160
     161    case syntax.OpAnyCharNotNL, syntax.OpAnyChar:
     162        // Not supported.
     163        return nil, false
     164
     165    case syntax.OpBeginLine, syntax.OpBeginText:
     166        return []string{"^"}, true
     167
     168    case syntax.OpEndLine, syntax.OpEndText:
     169        return []string{"$"}, true
     170
     171    case syntax.OpWordBoundary, syntax.OpNoWordBoundary:
     172        // Not supported.
     173        return nil, false
     174
     175    case syntax.OpCapture:
     176        subList, ok := literalizeRegexp(re.Sub[0], maxLiterals)
     177        if !ok {
     178            return nil, false
     179        }
     180
     181        return unwrapCase(re, subList, maxLiterals)
     182
     183    case syntax.OpStar, syntax.OpPlus:
     184        // Not supported.
     185        return nil, false
     186
     187    case syntax.OpQuest:
     188        if re.Flags&syntax.FoldCase != 0 {
     189            return nil, false
     190        }
     191
     192        subList, ok := literalizeRegexp(re.Sub[0], maxLiterals)
     193        if !ok {
     194            return nil, false
     195        }
     196        subList = append(subList, "")
     197
     198        return subList, true
     199
     200    case syntax.OpRepeat:
     201        // Not supported.
     202        return nil, false
     203
     204    case syntax.OpConcat:
     205        if re.Flags&syntax.FoldCase != 0 {
     206            return nil, false
     207        }
     208
     209        matrix := make([][]string, len(re.Sub))
     210        for i, sub := range re.Sub {
     211            subList, ok := literalizeRegexp(sub, maxLiterals)
     212            if !ok {
     213                return nil, false
     214            }
     215            matrix[i] = subList
     216        }
     217
     218        return combinations(matrix, maxLiterals)
     219
     220    case syntax.OpAlternate:
     221        results := []string{}
     222        for _, sub := range re.Sub {
     223            subList, ok := literalizeRegexp(sub, maxLiterals)
     224            if !ok {
     225                return nil, false
     226            }
     227            results = append(results, subList...)
     228        }
     229
     230        if len(results) > maxLiterals {
     231            return nil, false
     232        }
     233
     234        return unwrapCase(re, results, maxLiterals)
     235
     236    default:
     237        // Not supported.
     238        return nil, false
     239    }
     240}
     241
     242// combinations produces all combination of elements of matrix.
     243// Each sub-slice of matrix contributes one part of a resulting string.
     244// If the number of combinations is larger than maxLiterals, the function
     245// returns false.
     246func combinations(matrix [][]string, maxLiterals int) ([]string, bool) {
     247    if len(matrix) == 1 {
     248        if len(matrix[0]) > maxLiterals {
     249            return nil, false
     250        }
     251
     252        return matrix[0], true
     253    }
     254
     255    prefixes := matrix[0]
     256    suffixes, ok := combinations(matrix[1:], maxLiterals)
     257    if !ok {
     258        return nil, false
     259    }
     260
     261    size := len(prefixes) * len(suffixes)
     262    if size > maxLiterals {
     263        return nil, false
     264    }
     265
     266    results := make([]string, 0, size)
     267    for _, prefix := range prefixes {
     268        for _, suffix := range suffixes {
     269            results = append(results, prefix+suffix)
     270        }
     271    }
     272
     273    return results, true
     274}
     275
     276// unwrapCase takes the regexp and the list of patterns expanded from it and
     277// further expands it for a case-insensitive regexp, if needed. Argument
     278// maxLiterals regulates the maximum number of patterns to return. In case of an
     279// overflow, false is returned.
     280func unwrapCase(re *syntax.Regexp, patterns []string, maxLiterals int) ([]string, bool) {
     281    if re.Flags&syntax.FoldCase == 0 {
     282        return patterns, true
     283    }
     284
     285    results := []string{}
     286    for _, pattern := range patterns {
     287        matrix := make([][]string, len(pattern))
     288        for i, r := range pattern {
     289            upper := unicode.ToUpper(r)
     290            lower := unicode.ToLower(r)
     291            matrix[i] = []string{
     292                string([]rune{upper}),
     293                string([]rune{lower}),
     294            }
     295        }
     296
     297        patterns, ok := combinations(matrix, maxLiterals)
     298        if !ok {
     299            return nil, false
     300        }
     301
     302        results = append(results, patterns...)
     303        if len(results) > maxLiterals {
     304            return nil, false
     305        }
     306    }
     307
     308    return results, true
     309}
     310
     311// findLongestCommonLiteral finds the longest common literal in the regexp. It's
     312// such a string which is contained in any text matching the regexp. If such a
     313// literal can't be found, it returns an empty string.
     314func findLongestCommonLiteral(re *syntax.Regexp) string {
     315    if re.Flags&syntax.FoldCase != 0 {
     316        return ""
     317    }
     318
     319    switch re.Op {
     320    case syntax.OpNoMatch, syntax.OpEmptyMatch:
     321        return ""
     322
     323    case syntax.OpLiteral:
     324        return string(re.Rune)
     325
     326    case syntax.OpCharClass, syntax.OpAnyCharNotNL, syntax.OpAnyChar:
     327        return ""
     328
     329    case syntax.OpBeginLine, syntax.OpBeginText:
     330        return "^"
     331
     332    case syntax.OpEndLine, syntax.OpEndText:
     333        return "$"
     334
     335    case syntax.OpWordBoundary, syntax.OpNoWordBoundary:
     336        return ""
     337
     338    case syntax.OpCapture:
     339        return findLongestCommonLiteral(re.Sub[0])
     340
     341    case syntax.OpStar:
     342        return ""
     343
     344    case syntax.OpPlus:
     345        return findLongestCommonLiteral(re.Sub[0])
     346
     347    case syntax.OpQuest:
     348        return ""
     349
     350    case syntax.OpRepeat:
     351        if re.Min >= 1 {
     352            return findLongestCommonLiteral(re.Sub[0])
     353        }
     354
     355        return ""
     356
     357    case syntax.OpConcat:
     358        longest := ""
     359        for _, sub := range re.Sub {
     360            str := findLongestCommonLiteral(sub)
     361            if len(str) > len(longest) {
     362                longest = str
     363            }
     364        }
     365
     366        return longest
     367
     368    case syntax.OpAlternate:
     369        return ""
     370
     371    default:
     372        return ""
     373    }
     374}
     375
     376type regexpPattern struct {
     377    re    *regexp.Regexp
     378    index int
     379}
     380
     381type matcher struct {
     382    replacer *strings.Replacer
     383    regexps  []regexpPattern
     384}
     385
     386var uniqueToken = hex.EncodeToString((&maphash.Hash{}).Sum(nil))
     387
     388const (
     389    uniqueTokenLen = 2 * 8
     390    numLen         = 5
     391    literalLabel   = '-'
     392    regexpLabel    = '*'
     393)
     394
     395var m = func() matcher {
     396    if len(uniqueToken) != uniqueTokenLen {
     397        panic("len(uniqueToken) != uniqueTokenLen")
     398    }
     399
     400    regexps := []regexpPattern{}
     401    oldnew := make([]string, 0, len(Crawlers)*2)
     402
     403    // Put re-based patterns to the end to prevent AdsBot-Google from
     404    // shadowing AdsBot-Google-Mobile.
     405    var oldnew2 []string
     406
    85407    for i, crawler := range Crawlers {
    86         regexps[i] = regexp.MustCompile(crawler.Pattern)
    87     }
    88     return regexps
     408        literals, re, err := analyzePattern(crawler.Pattern)
     409        if err != nil {
     410            panic(err)
     411        }
     412
     413        label := literalLabel
     414        num := i
     415        if re != nil {
     416            label = regexpLabel
     417            num = len(regexps)
     418            regexps = append(regexps, regexpPattern{
     419                re:    re,
     420                index: i,
     421            })
     422        }
     423
     424        replaceWith := fmt.Sprintf(" %s%c%0*d ", uniqueToken, label, numLen, num)
     425
     426        for _, literal := range literals {
     427            if re != nil {
     428                oldnew2 = append(oldnew2, literal, replaceWith)
     429            } else {
     430                oldnew = append(oldnew, literal, replaceWith)
     431            }
     432        }
     433    }
     434    oldnew = append(oldnew, oldnew2...)
     435
     436    // Allocate another array with regexps of exact size to save memory.
     437    regexps2 := make([]regexpPattern, len(regexps))
     438    copy(regexps2, regexps)
     439
     440    r := strings.NewReplacer(oldnew...)
     441    r.Replace("") // To cause internal build process.
     442
     443    return matcher{
     444        replacer: r,
     445        regexps:  regexps2,
     446    }
    89447}()
    90448
    91449// Returns if User Agent string matches any of crawler patterns.
    92450func IsCrawler(userAgent string) bool {
    93     for _, re := range regexps {
    94         if re.MatchString(userAgent) {
     451    // This code is mostly copy-paste of MatchingCrawlers,
     452    // but with early exit logic, so it works a but faster.
     453
     454    text := "^" + userAgent + "$"
     455    replaced := m.replacer.Replace(text)
     456    if replaced == text {
     457        return false
     458    }
     459
     460    for {
     461        uniquePos := strings.Index(replaced, uniqueToken)
     462        if uniquePos == -1 {
     463            break
     464        }
     465
     466        start := uniquePos + uniqueTokenLen + 1
     467        if start+numLen >= len(replaced) {
     468            panic("corrupt replaced: " + replaced)
     469        }
     470
     471        label := replaced[start-1]
     472        switch label {
     473        case literalLabel:
    95474            return true
    96         }
    97     }
     475        case regexpLabel:
     476            // Rare case. Run regexp to confirm the match.
     477            indexStr := replaced[start : start+numLen]
     478            index, err := strconv.Atoi(indexStr)
     479            if err != nil {
     480                panic("corrupt replaced: " + replaced)
     481            }
     482            rp := m.regexps[index]
     483            if rp.re.MatchString(userAgent) {
     484                return true
     485            }
     486        default:
     487            panic("corrupt replaced: " + replaced)
     488        }
     489
     490        replaced = replaced[start+numLen:]
     491    }
     492
    98493    return false
    99494}
     
    101496// Finds all crawlers matching the User Agent and returns the list of their indices in Crawlers.
    102497func MatchingCrawlers(userAgent string) []int {
     498    text := "^" + userAgent + "$"
     499    replaced := m.replacer.Replace(text)
     500    if replaced == text {
     501        return []int{}
     502    }
     503
    103504    indices := []int{}
    104     for i, re := range regexps {
    105         if re.MatchString(userAgent) {
    106             indices = append(indices, i)
    107         }
    108     }
     505    for {
     506        uniquePos := strings.Index(replaced, uniqueToken)
     507        if uniquePos == -1 {
     508            break
     509        }
     510
     511        start := uniquePos + uniqueTokenLen + 1
     512        if start+numLen >= len(replaced) {
     513            panic("corrupt replaced: " + replaced)
     514        }
     515        indexStr := replaced[start : start+numLen]
     516        index, err := strconv.Atoi(indexStr)
     517        if err != nil {
     518            panic("corrupt replaced: " + replaced)
     519        }
     520
     521        label := replaced[start-1]
     522        switch label {
     523        case literalLabel:
     524            indices = append(indices, index)
     525        case regexpLabel:
     526            // Rare case. Run regexp to confirm the match.
     527            rp := m.regexps[index]
     528            if rp.re.MatchString(userAgent) {
     529                indices = append(indices, rp.index)
     530            }
     531        default:
     532            panic("corrupt replaced: " + replaced)
     533        }
     534
     535        replaced = replaced[start+numLen:]
     536    }
     537
    109538    return indices
    110539}
  • tenweb-speed-optimizer/trunk/vendor/monperrus/crawler-user-agents/validate_test.go

    r3111518 r3196166  
    55    "fmt"
    66    "net/http"
     7    "reflect"
     8    "regexp/syntax"
     9    "sort"
     10    "strings"
    711    "testing"
    812)
     13
     14// TestAnalyzePattern tests analyzePattern function on many cases, including
     15// edge cases.
     16func TestAnalyzePattern(t *testing.T) {
     17    cases := []struct {
     18        input            string
     19        wantError        string
     20        wantPatterns     []string
     21        wantRe           bool
     22        shouldMatchRe    []string
     23        shouldNotMatchRe []string
     24    }{
     25        {
     26            input:        "simple phrase",
     27            wantPatterns: []string{"simple phrase"},
     28        },
     29        {
     30            input:        "^begin anchor",
     31            wantPatterns: []string{"^begin anchor"},
     32        },
     33        {
     34            input:        "end anchor$",
     35            wantPatterns: []string{"end anchor$"},
     36        },
     37        {
     38            input:        "^both anchors$",
     39            wantPatterns: []string{"^both anchors$"},
     40        },
     41        {
     42            input:        "(alter|nation)",
     43            wantPatterns: []string{"alter", "nation"},
     44        },
     45        {
     46            input:            "too many [aA][lL][tT][eE][rR][nN][aA][tT][iI][oO][nN][sS]",
     47            wantPatterns:     []string{"too many "},
     48            wantRe:           true,
     49            shouldMatchRe:    []string{"too many ALTERNATIONs"},
     50            shouldNotMatchRe: []string{"too many combinations "},
     51        },
     52        {
     53            input: "(alter|nation) concatenation (alter|nation)",
     54            wantPatterns: []string{
     55                "alter concatenation alter",
     56                "alter concatenation nation",
     57                "nation concatenation alter",
     58                "nation concatenation nation",
     59            },
     60        },
     61        {
     62            input: "clas[sS] of [c]haract[eiu]rs",
     63            wantPatterns: []string{
     64                "clasS of characters",
     65                "clasS of charactirs",
     66                "clasS of characturs",
     67                "class of characters",
     68                "class of charactirs",
     69                "class of characturs",
     70            },
     71        },
     72        {
     73            input: "ranges [0-3]x[a-c]",
     74            wantPatterns: []string{
     75                "ranges 0xa", "ranges 0xb", "ranges 0xc",
     76                "ranges 1xa", "ranges 1xb", "ranges 1xc",
     77                "ranges 2xa", "ranges 2xb", "ranges 2xc",
     78                "ranges 3xa", "ranges 3xb", "ranges 3xc",
     79            },
     80        },
     81        {
     82            input:        "Quest?",
     83            wantPatterns: []string{"Ques", "Quest"},
     84        },
     85        {
     86            input:        "Q?ue(st)?",
     87            wantPatterns: []string{"Que", "Quest", "ue", "uest"},
     88        },
     89        {
     90            input:            "too many combinations [0-9][a-z]",
     91            wantPatterns:     []string{"too many combinations "},
     92            wantRe:           true,
     93            shouldMatchRe:    []string{"too many combinations 0a"},
     94            shouldNotMatchRe: []string{"too many combinations "},
     95        },
     96        {
     97            input:            "negation in char class [^x]",
     98            wantPatterns:     []string{"negation in char class "},
     99            wantRe:           true,
     100            shouldMatchRe:    []string{"negation in char class y"},
     101            shouldNotMatchRe: []string{"negation in char class x"},
     102        },
     103        {
     104            input:            "any char .",
     105            wantPatterns:     []string{"any char "},
     106            wantRe:           true,
     107            shouldMatchRe:    []string{"any char x"},
     108            shouldNotMatchRe: []string{"any char_x"},
     109        },
     110        {
     111            input:            `word \boundary`,
     112            wantPatterns:     []string{"oundary"},
     113            wantRe:           true,
     114            shouldMatchRe:    []string{"word oundary"},
     115            shouldNotMatchRe: []string{"word boundary"},
     116        },
     117        {
     118            input:            "asterisk*",
     119            wantPatterns:     []string{"asteris"},
     120            wantRe:           true,
     121            shouldMatchRe:    []string{"asteris", "asterisk", "asteriskk"},
     122            shouldNotMatchRe: []string{"asterik"},
     123        },
     124        {
     125            input:            "plus+",
     126            wantPatterns:     []string{"plu"},
     127            wantRe:           true,
     128            shouldMatchRe:    []string{"plus", "pluss"},
     129            shouldNotMatchRe: []string{"plu"},
     130        },
     131        {
     132            input:        "repeat{3,5}$",
     133            wantPatterns: []string{"repeattt$", "repeatttt$", "repeattttt$"},
     134        },
     135        {
     136            input:            "repeat{1,120}$",
     137            wantPatterns:     []string{"repea"},
     138            wantRe:           true,
     139            shouldMatchRe:    []string{"repeattt", "repeatttt", "repeattttt"},
     140            shouldNotMatchRe: []string{"repea5"},
     141        },
     142        {
     143            input:     "broken re[",
     144            wantError: "does not compile",
     145        },
     146        {
     147            input:     "n?o? ?l?o?n?g? ?l?i?t?e?r?a?l?",
     148            wantError: "does not contain sufficiently long literal",
     149        },
     150    }
     151
     152    for _, tc := range cases {
     153        tc := tc
     154
     155        t.Run(tc.input, func(t *testing.T) {
     156            gotPatterns, re, err := analyzePattern(tc.input)
     157            if tc.wantError != "" {
     158                if err == nil {
     159                    t.Fatalf("expected to get an error, got success")
     160                }
     161                if !strings.Contains(err.Error(), tc.wantError) {
     162                    t.Fatalf("the error returned must contain text %q, got %q", tc.wantError, err.Error())
     163                }
     164
     165                return
     166            }
     167
     168            if err != nil {
     169                t.Fatalf("unexpected error: %v", err)
     170            }
     171
     172            sort.Strings(tc.wantPatterns)
     173            sort.Strings(gotPatterns)
     174            if !reflect.DeepEqual(tc.wantPatterns, gotPatterns) {
     175                t.Fatalf("returned list of patterns (%#v) does not match the expected value (%#v)", gotPatterns, tc.wantPatterns)
     176            }
     177
     178            if !tc.wantRe {
     179                if re != nil {
     180                    t.Fatalf("unexpectedly got a re")
     181                }
     182
     183                return
     184            }
     185
     186            if re == nil {
     187                t.Fatalf("expected to get a re, got nil")
     188            }
     189            for _, text := range tc.shouldMatchRe {
     190                if !re.MatchString(text) {
     191                    t.Fatalf("test %q must match against the re, but it doesn't", text)
     192                }
     193            }
     194            for _, text := range tc.shouldNotMatchRe {
     195                if re.MatchString(text) {
     196                    t.Fatalf("test %q must not match against the re, but it does", text)
     197                }
     198            }
     199        })
     200    }
     201}
     202
     203// TestLiteralizeRegexp tests expansion of a regexp to a list of literals.
     204func TestLiteralizeRegexp(t *testing.T) {
     205    cases := []struct {
     206        input        string
     207        maxLiterals  int
     208        wantOutput   []string
     209        wantOverflow bool
     210    }{
     211        {
     212            input:       "simple phrase",
     213            maxLiterals: 100,
     214            wantOutput:  []string{"simple phrase"},
     215        },
     216        {
     217            input:       "cases [1-2x-z]",
     218            maxLiterals: 100,
     219            wantOutput:  []string{"cases 1", "cases 2", "cases x", "cases y", "cases z"},
     220        },
     221        {
     222            input:       "[Ii]gnore case",
     223            maxLiterals: 100,
     224            wantOutput:  []string{"Ignore case", "ignore case"},
     225        },
     226        {
     227            input:        "overflow [1-2x-z]",
     228            maxLiterals:  2,
     229            wantOverflow: true,
     230        },
     231    }
     232
     233    for _, tc := range cases {
     234        tc := tc
     235
     236        t.Run(tc.input, func(t *testing.T) {
     237            re, err := syntax.Parse(tc.input, syntax.Perl)
     238            if err != nil {
     239                t.Fatalf("failed to parse regexp %q: %v", tc.input, err)
     240            }
     241
     242            gotPatterns, ok := literalizeRegexp(re, tc.maxLiterals)
     243            if tc.wantOverflow {
     244                if ok {
     245                    t.Fatalf("expected to get an overflow, got success")
     246                }
     247
     248                return
     249            }
     250
     251            if !ok {
     252                t.Fatalf("unexpected overflow")
     253            }
     254
     255            sort.Strings(tc.wantOutput)
     256            sort.Strings(gotPatterns)
     257            if !reflect.DeepEqual(tc.wantOutput, gotPatterns) {
     258                t.Fatalf("returned list of patterns (%#v) does not match the expected value (%#v)", gotPatterns, tc.wantOutput)
     259            }
     260        })
     261    }
     262}
     263
     264// TestCombinations tests combinations() function.
     265func TestCombinations(t *testing.T) {
     266    cases := []struct {
     267        name         string
     268        input        [][]string
     269        maxLiterals  int
     270        wantOutput   []string
     271        wantOverflow bool
     272    }{
     273        {
     274            name:        "1x1",
     275            input:       [][]string{{"A"}, {"B"}},
     276            maxLiterals: 100,
     277            wantOutput:  []string{"AB"},
     278        },
     279        {
     280            name:        "0x1",
     281            input:       [][]string{{}, {"B"}},
     282            maxLiterals: 100,
     283            wantOutput:  []string{},
     284        },
     285        {
     286            name:        "1x2",
     287            input:       [][]string{{"A"}, {"1", "2"}},
     288            maxLiterals: 100,
     289            wantOutput:  []string{"A1", "A2"},
     290        },
     291        {
     292            name:        "2x2",
     293            input:       [][]string{{"A", "B"}, {"1", "2"}},
     294            maxLiterals: 100,
     295            wantOutput:  []string{"A1", "A2", "B1", "B2"},
     296        },
     297        {
     298            name:        "empty string as an option",
     299            input:       [][]string{{"A", ""}, {"1", "2"}},
     300            maxLiterals: 100,
     301            wantOutput:  []string{"A1", "A2", "1", "2"},
     302        },
     303        {
     304            name:         "overflow",
     305            input:        [][]string{{"A", "B"}, {"1", "2"}},
     306            maxLiterals:  3,
     307            wantOverflow: true,
     308        },
     309    }
     310
     311    for _, tc := range cases {
     312        tc := tc
     313
     314        t.Run(tc.name, func(t *testing.T) {
     315            gotPatterns, ok := combinations(tc.input, tc.maxLiterals)
     316            if tc.wantOverflow {
     317                if ok {
     318                    t.Fatalf("expected to get an overflow, got success")
     319                }
     320
     321                return
     322            }
     323
     324            if !ok {
     325                t.Fatalf("unexpected overflow")
     326            }
     327
     328            sort.Strings(tc.wantOutput)
     329            sort.Strings(gotPatterns)
     330            if !reflect.DeepEqual(tc.wantOutput, gotPatterns) {
     331                t.Fatalf("returned list of patterns (%#v) does not match the expected value (%#v)", gotPatterns, tc.wantOutput)
     332            }
     333        })
     334    }
     335}
     336
     337// TestUnwrapCase tests unwrapping literals of case-insensitive regexps.
     338func TestUnwrapCase(t *testing.T) {
     339    cases := []struct {
     340        name          string
     341        ignoreCase    bool
     342        inputPatterns []string
     343        maxLiterals   int
     344        wantOutput    []string
     345        wantOverflow  bool
     346    }{
     347        {
     348            name:          "simple phrase",
     349            inputPatterns: []string{"simple phrase"},
     350            maxLiterals:   100,
     351            wantOutput:    []string{"simple phrase"},
     352        },
     353        {
     354            name:          "ignore case",
     355            ignoreCase:    true,
     356            inputPatterns: []string{"i"},
     357            maxLiterals:   100,
     358            wantOutput:    []string{"i", "I"},
     359        },
     360        {
     361            name:          "ignore case two letters",
     362            ignoreCase:    true,
     363            inputPatterns: []string{"ic"},
     364            maxLiterals:   100,
     365            wantOutput:    []string{"IC", "Ic", "iC", "ic"},
     366        },
     367        {
     368            name:          "ignore case two words",
     369            ignoreCase:    true,
     370            inputPatterns: []string{"i", "c"},
     371            maxLiterals:   100,
     372            wantOutput:    []string{"C", "I", "c", "i"},
     373        },
     374        {
     375            name:          "ignore case overflow",
     376            ignoreCase:    true,
     377            inputPatterns: []string{"long text"},
     378            maxLiterals:   100,
     379            wantOverflow:  true,
     380        },
     381    }
     382
     383    for _, tc := range cases {
     384        tc := tc
     385
     386        t.Run(tc.name, func(t *testing.T) {
     387            re := &syntax.Regexp{}
     388            if tc.ignoreCase {
     389                re.Flags = syntax.FoldCase
     390            }
     391
     392            gotPatterns, ok := unwrapCase(re, tc.inputPatterns, tc.maxLiterals)
     393            if tc.wantOverflow {
     394                if ok {
     395                    t.Fatalf("expected to get an overflow, got success")
     396                }
     397
     398                return
     399            }
     400
     401            if !ok {
     402                t.Fatalf("unexpected overflow")
     403            }
     404
     405            sort.Strings(tc.wantOutput)
     406            sort.Strings(gotPatterns)
     407            if !reflect.DeepEqual(tc.wantOutput, gotPatterns) {
     408                t.Fatalf("returned list of patterns (%#v) does not match the expected value (%#v)", gotPatterns, tc.wantOutput)
     409            }
     410        })
     411    }
     412}
     413
     414// TestFindLongestCommonLiteral tests finding longest literal in a regexp.
     415func TestFindLongestCommonLiteral(t *testing.T) {
     416    cases := []struct {
     417        input      string
     418        wantOutput string
     419    }{
     420        {
     421            input:      "simple phrase",
     422            wantOutput: "simple phrase",
     423        },
     424        {
     425            input:      "simple (phrase)?",
     426            wantOutput: "simple ",
     427        },
     428        {
     429            input:      "[iI]",
     430            wantOutput: "",
     431        },
     432        {
     433            input:      "[i]b",
     434            wantOutput: "ib",
     435        },
     436        {
     437            input:      "simple (phrase)+",
     438            wantOutput: "simple ",
     439        },
     440        {
     441            input:      "a*",
     442            wantOutput: "",
     443        },
     444        {
     445            input:      "(abc)|(ab)",
     446            wantOutput: "",
     447        },
     448    }
     449
     450    for _, tc := range cases {
     451        tc := tc
     452
     453        t.Run(tc.input, func(t *testing.T) {
     454            re, err := syntax.Parse(tc.input, syntax.Perl)
     455            if err != nil {
     456                t.Fatalf("failed to parse regexp %q: %v", tc.input, err)
     457            }
     458
     459            gotOutput := findLongestCommonLiteral(re)
     460
     461            if gotOutput != tc.wantOutput {
     462                t.Fatalf("returned value (%q) does not match the expected value (%q)", gotOutput, tc.wantOutput)
     463            }
     464        })
     465    }
     466}
    9467
    10468func contains(list []int, value int) bool {
  • tenweb-speed-optimizer/trunk/vendor/symfony/filesystem/Filesystem.php

    r3158582 r3196166  
    4545
    4646        $doCopy = true;
    47         if (!$overwriteNewerFiles && null === parse_url($originFile, \PHP_URL_HOST) && is_file($targetFile)) {
     47        if (!$overwriteNewerFiles && !parse_url($originFile, \PHP_URL_HOST) && is_file($targetFile)) {
    4848            $doCopy = filemtime($originFile) > filemtime($targetFile);
    4949        }
     
    235235     *
    236236     * This method always throws on Windows, as the underlying PHP function is not supported.
     237     *
    237238     * @see https://www.php.net/chown
    238239     *
     
    265266     *
    266267     * This method always throws on Windows, as the underlying PHP function is not supported.
     268     *
    267269     * @see https://www.php.net/chgrp
    268270     *
Note: See TracChangeset for help on using the changeset viewer.