Исправление ошибки «Cannot instantiate abstract class Bitrix\Iblock\Component\Base»

19 февраля 2023
После обновления PHP на версию 7.4 в старых версиях Битрикса возникает ошибка вида Cannot instantiate abstract class Bitrix\Iblock\Component\Base. Это происходит из-за того, что в PHP у функции get_declared_classes (https://www.php.net/manual/en/function.get-declared-classes.php) изменился порядок возвращаемых классов — теперь строгость следования родительских классов не гарантируется.

Для решения проблемы нужно подредактировать файл /bitrix/modules/main/classes/general/component.php (перед внесением правок обязательно сделайте резервную копию файла).

ШАГ 1

После фрагмента:
private static $__classes_map = array();
 
добавляем вот этот код:
private static $classes = array();
 

ШАГ 2

Затем необходимо изменить определение метода __getClassForPath. Заменяем фрагмент:
final private function __getClassForPath($componentPath)
{
	if (!isset(self::$__classes_map[$componentPath]))
	{
		$fname = $_SERVER["DOCUMENT_ROOT"].$componentPath."/class.php";
		if (file_exists($fname) && is_file($fname))
		{
			$beforeClasses = get_declared_classes();
			$beforeClassesCount = count($beforeClasses);
			include_once($fname);
			$afterClasses = get_declared_classes();
			$afterClassesCount = count($afterClasses);
			for ($i = $beforeClassesCount; $i < $afterClassesCount; $i++)
			{
				if (is_subclass_of($afterClasses[$i], "cbitrixcomponent"))
					self::$__classes_map[$componentPath] = $afterClasses[$i];
			}
		}
		else
		{
			self::$__classes_map[$componentPath] = "";
		}
	}
	return self::$__classes_map[$componentPath];
}
 
на следующий код:
final private function __getClassForPath($componentPath)
{
	if (!isset(self::$__classes_map[$componentPath]))
	{
		$fname = $_SERVER["DOCUMENT_ROOT"].$componentPath."/class.php";
		if (file_exists($fname) && is_file($fname))
		{
			$beforeClasses = get_declared_classes();
			$beforeClassesCount = count($beforeClasses);
			include_once($fname);
			$afterClasses = get_declared_classes();
			$afterClassesCount = count($afterClasses);

			for ($i = $beforeClassesCount; $i < $afterClassesCount; $i++)
			{
				if (!isset(self::$classes[$afterClasses[$i]]) && is_subclass_of($afterClasses[$i], "cbitrixcomponent"))
				{
					if (!isset(self::$__classes_map[$componentPath]) || is_subclass_of($afterClasses[$i], self::$__classes_map[$componentPath]))
					{
						self::$__classes_map[$componentPath] = $afterClasses[$i];

						//recursion control
						self::$classes[$afterClasses[$i]] = true;
					}
				}
			}
		}
		else
		{
			self::$__classes_map[$componentPath] = "";
		}
	}
	return self::$__classes_map[$componentPath];
}
 
Готово, ошибка должна уйти