PHP equivalent of friend or internal
Asked Answered
S

5

38

Is there some equivalent of "friend" or "internal" in php? If not, is there any pattern to follow to achieve this behavior?

Edit: Sorry, but standard Php isn't what I'm looking for. I'm looking for something along the lines of what ringmaster did.

I have classes which are doing C-style system calls on the back end and the juggling has started to become cumbersome. I have functions in object A which take in object B as a parameter and have to call a method in object B passing in itself as an argument. The end user could call the method in B and the system would fall apart.

Staciestack answered 25/11, 2008 at 16:0 Comment(1)
There have been some RfCs about such features (Class Friendship, Namespace Visiblity for Class, Interface and Trait) but nothing implemented to date.Kenelm
B
49

PHP doesn't support any friend-like declarations. It's possible to simulate this using the PHP5 __get and __set methods and inspecting a backtrace for only the allowed friend classes, although the code to do it is kind of clumsy.

There's some sample code and discussion on the topic on PHP's site:

class HasFriends
{
    private $__friends = array('MyFriend', 'OtherFriend');

    public function __get($key)
    {
        $trace = debug_backtrace();
        if(isset($trace[1]['class']) && in_array($trace[1]['class'], $this->__friends)) {
            return $this->$key;
        }

        // normal __get() code here

        trigger_error('Cannot access private property ' . __CLASS__ . '::$' . $key, E_USER_ERROR);
    }

    public function __set($key, $value)
    {
        $trace = debug_backtrace();
        if(isset($trace[1]['class']) && in_array($trace[1]['class'], $this->__friends)) {
            return $this->$key = $value;
        }

        // normal __set() code here

        trigger_error('Cannot access private property ' . __CLASS__ . '::$' . $key, E_USER_ERROR);
    }
}

(Code proved by tsteiner at nerdclub dot net on bugs.php.net)

Bryophyte answered 25/11, 2008 at 16:14 Comment(9)
This is a lovely hack! I love it and am repulsed by it. Clever though. +1Absorbance
What's the performance like with this? Is it a noticeable hit? This would actually be exactly what I'm looking for.Staciestack
This is perfect for encapsulation. Now I have a Main class without getters, one Accesor who implements the getters and a View who's the only one who can use the getters. +100 if I couldBlavatsky
Please keep in mind that having to implement code like this probably means that you've made a wrong decision in your code/application design.Egidio
should note: this is stronger then friend because it enforce the usage on run-time, not only hints the compiler at compile-time (like C++ friend or access modifiers do).Cowan
This seems to be the only way I've found at the moment to protect aggregate (DDD) classes, that are not the aggregate root, from outside access. But backtrace performance I hear is horrible.Obeah
Unfortunately magic method hacks are unsuitable for the one use case where one needs "friend": there is an object type that is extensively used in the innermost loops, and you want to spare as many function calls as possible. You want to allow the next-to-innermost loop code to use the object's properties directly without calling getMyProperty(), but you still want the properties to be inaccessible to the rest of the world. The magic method hack above nullifies the performance benefit.Diuretic
@halfpastfour.am, not at all. Say we have a AVLTree class which uses AbstractNode. We want to store the height of the node in the node itself, so we expose a public method 'setHeight' in the node. Although it is public, it is not of use outside the AVL tree, therefore AVL Tree class should be 'Friend' of AbstractNode.Vanderpool
@Vanderpool I get your point. But the less you have to depend on magic methods and checking call stacks, the better.Egidio
R
10

It is also possible to elevate privileges, aka leaking data selectively, using a handshake and closures in php >=5.3.3.

Basically, the interaction goes: class A has a public method which accepts a class B object, and calls B->grantAccess (or whatever your interface defines), passing it a closure. The closure use($that,$anythingelseyouneed) where $that=$this, and anything else you need to determine what properties are allowed to be accessed. The closure has one argument - the property to return; if it is a property on $that and everything is cool, the closure returns the property. Otherwise, it returns '', or throws an exception, or maybe a default value.

Class B->grantAccess accepts a callable and stores it, using it in other methods to pluck out private properties the closure allows to be leaked. Make class B's default constructor private. Construct a B using a static factory method that takes a Class A argument, to ensure the handshake happens.

Gist here: https://gist.github.com/mcamiano/00592fb400e5043d8acd

Radionuclide answered 8/8, 2014 at 0:45 Comment(0)
S
0

maybe this ? only a hack not a real friends keyword from c++,In this case only for constructor...

<?php

class FooWithFriend {

    private function guardOnlyFriend(): void 
    {
        if (( is_a($this, BarFriendOfFoo::class) ) === false ) {
         throw new Exception('Only class BarFriendOfFoo he can create me');
        }
    }

    protected function __construct() 
    {
        $this->guardOnlyFriend();
        echo 'Hello friend - ';
    }
}


final class BarFriendOfFoo extends FooWithFriend {
    public function __construct()
    {
        parent::__construct();
    }
}

final class Bar2FriendOfFoo extends FooWithFriend {
    public function __construct()
    {
        parent::__construct();
    }
}

$bar = new BarFriendOfFoo();
$bar = new Bar2FriendOfFoo();
$dummy = new FooWithFriend();
Sha answered 31/3, 2020 at 22:20 Comment(0)
K
0

It isn't supported by the language. There are various hacky ways of approximating it, depending on what your goal is. If you just want to make it clear what's expected to be called (e.g. avoid autocompletion in an IDE for methods the user is not supposed to use), you can typehint the object with a narrower interface or parent class:

abstract class Entity {
    protected $value = null;
    public function getValue() { return $this->value; }
}
class MutableEntity extends Entity {
    public function setValue( $value ) { $this->value = $value; }
}
function getEntity(): Entity {
    // returns a MutableEntity but says it's an Entity so
    // the IDE will not offer setValue()
    return new MutableEntity();
 }

If you actually want to enforce the access limitations, you probably have to use closures. But then, a malicious user can easily work around any access limitations using reflection, so there isn't much in trying to enforce it.

Kenelm answered 13/12, 2023 at 3:46 Comment(0)
C
-20

I'm pretty sure what you're looking for is "protected" or "private", depending on your use case.

If you're defining an function in a class, and you only want it available to itself, you'll define it this way:

private function foo($arg1, $arg2) { /*function stuff goes here */ }

If you're defining a function in a class that you want to be available to classes which inherit from this class, but not available publicly, definite it this way:

protected function foo($arg1, $arg2)

I'm pretty sure that by default in PHP5, functions are public, meaning you don't have to use the following syntax, but it's optional:

public function foo($arg1, $arg2) { /*function stuff goes here */ }

You'll still have to instantiate the object before using a public function. So I'll just be thorough and let you know that in order to use a function in a class without instantiating an object, be sure to use the following syntax:

static function foo($arg1, $arg2) { /*function stuff goes here */ }

That will allow you to use the function by only referencing the class, as follows:

MyClass::foo($a1, $a2);

Otherwise, you'll need to do the following:

$myObject = new MyClass();
$myObject->foo($a1, $a2);
Cordiality answered 25/11, 2008 at 16:9 Comment(1)
I think what the OP was asking was how to implement in PHP something similar to the friend keyword in C++, i.e. making certain private/protected data/methods available to certain classes which did not inherit from it.Athalie

© 2022 - 2024 — McMap. All rights reserved.