Як отримати некваліфіковане (коротке) ім'я класу об'єкта?


153

Як перевірити клас об'єкта в середовищі, розміщеному в імені PHP, не вказуючи повний клас простору імен.

Наприклад, припустимо, у мене була бібліотека об'єктів / Суб'єкт / Контракт / Ім'я.

Наступний код не працює, оскільки get_class повертає повний клас простору імен.

If(get_class($object) == 'Name') {
... do this ...
}

Магічне ключове слово простору імен повертає поточний простір імен, який не є корисним, якщо тестований об'єкт має інший простір імен.

Я міг би просто вказати повне ім’я класу з просторами імен, але це, здається, замикається в структурі коду. Також не приносить великої користі, якщо я хотів динамічно змінити простір імен.

Хто-небудь може придумати ефективний спосіб зробити це. Я здогадуюсь, один із варіантів - регулярний вираз.


Це здається майже безглуздим, оскільки різні простори імен можуть мати однакові імена класів, визначені всередині них, так як ви будете з цим впоратися? І це тому, що повне ім'я класу повертається у вашому зразку
Alma Do

Я на мобільному пристрої, так що я не можу уявити гідну відповідь, але рішення відображення, в зокрема , ReflectionClass :: getShortName - php.net/manual/en/reflectionclass.getshortname.php
lonesomeday

Люди, які шукають причини, хочуть цього: це може бути корисним для функції помічника в загальному базовому класі (тобто декілька просторів імен ніколи не є проблемою у цій ситуації).
Даррен Кук

Відповіді:


182

Це можна зробити за допомогою роздумів. Зокрема, ви можете використовувати ReflectionClass::getShortNameметод, який отримує назву класу без його простору імен.

Спочатку потрібно створити ReflectionClassекземпляр, а потім викликати getShortNameметод цього примірника:

$reflect = new ReflectionClass($object);
if ($reflect->getShortName() === 'Name') {
    // do this
}

Однак я не уявляю багатьох обставин, коли це було б бажано. Якщо ви хочете вимагати, щоб об’єкт є членом певного класу, спосіб його перевірити instanceof. Якщо ви хочете більш гнучким способом передавати певні обмеження, спосіб зробити це - написати інтерфейс і вимагати, щоб код реалізував цей інтерфейс. Знову ж, правильний спосіб це зробити instanceof. (Ви можете це зробити ReflectionClass, але це матиме набагато гірші показники.)


1
@ Greg.Forbes Тому Tenantщо не існує в поточному просторі імен. Спробуйте var_dump($tenant instanceof \Library\Entity\People\Tenant)замість цього. Також вивчіть, як користуватися useоператором та загальною концепцією, що стоїть за просторами імен PHP!
самотній день

3
Мені довелося додати $reflect = new \ReflectionClass($object);
нахил наперед так,

7
Я, як правило, не люблю робити багато вуду в ReflectionClass у своїй програмі, тому що це може призвести до несподіваних результатів при неправильному використанні (захищені методи стають загальнодоступними тощо). Ви можете використовувати просту заміну рядка на магічні константи PHP замість: str_replace(__NAMESPACE__ . '\\', '', __CLASS__);. Це також набагато швидше, ефективніше.
Франклін П Струбе

2
@FranklinPStrube Якщо я чогось не пропускаю, він отримує коротке ім'я поточного класу, а не клас об'єкта. Я згоден, що використання рефлексії зазвичай означає, що ти робиш це неправильно.
самотній день

1
Багато людей використовують Роздуми для перегляду видимості членів, що є BAD. Не роби цього! Але твердження, що використання Роздумів взагалі є Вуду, і робити це неправильно, створює на людей неправильне враження. Вам не слід уникати їх, ви повинні їх розуміти і знати, коли вони приносять користь і на якому рівні абстрагування.
Ваня Д.

131

(new \ReflectionClass($obj))->getShortName(); є найкращим рішенням щодо продуктивності.

Мені було цікаво, яке із запропонованих рішень є найшвидшим, тому я склав невеликий тест.

Результати

Reflection: 1.967512512207 s ClassA
Basename:   2.6840535163879 s ClassA
Explode:    2.6507515668869 s ClassA

Код

namespace foo\bar\baz;

class ClassA{
    public function getClassExplode(){
        return explode('\\', static::class)[0];
    }

    public function getClassReflection(){
        return (new \ReflectionClass($this))->getShortName();
    }

    public function getClassBasename(){
        return basename(str_replace('\\', '/', static::class));
    }
}

$a = new ClassA();
$num = 100000;

$rounds = 10;
$res = array(
    "Reflection" => array(),
    "Basename" => array(),
    "Explode" => array(),
);

for($r = 0; $r < $rounds; $r++){

    $start = microtime(true);
    for($i = 0; $i < $num; $i++){
        $a->getClassReflection();
    }
    $end = microtime(true);
    $res["Reflection"][] = ($end-$start);

    $start = microtime(true);
    for($i = 0; $i < $num; $i++){
        $a->getClassBasename();
    }
    $end = microtime(true);
    $res["Basename"][] = ($end-$start);

    $start = microtime(true);
    for($i = 0; $i < $num; $i++){
        $a->getClassExplode();
    }
    $end = microtime(true);
    $res["Explode"][] = ($end-$start);
}

echo "Reflection: ".array_sum($res["Reflection"])/count($res["Reflection"])." s ".$a->getClassReflection()."\n";
echo "Basename: ".array_sum($res["Basename"])/count($res["Basename"])." s ".$a->getClassBasename()."\n";
echo "Explode: ".array_sum($res["Explode"])/count($res["Explode"])." s ".$a->getClassExplode()."\n";

Результати насправді мене здивували. Я думав, що вибухне рішення буде найшвидшим шляхом ...


1
Чудова відповідь. Я використовував той самий код, але отримав інший результат (Macbook Pro i7, 16 ГБ оперативної пам’яті). Відображення: 0,382, Базова назва: 0,380, Вибух: 0,399. Я думаю, що від вашої системи залежить те, що найкраще ...
Тобіас Ніхольм

4
Запустіть PHP 10 000 разів з цим кодом, і ви отримаєте кращий результат. Вищезазначене може отримати відображення з якогось пулу, але це не звичайна поведінка додатків там. Їм це потрібно лише раз чи два.
LeMike

6
Цікаво, чи справді цей тест справджується, коли екземпляр ReflectionClass на більш істотному об'єкті, ніж малий об’єкт класу A у вашому тесті ...
Джо Грін

2
запуск лише однієї ітерації замість 100000 дає набагато інший результат: Відбиття: 1.0967254638672 100000th / s ClassA Базова назва: 0.81062316894531 100000th / s ClassA Вибух: 0.50067901611328 100000th / s ClassA
mcmurphy

1
вибухнути ('\\', статичний :: клас) [0]? чи не повертається перша частина простору імен? слід повернути останню частину, а не першу
2

86

Я додав substr до тесту https://stackoverflow.com/a/25472778/2386943, і це найшвидший спосіб, який я міг би протестувати (CentOS PHP 5.3.3, Ubuntu PHP 5.5.9) і з i5.

$classNameWithNamespace=get_class($this);
return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\\')+1);

Результати

Reflection: 0.068084406852722 s ClassA
Basename: 0.12301609516144 s ClassA
Explode: 0.14073524475098 s ClassA
Substring: 0.059865570068359 s ClassA 

Код

namespace foo\bar\baz;
class ClassA{
  public function getClassExplode(){
    $c = array_pop(explode('\\', get_class($this)));
    return $c;
  }

  public function getClassReflection(){
    $c = (new \ReflectionClass($this))->getShortName();
    return $c;
  }

  public function getClassBasename(){
    $c = basename(str_replace('\\', '/', get_class($this)));
    return $c;
  }

  public function getClassSubstring(){
    $classNameWithNamespace = get_class($this);
    return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\\')+1);
  }
}

$a = new ClassA();
$num = 100000;

$rounds = 10;
$res = array(
    "Reflection" => array(),
    "Basename" => array(),
    "Explode" => array(),
    "Substring" => array()
);

for($r = 0; $r < $rounds; $r++){

  $start = microtime(true);
  for($i = 0; $i < $num; $i++){
    $a->getClassReflection();
  }
  $end = microtime(true);
  $res["Reflection"][] = ($end-$start);

  $start = microtime(true);
  for($i = 0; $i < $num; $i++){
    $a->getClassBasename();
  }
  $end = microtime(true);
  $res["Basename"][] = ($end-$start);

  $start = microtime(true);
  for($i = 0; $i < $num; $i++){
    $a->getClassExplode();
  }
  $end = microtime(true);
  $res["Explode"][] = ($end-$start);

  $start = microtime(true);
  for($i = 0; $i < $num; $i++){
    $a->getClassSubstring();
  }
  $end = microtime(true);
  $res["Substring"][] = ($end-$start);
}

echo "Reflection: ".array_sum($res["Reflection"])/count($res["Reflection"])." s ".$a->getClassReflection()."\n";
echo "Basename: ".array_sum($res["Basename"])/count($res["Basename"])." s ".$a->getClassBasename()."\n";
echo "Explode: ".array_sum($res["Explode"])/count($res["Explode"])." s ".$a->getClassExplode()."\n";
echo "Substring: ".array_sum($res["Substring"])/count($res["Substring"])." s ".$a->getClassSubstring()."\n";

== ОНОВЛЕННЯ ==

Як згадується в коментарях @MrBandersnatch, є навіть більш швидкий спосіб зробити це:

return substr(strrchr(get_class($this), '\\'), 1);

Ось оновлені результати тестування з "SubstringStrChr" (економить до 0,001 с):

Reflection: 0.073065280914307 s ClassA
Basename: 0.12585079669952 s ClassA
Explode: 0.14593172073364 s ClassA
Substring: 0.060415267944336 s ClassA
SubstringStrChr: 0.059880912303925 s ClassA

5
Тільки тому, що ми перераховуючи ефективність, я вважаю, що це найшвидше, порівняння з тесту, передбаченого в цьому рішенні substr (strrchr (get_class ($ obj), '\\'), 1); Відображення: 0,084223914146423 s ClassA - Базова назва: 0,13206427097321 s ClassA - Вибухнути: 0,15331919193268 s ClassA - Підряд: 0.068068099021912 s ClassA - Strrchar: 0.06472008228302 s ClassA -
ctatro85

Я щойно натрапив на цю тему і додав додатковий тест для тестування str_replace(__NAMESPACE__ . '\\', '', __CLASS__);. Результати на слабкій віртуальній машині показали, що вона майже вдвічі швидша за всі ці. php -f bench.php Reflection: 0.44037771224976 s ClassA Basename: 0.48089025020599 s ClassA Explode: 0.54955270290375 s ClassA Substring: 0.38200764656067 s ClassA Frank's Custom Benchmark: 0.22782742977142 s ClassA
Франклін П Струбе

1
@MrBandersnatch ви праві. Я перевірив ваше рішення, і це врятувало мене приблизно 0,001 с. Я оновив свою відповідь вашою!
MaBi

3
Попередження: цей код не працює з класами у глобальному просторі імен (тобто: їх повне ім'я дорівнює їх короткому імені)! Я рада тест що - щось на кшталт: if ($pos = strrchr(static::class, '\\')) { .. } else { ... }.
Трістан Джахір

1
Щоб він також працював у глобальному просторі імен, просто додайте ім'я класу із зворотним нахилом :) - тобто:$classNameShort = substr(strrchr('\\' . get_class($this), '\\'), 1);
rosell.dk

25

Ось більш простий спосіб зробити це, якщо ви використовуєте Laravel PHP:

<?php

// usage anywhere
// returns HelloWorld
$name = class_basename('Path\To\YourClass\HelloWorld');

// usage inside a class
// returns HelloWorld
$name = class_basename(__CLASS__);

8
Це не вбудована функція php, це схоже на функцію помічника, яку надає laravel.
Стів Бузонас

6
Я думаю, що він це сказав
Скотт,

4
Дякую, я використовую Laravel, і ця відповідь врятувала мені купу часу.
Джеремі Вадхамс


18

Я використовую це:

basename(str_replace('\\', '/', get_class($object)));

Ви також можете спробувати: $ className = explode ('\\', basename (get_class ($ this))); $ className = array_pop ($ className); щоб отримати звичайне ім’я класу. Або використовувати substr.
dompie

13
Працює лише в Windows У Windows як слэш (/), так і зворотна косова риса () використовуються як символ роздільника каталогів. В інших середовищах це пряма коса лінія
OzzyCzech

Я зараз це виправив. Дякую, @OzzyCzech.
Теодор Р. Сміт

1
@OzzyCzech Я просто зіткнувся з цим під час переходу з Windows на Ubuntu .... з розуму. Закінчується за допомогою рішення, згаданого в оновлення MaBi.
Кріс Бейкер

@OzzyCzech Як це працює лише у Windows? питання стосувалося повністю кваліфікованого імені простору імен, якщо я не помиляюся також багато років тому, а простори імен не є специфічними для ОС, і завжди мають зворотну косу рису, як роздільник каталогу Windows.
FantomX1


12

Я опинився в унікальній ситуації, коли instanceofне можна було використати (конкретно виділені риси імен), і мені було потрібне коротке ім'я найбільш ефективним способом, тому я зробив невеликий власний орієнтир. Він включає всі різні методи та варіації з відповідей у ​​цьому запитанні.

$bench = new \xori\Benchmark(1000, 1000);     # https://github.com/Xorifelse/php-benchmark-closure
$shell = new \my\fancy\namespace\classname(); # Just an empty class named `classname` defined in the `\my\fancy\namespace\` namespace

$bench->register('strrpos', (function(){
    return substr(static::class, strrpos(static::class, '\\') + 1);
})->bindTo($shell));

$bench->register('safe strrpos', (function(){
    return substr(static::class, ($p = strrpos(static::class, '\\')) !== false ? $p + 1 : 0);
})->bindTo($shell));

$bench->register('strrchr', (function(){
    return substr(strrchr(static::class, '\\'), 1);
})->bindTo($shell));

$bench->register('reflection', (function(){
    return (new \ReflectionClass($this))->getShortName();
})->bindTo($shell));

$bench->register('reflection 2', (function($obj){
    return $obj->getShortName();
})->bindTo($shell), new \ReflectionClass($shell));

$bench->register('basename', (function(){
    return basename(str_replace('\\', '/', static::class));
})->bindTo($shell));

$bench->register('explode', (function(){
    $e = explode("\\", static::class);
    return end($e);
})->bindTo($shell));

$bench->register('slice', (function(){
    return join('',array_slice(explode('\\', static::class), -1));
})->bindTo($shell));    

print_r($bench->start());

Список всього результату є тут, але ось основні моменти:

  • Якщо ви все-таки будете використовувати роздуми, однак використання $obj->getShortName()найшвидшого методу ; використовуючи роздуми лише для отримання короткого імені, це майже найповільніший метод.
  • 'strrpos'може повернути неправильне значення, якщо об’єкт не знаходиться в просторі імен, тому в той час як 'safe strrpos'він є трохи повільнішим, я б сказав, що це переможець.
  • Для 'basename'сумісності між Linux та Windows вам потрібно використовувати, str_replace()що робить цей метод найповільнішим з усіх.

Спрощена таблиця результатів, вимірюється швидкість порівняно з найповільнішим методом:

+-----------------+--------+
| registered name | speed  |
+-----------------+--------+
| reflection 2    | 70.75% |
| strrpos         | 60.38% |
| safe strrpos    | 57.69% |
| strrchr         | 54.88% |
| explode         | 46.60% |
| slice           | 37.02% |
| reflection      | 16.75% |
| basename        | 0.00%  |
+-----------------+--------+

8

Ви можете використовувати explodeдля розділення простору імен та endотримання імені класу:

$ex = explode("\\", get_class($object));
$className = end($ex);

7

Yii шлях

\yii\helpers\StringHelper::basename(get_class($model));

Yii використовує цей метод у своєму генераторі коду Gii

Методична документація

Цей метод схожий з базовою назвою функції php () за винятком того, що він буде розглядати і \, і / як роздільники каталогів, незалежно від операційної системи. Цей метод був головним чином створений для роботи над просторами імен php. Працюючи з реальними шляхами файлів, базування php () повинно працювати для вас добре. Примітка: цьому методу не відома фактична файлова система або компоненти контуру, такі як "..".

Більше інформації:

https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseStringHelper.php http://www.yiiframework.com/doc-2.0/yii-helpers-basestringhelper.html#basename()-detail


Ласкаво просимо до переповнення стека. Будь ласка, надайте більше інформації для вашої відповіді. Що це робить і як можна цим користуватися.
Єнс

1
Це працювало для мене в Windows, але не в Linux, можливо, тому, що простори імен у формі зворотних косих каталогів Windows '\', тоді як базове ім'я Linux розглядає роздільники каталогів вперед / / '. Тому я працював із Strtr '. базове ім'я (strtr ($ class, '\\', '/'))
FantomX1

6

Ось просте рішення для PHP 5.4+

namespace {
    trait Names {
        public static function getNamespace() {
            return implode('\\', array_slice(explode('\\', get_called_class()), 0, -1));
        }

        public static function getBaseClassName() {
            return basename(str_replace('\\', '/', get_called_class()));
        }
    }
}

Що буде повернення?

namespace x\y\z {
    class SomeClass {
        use \Names;
    }

    echo \x\y\z\SomeClass::getNamespace() . PHP_EOL; // x\y\z
    echo \x\y\z\SomeClass::getBaseClassName() . PHP_EOL; // SomeClass
}

Розширене ім’я класу та простір імен добре працює для:

namespace d\e\f {

    class DifferentClass extends \x\y\z\SomeClass {

    }

    echo \d\e\f\DifferentClass::getNamespace() . PHP_EOL; // d\e\f
    echo \d\e\f\DifferentClass::getBaseClassName() . PHP_EOL; // DifferentClass
}

Що з класом у глобальному просторі імен?

namespace {

    class ClassWithoutNamespace {
        use \Names;
    }

    echo ClassWithoutNamespace::getNamespace() . PHP_EOL; // empty string
    echo ClassWithoutNamespace::getBaseClassName() . PHP_EOL; // ClassWithoutNamespace
}

3

Якщо вам потрібно знати ім'я класу, яке було викликано всередині класу, і ви не хочете простору імен, ви можете використовувати це

$calledClass = get_called_class();
$name = strpos($calledClass, '\\') === false ?
    $calledClass : substr($calledClass, strrpos($calledClass, '\\') + 1);

Це чудово, коли у вас є метод всередині класу, який поширюється на інші класи. Крім того, це також працює, якщо простори імен взагалі не використовуються.

Приклад:

<?php
namespace One\Two {
    class foo
    {
        public function foo()
        {
            $calledClass = get_called_class();
            $name = strpos($calledClass, '\\') === false ?
                $calledClass : substr($calledClass, strrpos($calledClass, '\\') + 1);

            var_dump($name);
        }
    }
}

namespace Three {
    class bar extends \One\Two\foo
    {
        public function bar()
        {
            $this->foo();
        }
    }
}

namespace {
    (new One\Two\foo)->foo();
    (new Three\bar)->bar();
}

// test.php:11:string 'foo' (length=3)
// test.php:11:string 'bar' (length=3)

2

На основі відповіді @MaBi я зробив це:

trait ClassShortNameTrait
{
    public static function getClassShortName()
    {
        if ($pos = strrchr(static::class, '\\')) {
            return substr($pos, 1);
        } else {
            return static::class;
        }
    }
}

Що ви можете використовувати так:

namespace Foo\Bar\Baz;

class A
{
    use ClassShortNameTrait;
}

A::classповертається Foo\Bar\Baz\A, але A::getClassShortName()повертається A.

Працює для PHP> = 5,5.


2

Я знаю, що це стара публікація, але це те, що я використовую - Швидше, ніж усі, що розміщені вище, просто зателефонуйте цей метод з вашого класу, набагато швидше, ніж використання Reflection

namespace Foo\Bar\Baz;

class Test {
    public function getClass() {
        return str_replace(__NAMESPACE__.'\\', '', static::class);
    }
}

На жаль, це працює лише в тому випадку, якщо ви називаєте його в класі, ім'я якого ви хочете, а не на будь-якому імені класу як рядку.
jurchiks

1

Знайдено на сторінці документації get_class , де вона була розміщена мною на nwhiting dot com .

function get_class_name($object = null)
{
    if (!is_object($object) && !is_string($object)) {
        return false;
    }

    $class = explode('\\', (is_string($object) ? $object : get_class($object)));
    return $class[count($class) - 1];
}

Але ідея просторів імен полягає в структурі коду. Це також означає, що ви можете мати класи з тим самим іменем у кількох просторах імен. Тож теоретично об'єкт, який ви передаєте, міг би мати ім'я (позбавлене) назви класу, при цьому все-таки бути абсолютно іншим об'єктом, ніж ви очікували.

Крім того, ви можете перевірити конкретний базовий клас , і в цьому випадку get_classце зовсім не виходить. Ви можете перевірити оператора instanceof.


1

Ви можете отримати несподіваний результат, коли у класу немає простору імен. Тобто get_classповертається Foo, то $baseClassбуло б oo.

$baseClass = substr(strrchr(get_class($this), '\\'), 1);

Це легко виправити за допомогою префіксації get_classза допомогою нахилу:

$baseClass = substr(strrchr('\\'.get_class($this), '\\'), 1);

Тепер також класи без простору імен повернуть потрібне значення.


1

Старий хороший регулярний вираз здається швидшим, ніж більшість попередніх показаних методів:

// both of the below calls will output: ShortClassName

echo preg_replace('/.*\\\\/', '', 'ShortClassName');
echo preg_replace('/.*\\\\/', '', 'SomeNamespace\SomePath\ShortClassName');

Таким чином, це спрацьовує навіть тоді, коли ви надаєте коротке ім'я класу або повністю кваліфіковане (канонічне) ім'я класу.

Те, що робить регулярний вираз, - це те, що він споживає всі попередні символи, поки не знайдеться останній роздільник (який також споживається). Таким чином, що залишився рядок буде коротким класом.

Якщо ви хочете використовувати інший роздільник (наприклад, /), просто використовуйте його. Не забудьте уникнути зворотної косої риси (тобто. \), А також діаграму char (тобто. /) У вхідному шаблоні.


1

Оскільки "ReflectionClass" може бути версією залежно, просто використовуйте наступне:

if(class_basename(get_class($object)) == 'Name') {
... do this ...
}

або навіть зрозуміло

if(class_basename(ClassName::class) == 'ClassName') {
... do this ...
}

0

Цитуючи php.net:

У Windows як нахил (/), так і зворотний косий () використовується як символ роздільника каталогів. В інших середовищах це пряма коса риса (/).

На основі цієї інформації та розширення з відповіді arzzzen це має працювати як у системах Windows, так і в Nix *:

<?php

if (basename(str_replace('\\', '/', get_class($object))) == 'Name') {
    // ... do this ...
}

Примітка. Я зробив орієнтир ReflectionClassпроти basename+str_replace+get_classі використання відображення приблизно на 20% швидше, ніж використання підходу базової назви, але YMMV.


0

Найшвидше і найпростіше рішення, яке працює в будь-якому середовищі:

<?php

namespace \My\Awesome\Namespace;

class Foo {

  private $shortName;

  public function fastShortName() {
    if ($this->shortName === null) {
      $this->shortName = explode("\\", static::class);
      $this->shortName = end($this->shortName);
    }
    return $this->shortName;
  }

  public function shortName() {
    return basename(strtr(static::class, "\\", "/"));
  }

}

echo (new Foo())->shortName(); // "Foo"

?>

1
Ось чому я хочу, щоб PHP мав внутрішніх операторів інформації класу. Ігнорування зовнішнього відбивача зробити те, що повинно бути таким же простим, як $Object->__class->getShortName()насправді мене змушує про PHP. Ваш підхід працює, але тепер ви кладете конкретні методи у свої класи просто для того, щоб викрити те, що має бути мовною конструкцією.
AgmLauncher

PHP без “конкретних” (або, якщо ми їх називаємо процедурними) функцій неможливий. Зачекаємо PHP 6 (добре, якщо він коли-небудь прийде).
Fleshgrinder


0

Якщо ви просто знімаєте пробіли імен і хочете що-небудь після останнього \ у назві класу з простором імен (або просто ім'ям, якщо немає "\"), ви можете зробити щось подібне:

$base_class = preg_replace('/^([\w\\\\]+\\\\)?([^\\\\]+)$/', '$2', get_class($myobject));

По суті, за допомогою регулярного виразів можна отримати будь-яку комбінацію символів або зворотних косих косих ринків, і до останнього косого кута потім повернути лише символи, які не є косою рисою вгору і до кінця рядка. Додавання? після першого групування означає, що якщо відповідність шаблону не існує, вона просто повертає повний рядок.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.