किसी अन्य वर्ग के लिए एक stdClass ऑब्जेक्ट कन्वर्ट / कास्ट करें


89

मैं एक थर्ड पार्टी स्टोरेज सिस्टम का उपयोग कर रहा हूं जो केवल मुझे stdClass ऑब्जेक्ट लौटाता है, चाहे मैं किसी भी अस्पष्ट कारण के लिए फ़ीड करता हूं। इसलिए मुझे यह जानने की उत्सुकता है कि क्या किसी stdClass ऑब्जेक्ट को दिए गए प्रकार के पूर्ण विकसित ऑब्जेक्ट में बदलने / परिवर्तित करने का कोई तरीका है।

उदाहरण के लिए कुछ लाइनों के साथ:

//$stdClass is an stdClass instance
$converted = (BusinessClass) $stdClass;

मैं बस एक सरणी में stdClass कास्ट कर रहा हूं और इसे BusinessClass कंस्ट्रक्टर को खिलाता हूं, लेकिन शायद प्रारंभिक वर्ग को पुनर्स्थापित करने का एक तरीका है जिसके बारे में मुझे जानकारी नहीं है।

नोट: मुझे 'अपनी भंडारण प्रणाली बदलें' प्रकार के उत्तरों में कोई दिलचस्पी नहीं है क्योंकि यह ब्याज की बात नहीं है। कृपया इसे भाषा क्षमताओं पर अधिक अकादमिक प्रश्न मानें।

चियर्स


यह छद्म कोड नमूने के बाद मेरी पोस्ट में समझाया गया है। मैं एक सरणी में कास्टिंग कर रहा हूं और एक स्वचालित निर्माता को खिला रहा हूं।
माइटी रबर डक

@Adam Puza का उत्तर स्वीकार किए गए उत्तर में दिखाए गए हैक से बहुत बेहतर है। हालांकि मुझे यकीन है कि एक मैपर अभी भी पसंदीदा तरीका होगा
क्रिस

वैसे PDOStatement::fetchObjectयह कार्य कैसे पूरा होता है?
विलियम एंट्रिएन

जवाबों:


88

संभव कलाकारों पर टाइप जुगलिंग पर मैनुअल देखें ।

अनुमत जातियां हैं:

  • (int), (पूर्णांक) - पूर्णांक के लिए डाली
  • (बूल), (बूलियन) - कास्ट टू बूलियन
  • (फ्लोट), (डबल), (रियल) - कास्ट टू फ्लोट
  • (स्ट्रिंग) - कास्ट टू स्ट्रिंग
  • (अरै) - अरेंज टू अरेज
  • (ऑब्जेक्ट) - कास्ट टू ऑब्जेक्ट
  • (परेशान) - NULL (PHP 5) के लिए कास्ट करें

आपको एक Mapper लिखना होगा जो stdClass से दूसरे ठोस वर्ग तक कास्टिंग करता है। करने के लिए बहुत मुश्किल नहीं होना चाहिए।

या, यदि आप एक हैक मूड में हैं, तो आप निम्नलिखित कोड को अनुकूलित कर सकते हैं:

function arrayToObject(array $array, $className) {
    return unserialize(sprintf(
        'O:%d:"%s"%s',
        strlen($className),
        $className,
        strstr(serialize($array), ':')
    ));
}

जो एक निश्चित वर्ग की एक वस्तु के लिए एक सरणी pseudocasts। यह पहले सरणी को क्रमबद्ध करके और फिर क्रमबद्ध डेटा को बदलकर काम करता है ताकि यह एक निश्चित वर्ग का प्रतिनिधित्व करे। परिणाम तब इस वर्ग की एक मिसाल के तौर पर गैर-अधिकृत है। लेकिन जैसा मैंने कहा, यह हैकिश है, इसलिए साइड-इफेक्ट्स की उम्मीद करें।

ऑब्जेक्ट टू ऑब्जेक्ट के लिए, कोड होगा

function objectToObject($instance, $className) {
    return unserialize(sprintf(
        'O:%d:"%s"%s',
        strlen($className),
        $className,
        strstr(strstr(serialize($instance), '"'), ':')
    ));
}

9
यह हैक एक चतुर तकनीक है। समस्या को हल करने के मेरे मौजूदा तरीके के बाद से इसका उपयोग नहीं किया जाएगा, लेकिन फिर भी दिलचस्प है।
शक्तिशाली रबड़ बतख

1
आप __PHP_Incomplete_Classइस विधि का उपयोग करके किसी ऑब्जेक्ट के साथ समाप्त होते हैं (कम से कम PHP 5.6 के रूप में)।
TiMESPLiNTER

1
@TiMESPLiNTER नहीं, आप नहीं। Codepad.org/spGkyLzL देखें । सुनिश्चित करें कि फ़ंक्शन को कॉल करने से पहले कक्षा को शामिल किया गया था।
गॉर्डन

@TiMESPLiNTER निश्चित नहीं है कि आपका क्या मतलब है। इसने काम कर दिया। अब यह एक उदाहरण \ Foo है।
गॉर्डन

हाँ समस्या यह है कि यह stdClass संपत्ति को जोड़ता है। इसलिए आपके पास दो fooBarएस (निजी एक से example\Fooऔर सार्वजनिक एक से stdClass) है। इसके बजाय यह मान को प्रतिस्थापित करता है।
TiMESPLiNTER

53

आप समान वर्ग ऑब्जेक्ट्स (PHP> = 5.3) कास्टिंग के लिए उपरोक्त फ़ंक्शन का उपयोग कर सकते हैं

/**
 * Class casting
 *
 * @param string|object $destination
 * @param object $sourceObject
 * @return object
 */
function cast($destination, $sourceObject)
{
    if (is_string($destination)) {
        $destination = new $destination();
    }
    $sourceReflection = new ReflectionObject($sourceObject);
    $destinationReflection = new ReflectionObject($destination);
    $sourceProperties = $sourceReflection->getProperties();
    foreach ($sourceProperties as $sourceProperty) {
        $sourceProperty->setAccessible(true);
        $name = $sourceProperty->getName();
        $value = $sourceProperty->getValue($sourceObject);
        if ($destinationReflection->hasProperty($name)) {
            $propDest = $destinationReflection->getProperty($name);
            $propDest->setAccessible(true);
            $propDest->setValue($destination,$value);
        } else {
            $destination->$name = $value;
        }
    }
    return $destination;
}

उदाहरण:

class A 
{
  private $_x;   
}

class B 
{
  public $_x;   
}

$a = new A();
$b = new B();

$x = cast('A',$b);
$x = cast('B',$a);

5
यह एक बहुत सुंदर समाधान है, मुझे कहना होगा! मुझे आश्चर्य है कि यह कितना अच्छा है ... प्रतिबिंब मुझे डराता है।
थियोडोर आर। स्मिथ

हे एडम, इस समाधान ने यहां मेरे लिए एक समान समस्या हल कर दी: stackoverflow.com/questions/35350585/… यदि आप एक आसान जवाब देना चाहते हैं, तो सिर पर और मैं इसे बंद कर दूँगा। धन्यवाद!

यह मूल वर्ग से विरासत में मिली संपत्तियों के लिए काम नहीं करता है।
21

मैंने अभी-अभी इसका उपयोग बीहट के साथ एपीआई कार्यात्मक परीक्षण के लिए ज्ञात आईडी के साथ अपने डेटाबेस के बीजारोपण के लिए अपने समाधान के रूप में किया है। मेरा मुद्दा यह था कि मेरी सामान्य आईडी UUID बनती हैं और मैं अपनी परीक्षण परत के लिए केवल अपनी इकाई में एक setId () विधि जोड़ना नहीं चाहता था, और मैं जुड़नार फ़ाइलें लोड करना और परीक्षण धीमा करना नहीं चाहता था। अब मैं @Given the user :username has the id :idअपनी सुविधा में शामिल कर सकता हूं, और इसे संदर्भ वर्ग के प्रतिबिंबों के साथ संभाल सकता
हूं

2
महान समाधान, मैं जोड़ना चाहता हूं कि यदि आपको कंस्ट्रक्टर को कॉल करने से बचने की आवश्यकता है, तो इसके $destination = new $destination();साथ स्वैप किया जा सकता है $destination = ( new ReflectionClass( $destination ) )->newInstanceWithoutConstructor();
scuzzy

14

stdClassकिसी निर्दिष्ट वर्ग नाम के नए ऑब्जेक्ट के सभी मौजूदा गुणों को स्थानांतरित करने के लिए :

/**
 * recast stdClass object to an object with type
 *
 * @param string $className
 * @param stdClass $object
 * @throws InvalidArgumentException
 * @return mixed new, typed object
 */
function recast($className, stdClass &$object)
{
    if (!class_exists($className))
        throw new InvalidArgumentException(sprintf('Inexistant class %s.', $className));

    $new = new $className();

    foreach($object as $property => &$value)
    {
        $new->$property = &$value;
        unset($object->$property);
    }
    unset($value);
    $object = (unset) $object;
    return $new;
}

उपयोग:

$array = array('h','n');

$obj=new stdClass;
$obj->action='auth';
$obj->params= &$array;
$obj->authKey=md5('i');

class RestQuery{
    public $action;
    public $params=array();
    public $authKey='';
}

$restQuery = recast('RestQuery', $obj);

var_dump($restQuery, $obj);

आउटपुट:

object(RestQuery)#2 (3) {
  ["action"]=>
  string(4) "auth"
  ["params"]=>
  &array(2) {
    [0]=>
    string(1) "h"
    [1]=>
    string(1) "n"
  }
  ["authKey"]=>
  string(32) "865c0c0b4ab0e063e5caa3387c1a8741"
}
NULL

यह newऑपरेटर के कारण सीमित है क्योंकि यह अज्ञात है कि इसे किन मापदंडों की आवश्यकता होगी। अपने मामले के लिए शायद फिटिंग।


1
इस विधि का उपयोग करने का प्रयास करने वाले अन्य लोगों को सूचित करने के लिए। इस फ़ंक्शन के लिए एक चेतावनी है कि स्वयं के बाहर एक तात्कालिक वस्तु पर पुनरावृत्ति करने से, कास्ट किए गए ऑब्जेक्ट के भीतर निजी या संरक्षित गुण सेट नहीं कर पाएंगे। ईजी: सार्वजनिक $ ऑर्किटेक्ट की स्थापना = ''; से निजी $ ऑर्किटे = '' तक; E_ERROR में परिणाम: प्रकार 1 - निजी संपत्ति RestQuery तक नहीं पहुंच पा :: $ authKey
fyrye

निजी गुणों के साथ एक stdClass हालांकि?
फराज

@Frug ओपी विशेष रूप से इस आवश्यकता को इंगित करता है ... किसी दिए गए प्रकार के एक पूर्ण ऑब्जेक्ट में एक stdClass ऑब्जेक्ट को कास्ट / कनवर्ट करें
oucil

11

मुझे एक समान समस्या है। सरलीकृत प्रतिबिंब समाधान मेरे लिए ठीक काम किया:

public static function cast($destination, \stdClass $source)
{
    $sourceReflection = new \ReflectionObject($source);
    $sourceProperties = $sourceReflection->getProperties();
    foreach ($sourceProperties as $sourceProperty) {
        $name = $sourceProperty->getName();
        $destination->{$name} = $source->$name;
    }
    return $destination;
}

8

आशा है कि किसी को यह उपयोगी लगता है

// new instance of stdClass Object
$item = (object) array(
    'id'     => 1,
    'value'  => 'test object',
);

// cast the stdClass Object to another type by passing
// the value through constructor
$casted = new ModelFoo($item);

// OR..

// cast the stdObject using the method
$casted = new ModelFoo;
$casted->cast($item);
class Castable
{
    public function __construct($object = null)
    {
        $this->cast($object);
    }

    public function cast($object)
    {
        if (is_array($object) || is_object($object)) {
            foreach ($object as $key => $value) {
                $this->$key = $value;
            }
        }
    }
} 
class ModelFoo extends Castable
{
    public $id;
    public $value;
}

क्या आप बता सकते हैं कि "is_array ($ object) क्यों है_ is_array ($ object)"?
कुकिंसुला

5

गहरी ढलाई के लिए परिवर्तित कार्य (पुनरावर्तन का उपयोग करके)

/**
 * Translates type
 * @param $destination Object destination
 * @param stdClass $source Source
 */
private static function Cast(&$destination, stdClass $source)
{
    $sourceReflection = new \ReflectionObject($source);
    $sourceProperties = $sourceReflection->getProperties();
    foreach ($sourceProperties as $sourceProperty) {
        $name = $sourceProperty->getName();
        if (gettype($destination->{$name}) == "object") {
            self::Cast($destination->{$name}, $source->$name);
        } else {
            $destination->{$name} = $source->$name;
        }
    }
}

2

और फिर भी डेकोरेटर पैटर्न और PHPs मैजिक गेट्टर और सेटर का उपयोग करते हुए एक और दृष्टिकोण:

// A simple StdClass object    
$stdclass = new StdClass();
$stdclass->foo = 'bar';

// Decorator base class to inherit from
class Decorator {

    protected $object = NULL;

    public function __construct($object)
    {
       $this->object = $object;  
    }

    public function __get($property_name)
    {
        return $this->object->$property_name;   
    }

    public function __set($property_name, $value)
    {
        $this->object->$property_name = $value;   
    }
}

class MyClass extends Decorator {}

$myclass = new MyClass($stdclass)

// Use the decorated object in any type-hinted function/method
function test(MyClass $object) {
    echo $object->foo . '<br>';
    $object->foo = 'baz';
    echo $object->foo;   
}

test($myclass);

0

BTW: यदि आप क्रमबद्ध हैं, तो परिवर्तित करना बेहद महत्वपूर्ण है, क्योंकि मुख्य रूप से डी-क्रमांकन वस्तुओं के प्रकार को तोड़ता है और डेटाइम वस्तुओं सहित stdclass में बदल जाता है।

मैंने @Jadrovski का उदाहरण अद्यतन किया, अब यह वस्तुओं और सरणियों की अनुमति देता है।

उदाहरण

$stdobj=new StdClass();
$stdobj->field=20;
$obj=new SomeClass();
fixCast($obj,$stdobj);

उदाहरण सरणी

$stdobjArr=array(new StdClass(),new StdClass());
$obj=array(); 
$obj[0]=new SomeClass(); // at least the first object should indicates the right class.
fixCast($obj,$stdobj);

कोड: (इसकी पुनरावर्ती)। हालाँकि, मुझे नहीं पता कि इसकी सरणियाँ सरणियों के साथ हैं। हो सकता है कि इसकी अनुपस्थिति एक अतिरिक्त is_array हो

public static function fixCast(&$destination,$source)
{
    if (is_array($source)) {
        $getClass=get_class($destination[0]);
        $array=array();
        foreach($source as $sourceItem) {
            $obj = new $getClass();
            fixCast($obj,$sourceItem);
            $array[]=$obj;
        }
        $destination=$array;
    } else {
        $sourceReflection = new \ReflectionObject($source);
        $sourceProperties = $sourceReflection->getProperties();
        foreach ($sourceProperties as $sourceProperty) {
            $name = $sourceProperty->getName();
            if (is_object(@$destination->{$name})) {
                fixCast($destination->{$name}, $source->$name);
            } else {
                $destination->{$name} = $source->$name;
            }
        }
    }
}

0

BusinessClass में एक नई विधि जोड़ने पर विचार करें:

public static function fromStdClass(\stdClass $in): BusinessClass
{
  $out                   = new self();
  $reflection_object     = new \ReflectionObject($in);
  $reflection_properties = $reflection_object->getProperties();
  foreach ($reflection_properties as $reflection_property)
  {
    $name = $reflection_property->getName();
    if (property_exists('BusinessClass', $name))
    {
      $out->{$name} = $in->$name;
    }
  }
  return $out;
}

तब आप $ stdClass से एक नया BusinessClass बना सकते हैं:

$converted = BusinessClass::fromStdClass($stdClass);

0

फिर भी एक और दृष्टिकोण।

निम्नलिखित अब हाल ही में PHP 7 संस्करण के लिए संभव धन्यवाद है।

$theStdClass = (object) [
  'a' => 'Alpha',
  'b' => 'Bravo',
  'c' => 'Charlie',
  'd' => 'Delta',
];

$foo = new class($theStdClass)  {
  public function __construct($data) {
    if (!is_array($data)) {
      $data = (array) $data;
    }

    foreach ($data as $prop => $value) {
      $this->{$prop} = $value;
    }
  }
  public function word4Letter($letter) {
    return $this->{$letter};
  }
};

print $foo->word4Letter('a') . PHP_EOL; // Alpha
print $foo->word4Letter('b') . PHP_EOL; // Bravo
print $foo->word4Letter('c') . PHP_EOL; // Charlie
print $foo->word4Letter('d') . PHP_EOL; // Delta
print $foo->word4Letter('e') . PHP_EOL; // PHP Notice:  Undefined property

इस उदाहरण में, $ foo को एक अनाम वर्ग के रूप में आरंभीकृत किया जा रहा है जो एक सरणी या stdClass को कंस्ट्रक्टर के लिए केवल पैरामीटर के रूप में लेता है।

आखिरकार, हम पास की गई वस्तु में निहित प्रत्येक आइटम के माध्यम से लूप करते हैं और गतिशील रूप से किसी ऑब्जेक्ट की संपत्ति को असाइन करते हैं।

इस अनुमानित घटना को अधिक सामान्य बनाने के लिए, आप एक इंटरफ़ेस या एक विशेषता लिख ​​सकते हैं जिसे आप किसी भी वर्ग में लागू करेंगे जहां आप एक stdClass कास्ट करने में सक्षम होना चाहते हैं।


0

इसे एक सरणी में परिवर्तित करें, उस सरणी का पहला तत्व लौटाएं, और उस वर्ग में रिटर्न परम सेट करें। अब आपको उस वर्ग के लिए स्वत: पूर्ण होना चाहिए क्योंकि यह उसे स्टैडक्लास के बजाय उस वर्ग के रूप में फिर से जोड़ देगा।

/**
 * @return Order
 */
    public function test(){
    $db = new Database();

    $order = array();
    $result = $db->getConnection()->query("select * from `order` where productId in (select id from product where name = 'RTX 2070')");
    $data = $result->fetch_object("Order"); //returns stdClass
    array_push($order, $data);

    $db->close();
    return $order[0];
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.