├── 9781484224809.jpg ├── LICENSE.txt ├── README.md ├── appendix-A ├── p4-compile.sh └── p7-compile-ext.sh ├── cli-sapi ├── p11-arguments.php ├── p15-myscript.php ├── p19-myscript.desktop ├── p20-ourstats.bat ├── p21-our_text_ed.vbs └── p21-unity-myscript.desktop ├── contributing.md ├── devtools └── p4-initialise.php ├── distribution ├── p2-syslog.php ├── p4-embed.php └── p5-using-phar.php ├── interactions ├── p12-generator2.php ├── p13-display2.php ├── p22-tmpfs.sh ├── p4-semaphore.php ├── p7-generator.php └── p8-display.php ├── performance └── p5-profile.php ├── system-software ├── p15-inotify.php ├── p19-inotifywait.php ├── p4-daemon.php └── p9-libevent.php ├── system ├── p10-list-printers.php ├── p11-write-registry.php ├── p12-signals.php ├── p15-timed-event.php ├── p19-pdf.php ├── p2-bigfile.php ├── p29-gpio.php ├── p4-bigfilelines.php ├── p6-caches.php └── p9-read-registry.php └── user-software ├── p10-std-stream.php ├── p13-notify-send.php ├── p14-zenity.php ├── p16-html.php ├── p2-snake.php ├── p21-myapp.php └── p6-interface.php /9781484224809.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/php-beyond-the-web/37552081130418473c6af816abb82301c5c90622/9781484224809.jpg -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/php-beyond-the-web/37552081130418473c6af816abb82301c5c90622/LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*PHP Beyond the Web*](http://www.apress.com/9781484224809) by Rob Aley (Apress, 2017). 4 | 5 | ![Cover image](9781484224809.jpg) 6 | 7 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 8 | 9 | ## Releases 10 | 11 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 12 | 13 | ## Contributions 14 | 15 | See the file Contributing.md for more information on how you can contribute to this repository. 16 | -------------------------------------------------------------------------------- /appendix-A/p4-compile.sh: -------------------------------------------------------------------------------- 1 | mkdir php5.4 2 | cd php5.4 3 | wget http://uk3.php.net/get/php-7.0.11.tar.gz/from/uk.php.net/mirror -o php-7.0.11.tar.gz 4 | tar zxvf php-7.0.11.tar.gz 5 | cd php-7.0.11 6 | ./configure 7 | make clean 8 | make 9 | sudo make install 10 | -------------------------------------------------------------------------------- /appendix-A/p7-compile-ext.sh: -------------------------------------------------------------------------------- 1 | cd pcntl 2 | phpize 3 | ./configure 4 | make clean 5 | make 6 | sudo make install 7 | -------------------------------------------------------------------------------- /cli-sapi/p11-arguments.php: -------------------------------------------------------------------------------- 1 | addHeader ( 'Content-Type' , "text/plain; charset=ISO-8859-1", 19 | EventHttpRequest::OUTPUT_HEADER ); 20 | 21 | # Next we'll gather some information about the request, and format 22 | # it into a string to send back to the web browser. 23 | 24 | $replyText .= 'Command : ' . $req->getCommand() . "\n"; 25 | 26 | $replyText .= 'Host : ' . $req->getHost() . "\n"; 27 | 28 | $replyText .= 'Input Headers : ' . 29 | var_export($req->getInputHeaders(),true) . "\n"; 30 | 31 | $replyText .= 'Output Headers : ' . 32 | var_export($req->getOutputHeaders(),true) . "\n"; 33 | 34 | $replyText .= 'URI : ' . $req->getUri() . "\n"; 35 | 36 | # To send a reply back, we create an "EventBuffer" containing the 37 | # reply contents, in our case the $replyText above 38 | 39 | $reply = new EventBuffer; 40 | 41 | $reply->add($replyText); 42 | 43 | # Finally we send our EventBuffer to the browser, with an HTTP 44 | # status of 200-OK to confirm everything happened correctly. 45 | 46 | $req->sendReply(200, "OK", $reply); 47 | 48 | }; 49 | 50 | function closeServer($req) { 51 | 52 | # Our next function allows the visitor to shut down the server by 53 | # simply visiting a URL. We'll send them a message before we shut down 54 | # to let them know. 55 | 56 | $reply = new EventBuffer; 57 | 58 | $reply->add("Ok 1337 haxor, you've killed the server..."); 59 | 60 | $req->sendReply(200, 'OK', $reply); 61 | 62 | # We then call the exit method of the event base, to exit the event 63 | # loop, which we'll look at towards the end of the program. 64 | 65 | global $base; 66 | 67 | $base->exit(); 68 | 69 | }; 70 | 71 | function notFound($req) { 72 | 73 | # This function handles the case where we can't find a resource 74 | 75 | $req->sendError(404, 'Does not appear to be here. Sorry.'); 76 | 77 | }; 78 | 79 | function cat($req) { 80 | 81 | # This function is one of the most important on the internet. It 82 | # returns a picture of a cat. You will need a cat picture named 83 | # cat.jpg in the same directory for this to work, but that shouldn't 84 | # be too difficult to arrange... 85 | 86 | # As we're returning a binary image file, we need to set the 87 | # appropriate mime-type output header. 88 | 89 | $req->addHeader ( 'Content-Type' , "image/jpeg" , 90 | EventHttpRequest::OUTPUT_HEADER ); 91 | 92 | # Get the contents of the image file .... 93 | 94 | $cat = file_get_contents('cat.jpg'); 95 | 96 | # and add them to a new EventBuffer ... 97 | 98 | $reply = new EventBuffer; 99 | 100 | $reply->add($cat); 101 | 102 | # finally delivery the cat to an appreciative audience .... 103 | 104 | $req->sendReply(200, "OK", $reply); 105 | 106 | }; 107 | 108 | function genericHandler($req) { 109 | 110 | # This function will handle any requests that the previous functions 111 | # haven't. We'll use the opportunity to serve up an HTML page with a 112 | # title and a picture of a cat. The tag will cause the browser 113 | # to make a second call, which will be routed to the cat() function 114 | # above to deliver the image file. 115 | 116 | $replyText = ''.$req->getUri().''; 117 | $replyText .= '

Picture of cat


'; 118 | $replyText .= ''; 119 | $replyText .= ''; 120 | 121 | $reply = new EventBuffer; 122 | 123 | $reply->add($replyText); 124 | 125 | $req->sendReply(200, "OK", $reply); 126 | 127 | }; 128 | 129 | # Now we've defined all of our functions for delivering content, we need 130 | # to actually set up our server. 131 | 132 | # First we create an "EventBase", which is libevent's vehicle for holding 133 | # and polling a set of events. 134 | 135 | $base = new EventBase(); 136 | 137 | # Then we add an EventHttp object to the base, which is the Event 138 | # extension's helper for HTTP connections/events. 139 | 140 | $http = new EventHttp($base); 141 | 142 | # We'll choose to respond to just GET and POST HTTP requests 143 | 144 | $http->setAllowedMethods( 145 | EventHttpRequest::CMD_GET | EventHttpRequest::CMD_POST); 146 | 147 | # Next we'll tie the functions we created above to specific URIs using 148 | # function callbacks. 149 | 150 | $http->setCallback("/info", "techInfo"); 151 | $http->setCallback("/close", "closeServer"); 152 | $http->setCallback("/notfound", "notFound"); 153 | $http->setCallback("/images/cat.jpg", "cat"); 154 | 155 | # Finally we'll add a default function callback to handle all other URIs. 156 | # You could, in fact, just specify this default handler and not those 157 | # above, and then handle URIs as you wish from inside this function using 158 | # it as a router function. 159 | 160 | $http->setDefaultCallback("genericHandler"); 161 | 162 | # We'll bind our script to an address and port to enable it to listen for 163 | # connections 164 | 165 | $http->bind("0.0.0.0", 12345); 166 | 167 | # Then we start our event loop using the loop() function of our base. Our 168 | # script will remain in this loop indefinitely, servicing http requests 169 | # with the functions above, until we exit it by killing the script or, 170 | # more ideally, calling $base->exit() as we do in the closeServer() 171 | # function above. 172 | 173 | $base->loop(); 174 | -------------------------------------------------------------------------------- /system/p10-list-printers.php: -------------------------------------------------------------------------------- 1 | $subkey) { 11 | 12 | echo "Printer $index is $subkey \n"; 13 | 14 | }; 15 | 16 | reg_close_key($keyHandle); 17 | 18 | } else { die ("Couldn't open Registry key"); }; 19 | -------------------------------------------------------------------------------- /system/p11-write-registry.php: -------------------------------------------------------------------------------- 1 | AddPage(); 12 | 13 | # Add an image. Here we can use PHP's http wrapper to include a web image 14 | 15 | $pdf->Image('http://static.php.net/www.php.net/images/php.gif',10); 16 | 17 | # Now we'll set the font and add a header, plus some other text 18 | 19 | $pdf->SetFont('Arial','B',24); 20 | 21 | $pdf->Cell(0,30,'An Important Report','B',1,'C'); 22 | 23 | $pdf->SetFont('Arial',null,10); 24 | 25 | $pdf->Cell(0,12,'Lots of really important text goes here. Etc.', null, 1); 26 | 27 | # Generate a unique temporary file name 28 | 29 | $filename = tempnam(sys_get_temp_dir(), "rep").'.pdf'; 30 | 31 | # Save the PDF to that temporary file 32 | 33 | $pdf->Output($filename, 'F'); 34 | 35 | # Finally open the PDF for "print preview" in the default viewer 36 | 37 | `xdg-open $filename`; 38 | -------------------------------------------------------------------------------- /system/p2-bigfile.php: -------------------------------------------------------------------------------- 1 | setup(17, "in"); 17 | 18 | # Enter a loop and wait for the doorbell to be pressed 19 | 20 | while (1) { 21 | 22 | # Read the current state of pin 17 23 | 24 | $currently = $gpio->input(17); 25 | 26 | # If the switch is not pressed, the state will be 1. If the switch 27 | # IS pressed, the state will be 0. As the state is a "file" designed 28 | # to be read with a shell command like cat, it will be returned as 29 | # a string containing the 1 or 0 and \n. Don't just test for 0, 30 | # e.g. $currently == 0 as the function will return false on certain 31 | # errors, which would evaluate as 0. 32 | 33 | if ($currently === "0\n") { 34 | 35 | # Ding dong! Someone has pressed our doorbell. Use mplayer to 36 | # play an appropriate sound file. This requires amplified speakers 37 | # connected to either the sound jack or HDMI connector on the RP. 38 | 39 | shell_exec('mplayer dingdong.mp3'); 40 | 41 | # We then continue the loop, awaiting our next visitor. 42 | 43 | }; 44 | 45 | # Give the processor a chance to breath. Not too long though, or 46 | # we may miss a quick press of the doorbell switch. 47 | 48 | usleep(1000); 49 | }; 50 | -------------------------------------------------------------------------------- /system/p4-bigfilelines.php: -------------------------------------------------------------------------------- 1 | $memoryBase) { $memoryHigh = $memoryCurrent;}; 22 | 23 | }; 24 | 25 | echo("Memory used by single string : ".($memory2-$memory1)." bytes\n"); 26 | 27 | echo("Max memory used when reading by line : ". 28 | ($memoryHigh-$memoryBase)." bytes\n"); 29 | -------------------------------------------------------------------------------- /system/p6-caches.php: -------------------------------------------------------------------------------- 1 | '); 6 | 7 | $line1 = fgets(STDIN); 8 | 9 | echo ('**** Line 1 : '.$line1." ****\n\n"); 10 | 11 | # Get one line of input, without the newline character 12 | 13 | echo ('Please Type Something Else In : >'); 14 | 15 | $line2 = trim(fgets(STDIN)); 16 | 17 | echo ('**** Line 2 : '.$line2." ****\n\n"); 18 | 19 | # Write an array out to STDOUT in CSV format. 20 | # First, create an array of arrays... 21 | 22 | $records[] = ['User', 'Full Name', 'Gender']; 23 | $records[] = ['Rob', 'Robert Aley', 'M']; 24 | $records[] = ['Ada', 'Augusta Ada King, Countess of Lovelace', 'F']; 25 | $records[] = ['Grete', 'Grete Hermann', 'F']; 26 | 27 | echo ("The following is your Data in CSV format :\n\n"); 28 | 29 | # ...then convert each array to CSV on the fly as we write it out 30 | 31 | foreach ($records as $record) { 32 | 33 | fputcsv(STDOUT, $record); 34 | 35 | }; 36 | 37 | echo ("\n\nEnd of your CSV data\n"); 38 | 39 | # Pause until the user enters something starting with a number 40 | 41 | echo ('Please type one or more numbers : >'); 42 | 43 | while (! fscanf(STDIN, "%d\n", $your_number) ) { 44 | 45 | echo ("No numbers found :>"); 46 | 47 | }; 48 | 49 | echo ("Your number was $your_number\n\n"); 50 | 51 | # Send the text of a web page to STDOUT 52 | 53 | echo ("Press enter for some interwebs :\n\n"); 54 | 55 | fread(STDIN, 1); # fread blocks until enter pressed 56 | 57 | fwrite(STDOUT, strip_tags( file_get_contents('http://www.cam.ac.uk') ) ); 58 | 59 | # Send an error message to STDERR. You can just fwrite(STDERR,... 60 | # if you want, or you can use the error_log function, which uses the 61 | # defined error handling routine. By default for the CLI SAPI this is 62 | # printing to STDERR. 63 | 64 | error_log('System ran out of beer. ABORT. ABORT.', 4); 65 | -------------------------------------------------------------------------------- /user-software/p13-notify-send.php: -------------------------------------------------------------------------------- 1 | '); 20 | 21 | print("My HTML Page\n"); 22 | 23 | ?> 24 | 25 |

Intermingle Some HTML

26 |

In the traditional PHP Way

27 |

28 | An Important Link 29 | '); 32 | 33 | fwrite(STDOUT, "Finished HTML Generation\n"); # displayed in terminal 34 | 35 | # ob_get_contents() creates a string with everything buffered so far. 36 | 37 | $ourHtml = ob_get_contents(); 38 | 39 | # We can continue with buffering if we need to, or in this case we 40 | # end "ob_end_clean"ly. If we ob_end_flush() instead, then the contents of 41 | # the buffer would be pushed to php://output, which after ending the 42 | # buffering is STDOUT, which we don't want in this case. 43 | 44 | ob_end_clean(); 45 | 46 | # Now that we've ended buffering… 47 | 48 | echo ("This Text will go to our terminal via STDOUT\n"); 49 | 50 | # Finally, we want to save the buffered HTML to a file. Lets create 51 | # a unique temporary file name .... 52 | 53 | $filename = tempnam(sys_get_temp_dir(), 'my_report_').'.html'; 54 | 55 | # and write the HTML string to it. 56 | 57 | file_put_contents($filename, $ourHtml); 58 | 59 | # Finally, we want to open a web browser to view the HTML "report" we 60 | # just created. Here we use the helper command "see" (available on 61 | # most Debian based distros) to open the default viewer for the filetype 62 | # (HTML). The command "open" achieves the same thing on other platforms. 63 | # On Windows, you can ommit the helper command and just "execute" the 64 | # $filename (i.e. `$filename`) and Windows will open the default viewer 65 | # (browser) for that filetype. 66 | # 67 | # You can also specify a particular browser if you want, 68 | # e.g. `firefox $filename`. 69 | 70 | `see $filename`; 71 | -------------------------------------------------------------------------------- /user-software/p2-snake.php: -------------------------------------------------------------------------------- 1 | intval($cols/2), "y"=>intval($rows/2)]; 89 | 90 | # Now for our first element of flow control. We need to keep the program 91 | # running until the user provides input. The simplest way to do this is to 92 | # use a never-ending loop using while(1). "1" always evaluates to true, so 93 | # the while loop will never end. When we (or the user) are ready to end 94 | # the program, we can use the "break" construct to step out of the loop 95 | # and continue the remaining script after the end of the loop. 96 | 97 | while (1) { 98 | 99 | # Each time we go through the loop, we want to check if the user has 100 | # pressed enter while we were in the last loop. Remember that STDIN is 101 | # no longer blocking, so if there is no input the program continues 102 | # immediately. If there is input we use break to leave the while loop. 103 | 104 | if (fread(STDIN,1)) { break; }; 105 | 106 | # We will step the position of the cursor, stored in $p, by a random 107 | # amount in both the x and y axis. This makes our snake crawl! 108 | 109 | $p['x'] = $p['x'] + rand(-1,1); 110 | $p['y'] = $p['y'] + rand(-1,1); 111 | 112 | # We check that our snake won't step onto or over the frame, to keep 113 | # it in its box! 114 | 115 | if ($p['x'] > ($cols-1)) { $p['x'] = ($cols-1);}; 116 | if ($p['y'] > ($rows-1)) { $p['y'] = ($rows-1);}; 117 | if ($p['x'] < 2) { $p['x'] = 2;}; 118 | if ($p['y'] < 2) { $p['y'] = 2;}; 119 | 120 | # We want a pretty trail, so we need to pick random colours for the 121 | # foreground and background colour of our snake, that change at 122 | # each step. Colours in the terminal are set with yet more escape 123 | # codes, from a limited palette, specified by integers. 124 | 125 | $fg_color = rand(30,37); 126 | $bg_color = rand(40,47); 127 | 128 | # Once chosen, we set the colours by outputting the escape codes. This 129 | # doesn't immediately print anything, it just sets the colour of 130 | # whatever else follows. 131 | 132 | fwrite(STDOUT, ESC."[${fg_color}m"); # \033[$32m sets green foreground 133 | fwrite(STDOUT, ESC."[${bg_color}m"); # \033[$42m sets green background 134 | 135 | # Finally we output a segment of snake (another box drawing character) 136 | # at the new location. It will appear with the colours we just set, at 137 | # the location stored in $p 138 | 139 | fwrite(STDOUT, ESC."[${p['y']};${p['x']}f"."╬"); 140 | 141 | # Before we let the while loop start again, we need to do one more 142 | # very important thing. We need to give your processor a rest. 143 | # If we just continued our loop straight away, you would find your 144 | # processor being hammered, just for our relatively simple program. 145 | # Our snake would also consume the screen at super-speed! 146 | # usleep pauses execution of the program, so others can use the 147 | # processor or the processor can "rest". Every little bit helps the 148 | # responsiveness of your machine, so even if you need your program 149 | # to loop as fast as possible, consider even a small usleep if you can 150 | 151 | usleep(1000); 152 | }; 153 | 154 | # If this line of code has been reached, it means that we have 'break'd 155 | # from the while loop. 156 | 157 | # To be a good citizen of the terminal, we need to clean up the screen 158 | # before we exit. Otherwise, the cursor will remain on which-ever line 159 | # our snake left it, and the background/foreground colours will be 160 | # the last ones chosen for our snake segment. 161 | 162 | # The following escape code tells the terminal to use its default colours. 163 | 164 | fwrite(STDOUT, ESC."[0m"); 165 | 166 | # We then clear the screen and put the cursor at the top-left, as we 167 | # did earlier. 168 | 169 | fwrite(STDOUT, CLEAR.HOME); 170 | -------------------------------------------------------------------------------- /user-software/p21-myapp.php: -------------------------------------------------------------------------------- 1 | Destroy(); 18 | } 19 | 20 | # We'll add a function to display an "about" dialog to display some 21 | # information about the application 22 | 23 | function onAbout() 24 | { 25 | 26 | # "wxMessageDialog" is one of the many components or "widgets" 27 | # available in the toolkit. This saves you having to create a 28 | # new frame/window to display your message. 29 | 30 | $dlg = new wxMessageDialog( 31 | $this, 32 | "Welcome to wxPHP!!\nBased on wxWidgets 3.0.0\n\n". 33 | "This is a minimal wxPHP sample!", 34 | "About box...", 35 | wxICON_INFORMATION 36 | ); 37 | 38 | # Show the dialog box we create above. 39 | 40 | $dlg->ShowModal(); 41 | } 42 | 43 | # Add a constructor function. This function is run when we create our 44 | # application window by creating a new object from this class. 45 | 46 | function __construct() 47 | { 48 | 49 | # This calls the constructor function from the wxFrame class that 50 | # we have extended, creating a frame with the title 51 | # "Minimal wxPHP App" in the default position on screen, with 52 | # the initial size of 350 x 260 pixels. Note that this frame is 53 | # not visible by default, it's just created in memory at the 54 | # moment. This means that we can add things to it and fully 55 | # prepare it before we show it to the user, rather than the 56 | # user seeing a blank window that then suddenly fills with 57 | # buttons etc. 58 | 59 | parent::__construct(null, null, "Minimal wxPHP App", 60 | wxDefaultPosition, new wxSize(350, 260)); 61 | 62 | # We're going to add menus to our window with various options, 63 | # which means first adding a menu bar into which we put the menus. 64 | 65 | $mb = new wxMenuBar(); 66 | 67 | # Now we add the menus. First a "File" menu with a "Quit" option 68 | 69 | $mn = new wxMenu(); 70 | $mn->Append(2, "E&xit", "Quit this program"); 71 | $mb->Append($mn, "&File"); 72 | 73 | # And now a "Help" menu with an "About" option. 74 | 75 | $mn = new wxMenu(); 76 | $mn->AppendCheckItem(4, "&About...", "Show about dialog"); 77 | $mb->Append($mn, "&Help"); 78 | 79 | # Note the menu and options above all have "&"" symbols in. This 80 | # comes before the letter that should be used for keyboard 81 | # shortcuts. Using a widget toolkit like this means that you don't 82 | # have to write your own code for managing things like keyboard 83 | # shortcuts, saving you time and effort and creating a consistent 84 | # experience for your users. 85 | 86 | # Finally add the menu bar to the frame. 87 | 88 | $this->SetMenuBar($mb); 89 | 90 | # Lets add a source-code editing box to the frame, which is 91 | # another one of the available widgets in the toolkit and has 92 | # functionality like syntax highlighting, smart indentation etc. 93 | 94 | $scite = new wxStyledTextCtrl($this); 95 | 96 | # The final widget we're going to add is a status bar at the 97 | # bottom of the window. 98 | 99 | $sbar = $this->CreateStatusBar(2); 100 | $sbar->SetStatusText("Welcome to wxPHP..."); 101 | 102 | # At the start of this class we defined a couple of functions, one 103 | # to show an about box, and one to quit the app. On their own 104 | # they won't do anything, we need to connect them to the menu 105 | # options we created earlier. More specifically, to the 106 | # "wxEVT_COMMAND_MENU_SELECTED" event which is called when the 107 | # user selects something from the menu. 108 | 109 | $this->Connect(2, wxEVT_COMMAND_MENU_SELECTED, array($this,"onQuit")); 110 | 111 | $this->Connect(4, wxEVT_COMMAND_MENU_SELECTED, array($this,"onAbout")); 112 | 113 | } 114 | } 115 | 116 | # We've now designed our frame and populated it with widgets and functions 117 | # but at this stage in the code it doesn't yet exist. We need to create 118 | # a new object using our class, which will bring it to life and call 119 | # the constructor function above. 120 | 121 | $mf = new mainFrame(); 122 | 123 | # The frame now exists, it is populated with widgets and the functions 124 | # are all hooked up to the events. However it is hidden, so we need 125 | # to make it visible. 126 | 127 | $mf->Show(); 128 | 129 | # At this point, we need to let wxWidgets take over and run the show. 130 | # Calling wxEntry lets wxWidgets manage the application, wait for and 131 | # react to events and call the functions that we've previously specified. 132 | # As you can see, we need to have specified all of our applications code 133 | # before we get to this point. 134 | 135 | wxEntry(); 136 | 137 | # If we reach this point, it means that our application has quit (either 138 | # the user has closed it or something in our code has closed it), and 139 | # we can either do any tidy-up necessary or just quit. 140 | -------------------------------------------------------------------------------- /user-software/p6-interface.php: -------------------------------------------------------------------------------- 1 | "); 51 | 52 | # We need to manually add commands to the history. This is used for 53 | # the command history that the user accesses with the up/down cursor 54 | # keys. We could choose to ignore commands (mis-typed ones or 55 | # intermediate input, for example) if we want, although we'll add 56 | # everything our users enter in this example. 57 | 58 | readline_add_history($line); 59 | 60 | # If we want to programmatically retrieve the history, we can use a 61 | # function called readline_list_history(). However, this is only 62 | # available if PHP has been compiled using libreadline. In most cases, 63 | # modern distributions compile it using the compatible libedit library 64 | # for licensing and other reasons. So we will keep a parallel copy of 65 | # the history in an array for programatic access. 66 | 67 | $history[] = $line; 68 | 69 | # Now we decide what to do with the users input. In real life, we may 70 | # want to trim(), strtolower() and otherwise filter the input first. 71 | 72 | switch ($line) { 73 | 74 | case "kill": 75 | 76 | echo "You don't want to do that.\n"; 77 | 78 | break; 79 | 80 | case "destroy": 81 | 82 | echo "That really isn't a good idea.\n"; 83 | 84 | break; 85 | 86 | case "obliterate": 87 | 88 | echo "Well, if we really must.\n"; 89 | 90 | break; 91 | 92 | case "history": 93 | 94 | # We will use the parallel copy of the command history that we 95 | # created earlier to display the command history. 96 | 97 | $counter = 0; 98 | 99 | foreach($history as $command) { 100 | 101 | $counter++; 102 | 103 | echo("$counter: $command\n"); 104 | 105 | }; 106 | 107 | break; 108 | 109 | case "byebye": 110 | 111 | # If it's time to leave, we want to break from both the switch 112 | # statement and the while loop, so we break with a level of 2. 113 | 114 | break 2; 115 | 116 | default : 117 | 118 | # Always remember to give feedback in the case of user error. 119 | 120 | echo("Sorry, command ".$line." was not recognised.\n"); 121 | } 122 | 123 | }; 124 | 125 | # If we reached here, outside of the while(1) loop, the user typed byebye. 126 | 127 | echo("Bye bye, come again soon!\n"); 128 | --------------------------------------------------------------------------------