ฉันจะรับชื่อคลาส (สั้น) ที่ไม่มีเงื่อนไขของวัตถุได้อย่างไร


153

ฉันจะตรวจสอบคลาสของวัตถุภายในสภาพแวดล้อมแบบเว้นวรรคชื่อ PHP ได้อย่างไรโดยไม่ต้องระบุคลาสเนมสเปซเต็มรูปแบบ

ตัวอย่างเช่นสมมติว่าฉันมีไลบราวัตถุ / นิติบุคคล / สัญญา / ชื่อ

รหัสต่อไปนี้ใช้งานไม่ได้เนื่องจาก get_class ส่งคืนคลาสเนมสเปซเต็ม

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

คีย์เวิร์ดมายากลเนมสเปซส่งคืนเนมสเปซปัจจุบันซึ่งไม่มีประโยชน์หากอ็อบเจ็กต์ที่ทดสอบมีเนมสเปซอื่น

ฉันสามารถระบุชื่อคลาสแบบเต็มด้วยเนมสเปซได้ แต่นี่ดูเหมือนจะล็อคในโครงสร้างของโค้ด ยังไม่ได้ใช้มากถ้าฉันต้องการเปลี่ยน namespace แบบไดนามิก

ทุกคนสามารถคิดวิธีที่มีประสิทธิภาพในการทำเช่นนี้ ฉันเดาตัวเลือกหนึ่งคือ regex


ดูเหมือนว่าใกล้จะไม่มีจุดหมายเพราะเนมสเปซที่แตกต่างกันอาจมีชื่อคลาสเดียวกันที่กำหนดไว้ในนั้นดังนั้นคุณจะจัดการกับมันอย่างไร และนั่นเป็นเพราะชื่อคลาสแบบเต็มถูกส่งคืนในตัวอย่างของคุณ
แอลมาโด

ฉันใช้อุปกรณ์พกพาดังนั้นฉันจึงไม่สามารถส่งคำตอบที่เหมาะสม แต่โซลูชันคือภาพสะท้อนโดยเฉพาะ ReflectionClass :: getShortName - php.net/manual/en/reflectionclass.getshortname.php
lonesomeday

สำหรับผู้ที่กำลังมองหาเหตุผลที่ต้องการสิ่งนี้: มันอาจมีประโยชน์ในฟังก์ชั่นตัวช่วยในคลาสพื้นฐานทั่วไป (เช่นหลายเนมสเปซไม่เคยมีปัญหาในสถานการณ์นี้)
Darren Cook

คำตอบ:


182

คุณสามารถทำได้ด้วยการสะท้อนกลับ คุณสามารถใช้ReflectionClass::getShortNameเมธอดซึ่งรับชื่อคลาสโดยไม่มีเนมสเปซ

ก่อนอื่นคุณต้องสร้างReflectionClassอินสแตนซ์จากนั้นเรียกใช้getShortNameเมธอดของอินสแตนซ์นั้น:

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

อย่างไรก็ตามฉันไม่สามารถจินตนาการถึงสถานการณ์มากมายที่น่าพอใจ instanceofหากคุณต้องการที่จะต้องว่าวัตถุที่เป็นสมาชิกของชั้นหนึ่งวิธีในการทดสอบจะมี หากคุณต้องการวิธีที่ยืดหยุ่นมากขึ้นในการส่งสัญญาณข้อ จำกัด บางอย่างวิธีการทำเช่นนั้นคือการเขียนอินเทอร์เฟซและต้องการให้โค้ดใช้อินเตอร์เฟสนั้น instanceofอีกครั้งวิธีที่ถูกต้องที่จะทำนี้ด้วย (คุณสามารถทำได้ด้วยReflectionClassแต่จะมีประสิทธิภาพแย่ลงกว่าเดิมมาก)


1
@ Greg.Forbes เนื่องจากTenantไม่มีอยู่ใน namespace ปัจจุบัน ลองvar_dump($tenant instanceof \Library\Entity\People\Tenant)แทน ตรวจสอบวิธีการใช้useโอเปอเรเตอร์และแนวคิดทั่วไปที่อยู่เบื้องหลัง PHP namespaces!
lonesomeday

3
ฉันต้องเพิ่มเครื่องหมายทับด้านหน้าแบบนี้$reflect = new \ReflectionClass($object);
prograhammer

7
โดยทั่วไปฉันไม่ชอบ ReflectionClass voodoo จำนวนมากในแอปพลิเคชันของฉันเพราะสามารถนำไปสู่ผลลัพธ์ที่ไม่คาดคิดหากใช้ผิดวิธี คุณสามารถใช้การแทนที่สตริงอย่างง่ายบนค่าคงที่เวทย์ PHP แทน: str_replace(__NAMESPACE__ . '\\', '', __CLASS__);. นอกจากนี้ยังเร็วกว่าและมีประสิทธิภาพดีกว่า
Franklin P Strube

2
@ FrankSPSTrube ถ้าฉันไม่ได้ทำอะไรบางอย่างที่ได้รับชื่อสั้น ๆ ของคลาสปัจจุบันมากกว่าคลาสของวัตถุ ฉันเห็นด้วยว่าการใช้ภาพสะท้อนมักจะหมายความว่าคุณทำผิด
lonesomeday

1
หลายคนใช้ Reflections เพื่อลบล้างการมองเห็นสมาชิกซึ่งก็คือ BAD อย่าทำอย่างนั้น! แต่การระบุว่าการใช้งานรีเฟลคชั่นโดยทั่วไปคือวูดูและทำในสิ่งที่ผิดจะทำให้ผู้คนประทับใจ คุณไม่ควรหลีกเลี่ยงพวกเขาคุณควรเข้าใจพวกเขาและรู้ว่าเมื่อใดที่พวกเขาได้รับประโยชน์และในระดับที่เป็นนามธรรม
Vanja D.

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 GB ram) การสะท้อนกลับ: 0.382, ชื่อฐาน: 0.380, ระเบิด: 0.399 ฉันคิดว่ามันขึ้นอยู่กับระบบของคุณว่าอะไรดีที่สุด ...
Tobias Nyholm

4
เรียกใช้ PHP 10 000 ครั้งด้วยรหัสนั้นและคุณจะได้รับผลลัพธ์ที่ดีขึ้น ด้านบนอาจดึงการสะท้อนกลับจากพูลบางตัว แต่นี่ไม่ใช่พฤติกรรมปกติของแอปพลิเคชัน พวกเขาต้องการเพียงครั้งเดียวหรือสองครั้ง
LeMike

6
ฉันสงสัยว่าการทดสอบนี้จะเกิดขึ้นจริงหรือไม่เมื่อใช้อินเทอร์เฟซ ReflectionClass บนวัตถุที่มีขนาดใหญ่กว่าวัตถุขนาดเล็กของ Class A ในการทดสอบของคุณ ...
Joe Green

2
ใช้การวนซ้ำเพียงครั้งเดียวแทนที่จะเป็น 100,000 ให้ผลลัพธ์ที่แตกต่างกันมาก: การสะท้อนกลับ: 1.0967254638672 100000th / s ClassA Basename: 0.81062316894531 100000th / s ClassA ระเบิด: 0.50067901611328 100000th / s
ClassA

1
explode ('\\', static :: class) [0]? จะไม่ส่งคืนส่วนแรกของเนมสเปซหรือไม่ ควรกลับส่วนสุดท้ายไม่ใช่ครั้งแรก
2oppin

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";

== == UPDATE

ดังที่ได้กล่าวไว้ในความคิดเห็นโดย @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 - Basename: 0.13206427097321 s ClassA - ระเบิด: 0.15331919193268 s ClassA - Substring: 0.068068099021912 s ClassA - Strrchar: 0.06472008228302 s
ClassA

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
Franklin P Strube

1
@MrBandersnatch คุณถูกต้อง ฉันทดสอบโซลูชันของคุณและมันช่วยฉันได้ประมาณ 0.001 วิ ฉันอัปเดตคำตอบของคุณกับคุณ!
MaBi

3
คำเตือน: รหัสนี้ใช้ไม่ได้กับคลาสในเนมสเปซส่วนกลาง (เช่น: ชื่อเต็มของพวกเขาเท่ากับชื่อย่อ)! ฉันแนะนำให้ทดสอบสิ่งif ($pos = strrchr(static::class, '\\')) { .. } else { ... }ต่อไปนี้:
อุโมงค์ Jahier

1
ในการทำให้มันทำงานในเนมสเปซส่วนกลางด้วยเพียงเพิ่ม classname ด้วยแบ็กสแลช :) - เช่น:$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
Steve Buzonas

6
ฉันคิดว่าเขาพูดแบบนั้น
สก็อตต์

4
ขอบคุณฉันใช้ Laravel และคำตอบนี้ช่วยให้ฉันประหยัดเวลา
Jeremy Wadhams


18

ฉันใช้สิ่งนี้:

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

คุณสามารถลอง: $ className = explode ('\\', basename (get_class ($ this))); $ className = array_pop ($ className); เพื่อรับ classname ธรรมดา หรือใช้พื้นผิว
dompie

13
ใช้งานได้กับ Windows บน Windows ทั้งสองใช้เครื่องหมายทับ (/) และแบ็กสแลช () เป็นอักขระตัวคั่นไดเรกทอรี ในสภาพแวดล้อมอื่นมันเป็นฟอร์เวิร์ดสแลช (/) php.net/manual/en/function.basename.php
OzzyCzech

ฉันได้แก้ไขแล้ว ขอบคุณ @OzzyCzech
Theodore R. Smith

1
@OzzyCzech ฉันเพิ่งพบเจอสิ่งนี้ในขณะที่ย้ายจาก Windows ไปยัง Ubuntu .... น่าคลั่ง แผลโดยใช้วิธีการแก้ปัญหาที่กล่าวถึงในการอัพเดทของ MaBi
Chris Baker

@OzzyCzech วิธีการทำงานบน Windows เท่านั้น? คำถามนั้นเกี่ยวกับชื่อเนมสเปซที่ผ่านการรับรองหากฉันไม่ผิดเมื่อหลายปีก่อนและเนมสเปซไม่ได้เฉพาะกับระบบปฏิบัติการและมักมีเครื่องหมายแบ็กสแลชเหมือนตัวคั่นไดเรกทอรี windows
FantomX1

16

ในการรับชื่อย่อเป็นหนึ่งซับ (ตั้งแต่PHP 5.4 ):

echo (new ReflectionClass($obj))->getShortName();

มันเป็นวิธีการทำความสะอาดและรวดเร็วที่เหมาะสม


1
ฉันสงสัยว่าสิ่งนี้เปรียบเทียบกับการแยกสตริงในการวัดประสิทธิภาพได้อย่างไร ดูเหมือนว่าจะช้ากว่านี้มาก
ติดต่อที่ไม่ผ่านการยืนยัน

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());

รายการผลลัพธ์ทั้งหมดอยู่ที่นี่แต่นี่คือไฮไลท์:

  • หากคุณกำลังจะสะท้อนการใช้ anyways ใช้$obj->getShortName()เป็นวิธีที่เร็วที่สุดอย่างไรก็ตาม ; ใช้การสะท้อนเพียงเพื่อให้ได้ชื่อสั้น ๆ มันเกือบจะเป็นวิธีที่ช้าที่สุด
  • 'strrpos'สามารถคืนค่าผิดถ้าวัตถุไม่อยู่ใน namespace ดังนั้นในขณะที่'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 function basename () ยกเว้นว่าจะใช้ทั้ง \ และ / เป็นตัวคั่นไดเรกทอรีโดยไม่ขึ้นกับระบบปฏิบัติการ วิธีนี้ส่วนใหญ่ถูกสร้างขึ้นเพื่อทำงานกับ php namespaces เมื่อทำงานกับพา ธ ไฟล์จริง phename ของ 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


ยินดีต้อนรับสู่ Stack Overflow โปรดให้ข้อมูลเพิ่มเติมสำหรับคำตอบของคุณ สิ่งนี้ทำอะไรและจะใช้งานได้อย่างไร
เจนส์

1
สิ่งนี้ใช้ได้กับฉันบน Windows แต่ไม่ใช่ใน Linux อาจเป็นเพราะเนมสเปซอยู่ในรูปแบบของไดเรกทอรี Windows backslashes '\' ในขณะที่ linux basename พิจารณาว่าตัวคั่นไดเรกทอรีส่งต่อเครื่องหมายทับ '/' ดังนั้นฉันจึงทำงานกับ strtr ' basename (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 ดอทคอม

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แล้วจะเป็น$baseClassoo

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

สิ่งนี้สามารถแก้ไขได้ง่าย ๆget_classด้วยการใส่แบ็กสแลชไว้ล่วงหน้า:

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

ตอนนี้คลาสที่ไม่มีเนมสเปซจะส่งคืนค่าที่ถูกต้อง


1

Regex แบบเก่าที่ดีดูเหมือนจะเร็วกว่าวิธีที่แสดงไว้ก่อนหน้านี้ส่วนใหญ่:

// both of the below calls will output: ShortClassName

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

ดังนั้นสิ่งนี้จึงใช้ได้แม้ว่าคุณจะให้ชื่อคลาสแบบสั้นหรือชื่อคลาสแบบเต็ม (มาตรฐาน)

สิ่งที่ regex ทำคือมันใช้ตัวอักษรก่อนหน้าทั้งหมดจนกระทั่งพบตัวคั่นล่าสุด (ซึ่งถูกใช้ด้วย) ดังนั้นสตริงที่เหลือจะเป็นชื่อคลาสสั้น ๆ

หากคุณต้องการใช้ตัวคั่นอื่น (เช่น. /) ให้ใช้ตัวคั่นนั้นแทน อย่าลืม backslash (เช่น. \) และรูปแบบอักขระ 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

ทางออกที่ง่ายที่สุดที่เร็วที่สุดและ imho ที่ทำงานในสภาพแวดล้อมใด ๆ คือ:

<?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));

โดยทั่วไปมันเป็น regex ที่จะได้รับการรวมกันของตัวละครหรือแบ็กสแลชและจนกระทั่งแบ็กสแลชสุดท้ายจากนั้นจะส่งกลับเฉพาะตัวละครที่ไม่ใช่แบ็กสแลชขึ้นและจนถึงจุดสิ้นสุดของสตริง กำลังเพิ่ม หลังจากการจัดกลุ่มครั้งแรกหมายความว่าหากไม่มีรูปแบบการจับคู่มันก็แค่ส่งกลับสตริงเต็ม


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.