Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:111850 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 33808 invoked from network); 12 Sep 2020 21:15:05 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 12 Sep 2020 21:15:05 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 07648180502 for ; Sat, 12 Sep 2020 13:22:33 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.2 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-ej1-f50.google.com (mail-ej1-f50.google.com [209.85.218.50]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sat, 12 Sep 2020 13:22:32 -0700 (PDT) Received: by mail-ej1-f50.google.com with SMTP id p9so17959411ejf.6 for ; Sat, 12 Sep 2020 13:22:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=F7zQl+L8cTHYsSvZbExx3awuV1wT5gGAsLnG1xqwxYU=; b=pPqvow3O5YgD9vTkdIKxRk7ppDKqq738MF4JAqess3zjD6bgFpQk6LXreOYV07kECS mLit2XmZBSG37fphSAGFb3sQoLVvq/z6YvuRFbfOysu9xcG03Ir/qj7OfH7HDAGpcG5I ScTCxeWXPHji/CTgvYlydSzAqYAa8ZHn9GvbRSaYV7pDZh9pD6R/s7SdfTgr/dlZicvV i2hTcJBUxLQOZCetkr5uy98V6iPGnKgM/ErWrGSTakFzDBdFTrygiNdsJf7hxefwAt1T 3Dmc/1xpBXr3RtOu3EJ9RtUN9a1v4gI6w4JZXUah8FZxlWzc8K/4IBfrdzbWm5UfCKMi +ctQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=F7zQl+L8cTHYsSvZbExx3awuV1wT5gGAsLnG1xqwxYU=; b=bGLEnh5qOzyz6OQMs79aD8k1LzXtaY1OHmFaDqSDxPo8npZRutbkYsXyqAL8TlJ6oY mah4QD16Fl8oMVTt42XZu215YV/yQEvH/rAtpKZgq/LG6ZMou5EBMUGXPYO/jWfwuJfI pVsNroGajamzQ8vfjgKLO5UtsKOT2O5dqUNVKADin7YK1Vp81hvvQ1Q0gttD1r8Yiem0 D23Pdyu3ckhkMem6D0tEjZMOCQkzBTfxIjl3KF0hk/YSBH4jTN47JH4X0vReJERZ90M9 fMzSORoNmW43slLZ5WzuAltvZx1DlNqKqTWWf8rQ063ByeLb9NdtqWC+0WTwaxR7jq1j K/mQ== X-Gm-Message-State: AOAM531iPOWS2avVwZnCG+YORUUSJ6QChWbKH3kkkSwazR1ciAa17qgI 7SEvFNaou76ppY6Z1emNJWqTiW8yAyoTuP3Q0trFYpgv X-Google-Smtp-Source: ABdhPJyp/RplNmDHdLDsaiwGxY5bZBJ4HZL+xFTT7/w9Hu8GuwZSctRUnXaVApUzrnFbv25wHxY3L/qPEENsu9FRxqU= X-Received: by 2002:a17:906:3955:: with SMTP id g21mr7453062eje.69.1599942150155; Sat, 12 Sep 2020 13:22:30 -0700 (PDT) MIME-Version: 1.0 Received: by 2002:a54:3ac6:0:0:0:0:0 with HTTP; Sat, 12 Sep 2020 13:22:29 -0700 (PDT) Date: Sat, 12 Sep 2020 20:22:29 +0000 Message-ID: To: internals@lists.php.net Content-Type: text/plain; charset="UTF-8" Subject: Add interface implementation to class in separate file From: olleharstedt@gmail.com (=?UTF-8?Q?Olle_H=C3=A4rstedt?=) Hi internals! Separation of data and behaviour is both a fun and hard discussion, especially considering: * "It should be possible to add new features without touching old code"; and * "Principle of Least Privilege" (never expose more than you have to) (https://en.wikipedia.org/wiki/Principle_of_least_privilege). There should (could) be a way to add new behaviour to old data without touching the old data (class). Traits won't work in this use-case, since they assume the same internal structure for all trait-using classes. Imagine the `stringable` interface and a `toString` trait. A __toString() method needs knowledge about the internal structure of a class Foo. Yet if we want to keep adding behaviour to Foo, we'll end up with either exposing too much of Foo, or expanding the class file indefinitely. Please note that composition is not a proper solution, since it requires exposure of Foo; composition leads to lack of proper encapsulation, or representation exposure. In Haskell it's possible to split instance implementation of type-classes into separate files. In Rust you can have a struct with private fields and put impl of behaviour in different files (but same crate). A similar feature in PHP could look like (using new keyword `expand` but could be anything, or even `extend` in new context): ``` // File FooStringable.php expand Foo implements stringable { public function __toString() { // Full access to Foo's all private fields here. // Assumes you can autoload Foo. // Assumes usage of $foo->__toString(); will be configured with autoload to dynamically find the correct behaviour of Foo. } } ``` If you'd use composition instead, you'd maybe have a formatter class with a method `$formatter->toString(stringable $foo)`. This has the problem I mentioned with exposing too much of $foo; it breaks encapsulation. It has the benefit of being able to provide multiple toString methods with different formats, but would have to assume similar structure of the objects passed to it (defined with an interface), which is not always possible or desirable. The other way is inheritance, which doesn't scale over multiple behaviours. `FooWithStringable extends Foo`? No. Was I clear here? Do you understand the issues that this design pattern is trying to solve? Its purpose is to solve "keep adding new feature to old data" in a clean and proper way, while keeping information encapsulation. Enjoy the weekend! Olle