Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for inheritance in #[Friend] #29

Open
alek-as opened this issue Jul 26, 2024 · 3 comments
Open

Add support for inheritance in #[Friend] #29

alek-as opened this issue Jul 26, 2024 · 3 comments

Comments

@alek-as
Copy link

alek-as commented Jul 26, 2024

The library works very well for most of our use cases, thank you for creating it!

We've recently discovered that adding #[Friend] annotation with an interface as friend class will produce an error when the method is called from implementation classes.

See below for simple code snippet:

<?php

declare(strict_types=1);

namespace App;

use DaveLiddament\PhpLanguageExtensions\Friend;

interface Foo
{
}

class Bar
{
    #[Friend(Foo::class)]
    public function bar(): void
    {
    }
}

class Baz implements Foo
{
    public function baz(): void
    {
        $bar = new Bar();

        $bar->bar();
    }
}

Produced error:

------ ----------------------------------------------------------------------------------------------- 
  Line   src/Foo.php                                                                                    
 ------ ----------------------------------------------------------------------------------------------- 
  27     App\Bar::bar cannot be called from App\Baz, it can only be called from its friend(s): App\Foo  
 ------ -----------------------------------------------------------------------------------------------

Is this error intentional?

@DaveLiddament
Copy link
Owner

The original design was for friends to specify the exact class, however your suggestion makes a lot of sense, so I will look to get this implemented.

@DaveLiddament
Copy link
Owner

@alek-as have you got a more real life example you'd be happy to share? Having something real, rather than abstract will probably help me implement the correct thing.

@alek-as (cc @TomAdam)
I've added a few examples in PR #36, looks for the lines of code that end // OK and // ERROR for examples of code that I think should and should not work. Do you agree with these? Also if you have any more to add that'd be great too.

Thanks

@alek-as
Copy link
Author

alek-as commented Aug 13, 2024

@DaveLiddament

In our case, the Friend attribute needed to be added on a class method, not an interface method. See below for more generalized example:

<?php

declare(strict_types=1);

namespace App;

use DaveLiddament\PhpLanguageExtensions\Friend;

interface Invoice
{
}

class InvoiceLine
{
    #[Friend(Invoice::class)]
    public function changeAmount(int $newAmount): void
    {
    }
}

class RegularInvoice implements Invoice
{
    /**
     * @var array<InvoiceLine>
     */
    private array $invoiceLines;

    public function changeInvoiceLineAmount(InvoiceLine $line, int $newAmount): void
    {
        // verify that the invoice line belongs to this invoice
        // protect some invariants

        $line->changeAmount($newAmount);
    }
}

The examples from PR look good, not sure if adding another one is necessary (I'd assume above case will also be covered now)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants