System/CodeIgniter: Update system to 4.6.0 Codeigniter

This commit is contained in:
Nightprince 2026-01-04 15:23:31 +03:30
parent 6ad73340be
commit 90d737fecb
14 changed files with 424 additions and 84 deletions

View file

@ -89,18 +89,3 @@ defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid u
defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error
defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
/**
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_LOW instead.
*/
define('EVENT_PRIORITY_LOW', 200);
/**
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_NORMAL instead.
*/
define('EVENT_PRIORITY_NORMAL', 100);
/**
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_HIGH instead.
*/
define('EVENT_PRIORITY_HIGH', 10);

View file

@ -179,6 +179,13 @@ class Database extends Config
"strictOn" => false,
"failover" => [],
"port" => '.(int)$_POST['cms_port'].',
"numberNative" => false,
"foundRows" => false,
"dateFormat" => [
"date" => "Y-m-d",
"datetime" => "Y-m-d H:i:s",
"time" => "H:i:s,
],
];
public array $account = [
@ -199,6 +206,13 @@ class Database extends Config
"strictOn" => false,
"failover" => [],
"port" => '.(int)$_POST['auth_port'].',
"numberNative" => false,
"foundRows" => false,
"dateFormat" => [
"date" => "Y-m-d",
"datetime" => "Y-m-d H:i:s",
"time" => "H:i:s,
],
];
}
';
@ -450,6 +464,13 @@ class Database extends Config
'compress' => false,
'strictOn' => false,
'failover' => [],
'numberNative' => false,
'foundRows' => false,
'dateFormat' => [
'date' => 'Y-m-d',
'datetime' => 'Y-m-d H:i:s',
'time' => 'H:i:s',
],
];
$worldConfig = [
@ -470,6 +491,13 @@ class Database extends Config
'compress' => false,
'strictOn' => false,
'failover' => [],
'numberNative' => false,
'foundRows' => false,
'dateFormat' => [
'date' => 'Y-m-d',
'datetime' => 'Y-m-d H:i:s',
'time' => 'H:i:s',
],
];
// Connect to characters

View file

@ -21,7 +21,7 @@ if (version_compare(PHP_VERSION, $minPhpVersion, '<')) {
$message = sprintf(
'Your PHP version must be %s or higher to run FusionCMS. Current version: %s',
$minPhpVersion,
PHP_VERSION
PHP_VERSION,
);
header('HTTP/1.1 503 Service Unavailable.', true, 503);

View file

@ -298,7 +298,7 @@ class BaseBuilder
/**
* Constructor
*
* @param array|string $tableName tablename or tablenames with or without aliases
* @param array|string|TableName $tableName tablename or tablenames with or without aliases
*
* Examples of $tableName: `mytable`, `jobs j`, `jobs j, users u`, `['jobs j','users u']`
*
@ -315,15 +315,20 @@ class BaseBuilder
*/
$this->db = $db;
if ($tableName instanceof TableName) {
$this->tableName = $tableName->getTableName();
$this->QBFrom[] = $this->db->escapeIdentifier($tableName);
$this->db->addTableAlias($tableName->getAlias());
}
// If it contains `,`, it has multiple tables
if (is_string($tableName) && strpos($tableName, ',') === false) {
elseif (is_string($tableName) && strpos($tableName, ',') === false) {
$this->tableName = $tableName; // @TODO remove alias if exists
$this->from($tableName);
} else {
$this->tableName = '';
$this->from($tableName);
}
$this->from($tableName);
if ($options !== null && $options !== []) {
foreach ($options as $key => $value) {
if (property_exists($this, $key)) {
@ -3008,7 +3013,7 @@ class BaseBuilder
*
* @param array|string $table The table to inspect
*
* @return string|void
* @return string|null
*/
protected function trackAliases($table)
{
@ -3017,26 +3022,28 @@ class BaseBuilder
$this->trackAliases($t);
}
return;
return null;
}
// Does the string contain a comma? If so, we need to separate
// the string into discreet statements
if (strpos($table, ',') !== false) {
if (str_contains($table, ',')) {
return $this->trackAliases(explode(',', $table));
}
// if a table alias is used we can recognize it by a space
if (strpos($table, ' ') !== false) {
if (str_contains($table, ' ')) {
// if the alias is written with the AS keyword, remove it
$table = preg_replace('/\s+AS\s+/i', ' ', $table);
// Grab the alias
$table = trim(strrchr($table, ' '));
$alias = trim(strrchr($table, ' '));
// Store the alias, if it doesn't already exist
$this->db->addTableAlias($table);
$this->db->addTableAlias($alias);
}
return null;
}
/**

View file

@ -338,7 +338,7 @@ abstract class BaseConnection implements ConnectionInterface
/**
* Array of table aliases.
*
* @var array
* @var list<string>
*/
protected $aliasedTables = [];
@ -574,10 +574,14 @@ abstract class BaseConnection implements ConnectionInterface
*
* @return $this
*/
public function addTableAlias(string $table)
public function addTableAlias(string $alias)
{
if (! in_array($table, $this->aliasedTables, true)) {
$this->aliasedTables[] = $table;
if ($alias === '') {
return $this;
}
if (! in_array($alias, $this->aliasedTables, true)) {
$this->aliasedTables[] = $alias;
}
return $this;
@ -823,6 +827,16 @@ abstract class BaseConnection implements ConnectionInterface
return $this->transStatus;
}
/**
* Reset transaction status - to restart transactions after strict mode failure
*/
public function resetTransStatus(): static
{
$this->transStatus = true;
return $this;
}
/**
* Begin Transaction
*/
@ -913,7 +927,7 @@ abstract class BaseConnection implements ConnectionInterface
/**
* Returns a non-shared new instance of the query builder for this connection.
*
* @param array|string $tableName
* @param array|string|TableName $tableName
*
* @return BaseBuilder
*
@ -1045,9 +1059,10 @@ abstract class BaseConnection implements ConnectionInterface
* the correct identifiers.
*
* @param array|int|string $item
* @param bool $prefixSingle Prefix a table name with no segments?
* @param bool $protectIdentifiers Protect table or column names?
* @param bool $fieldExists Supplied $item contains a column name?
* @param array|int|string|TableName $item
* @param bool $prefixSingle Prefix a table name with no segments?
* @param bool $protectIdentifiers Protect table or column names?
* @param bool $fieldExists Supplied $item contains a column name?
*
* @return array|string
* @phpstan-return ($item is array ? array : string)
@ -1068,6 +1083,11 @@ abstract class BaseConnection implements ConnectionInterface
return $escapedArray;
}
if ($item instanceof TableName) {
/** @psalm-suppress NoValue I don't know why ERROR. */
return $this->escapeTableName($item);
}
// If you pass `['column1', 'column2']`, `$item` will be int because the array keys are int.
$item = (string) $item;
@ -1135,6 +1155,42 @@ abstract class BaseConnection implements ConnectionInterface
return $item . $alias;
}
/**
* Escape the SQL Identifier
*
* This function escapes single identifier.
*
* @param non-empty-string|TableName $item
*/
public function escapeIdentifier($item): string
{
if ($item === '') {
return '';
}
if ($item instanceof TableName) {
return $this->escapeTableName($item);
}
return $this->escapeChar
. str_replace(
$this->escapeChar,
$this->escapeChar . $this->escapeChar,
$item,
)
. $this->escapeChar;
}
/**
* Returns escaped table name with alias.
*/
private function escapeTableName(TableName $tableName): string
{
$alias = $tableName->getAlias();
return $this->escapeIdentifier($tableName->getActualTableName())
. (($alias !== '') ? ' ' . $this->escapeIdentifier($alias) : '');
}
private function protectDotItem(string $item, string $alias, bool $protectIdentifiers, bool $fieldExists): string
{
@ -1518,12 +1574,16 @@ abstract class BaseConnection implements ConnectionInterface
/**
* Fetch Field Names
*
* @param string|TableName $tableName
*
* @return false|list<string>
*
* @throws DatabaseException
*/
public function getFieldNames(string $table)
public function getFieldNames($tableName)
{
$table = ($tableName instanceof TableName) ? $tableName->getTableName() : $tableName;
// Is there a cached result?
if (isset($this->dataCache['field_names'][$table])) {
return $this->dataCache['field_names'][$table];
@ -1533,7 +1593,7 @@ abstract class BaseConnection implements ConnectionInterface
$this->initialize();
}
if (false === ($sql = $this->_listColumns($table))) {
if (false === ($sql = $this->_listColumns($tableName))) {
if ($this->DBDebug) {
throw new DatabaseException('This feature is not available for the database you are using.');
}
@ -1747,9 +1807,11 @@ abstract class BaseConnection implements ConnectionInterface
/**
* Generates a platform-specific query string so that the column names can be fetched.
*
* @param string|TableName $table
*
* @return false|string
*/
abstract protected function _listColumns(string $table = '');
abstract protected function _listColumns($table = '');
/**
* Platform-specific field data information.

View file

@ -15,6 +15,7 @@ namespace CodeIgniter\Database\MySQLi;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\TableName;
use LogicException;
use mysqli;
use mysqli_result;
@ -81,6 +82,16 @@ class Connection extends BaseConnection
*/
public $numberNative = false;
/**
* Use MYSQLI_CLIENT_FOUND_ROWS
*
* Whether affectedRows() should return number of rows found,
* or number of rows changed, after an UPDATE query.
*
* @var bool
*/
public $foundRows = false;
/**
* Connect to the database.
*
@ -182,6 +193,10 @@ class Connection extends BaseConnection
$clientFlags += MYSQLI_CLIENT_SSL;
}
if ($this->foundRows) {
$clientFlags += MYSQLI_CLIENT_FOUND_ROWS;
}
try {
if ($this->mysqli->real_connect(
$hostname,
@ -408,10 +423,19 @@ class Connection extends BaseConnection
/**
* Generates a platform-specific query string so that the column names can be fetched.
*
* @param string|TableName $table
*/
protected function _listColumns(string $table = ''): string
protected function _listColumns($table = ''): string
{
return 'SHOW COLUMNS FROM ' . $this->protectIdentifiers($table, true, null, false);
$tableName = $this->protectIdentifiers(
$table,
true,
null,
false,
);
return 'SHOW COLUMNS FROM ' . $tableName;
}
/**

View file

@ -16,6 +16,7 @@ namespace CodeIgniter\Database\OCI8;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\Query;
use CodeIgniter\Database\TableName;
use ErrorException;
use stdClass;
@ -285,17 +286,22 @@ class Connection extends BaseConnection
/**
* Generates a platform-specific query string so that the column names can be fetched.
*/
protected function _listColumns(string $table = ''): string
protected function _listColumns($table = ''): string
{
if (strpos($table, '.') !== false) {
sscanf($table, '%[^.].%s', $owner, $table);
if ($table instanceof TableName) {
$tableName = $this->escape(strtoupper($table->getActualTableName()));
$owner = $this->username;
} elseif (str_contains($table, '.')) {
sscanf($table, '%[^.].%s', $owner, $tableName);
$tableName = $this->escape(strtoupper($this->DBPrefix . $tableName));
} else {
$owner = $this->username;
$owner = $this->username;
$tableName = $this->escape(strtoupper($this->DBPrefix . $table));
}
return 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS
WHERE UPPER(OWNER) = ' . $this->escape(strtoupper($owner)) . '
AND UPPER(TABLE_NAME) = ' . $this->escape(strtoupper($this->DBPrefix . $table));
AND UPPER(TABLE_NAME) = ' . $tableName;
}
/**

View file

@ -16,6 +16,7 @@ namespace CodeIgniter\Database\Postgre;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\RawSql;
use CodeIgniter\Database\TableName;
use ErrorException;
use PgSql\Connection as PgSqlConnection;
use PgSql\Result as PgSqlResult;
@ -302,13 +303,20 @@ class Connection extends BaseConnection
/**
* Generates a platform-specific query string so that the column names can be fetched.
*
* @param string|TableName $table
*/
protected function _listColumns(string $table = ''): string
protected function _listColumns($table = ''): string
{
if ($table instanceof TableName) {
$tableName = $this->escape($table->getActualTableName());
} else {
$tableName = $this->escape($this->DBPrefix . strtolower($table));
}
return 'SELECT "column_name"
FROM "information_schema"."columns"
WHERE LOWER("table_name") = '
. $this->escape($this->DBPrefix . strtolower($table))
WHERE LOWER("table_name") = ' . $tableName
. ' ORDER BY "ordinal_position"';
}

View file

@ -15,6 +15,7 @@ namespace CodeIgniter\Database\SQLSRV;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\TableName;
use stdClass;
/**
@ -225,12 +226,20 @@ class Connection extends BaseConnection
/**
* Generates a platform-specific query string so that the column names can be fetched.
*
* @param string|TableName $table
*/
protected function _listColumns(string $table = ''): string
protected function _listColumns($table = ''): string
{
if ($table instanceof TableName) {
$tableName = $this->escape(strtolower($table->getActualTableName()));
} else {
$tableName = $this->escape($this->DBPrefix . strtolower($table));
}
return 'SELECT [COLUMN_NAME] '
. ' FROM [INFORMATION_SCHEMA].[COLUMNS]'
. ' WHERE [TABLE_NAME] = ' . $this->escape($this->DBPrefix . $table)
. ' WHERE [TABLE_NAME] = ' . $tableName
. ' AND [TABLE_SCHEMA] = ' . $this->escape($this->schema);
}

View file

@ -15,6 +15,7 @@ namespace CodeIgniter\Database\SQLite3;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\TableName;
use Exception;
use SQLite3;
use SQLite3Result;
@ -55,6 +56,15 @@ class Connection extends BaseConnection
*/
protected $busyTimeout;
/**
* The setting of the "synchronous" flag
*
* @var int<0, 3>|null flag
*
* @see https://www.sqlite.org/pragma.html#pragma_synchronous
*/
protected ?int $synchronous = null;
/**
* @return void
*/
@ -69,6 +79,13 @@ class Connection extends BaseConnection
if (is_int($this->busyTimeout)) {
$this->connID->busyTimeout($this->busyTimeout);
}
if (is_int($this->synchronous)) {
if (! in_array($this->synchronous, [0, 1, 2, 3], true)) {
throw new InvalidArgumentException('Invalid synchronous value.');
}
$this->connID->exec('PRAGMA synchronous = ' . $this->synchronous);
}
}
/**
@ -209,19 +226,31 @@ class Connection extends BaseConnection
/**
* Generates a platform-specific query string so that the column names can be fetched.
*
* @param string|TableName $table
*/
protected function _listColumns(string $table = ''): string
protected function _listColumns($table = ''): string
{
return 'PRAGMA TABLE_INFO(' . $this->protectIdentifiers($table, true, null, false) . ')';
if ($table instanceof TableName) {
$tableName = $this->escapeIdentifier($table);
} else {
$tableName = $this->protectIdentifiers($table, true, null, false);
}
return 'PRAGMA TABLE_INFO(' . $tableName . ')';
}
/**
* @param string|TableName $tableName
*
* @return false|list<string>
*
* @throws DatabaseException
*/
public function getFieldNames(string $table)
public function getFieldNames($tableName)
{
$table = ($tableName instanceof TableName) ? $tableName->getTableName() : $tableName;
// Is there a cached result?
if (isset($this->dataCache['field_names'][$table])) {
return $this->dataCache['field_names'][$table];
@ -231,7 +260,7 @@ class Connection extends BaseConnection
$this->initialize();
}
$sql = $this->_listColumns($table);
$sql = $this->_listColumns($tableName);
$query = $this->query($sql);
$this->dataCache['field_names'][$table] = [];

View file

@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace CodeIgniter\Database;
/**
* Represents a table name in SQL.
*
* @interal
*
* @see \CodeIgniter\Database\TableNameTest
*/
class TableName
{
/**
* @param string $actualTable Actual table name
* @param string $logicalTable Logical table name (w/o DB prefix)
* @param string $schema Schema name
* @param string $database Database name
* @param string $alias Alias name
*/
protected function __construct(
private readonly string $actualTable,
private readonly string $logicalTable = '',
private readonly string $schema = '',
private readonly string $database = '',
private readonly string $alias = '',
) {
}
/**
* Creates a new instance.
*
* @param string $table Table name (w/o DB prefix)
* @param string $alias Alias name
*/
public static function create(string $dbPrefix, string $table, string $alias = ''): self
{
return new self(
$dbPrefix . $table,
$table,
'',
'',
$alias,
);
}
/**
* Creates a new instance from an actual table name.
*
* @param string $actualTable Actual table name with DB prefix
* @param string $alias Alias name
*/
public static function fromActualName(string $dbPrefix, string $actualTable, string $alias = ''): self
{
$prefix = $dbPrefix;
$logicalTable = '';
if (str_starts_with($actualTable, $prefix)) {
$logicalTable = substr($actualTable, strlen($prefix));
}
return new self(
$actualTable,
$logicalTable,
'',
$alias,
);
}
/**
* Creates a new instance from full name.
*
* @param string $table Table name (w/o DB prefix)
* @param string $schema Schema name
* @param string $database Database name
* @param string $alias Alias name
*/
public static function fromFullName(
string $dbPrefix,
string $table,
string $schema = '',
string $database = '',
string $alias = '',
): self {
return new self(
$dbPrefix . $table,
$table,
$schema,
$database,
$alias,
);
}
/**
* Returns the single segment table name w/o DB prefix.
*/
public function getTableName(): string
{
return $this->logicalTable;
}
/**
* Returns the actual single segment table name w/z DB prefix.
*/
public function getActualTableName(): string
{
return $this->actualTable;
}
public function getAlias(): string
{
return $this->alias;
}
public function getSchema(): string
{
return $this->schema;
}
public function getDatabase(): string
{
return $this->database;
}
}

View file

@ -651,6 +651,15 @@ class CURLRequest extends OutgoingRequest
$this->setHeader('Content-Length', (string) strlen($json));
}
// Resolve IP
if (array_key_exists('force_ip_resolve', $config)) {
$curlOptions[CURLOPT_IPRESOLVE] = match ($config['force_ip_resolve']) {
'v4' => CURL_IPRESOLVE_V4,
'v6' => CURL_IPRESOLVE_V6,
default => CURL_IPRESOLVE_WHATEVER,
};
}
// version
if (! empty($config['version'])) {
$version = sprintf('%.1F', $config['version']);
@ -660,6 +669,12 @@ class CURLRequest extends OutgoingRequest
$curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
} elseif ($version === '2.0') {
$curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
} elseif ($version === '3.0') {
if (! defined('CURL_HTTP_VERSION_3')) {
define('CURL_HTTP_VERSION_3', 30);
}
$curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_3;
}
}

View file

@ -12,6 +12,8 @@
namespace CodeIgniter\I18n;
use DateTime;
use Exception;
use ReturnTypeWillChange;
/**
* Legacy Time class.
@ -37,10 +39,29 @@ use DateTime;
* @property string $weekOfYear read-only
* @property string $year read-only
*
* @phpstan-consistent-constructor
*
* @deprecated Use Time instead.
* @see \CodeIgniter\I18n\TimeLegacyTest
*/
class TimeLegacy extends DateTime
{
use TimeTrait;
/**
* Returns a new instance with the date set to the new timestamp.
*
* @param int $timestamp
*
* @return static
*
* @throws Exception
*/
#[ReturnTypeWillChange]
public function setTimestamp($timestamp)
{
$time = date('Y-m-d H:i:s', $timestamp);
return static::parse($time, $this->timezone, $this->locale);
}
}

View file

@ -53,35 +53,40 @@ if (!function_exists('character_limiter')) {
/**
* Character Limiter
*
* Limits the string based on the character count. Preserves complete words
* Limits the string based on the character count. Preserves complete words
* so the character count may not be exactly as specified.
*
* @param string $endChar the end character. Usually an ellipsis
*/
function character_limiter(string $str, int $n = 500, string $endChar = '&#8230;'): string
function character_limiter(string $string, int $limit = 500, string $endChar = '&#8230;'): string
{
if (mb_strlen($str) < $n) {
return $str;
if (mb_strlen($string) < $limit) {
return $string;
}
// a bit complicated, but faster than preg_replace with \s+
$str = preg_replace('/ {2,}/', ' ', str_replace(["\r", "\n", "\t", "\x0B", "\x0C"], ' ', $str));
$string = preg_replace('/ {2,}/', ' ', str_replace(["\r", "\n", "\t", "\x0B", "\x0C"], ' ', $string));
$stringLength = mb_strlen($string);
if (mb_strlen($str) <= $n) {
return $str;
if ($stringLength <= $limit) {
return $string;
}
$out = '';
$output = '';
$outputLength = 0;
$words = explode(' ', trim($string));
foreach (explode(' ', trim($str)) as $val) {
$out .= $val . ' ';
if (mb_strlen($out) >= $n) {
$out = trim($out);
foreach ($words as $word) {
$output .= $word . ' ';
$outputLength = mb_strlen($output);
if ($outputLength >= $limit) {
$output = trim($output);
break;
}
}
return (mb_strlen($out) === mb_strlen($str)) ? $out : $out . $endChar;
return ($outputLength === $stringLength) ? $output : $output . $endChar;
}
}
@ -729,38 +734,44 @@ if (!function_exists('excerpt')) {
function excerpt(string $text, ?string $phrase = null, int $radius = 100, string $ellipsis = '...'): string
{
if (isset($phrase)) {
$phrasePos = stripos($text, $phrase);
$phraseLen = strlen($phrase);
$phrasePosition = mb_stripos($text, $phrase);
$phraseLength = mb_strlen($phrase);
} else {
$phrasePos = $radius / 2;
$phraseLen = 1;
$phrasePosition = $radius / 2;
$phraseLength = 1;
}
$pre = explode(' ', substr($text, 0, $phrasePos));
$pos = explode(' ', substr($text, $phrasePos + $phraseLen));
$beforeWords = explode(' ', mb_substr($text, 0, $phrasePosition));
$afterWords = explode(' ', mb_substr($text, $phrasePosition + $phraseLength));
$prev = ' ';
$post = ' ';
$count = 0;
$firstPartOutput = ' ';
$endPartOutput = ' ';
$count = 0;
foreach (array_reverse($pre) as $e) {
if ((strlen($e) + $count + 1) < $radius) {
$prev = ' ' . $e . $prev;
foreach (array_reverse($beforeWords) as $beforeWord) {
$beforeWordLength = mb_strlen($beforeWord);
if (($beforeWordLength + $count + 1) < $radius) {
$firstPartOutput = ' ' . $beforeWord . $firstPartOutput;
}
$count = ++$count + strlen($e);
$count = ++$count + $beforeWordLength;
}
$count = 0;
foreach ($pos as $s) {
if ((strlen($s) + $count + 1) < $radius) {
$post .= $s . ' ';
foreach ($afterWords as $afterWord) {
$afterWordLength = mb_strlen($afterWord);
if (($afterWordLength + $count + 1) < $radius) {
$endPartOutput .= $afterWord . ' ';
}
$count = ++$count + strlen($s);
$count = ++$count + $afterWordLength;
}
$ellPre = $phrase !== null ? $ellipsis : '';
return str_replace(' ', ' ', $ellPre . $prev . $phrase . $post . $ellipsis);
return str_replace(' ', ' ', $ellPre . $firstPartOutput . $phrase . $endPartOutput . $ellipsis);
}
}