$id, 'name' => Text::substr( $name, 0, 40 ), 'type' => isset( $seg['type'] ) && self::TYPE_PAGE === $seg['type'] ? self::TYPE_PAGE : self::TYPE_QUERY, 'mode' => isset( $seg['mode'] ) && self::MODE_REGEX === $seg['mode'] ? self::MODE_REGEX : self::MODE_CONTAINS, 'value' => Text::substr( $value, 0, self::MAX_VALUE ), ); if ( self::MODE_REGEX === $item['mode'] && ! self::valid_regex( $item['value'] ) ) { continue; } $out[] = $item; if ( count( $out ) >= self::MAX_SEGMENTS ) { break; } } return $out; } /** * Whether a regular expression is safe to send to Google. * * Google uses RE2, which has no look-arounds or back-references, so * those are rejected up front rather than failing at Google. * * @param string $pattern Pattern. * @return bool */ public static function valid_regex( $pattern ) { if ( '' === $pattern || preg_match( '/\(\?[=!<]|\\\\[1-9]/', $pattern ) ) { return false; } // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Testing user input; a warning means invalid. return false !== @preg_match( '~' . str_replace( '~', '\~', $pattern ) . '~u', '' ); } /** * RE2 pattern for a segment, without flags. * * @param array $segment Segment. * @return string */ public static function pattern( array $segment ) { if ( self::MODE_REGEX === $segment['mode'] ) { return $segment['value']; } $terms = array_filter( array_map( 'trim', preg_split( '/[,\n]+/', $segment['value'] ) ) ); $terms = array_map( static function ( $term ) { return preg_replace( '/[.\\\\+*?()|\[\]{}^$]/', '\\\\$0', $term ); }, $terms ); return '(' . implode( '|', $terms ) . ')'; } /** * Ready-made segments offered in the site editor. * * @return array[] */ public static function presets() { $presets = array( array( 'label' => __( 'Brand searches', 'turtlereport' ), 'name' => __( 'Brand', 'turtlereport' ), 'type' => self::TYPE_QUERY, 'mode' => self::MODE_CONTAINS, 'value' => '', 'hint' => __( 'Enter your brand and product names, separated by commas.', 'turtlereport' ), ), array( 'label' => __( 'Commercial intent', 'turtlereport' ), 'name' => __( 'Commercial', 'turtlereport' ), 'type' => self::TYPE_QUERY, 'mode' => self::MODE_CONTAINS, 'value' => 'buy, price, pricing, cost, cheap, quote, hire, near me, best, top, review, vs, compare, alternative, service, company, agency, software, tool, discount, deal, order, book, demo, trial', 'hint' => '', ), array( 'label' => __( 'Informational', 'turtlereport' ), 'name' => __( 'Informational', 'turtlereport' ), 'type' => self::TYPE_QUERY, 'mode' => self::MODE_CONTAINS, 'value' => 'how, what, why, when, where, who, guide, tutorial, tips, ideas, examples, meaning, definition, learn, template', 'hint' => '', ), array( 'label' => __( 'Blog pages', 'turtlereport' ), 'name' => __( 'Blog', 'turtlereport' ), 'type' => self::TYPE_PAGE, 'mode' => self::MODE_CONTAINS, 'value' => '/blog/', 'hint' => __( 'Change /blog/ if your articles live elsewhere, e.g. /resources/ or /news/.', 'turtlereport' ), ), array( 'label' => __( 'Product or service pages', 'turtlereport' ), 'name' => __( 'Product & service pages', 'turtlereport' ), 'type' => self::TYPE_PAGE, 'mode' => self::MODE_CONTAINS, 'value' => '/services/, /product/, /products/, /pricing, /features', 'hint' => '', ), ); /** * Filters the segment presets offered in the site editor. * * @param array[] $presets Presets with label, name, type, mode, value and hint. */ return (array) apply_filters( 'turtlereport_segment_presets', $presets ); } }