skip to Main Content

Using the PHP Stripe Library (https://github.com/stripe) you can instantiate an object like so:

$stripe = new StripeStripeClient( 'sk_test_secretkeyhere' );
$product = $stripe->products->retrieve( 'prod_abc123' );

I’m not looking to understand specifically how Stripe does it. But rather, how a simple example of this could be coded. In other words, how to ensure that a Products object can be instantiated with $prod = $someobject->products->retrieve() but prevent it being instantiated with $prod = new Products.

Also, making this happen without instantiating a Products object until the retrieve() method is called. So for example without instantiating a new Products object in the constructor of the Thing class – see below.

I got as far as this:

class Thing {

    function __get($name) {
      if ($name === 'products') {
        $p = new Products();
        return $p;
      }
    }

  }
  
class Products {

    public $id;
    public $option;

    function retrieve($id, $option) {
        $this->id = $id;
        $this->option = $option;
        return $this;
    }

}

With this code I can get a Products object using this:

$thing = new Thing();
$prod = $thing->products->retrieve('123', 'bob');

But in this case we can also instantiate a Products object like so:

$prod = new Products;

I’d like to prevent instantiating a Products object directly, and force it to happen via the $thing->products->retrieve() notation.

I feel like I must be missing something obvious.

TO CLARIFY: In Stripe’s code, if you try to instantiate a Products object directly, it says there is no such class. You have to do it via the StripeClient object. It is this restriction that I am trying to emulate.

Any help would be greatly appreciated. Thanks.

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to anyone who posted those comments and gave this question some time.

    I think you were all on the right track, and I had missed an obvious thing to check which would have shed a lot of light on this.

    So...

    In response to $prod = $stripe->products->retrieve(...) the Stripe code returns a Product object (not a Products object). And it turns out you CAN instantiate this with new StripeProduct.

    So Stripe haven't prevented instantiating the object directly. I was just testing with the wrong class name.


  2. I don’t think this is possible.

    In order to create an instance of a class, either its constructor must be public, or it must have some other public method(s) that call the constructor. And if there’s a public method that constructs it, that method can be called from anywhere.

    You could make things difficult by having the constructor require an argument that can only come from private data of the class that’s allowed to call it.

    class ClassOne {
        private static $all_sentinels = [];
        private $sentinel;
    
        function __construct() {
            $this->sentinel = randomString();
            $this->all_sentinels[] = $this->sentinels;
        }
    
        function __get($name) {
            if ($name == 'classTwo') {
                return new ClassTwo($this, $this->sentinel);
            }
        }
    
        static function checkSentinel($s) {
            return in_array($s, $self::sentinels);
        }
    }
    
    class ClassTwo {
        function __construct($caller, $sentinel) {
            if (!ClassOne::checkSentinel($sentinel)) {
                throw new Exception("ClassTwo::__construct must be called from ClassOne");
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search