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.
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. Thechange
, 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, ornull
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, ornull
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.
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.
Let's implement the solution step-by-step. First, we define our class and set up the initial state.
php1class 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.
Next, we put into effect the method to apply changes.
php1class 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.
Now, we will implement the method to undo the most recent change.
php1class 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.
Now we'll create the method to redo the most recent undone change.
php1class 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.
Lastly, we implement the method to retrieve all applied changes.
php1class 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.
Let's test this final implementation of DocumentHistory
with an example:
php1class 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:
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!