Lesson 5
Managing Document Edit History with PHP Stacks
Introduction

Welcome! Today, we will delve into managing a document's editing history using stacks in PHP. Imagine building a text editor; you would need to handle actions like adding text, undoing changes, and redoing them. We will see how these features can be efficiently implemented using stacks, which are represented by arrays in PHP. PHP's built-in functions like array_push and array_pop allow us to effortlessly manage these stacks. By the end of this lesson, you will possess an in-depth understanding of applying stacks in practical scenarios using PHP.

Introducing Methods to Implement

Before starting the coding portion, let's dissect the methods we will implement. These methods will manage a document's edit history, allowing us to apply changes, undo them, and redo them effectively.

  • function applyChange($change): This method applies a change to the document. The change, represented as a string, is stored in a way that allows us to remember the order of applied changes. Any previously undone changes are discarded.

  • function undo(): This method undoes the most recent change and allows us to store it for a possible redo. It returns the change that was undone, or null if there are no changes available to undo.

  • function redo(): This method redoes the most recent undone change, making it active again. It returns the change that was redone, or null if there are no changes available to redo.

  • function getChanges(): This method returns an array of all applied changes in the order they were applied.

Methods Implementation with Stacks

To implement these methods efficiently, we'll use arrays in PHP to simulate stack behavior. Arrays provide dynamic resizing and efficient element insertion or removal from the end. Stacks are ideal for keeping track of applied changes and undone changes because of their LIFO (Last In, First Out) property. The most recent change is always at the top, making it easy to undo and redo.

Let's break down each method's implementation and study how they interact with the stacks.

Step 1: Define the Class and Initialize the Stacks

Let's implement the solution step-by-step. First, we define our class and set up the initial state.

php
1class DocumentHistory { 2 private $changesStack = []; 3 private $redoStack = []; 4}

Here, DocumentHistory is the class, and we initialize two empty arrays named $changesStack to act as our stack of applied changes, and $redoStack to represent our stack for undone changes.

Step 2: Implement the 'applyChange' Method

Next, we put into effect the method to apply changes.

php
1class DocumentHistory { 2 private $changesStack = []; 3 private $redoStack = []; 4 5 public function applyChange($change) { 6 array_push($this->changesStack, $change); 7 $this->redoStack = []; // Clear the redo stack 8 } 9}

With applyChange, we take a string $change and append it to the $changesStack array using array_push. Additionally, we reinitialize the $redoStack to ensure that once a new change is applied, any previously undone changes cannot be redone.

Step 3: Implement the 'undo' Method

Now, we will implement the method to undo the most recent change.

php
1class DocumentHistory { 2 private $changesStack = []; 3 private $redoStack = []; 4 5 public function applyChange($change) { 6 array_push($this->changesStack, $change); 7 $this->redoStack = []; 8 } 9 10 public function undo() { 11 if (empty($this->changesStack)) { 12 return null; 13 } 14 $change = array_pop($this->changesStack); 15 array_push($this->redoStack, $change); 16 return $change; 17 } 18}

Here, undo checks if the $changesStack is empty. If it is, it returns null. Otherwise, it pops the last item from the array using array_pop, pushes it to the $redoStack, and returns the undone change.

Step 4: Implement the 'redo' Method

Now we'll create the method to redo the most recent undone change.

php
1class DocumentHistory { 2 private $changesStack = []; 3 private $redoStack = []; 4 5 public function applyChange($change) { 6 array_push($this->changesStack, $change); 7 $this->redoStack = []; 8 } 9 10 public function undo() { 11 if (empty($this->changesStack)) { 12 return null; 13 } 14 $change = array_pop($this->changesStack); 15 array_push($this->redoStack, $change); 16 return $change; 17 } 18 19 public function redo() { 20 if (empty($this->redoStack)) { 21 return null; 22 } 23 $change = array_pop($this->redoStack); 24 array_push($this->changesStack, $change); 25 return $change; 26 } 27}

The redo method checks if the $redoStack is empty. If it is, it returns null. Otherwise, it pops the last item from the array, pushes it back to the $changesStack, and returns the redone change.

Step 5: Implement the 'getChanges' Method

Lastly, we implement the method to retrieve all applied changes.

php
1class DocumentHistory { 2 private $changesStack = []; 3 private $redoStack = []; 4 5 public function applyChange($change) { 6 array_push($this->changesStack, $change); 7 $this->redoStack = []; 8 } 9 10 public function undo() { 11 if (empty($this->changesStack)) { 12 return null; 13 } 14 $change = array_pop($this->changesStack); 15 array_push($this->redoStack, $change); 16 return $change; 17 } 18 19 public function redo() { 20 if (empty($this->redoStack)) { 21 return null; 22 } 23 $change = array_pop($this->redoStack); 24 array_push($this->changesStack, $change); 25 return $change; 26 } 27 28 public function getChanges() { 29 return $this->changesStack; 30 } 31}

The getChanges method simply returns a copy of the $changesStack array, which includes all changes applied to the document in the order they were applied.

The Final Implementation

Let's test this final implementation of DocumentHistory with an example:

php
1class DocumentHistory { 2 private $changesStack = []; 3 private $redoStack = []; 4 5 public function applyChange($change) { 6 array_push($this->changesStack, $change); 7 $this->redoStack = []; 8 } 9 10 public function undo() { 11 if (empty($this->changesStack)) { 12 return null; 13 } 14 $change = array_pop($this->changesStack); 15 array_push($this->redoStack, $change); 16 return $change; 17 } 18 19 public function redo() { 20 if (empty($this->redoStack)) { 21 return null; 22 } 23 $change = array_pop($this->redoStack); 24 array_push($this->changesStack, $change); 25 return $change; 26 } 27 28 public function getChanges() { 29 return $this->changesStack; 30 } 31} 32 33$docHist = new DocumentHistory(); 34 35// Apply changes 36$docHist->applyChange("Added header"); 37$docHist->applyChange("Added footer"); 38print_r($docHist->getChanges()); // Output: Array ( [0] => Added header [1] => Added footer ) 39 40// Undo last change 41echo $docHist->undo() . PHP_EOL; // Output: Added footer 42print_r($docHist->getChanges()); // Output: Array ( [0] => Added header ) 43 44// Redo last undone change 45echo $docHist->redo() . PHP_EOL; // Output: Added footer 46print_r($docHist->getChanges()); // Output: Array ( [0] => Added header [1] => Added footer ) 47 48// Undo all changes 49echo $docHist->undo() . PHP_EOL; // Output: Added footer 50echo $docHist->undo() . PHP_EOL; // Output: Added header 51print_r($docHist->getChanges()); // Output: Array ( ) 52 53// Try undoing when no changes are left 54echo $docHist->undo() . PHP_EOL; // Output: 55 56// Redo changes 57echo $docHist->redo() . PHP_EOL; // Output: Added header 58echo $docHist->redo() . PHP_EOL; // Output: Added footer 59print_r($docHist->getChanges()); // Output: Array ( [0] => Added header [1] => Added footer ) 60 61// Try redoing when no changes are left to redo 62echo $docHist->redo() . PHP_EOL; // Output:
Summary

In today's lesson, we learned how to manage a document's editing history using stacks in PHP. We implemented methods to apply changes, undo the last change, redo the most recent undone change, and retrieve all applied changes. This exercise provided us with practical experience in using PHP arrays to efficiently track and revert operations in a real-life scenario. Keep practicing similar challenges to deepen your understanding of PHP's data-handling capabilities. Fantastic job today, and keep up the good work!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.