تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

App Overview and Goal0:00

Let's build a little directory watcher. So this will be a little app that watches a directory and highlights changes in the directory as they happen. Very simple. We've got our application class set up here. We've got our Renderer class set up here. We're not doing anything with them yet, but they're all set up and ready to go. One thing to notice is that we have a public string path. This is the path of the directory that we're going to be watching. I've also written a simple chaos script, and all this script does is it looks at a directory

Running the Chaos Script0:26

This is the path of the directory that we're going to be watching. I've also written a simple chaos script, and all this script does is it looks at a directory and randomly creates or deletes or modifies files in that directory so we can watch changes happen live. It's a very simple script. It's not that great, but it'll do the job. So we're going to run that in a separate tab. So if we run php lab chaos, you'll see that it's clearing the directory every once in a while. It'll add random files.

Creating the Watch Loop0:55

a while. It'll add random files. It'll modify files, and it'll just keep a log. So we'll just keep that running in the background so that we can see if our app is working as files change. So you can see in the script that we're running the app with, we are newing it up with the directory that we're watching, and we're firing this watch method. So let's go ahead and create this watch method. If we go into our App class, we want to import something from Chewy. So we're going to import use, setsUp, and resets.

If we go into our app class, we want to import something from Chewy. So we're going to import use, setsUp, and resets. And so what does this do? This allows us to set up the world just as prompts would and reset when we're all done. But in between, we can do something different than prompts would normally do in the setup. So we're going to say public function watch, and it'll be this setup. And setup takes a callback. So after we've set up, what do you want to do? We'll say this->watchDirectory(). OK, so after we've set up, we're going to watchDirectory.

We'll say this watchDirectory. OK, so after we've set up, we're going to watchDirectory. And so just a peek into setup, this just sets up the world exactly as prompts would in a normal app. And so we're just doing that in a different way for this particular app because we're not responding to input. We are just going to run a continual loop inside the app. So let's just scaffold out this watchDirectory method. So we'll have protected function watchDirectory, and it's not going to return anything. So first, let's set up our listener so that we can listen for quit while we're in our

So we'll have protected function watchDirectory, and it's not going to return anything. So first, let's set up our listener so that we can listen for quit while we're in our while loop so that we can easily exit our app. listener equals KeepPressListener from Chewy for this listen for quit. And then we're going to be running a while true loop, while true. And while we're running the loop, we're going to render. We're going to listener once, so we're going to handle any key presses once per loop. And we're going to usleep for 100000 microseconds or 100 milliseconds. So we're going to sleep for 100 milliseconds between loops. So we want to list out continually the contents of our directory.

Listing Directory Contents3:05

So we're going to sleep for 100 milliseconds between loops. So we want to list out continually the contents of our directory. So we have a command for that, ls, and we're going to run it with a couple of arguments to make our lives a little easier. So we're going to say output equals shell_exec('ls -lah this path'). And so this will grab the output from this command. Let's take a look at these arguments real quick. I love this service. This is explainshell.com. You can just punch a command in here and it'll break it down for you one by one.

This is explainshell.com. You can just punch a command in here and it'll break it down for you one by one. So we're listing directory contents, and then we are using the long listing format. We are listing almost all, but do not list the dot directories. And we are going to make it human-readable so that our sizes are in human-readable format so we don't have to do that math. Okay, back in our command, let's collect our output as a collection. So we'll say that $items are equal to collect(explode("\n", $output)). Now if we run this command manually, ls -lah, you'll see that the first line is this total.

we received. Now if we run this command manually, ls -lah, you'll see that the first line is this total line. And then you have each of the items in the directory. So let's handle this total line, and then we'll go item by item and put that into our app state. So we want to keep that total line in our output. So let's actually set that up here, public string $total, and we'll default it to a blank string. And then down here we can say this $total is equal to str_replace('total', '', ...).

string. And then down here we can say this $total is equal to str_replace $total with a blank string. And let's just grab the first item, because that'll be the first item in the collection. Next, our renderer is going to need to know about these items, so that sounds like its state to me. So let's set this to a collection of items, public collection of items. And so let's set that up here. In the constructor, let's say this items is equal to collect, so we have a sensible default. So for now, we'll set it to this items is equal to $items.

In the constructor, let's say $items is equal to collect, so we have a sensible default. So for now, we'll set it to $items is equal to $items. We'll just set it to the output that exists. In our renderer, let's just print that to the screen. So in order to print these lines, we can say prompt($items) each $line. Print each of the $items to the screen. Let's see what that looks like. Okay, so as the directory is changing, it's just printing those $items to the screen. But that's not very exciting. I think we should extract information from each of these $lines and display it in a nicer

Parsing ls Output5:42

But that's not very exciting. I think we should extract information from each of these lines and display it in a nicer way. So instead of just setting it to items, let's say items map, and we'll just parse information out of the item so that we can create a custom display. So we'll say this parseItem. We'll fill out this method now. So we'll say protected function parseItem, and this accepts a string item, and it's going to return an array. So first, let's split this on blank strings.

to return an array. So first, let's split this on blank strings. So we can say $parts is equal to preg_split, and let's provide a regex. So any blank string, any space, one or more of them, we're going to split it on that item. So that's going to give us an array of this, this, this, this, this. So anywhere there's one or more spaces, it's going to chop this up into those individual parts. So we'll sanity check, and we'll say if count($parts) is less than or equal to one, it's probably not a legitimate row. Let's just return a blank array for now.

not a legitimate row. Let's just return a blank array for now. Now let's extract the field names to the parts that we are splitting on. So we'll say the fields that we're interested in are permissions, hard links, owner, shape, size, month, day, and time. So this order correlates with the output that is coming out line by line. So we'll set up a blank data array, and we'll say for each fields as field data field equals array_shift parts. Okay, so we'll just keep taking the first part off and tacking it on and making a keyed array for our data.

Okay, so we'll just keep taking the first part off and tacking it on and making a keyed array for our data. Next we need the name of the file, and that's really whatever is remaining in the array at this point, because we keep shifting items off. So we can just upload the parts as the data name. Let's make our lives a little bit easier and say dataDate is equal to dataMonth, dataDay, and dataTime. And so finally we also want to know if the data is a directory or not. So how do we know that from the output? Well, directories start with D in their permissions.

So how do we know that from the output? Well, directories start with D in their permissions. So anything that starts with D in permissions is a directory. So we can say dataIsDir is going to be equal to substring(dataPermissions[0], 1) is equal to D. And we can just return this data. So now we have an array of data that we can actually look up and format accordingly in our renderer. Last but not least, let's just, after mapping, filter only for items that have at least one item in the array. So we filter out anything that is just an empty array.

Rendering a Table View9:06

item in the array. So we filter out anything that is just an empty array. Great. Let's render this out in our renderer. So let's get rid of this line, and we'll say rows are equal to promptItems map, and for each of these items, we're going to say this getRow item prompt, and we're going to pass the prompt into this soon-to-be-implemented getRow method. Let's implement it now. protected function getRow is equal to the item and then our directoryWatcher prompt. And this is going to return an array.

protected function getRow is equal to the item and then our directory watcher prompt. And this is going to return an array. So how do we want this formatted? Let's return an array, and we'll say this $itemPermissions, want the permissions up front, and we'll $item a little bit because they're less important. We'll say $itemOwner, $itemGroup, this $itemSize, and we'll say this $itemDate. And we will highlight the name based on whether it's a directory or not. So if $item is dir, then we'll say this cyan, so we can differentiate between directories and regular files, $itemName, otherwise just return the $itemName. Okay.

files, item name, otherwise just return the item name. Okay. So now we're returning an array because we're going to take advantage of laying this out in a table, and we're going to use the symfony/table helper to do this. Now, we're not going to draw any borders on the table. It'll still be blank, but it'll allow us to auto-size the columns based on the data that we have. Let's collect some lines, and we'll say this getTable based on our rows, and now let's implement getTable. So we're going to do protected function getTable, and this will accept a collection of rows, and it's going to return a collection as well. So I have this pre-written out so that you don't have to watch me type this entire thing.

going to return a collection as well. So I have this pre-written out so that you don't have to watch me type this entire thing. But we're basically going to create a BufferedConsoleOutput, so it's just going to print, quote-unquote, this table to our buffer. We're going to use this Symfony table style to define basically blank characters for all of our crossing characters and horizontal border characters and vertical border characters so that it lays out as a table, but you can't actually see the borders of the table. Then we're going to create a table here from Symfony. We're going to use the BufferedConsoleOutput in order to render the table. We're going to set our rows, and we're going to set our table style, and then we're going to render.

We're going to use the buffered console output in order to render the table. We're going to set our rows, and we're going to set our table style, and then we're going to render. Now, to actually retrieve this output, we can say output is equal to trim bufferedContent, and we're going to trim any blank lines off of that. Then we can say return, collect, explode, PHP, PHP_EOL, output. Great, so now we're returning a collection of the resulting lines to our original method up here. So let's also add the total onto this. So let's lines prepend, let's prepend a blank line, and then we'll say lines prepend, this, $dimTotal, and then we'll say prompt, $total, which is something we set up beforehand. And then we'll just say lines each, this, line.

total, and then we'll say prompt, total, which is something we set up beforehand. And then we'll just say lines, each, this, line. Okay, so this will result in the total being shown, a blank line, and then our table. Let's see what it looks like. Okay, if we run our command, okay, this is good. See, we've got some custom formatting here. Our total is changing based on what's going on. Our directories have a slightly different style than our files. This is looking good. This is looking pretty nice.

Stabilizing Terminal Layout13:12

This is looking good. This is looking pretty nice. Let's pad this out to the height of the terminal so that it's not jumping around and changing as files change. So first, let's grab our height. height is going to be equal to the prompt, terminal, lines, and we'll just give it a buffer of six. Next, if we import the Aligns trait from Chewy, there is a function that allows us to pad vertically. So we can say padVertically our lines out to our height. So it'll keep appending lines until we're at our height value. Let's see what that looks like. Great, this is much better.

Let's see what that looks like. Great, this is much better. So things aren't jumping around anymore. It's a little more stable. It's easily readable. It cleared out the directory just now. I should add more files in a moment. There it is. Cool. So this is looking good.

Highlighting File Changes14:04

Cool. So this is looking good. Here's what would be cool. It would be neat if, as things changed, we could highlight what exactly changed. So if a new file, say, was added, the whole row was green. Or if the size of the file changed or the date of file change or the permissions changed, we could just highlight that in yellow so that we just knew what was going on. So it seems like as we go, we need to maintain a short version history of the items that we have seen previously. So let's just copy this down. And we'll make a little collection called versions. And we will initialize it with an empty collection.

And we'll make a little collection called versions. And we will initialize it with an empty collection. So we have a collection of versions. So it seems like, as we have items, we should add those items to our versions. So let's say this versions push this items. OK, so now we're collecting a history of the items that we have seen. But we don't really want to continue this history forever. So we'll always only take the last 20 of these versions so that this collection doesn't get just absolutely monstrous. This versions is equal to this versions take the last 20. All right, so now we have a history of the last 20 versions of this list that we have seen as we loop over this while loop.

This versions is equal to this versions take the last 20. All right, so now we have a history of the last 20 versions of this list that we have seen as we loop over this while loop. So what can we do with that in our renderer? Let's take a look. So if we go down to our getRow method, what we want is all of the past versions of this item. So what is unique about this item? Well, the name of the item is unique. So pastVersions is equal to promptVersions. And we're going to map and bring back only the particular item that we care about from the version history. So we'll say from this version, get where first where name is itemName.

And we're going to map and bring back only the particular item that we care about from the version history. So we'll say from this version, get where first where name is itemName. And we can go ahead and just filter out anything that returned null. So we only want actual items to come back. So now we have a history of this particular item from the versions. So what can we determine? We can say item is new if the promptVersions count is greater than zero and the pastVersions count is less than the promptVersions count divided by two. That means this item is new. It has newly arrived recently in the items list. So we can style that accordingly if the item is new.

It has newly arrived recently in the items list. So we can style that accordingly if the item is new. So we're going to change this array a little bit. So we're going to assign this to a variable called row. And we are going to grab all of the keys we are looking at. And we're going to make this an associative array. And instead of isdir, it's actually name. Whoops, name. OK. So we're making this an associative array so that we can check and see if anything has changed.

OK. So we're making this an associative array so that we can check and see if anything has changed. So we're actually going to return collect $row. And we are going to map over this $row value key. And we are going to need to use $pastVersions, the $item, and $item is new. So we can say if $item is new, well, let's just highlight the entire row green. So we'll say return $this green $item key. So we're saying forget the formatting that you want on this. Forget all of this default formatting. Just make the $item green.

Forget all of this default formatting. Just make the item green. Now let's check if this particular key, whether it's permissions, owner, group, size, date, or name, has changed. So we'll say changed is equal to pastVersions, filter, function, version. Version key is not equal to item key. So now we're looking for any scenarios where this has changed within our current version history. So we can say return changed is not empty. So if our collection of changed is not empty, let's just say this yellow, let's highlight it yellow, item key.

So if our collection of changed is not empty, let's just say this yellow, let's highlight it yellow, item key. Otherwise we can just return the default value, which is the default formatting of whatever is coming back here. And then we can say values, because we don't need the keys anymore, all. Now we're just returning this back to an array. Okay, let's see what this looks like. Great, you already saw it turn green. So as things are adding, it's green, and as things are changing, they're yellow. Just for a little bit, we just cleared out the directory.

So as things are adding, it's green, and as things are changing, they're yellow. Just for a little bit, we just cleared out the directory. Let's see what happens again. Great, green, we added it. We're changing things. So this is, we're now highlighting the changes that are happening in the directory as they're happening. This is so useful. This is so interesting. This is actually a valuable piece of software.

This is so interesting. This is actually a valuable piece of software. This is a really useful way to watch a directory and know exactly what is actually changing about the directory as it's happening.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟