Home Backend Development PHP Tutorial An Intro to Virtual Proxies, Part 2

An Intro to Virtual Proxies, Part 2

Feb 27, 2025 am 10:41 AM

An Intro to Virtual Proxies, Part 2

Core points

  • Virtual proxy based on polymorphism allows delayed construction/loading of costly object graphs without modifying client code.
  • Agents can be designed to work with a single object or collection of objects, providing flexibility and efficiency in managing data.
  • Virtual agents are particularly effective in lazy loading collections of domain objects in storage (e.g., a batch of blog posts where relevant comments can be extracted from the database on demand).
  • A proxy that simulates the behavior of real comment collections is realized through a data mapper, thereby improving persistence irrelevance.
  • Virtual proxy is an effective tool for delaying execution of costly tasks (e.g. delaying loading large amounts of data in storage tiers) and reducing common rigidity and vulnerability issues in object-oriented applications.

The name of virtual agent sounds fancy, but it is probably one of the most prominent examples of the concept of "interface-oriented programming" that is more than just a boring dogmatic principle. Virtual proxy is based on polymorphism (dynamic polymorphism, rather than temporary polymorphism implemented through simple methods), and is a simple and reliable concept that allows you to delay building/loading costly object graphs without modifying client code. A big advantage of agents is that they can be conceptually designed to work with a single object or collection of objects (or both, although doing so can jeopardize separation of concerns and are difficult to manage over time). To demonstrate from a practical perspective how to take advantage of the functionality provided by a virtual proxy, in the first part of this series, I introduce several example development processes that show how to extract aggregates from a database using a basic proxy to satisfy a simple domain model. While the experience can be beneficial and, luckily, it's also fun, the other side of it is a bit deceptive as it shows the ins and outs of virtual agents, but does not show how to implement them in a more realistic scenario. The proxy is unparalleled in terms of the collection of domain objects in lazy loading storage. To understand this concept, just consider a batch of blog posts, where each set of related comments can be extracted from the database on demand; you will surely understand why the agent can deliver on its promises in this case.

Normally, practice is the best teacher. In this section, I will show how to connect a proxy to a collection of domain-specific objects. I will reproduce this typical scenario on a very basic level so you can easily understand its driving logic.

Create a collection of realm objects

As mentioned earlier, virtual agents are usually created when obtaining aggregate roots bound to the underlying realm object collection from the persistence layer. Due to the nature of collections, in many cases, pre-setting of collections is expensive, making them a good candidate for loading on demand to reduce database round trip overhead. Furthermore, considering that the "one-to-many" relationship between a blog post and its corresponding comments is quite faithfully reflected in this use case, it would be instructive to first model the relationship through some simple domain classes before dealing with specific agents. To make things easier to understand, the two participants I will add during the testing phase will be an isolated interface and a basic implementer. These two combine to define the contract and implementation of generic blog post objects:

<?php namespace Model;
use ModelCollectionCommentCollectionInterface;

interface PostInterface
{
    public function setId($id);
    public function getId();

    public function setTitle($title);
    public function getTitle();

    public function setContent($content);
    public function getContent();

    public function setComments(CommentCollectionInterface $comments);
    public function getComments();
}
Copy after login
Copy after login
<?php namespace Model;
    use ModelCollectionCommentCollectionInterface;

class Post implements PostInterface
{
    protected $id;
    protected $title;
    protected $content;
    protected $comments;

    public function __construct($title, $content, CommentCollectionInterface $comments = null)  {
        $this->setTitle($title);
        $this->setContent($content);
        $this->comments = $comments;
    }

    // ... (Post class methods remain largely unchanged) ...
}
Copy after login
Copy after login

Understanding the logic of the above Post class is a simple process and does not require real explanation. Nevertheless, here is a noteworthy related detail: the class explicitly declares dependencies on a collection of comments that have not been defined in the constructor. Now let's create the class that generates post comments:

<?php namespace Model;

interface CommentInterface
{
     public function setId($id);
     public function getId();

     public function setContent($content);
     public function getContent();

     public function setPoster($poster);
     public function getPoster();
}
Copy after login
Copy after login
<?php namespace Model;

class Comment implements CommentInterface
{
    protected $id;
    protected $content;
    protected $poster;

    public function __construct($content, $poster) {
        $this->setContent($content);
        $this->setPoster($poster);
    }

    // ... (Comment class methods remain largely unchanged) ...
}
Copy after login
Copy after login

So far, everything has been going well. Apart from their thin blocks of the basic domain model, there is nothing to say about the above domain classes, where each blog post object opens the "one-to-many" association with related comments. You can call me a purist if you want, but it seems to me that if the current implementation of the model is not enhanced with a collection of comments, it looks incomplete and clumsy. Let's make the model richer by adding this extra component to it:

<?php namespace ModelCollection;

interface CommentCollectionInterface extends Countable, IteratorAggregate
{
    public function getComments();
}
Copy after login
Copy after login
<?php namespace ModelCollection;
use ModelCommentInterface;

class CommentCollection implements CommentCollectionInterface
{
    protected $comments = array();

    public function __construct(array $comments = array()) {
        // ... (CommentCollection class methods remain largely unchanged) ...
    }
}
Copy after login

If you look carefully and browse the CommentCollection class, you will first notice that it is nothing more than an iterable, countable array wrapper hidden behind the gorgeous disguise. In fact, array collections come in many forms and styles, but most of the time they are just simple usages of Iterator and ArrayAccess SPL classes. In this case, I want to save myself (and you) from such a boring task and make the class the implementer of IteratorAggregate. With the comment collection, we can go a step further and let the domain model do what it should do—operate on some blog post objects, or even interconnect them with a batch of comments eagerly fetched from the database. But doing so will only fool ourselves without taking full advantage of the capabilities provided by the virtual proxy. Considering that in a typical implementation, the proxy exposes the same API as the real domain object, the proxy interacting with the previous CommentCollection class should also implement the CommentCollectionInterface so as to adhere to the contract with the client code without introducing a bunch of problematic conditional statements.

Interaction with collections of domain objects through virtual agents

Frankly speaking, a collection of wrapping arrays, as mentioned earlier, can exist independently without relying on any other dependencies. (If you are skeptical, feel free to check how collections in Doctrine work behind the scenes.) Still, remember that I'm trying to implement a proxy that simulates the behavior of a real review collection, but it's actually a lightweight alternative. The question to be asked is: How do you extract comments from the database and put them into the previous collection? There are several ways to achieve this, but I think the most attractive one is through the data mapper because it improves persistence irrelevance. The mapper below makes it nice to get a collection of comments from storage. Please check:

<?php namespace Model;
use ModelCollectionCommentCollectionInterface;

interface PostInterface
{
    public function setId($id);
    public function getId();

    public function setTitle($title);
    public function getTitle();

    public function setContent($content);
    public function getContent();

    public function setComments(CommentCollectionInterface $comments);
    public function getComments();
}
Copy after login
Copy after login
<?php namespace Model;
    use ModelCollectionCommentCollectionInterface;

class Post implements PostInterface
{
    protected $id;
    protected $title;
    protected $content;
    protected $comments;

    public function __construct($title, $content, CommentCollectionInterface $comments = null)  {
        $this->setTitle($title);
        $this->setContent($content);
        $this->comments = $comments;
    }

    // ... (Post class methods remain largely unchanged) ...
}
Copy after login
Copy after login

While the finders exposed by the CommentMapper class usually stick to the API that might be expected in standard data mapper implementations, the fetchAll() method is by far the most compelling method. It first extracts all blog post comments from the storage and puts them into the collection, and finally returns the collection to the client code. If you are like me, you may have a wake-up call in your mind because the collection is instantiated directly inside the method. In fact, there is no need to panic about new operators sneaking outside the factory, at least in this case, because the collection is actually a general structure that falls under the "created" category, rather than the "injectable" category. Anyway, feel free to do it if you feel a little less guilty by injecting the collection into the constructor of the mapper. With the comment mapper, it's time to experience the real epiphany and build a proxy class that mediates with the previous set:

<?php namespace Model;

interface CommentInterface
{
     public function setId($id);
     public function getId();

     public function setContent($content);
     public function getContent();

     public function setPoster($poster);
     public function getPoster();
}
Copy after login
Copy after login

As you might expect, CommentCollectionProxy implements the same interface as a collection of real comments. However, its getComments() method does the actual work behind the scenes and delays the loading of comments from the database via the mapper passed in the constructor. This simple and effective method allows you to do all kinds of clever actions on your comments without overworking. Do you want to see what methods do you want to see? Suppose you need to get all the comments bound to a specific blog post from the database. The following code snippet can do this:

<?php namespace Model;

class Comment implements CommentInterface
{
    protected $id;
    protected $content;
    protected $poster;

    public function __construct($content, $poster) {
        $this->setContent($content);
        $this->setPoster($poster);
    }

    // ... (Comment class methods remain largely unchanged) ...
}
Copy after login
Copy after login

The disadvantage of this approach is that the comments are first extracted from the storage and then injected into the inside of the post object. How to do it the other way around, but this time it is to "spoof" the client code with a proxy?

<?php namespace ModelCollection;

interface CommentCollectionInterface extends Countable, IteratorAggregate
{
    public function getComments();
}
Copy after login
Copy after login

Comments not only transparently delay loading from the database after the proxy is placed in the foreach loop, but the API exposed to the client code keeps its original structure unchanged throughout the process. Do we even dare to ask for something better? Unless you are very greedy, it's hard for me to think so. In either case, you should understand the reality behind the scenes of the virtual agent and how to make the most of its capabilities in improving the operational efficiency of the domain object and the underlying persistence layer.

Conclusion

While it is simple, especially if you are bold enough to use it in a production environment, the previous example briefly shows some interesting concepts. First, virtual agents are not only easy to set up and use, but are unparalleled in mixing different implementations at runtime to delay performing costly tasks (e.g., delaying loading large amounts of data in the storage tier or creating heavyweight object graphs). Second, they are classic examples of how polymorphisms can become effective vaccines that reduce common rigidity and vulnerability problems that many object-oriented applications suffer from. Furthermore, because PHP's object model is simple and supports closures, it is possible to cleverly mix these features and build a proxy whose underlying logic is driven by the advantages of closures. If you want to deal with this challenge yourself, I wish you all the best in advance.

(Picture from imredesiuk / Shutterstock)

The above is the detailed content of An Intro to Virtual Proxies, Part 2. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1662
14
PHP Tutorial
1261
29
C# Tutorial
1234
24
Explain different error types in PHP (Notice, Warning, Fatal Error, Parse Error). Explain different error types in PHP (Notice, Warning, Fatal Error, Parse Error). Apr 08, 2025 am 12:03 AM

There are four main error types in PHP: 1.Notice: the slightest, will not interrupt the program, such as accessing undefined variables; 2. Warning: serious than Notice, will not terminate the program, such as containing no files; 3. FatalError: the most serious, will terminate the program, such as calling no function; 4. ParseError: syntax error, will prevent the program from being executed, such as forgetting to add the end tag.

PHP and Python: Comparing Two Popular Programming Languages PHP and Python: Comparing Two Popular Programming Languages Apr 14, 2025 am 12:13 AM

PHP and Python each have their own advantages, and choose according to project requirements. 1.PHP is suitable for web development, especially for rapid development and maintenance of websites. 2. Python is suitable for data science, machine learning and artificial intelligence, with concise syntax and suitable for beginners.

Explain secure password hashing in PHP (e.g., password_hash, password_verify). Why not use MD5 or SHA1? Explain secure password hashing in PHP (e.g., password_hash, password_verify). Why not use MD5 or SHA1? Apr 17, 2025 am 12:06 AM

In PHP, password_hash and password_verify functions should be used to implement secure password hashing, and MD5 or SHA1 should not be used. 1) password_hash generates a hash containing salt values ​​to enhance security. 2) Password_verify verify password and ensure security by comparing hash values. 3) MD5 and SHA1 are vulnerable and lack salt values, and are not suitable for modern password security.

PHP in Action: Real-World Examples and Applications PHP in Action: Real-World Examples and Applications Apr 14, 2025 am 12:19 AM

PHP is widely used in e-commerce, content management systems and API development. 1) E-commerce: used for shopping cart function and payment processing. 2) Content management system: used for dynamic content generation and user management. 3) API development: used for RESTful API development and API security. Through performance optimization and best practices, the efficiency and maintainability of PHP applications are improved.

What are HTTP request methods (GET, POST, PUT, DELETE, etc.) and when should each be used? What are HTTP request methods (GET, POST, PUT, DELETE, etc.) and when should each be used? Apr 09, 2025 am 12:09 AM

HTTP request methods include GET, POST, PUT and DELETE, which are used to obtain, submit, update and delete resources respectively. 1. The GET method is used to obtain resources and is suitable for read operations. 2. The POST method is used to submit data and is often used to create new resources. 3. The PUT method is used to update resources and is suitable for complete updates. 4. The DELETE method is used to delete resources and is suitable for deletion operations.

PHP: A Key Language for Web Development PHP: A Key Language for Web Development Apr 13, 2025 am 12:08 AM

PHP is a scripting language widely used on the server side, especially suitable for web development. 1.PHP can embed HTML, process HTTP requests and responses, and supports a variety of databases. 2.PHP is used to generate dynamic web content, process form data, access databases, etc., with strong community support and open source resources. 3. PHP is an interpreted language, and the execution process includes lexical analysis, grammatical analysis, compilation and execution. 4.PHP can be combined with MySQL for advanced applications such as user registration systems. 5. When debugging PHP, you can use functions such as error_reporting() and var_dump(). 6. Optimize PHP code to use caching mechanisms, optimize database queries and use built-in functions. 7

Explain the difference between self::, parent::, and static:: in PHP OOP. Explain the difference between self::, parent::, and static:: in PHP OOP. Apr 09, 2025 am 12:04 AM

In PHPOOP, self:: refers to the current class, parent:: refers to the parent class, static:: is used for late static binding. 1.self:: is used for static method and constant calls, but does not support late static binding. 2.parent:: is used for subclasses to call parent class methods, and private methods cannot be accessed. 3.static:: supports late static binding, suitable for inheritance and polymorphism, but may affect the readability of the code.

How does PHP handle file uploads securely? How does PHP handle file uploads securely? Apr 10, 2025 am 09:37 AM

PHP handles file uploads through the $\_FILES variable. The methods to ensure security include: 1. Check upload errors, 2. Verify file type and size, 3. Prevent file overwriting, 4. Move files to a permanent storage location.

See all articles