Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion scripts/copyicons.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

use craft\helpers\Search;
declare(strict_types=1);

use CraftCms\Cms\Support\Search;

require dirname(__DIR__).'/vendor/autoload.php';

Expand Down
2 changes: 1 addition & 1 deletion src/Field/Money.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
use craft\gql\types\Money as MoneyType;
use craft\helpers\Cp;
use craft\helpers\Db;
use craft\helpers\MoneyHelper;
use craft\validators\MoneyValidator;
use CraftCms\Cms\Field\Contracts\CrossSiteCopyableFieldInterface;
use CraftCms\Cms\Field\Contracts\InlineEditableFieldInterface;
use CraftCms\Cms\Field\Contracts\MergeableFieldInterface;
use CraftCms\Cms\Field\Contracts\SortableFieldInterface;
use CraftCms\Cms\Support\Facades\I18N;
use CraftCms\Cms\Support\Money as MoneyHelper;
use GraphQL\Type\Definition\Type;
use Money\Currencies\ISOCurrencies;
use Money\Currency;
Expand Down
101 changes: 101 additions & 0 deletions src/Support/Money.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

declare(strict_types=1);

namespace CraftCms\Cms\Support;

use CraftCms\Cms\Support\Facades\I18N;
use Money\Currencies\ISOCurrencies;
use Money\Currency;
use Money\Formatter\DecimalMoneyFormatter;
use Money\Formatter\IntlMoneyFormatter;
use Money\Money as MoneyLibrary;
use Money\Parser\DecimalMoneyParser;
use Money\Parser\IntlMoneyParser;
use NumberFormatter;

final class Money
{
private static ISOCurrencies $isoCurrencies;

public static function toMoney(mixed $value): MoneyLibrary|false
{
if ($value instanceof MoneyLibrary) {
return $value;
}

if (! is_array($value) || empty($value) || (! array_key_exists('value', $value) || ! array_key_exists('currency', $value))) {
return false;
}

if (isset($value['locale'])) {
$value['value'] = I18N::normalizeNumber($value['value'], $value['locale']);
}

$currency = ! $value['currency'] instanceof Currency ? new Currency($value['currency']) : $value['currency'];

return new DecimalMoneyParser(self::getIsoCurrencies())->parse(
money: (string) $value['value'],
fallbackCurrency: $currency,
);
}

public static function toDecimal(mixed $value): string|false
{
if (! $value instanceof MoneyLibrary) {
return false;
}

return new DecimalMoneyFormatter(self::getIsoCurrencies())->format($value);
}

public static function toString(mixed $value, ?string $formatLocale = null): string|false
{
if (is_string($value)) {
return $value;
}

if (! $value instanceof MoneyLibrary) {
return false;
}

$locale = $formatLocale ?? I18N::getFormattingLocale()->id;

return new IntlMoneyFormatter(
formatter: new NumberFormatter($locale, NumberFormatter::CURRENCY),
currencies: self::getIsoCurrencies()
)->format($value);
}

public static function toNumber(mixed $value, ?string $formatLocale = null): string|false
{
if (is_string($value)) {
return $value;
}

if (! $value instanceof MoneyLibrary) {
return false;
}

$locale = $formatLocale ?? I18N::getFormattingLocale()->id;

return new IntlMoneyFormatter(
formatter: new NumberFormatter($locale, NumberFormatter::DECIMAL),
currencies: self::getIsoCurrencies(),
)->format($value);
}

public static function normalizeString(string $value, ?Currency $fallbackCurrency = null): string
{
$locale = I18N::getFormattingLocale()->id;
$numberFormatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
$moneyParser = new IntlMoneyParser($numberFormatter, self::getIsoCurrencies());

return $moneyParser->parse($value, $fallbackCurrency)->getAmount();
}

private static function getIsoCurrencies(): ISOCurrencies
{
return self::$isoCurrencies ??= new ISOCurrencies;
}
}
256 changes: 256 additions & 0 deletions src/Support/Search.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
<?php

declare(strict_types=1);

namespace CraftCms\Cms\Support;

final readonly class Search
{
/**
* Normalizes search keywords.
*
* @param string|string[] $str The dirty keywords
* @param array $ignore Ignore words to strip out
* @param bool $processCharMap Whether to remove punctuation and diacritics (default is true)
* @param string|null $language The language that the character map should be based on, if `$processCharMap` is `true`.
* @return string The cleansed keywords.
*/
public static function normalizeKeywords(array|string $str, array $ignore = [], bool $processCharMap = true, ?string $language = null): string
{
// Flatten
if (is_array($str)) {
$str = Str::toString($str, ' ');
}

// Get rid of tags
$str = strip_tags((string) preg_replace(['/<br\s*\/?>/i', '/<\/\w+>/'], [' ', ' $1'], $str));

// Convert non-breaking spaces entities to regular ones
$str = str_replace(['&nbsp;', '&#160;', '&#xa0;'], ' ', $str);

// Get rid of entities
$str = preg_replace('/&#?[a-z0-9]{2,8};/i', '', $str);

// Get rid of emoji
$str = Str::replaceMb4($str, '');

// Normalize to lowercase
$str = mb_strtolower($str);

if ($processCharMap) {
$str = strtr($str, Str::asciiCharMap(true, $language ?? app()->getLocale()));

$str = preg_replace(self::getElisionsRegex(), '', $str);

// Remove punctuation and diacritics
$punctuation = self::getPunctuation();
$str = str_replace(array_keys($punctuation), $punctuation, $str);
}

// Remove ignore-words?
if (! empty($ignore)) {
foreach ($ignore as $word) {
$word = preg_quote(self::normalizeKeywords($word, [], true, $language), '/');
$str = preg_replace("/\b$word\b/u", '', (string) $str);
}
}

// Get rid of invisible Unicode special characters
// (see https://github.com/craftcms/cms/issues/16430)
$str = preg_replace(Str::invisibleCharsPattern(), '', (string) $str);

// Strip out new lines and superfluous spaces
return trim((string) preg_replace(['/[\n\r]+/u', '/\s{2,}/u'], ' ', (string) $str));
}

/**
* Returns a regex pattern for elisions.
*/
private static function getElisionsRegex(): string
{
static $elisions = null;

if (! $elisions) {
$elisionsArr = [
'l',
'm',
't',
'qu',
'n',
's',
'j',
'd',
'c',
'jusqu',
'quoiqu',
'lorsqu',
'puisqu',
];
$elisions = sprintf('/\b(%s)\'/', implode('|', $elisionsArr));
}

return $elisions;
}

/**
* Returns the asciiPunctuation array.
*/
private static function getPunctuation(): array
{
// Keep local copy
static $asciiPunctuation = [];

if (empty($asciiPunctuation)) {
$asciiPunctuation = [
'!' => ' ',
'"' => ' ',
'#' => ' ',
'&' => ' ',
"'" => '',
'(' => ' ',
')' => ' ',
'*' => ' ',
'+' => ' ',
',' => ' ',
'-' => ' ',
'.' => ' ',
'/' => ' ',
':' => ' ',
';' => ' ',
'<' => ' ',
'>' => ' ',
'?' => ' ',
'@' => ' ',
'[' => ' ',
'\\' => ' ',
']' => ' ',
'^' => ' ',
'{' => ' ',
'|' => ' ',
'}' => ' ',
'~' => ' ',
'¡' => ' ',
'¢' => ' ',
'£' => ' ',
'¤' => ' ',
'¥' => ' ',
'¦' => ' ',
'§' => ' ',
'¨' => ' ',
'©' => ' ',
'ª' => ' ',
'«' => ' ',
'¬' => ' ',
'®' => ' ',
'¯' => ' ',
'°' => ' ',
'±' => ' ',
'²' => ' ',
'³' => ' ',
'´' => ' ',
'µ' => ' ',
'¶' => ' ',
'·' => ' ',
'¸' => ' ',
'¹' => ' ',
'º' => ' ',
'»' => ' ',
'¼' => ' ',
'½' => ' ',
'¾' => ' ',
'¿' => ' ',
'×' => ' ',
'ƒ' => ' ',
'ˆ' => ' ',
'˜' => ' ',
'–' => ' ',
'—' => ' ',
'―' => ' ',
'_' => ' ',
'‘' => '',
'’' => '',
'‚' => ' ',
'“' => ' ',
'”' => ' ',
'„' => ' ',
'†' => ' ',
'‡' => ' ',
'•' => ' ',
'‣' => ' ',
'…' => ' ',
'‰' => ' ',
'′' => ' ',
'″' => ' ',
'‹' => ' ',
'›' => ' ',
'‼' => ' ',
'‾' => ' ',
'⁄' => ' ',
'€' => ' ',
'™' => ' ',
'←' => ' ',
'↑' => ' ',
'→' => ' ',
'↓' => ' ',
'↔' => ' ',
'↵' => ' ',
'⇐' => ' ',
'⇑' => ' ',
'⇒' => ' ',
'⇓' => ' ',
'⇔' => ' ',
'∀' => ' ',
'∂' => ' ',
'∃' => ' ',
'∅' => ' ',
'∇' => ' ',
'∈' => ' ',
'∉' => ' ',
'∋' => ' ',
'∏' => ' ',
'∑' => ' ',
'−' => ' ',
'∗' => ' ',
'√' => ' ',
'∝' => ' ',
'∞' => ' ',
'∠' => ' ',
'∧' => ' ',
'∨' => ' ',
'∩' => ' ',
'∪' => ' ',
'∫' => ' ',
'∴' => ' ',
'∼' => ' ',
'≅' => ' ',
'≈' => ' ',
'≠' => ' ',
'≡' => ' ',
'≤' => ' ',
'≥' => ' ',
'⊂' => ' ',
'⊃' => ' ',
'⊄' => ' ',
'⊆' => ' ',
'⊇' => ' ',
'⊕' => ' ',
'⊗' => ' ',
'⊥' => ' ',
'⋅' => ' ',
'⌈' => ' ',
'⌉' => ' ',
'⌊' => ' ',
'⌋' => ' ',
'〈' => ' ',
'〉' => ' ',
'◊' => ' ',
'♠' => ' ',
'♣' => ' ',
'♥' => ' ',
'♦' => ' ',
];
}

return $asciiPunctuation;
}
}
Loading
Loading