Coding Challenge — Building wc in PHP

Mohasin Hossain
5 min readMay 30, 2024

--

This challenge is to build your own version of the Unix command line tool wc!

Photo by Ben Griffiths on Unsplash

Step One: Count Bytes (-c option)

Create a PHP file named ccwc.php with the correct shebang line and PHP code and implement the functionality to count the bytes of a given file.

#!/usr/bin/env php
<?php
if ($argc < 3 || $argv[1] != '-c') {
echo "Usage: php ccwc.php -c <filename>\n";
exit(1);
}

$filename = $argv[2];
if (!file_exists($filename)) {
echo "File not found: $filename\n";
exit(1);
}

$filesize = filesize($filename);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . " $filename\n";
?>

Step Two: Count Lines (-l option)

Extend the script to support the -l option to count lines in a file.

#!/usr/bin/env php
<?php
if ($argc < 3) {
echo "Usage: php ccwc.php -c|-l <filename>\n";
exit(1);
}

$option = $argv[1];
$filename = $argv[2];

if (!file_exists($filename)) {
echo "File not found: $filename\n";
exit(1);
}

if ($option == '-c') {
$filesize = filesize($filename);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-l') {
$file = fopen($filename, "r");
$linecount = 0;
while (!feof($file)) {
fgets($file);
$linecount++;
}
fclose($file);
echo str_pad($linecount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} else {
echo "Unknown option: $option\n";
exit(1);
}
?>

Step Three: Count Words (-w option)

Add support for the -w option to count words in a file.

#!/usr/bin/env php
<?php
if ($argc < 3) {
echo "Usage: php ccwc.php -c|-l|-w <filename>\n";
exit(1);
}

$option = $argv[1];
$filename = $argv[2];

if (!file_exists($filename)) {
echo "File not found: $filename\n";
exit(1);
}

if ($option == '-c') {
$filesize = filesize($filename);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-l') {
$file = fopen($filename, "r");
$linecount = 0;
while (!feof($file)) {
fgets($file);
$linecount++;
}
fclose($file);
echo str_pad($linecount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-w') {
$file = file_get_contents($filename);
$wordcount = str_word_count($file);
echo str_pad($wordcount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} else {
echo "Unknown option: $option\n";
exit(1);
}
?>

Step Four: Count Characters (-m option)

Add support for the -m option to count characters in a file.

#!/usr/bin/env php
<?php
if ($argc < 3) {
echo "Usage: php ccwc.php -c|-l|-w|-m <filename>\n";
exit(1);
}

$option = $argv[1];
$filename = $argv[2];

if (!file_exists($filename)) {
echo "File not found: $filename\n";
exit(1);
}

if ($option == '-c') {
$filesize = filesize($filename);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-l') {
$file = fopen($filename, "r");
$linecount = 0;
while (!feof($file)) {
fgets($file);
$linecount++;
}
fclose($file);
echo str_pad($linecount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-w') {
$file = file_get_contents($filename);
$wordcount = str_word_count($file);
echo str_pad($wordcount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-m') {
$file = file_get_contents($filename);
$charcount = mb_strlen($file);
echo str_pad($charcount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} else {
echo "Unknown option: $option\n";
exit(1);
}
?>

Step Five: Default Option

Extend the script to handle the default option (no specific option provided).

#!/usr/bin/env php
<?php
if ($argc < 2) {
echo "Usage: php ccwc.php [-c|-l|-w|-m] <filename>\n";
exit(1);
}

$option = $argv[1];
$filename = $argv[$argc - 1];

if (!file_exists($filename)) {
echo "File not found: $filename\n";
exit(1);
}

if ($option == '-c') {
$filesize = filesize($filename);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-l') {
$file = fopen($filename, "r");
$linecount = 0;
while (!feof($file)) {
fgets($file);
$linecount++;
}
fclose($file);
echo str_pad($linecount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-w') {
$file = file_get_contents($filename);
$wordcount = str_word_count($file);
echo str_pad($wordcount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($option == '-m') {
$file = file_get_contents($filename);
$charcount = mb_strlen($file);
echo str_pad($charcount, 8, " ", STR_PAD_LEFT) . " $filename\n";
} elseif ($argc == 2) {
$file = file_get_contents($filename);
$filesize = filesize($filename);
$linecount = substr_count($file, "\n") + 1;
$wordcount = str_word_count($file);
echo str_pad($linecount, 8, " ", STR_PAD_LEFT);
echo str_pad($wordcount, 8, " ", STR_PAD_LEFT);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . " $filename\n";
} else {
echo "Unknown option: $option\n";
exit(1);
}
?>

Final Step: Read from Standard Input

Finally, modify the script to support reading from standard input if no filename is specified.

#!/usr/bin/env php
<?php
if ($argc < 2) {
echo "Usage: php ccwc.php [-c|-l|-w|-m] [<filename>]\n";
exit(1);
}

$option = $argv[1];
$filename = isset($argv[2]) ? $argv[2] : null;

if ($filename && !file_exists($filename)) {
echo "File not found: $filename\n";
exit(1);
}

if ($filename) {
$file = file_get_contents($filename);
} else {
$file = stream_get_contents(STDIN);
}

if ($option == '-c') {
$filesize = strlen($file);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . ($filename ? " $filename" : "") . "\n";
} elseif ($option == '-l') {
$linecount = substr_count($file, "\n");
echo str_pad($linecount, 8, " ", STR_PAD_LEFT) . ($filename ? " $filename" : "") . "\n";
} elseif ($option == '-w') {
$wordcount = str_word_count($file);
echo str_pad($wordcount, 8, " ", STR_PAD_LEFT) . ($filename ? " $filename" : "") . "\n";
} elseif ($option == '-m') {
$charcount = mb_strlen($file);
echo str_pad($charcount, 8, " ", STR_PAD_LEFT) . ($filename ? " $filename" : "") . "\n";
} elseif ($argc == 2) {
$filesize = strlen($file);
$linecount = substr_count($file, "\n");
$wordcount = str_word_count($file);
echo str_pad($linecount, 8, " ", STR_PAD_LEFT);
echo str_pad($wordcount, 8, " ", STR_PAD_LEFT);
echo str_pad($filesize, 8, " ", STR_PAD_LEFT) . ($filename ? " $filename" : "") . "\n";
} else {
echo "Unknown option: $option\n";
exit(1);
}
?>

Save the above code into ccwc.php, make the file executable (chmod +x ccwc.php), and run it as described in the steps to validate its correctness.

Run ccwc.php

./ccwc.php -c test.txt // Count Bytes (-c option)
./ccwc.php -l test.txt // Count Lines (-l option)
./ccwc.php -w test.txt // Count Words (-w option)
./ccwc.php -m test.txt // Count Characters (-m option)
./ccwc.php test.txt // Default Option (counts lines, words, and bytes)
cat test.txt | ./ccwc.php -l // use cat or other commands to pipe input to ccwc.php

Conclusion

By following the detailed steps outlined in this guide, you have successfully created and executed a custom version of the Unix wc command in PHP, named ccwc.php. This script now supports multiple command-line options to count bytes, lines, words, and characters in a file, as well as a default option to display all these metrics when no specific option is provided. Additionally, it can read from standard input, making it versatile for various use cases.

Through this exercise, you have not only implemented a useful command-line tool but also gained experience with PHP scripting, handling command-line arguments, and working with file I/O operations in PHP. This foundational knowledge will be valuable as you continue to explore and build more complex scripts and applications in PHP and other programming languages.

Keep experimenting with and enhancing your ccwc.php script. You might consider adding more features, such as error handling improvements, support for multiple files, or even more statistics (like the longest line length). The skills and concepts you've practiced here will serve you well in a wide range of programming tasks. Happy coding!

--

--

Mohasin Hossain

Senior Software Engineer | Mentor @ADPList | Backend focused | PHP, JavaScript, Laravel, Vue.js, Nuxt.js, MySQL, TDD, CI/CD, Docker, Linux