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
204 changes: 1 addition & 203 deletions lib/Horde/Array.php
Original file line number Diff line number Diff line change
@@ -1,204 +1,2 @@
<?php

/**
* The Horde_Array:: class provides various methods for array manipulation.
*
* Copyright 2003-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Michael Slusarz <slusarz@horde.org>
* @author Marko Djukic <marko@oblo.com>
* @author Jan Schneider <jan@horde.org>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Util
*/
class Horde_Array
{
/**
* Sorts an array on a specified key. If the key does not exist,
* defaults to the first key of the array.
*
* @param array &$array The array to be sorted, passed by reference.
* @param string $key The key by which to sort. If not specified then
* the first key is used.
* @param integer $dir Sort direction:
* 0 = ascending (default)
* 1 = descending
* @param boolean $assoc Keep key value association?
*/
public static function arraySort(array &$array, $key = null, $dir = 0, $assoc = true)
{
/* Return if the array is empty. */
if (!$array) {
return;
}

/* If no key to sort by is specified, use the first key of the
* first element. */
if (is_null($key)) {
$keys = array_keys(reset($array));
$key = array_shift($keys);
}

/* Call the appropriate sort function. */
$helper = new Horde_Array_Sort_Helper();
$helper->key = $key;
$function = $dir ? 'reverseCompare' : 'compare';
if ($assoc) {
uasort($array, [ $helper, $function ]);
} else {
usort($array, [ $helper, $function ]);
}
}

/**
* Given an HTML type array field "example[key1][key2][key3]" breaks up
* the keys so that they could be used to reference a regular PHP array.
*
* @param string $field The field name to be examined.
* @param string &$base Will be set to the base element.
* @param array &$keys Will be set to the list of keys.
*
* @return boolean True on success, false on error.
*/
public static function getArrayParts($field, &$base, &$keys)
{
if (!preg_match('|([^\[]*)((\[[^\[\]]*\])+)|', $field, $matches)) {
return false;
}

$base = $matches[1];
$keys = explode('][', $matches[2]);
$keys[0] = substr($keys[0], 1);
$keys[count($keys) - 1] = substr($keys[count($keys) - 1], 0, strlen($keys[count($keys) - 1]) - 1);
return true;
}

/**
* Given an HTML type array field "example[key1][key2][key3]" breaks up
* the keys into [ 'example', 'key1', 'key2', 'key3' ] so that they could be
* used to reference a regular PHP array.
*
* @param string $field The field name to be examined.
*
* @return array|null Array of keys, null on error.
*/
public static function getFieldParts($field)
{
if (preg_match('|^([^\[]*)((\[[^\[\]]*\])*)$|', $field, $matches)) {
$keys = [ $matches[1] ];
if (strlen($matches[2])) {
$keys = array_merge($keys, explode('][', substr($matches[2], 1, -1)));
}
return $keys;
}
return null;
}

/**
* Using an array of keys iterate through the array following the
* keys to find the final key value.
*
* @param array $array The array to be used.
* @param array|string $keys The key path to follow as an array or string.
* @param string $extraKey Additional key to add at position 1, if any.
*
* @return mixed The final value of the key path.
*/
public static function getElement(array $array, $keys, $extraKey = null)
{
$ref = &$array;

if (!is_array($keys)) {
$keys = [ $keys ];
}

if (isset($extraKey)) {
array_splice($keys, 1, 0, $extraKey);
}

foreach ($keys as $key) {
if (!isset($ref[$key])) {
return null;
}
$ref = &$ref[$key];
}

return $ref;
}

/**
* Using an array of keys iterate through the $array following the
* nested $keys to find the final key's value. If a $value is passed then set
* that value. If missing, create array levels along that path.
* Existing value in that path will be overwritten.
*
* @param array &$array The array to be used.
* @param array|string $keys The key path to follow as an array or string.
* @param mixed $value Target element will have this value set to it.
* @param string $extraKey Additional key to add at position 1, if any.
*/
public static function setElement(array &$array, $keys, $value, $extraKey = null)
{
$ref = &$array;

if (!is_array($keys)) {
$keys = [ $keys ];
}

if (isset($extraKey)) {
array_splice($keys, 1, 0, $extraKey);
}

foreach ($keys as $key) {
if (!isset($ref[$key])) {
$ref[$key] = [];
}
$ref = &$ref[$key];
}

$ref = $value;
}

/**
* Returns a rectangle of a two-dimensional array.
*
* @param array $array The array to extract the rectangle from.
* @param integer $row The start row of the rectangle.
* @param integer $col The start column of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $width The width of the rectangle.
*
* @return array The extracted rectangle.
*/
public static function getRectangle(array $array, $row, $col, $height, $width)
{
$rec = [];
for ($y = $row; $y < $row + $height; $y++) {
$rec[] = array_slice($array[$y], $col, $width);
}
return $rec;
}

/**
* Given an array, returns an associative array with each element key
* derived from its value.
* For example:
* [ 0 => 'foo', 1 => 'bar' ]
* would become:
* [ 'foo' => 'foo', 'bar' => 'bar' ]
*
* @param array $array An array of values.
*
* @return array An array with keys the same as values.
*/
public static function valuesToKeys(array $array)
{
return $array
? array_combine($array, $array)
: [];
}
}
Horde\Util\DeprecationHelper::redirectClass('Horde_Array', 'Horde\Util\ArrayUtils');
83 changes: 83 additions & 0 deletions src/DeprecationHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

namespace Horde\Util;

/**
* Utility for managing deprecations across the Horde framework.
*
* Provides helpers for emitting deprecation notices that point to the
* actual call site in user code, and for maintaining backwards compatibility
* during the transition period.
*
* Copyright 2026 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Dmitry Petrov <dpetrov67@gmail.com>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Util
*/
class DeprecationHelper
{
/**
* Files to skip when walking the call stack to find the user-code call site.
* Autoloader infrastructure (e.g. ClassLoader.php) sits between user code
* and this class in the backtrace and must be stepped over.
*
* @var string[]
*/
private static array $ignoreFiles = ['ClassLoader.php'];

/**
* Emits a deprecation notice and aliases $oldClass to $newClass.
*
* @param string $oldClass Fully-qualified name of the deprecated class.
* @param string $newClass Fully-qualified name of the replacement class.
* @param bool $trigger True: raise E_USER_DEPRECATED (respects error handlers).
* False: write silently to the server error log.
*/
public static function redirectClass(string $oldClass, string $newClass, bool $trigger = true): void
{
$message = sprintf('The %s class is deprecated. Switch to %s', $oldClass, $newClass);

$location = self::resolveCallSite();
if ($location) {
[$file, $line] = $location;
$message .= " ($file:$line)";
}

if ($trigger) {
trigger_error($message, E_USER_DEPRECATED);
} else {
error_log('DEPRECATED: ' . $message);
}

class_alias($newClass, $oldClass);
}

/**
* Walks the backtrace to find the first user-code frame after the autoloader boundary.
*
* @return array{0: string, 1: int}|null [$file, $line] of the call site, or null if not found.
*/
private static function resolveCallSite(): ?array
{
$cl = false;

foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
$file = $frame['file'] ?? null;

if (in_array(basename($file), self::$ignoreFiles, strict: true)) {
$cl = true;
} elseif ($cl) {
return [$file, $frame['line'] ?? null];
}
}

return null;
}
}
Loading