The quick and easy solution
You could make your life easier and use a DirectoryIterator
to go over the directory.
echo '<select name="vids" size="4">';
foreach( new DirectoryIterator('/path/to/videos') as $file) {
if( $file->isFile() === TRUE && $file->getBasename() !== '.DS_Store') {
printf("<option>%s</option>\n", htmlentities($file->getBasename()));
}
}
echo '</select>';
Improvement: Decoupling Directory Filtering from SelectBox building
If you want to decouple the filtering logic from the foreach
loop, you can subclass a FilterIterator
to capsule that logic into it's accept()
method. The DirectoryIterator
has to be wrapped into the FilterIterator
then. The main point is reusability of course:
class MyFilter extends FilterIterator
{
public function accept()
{
return $this->current()->isFile() === TRUE &&
$this->current()->getBasename() !== '.DS_Store';
}
}
$iterator = new MyFilter(new DirectoryIterator('/path/to/videos'));
When you use foreach
on a filtered iterator, it will trigger accept()
automatically. If accept()
returns FALSE
, the current element will be filtered out in the iteration.
You create the SelectBox like this then:
echo '<select name="vids" size="4">';
foreach( $iterator as $file) {
printf("<option>%s</option>\n", htmlentities($file->getBasename()));
}
echo '</select>';
Alternative to subclassing FilterIterator
If you are too lazy to write a separate FilterIterator
or think it's just not worth it for a specific case or already have validators somewhere and dont want to duplicate their code, but still want to decouple Filtering and SelectBox creation, you can also use this custom FilterChainIterator
and add callbacks to it:
$iterator = new FilterChainIterator(new DirectoryIterator('/path/to/videos'));
$iterator->addCallback(function($file) {
return $file->isFile() === TRUE &&
$file->getBasename() !== '.DS_Store';});
The SelectBox creation would be the same as shown above.
Improvement: Making SelectBox creation reusable
In addition, if you want to make the SelectBox creation reusable, why not create a Helper for it. Below is a very simple one that uses DOM to create the actual HTML. You pass in any Iterator and it will create the HTML for you when you call it's render()
method or use it in a string context:
class SelectBox
{
protected $iterator;
public function __construct(Iterator $iterator)
{
$this->iterator = $iterator;
}
public function render()
{
$dom = new DOMDocument;
$dom->formatOutput = TRUE;
$dom->loadXml('<select name="vids"/>');
$dom->documentElement->appendChild(new DOMElement('option', 'Pick One'));
foreach($this->iterator as $option) {
$dom->documentElement->appendChild(
new DOMElement('option', $option));
}
return $dom->saveXml($dom->documentElement);
}
public function __toString()
{
return $this->render();
}
}
And then printing a SelectBox from an Iterator is as simple as
echo new SelectBox(new MyFilter(new DirectoryIterator('/path/to/videos')));
That's pretty flexible then, given that there is Iterators for everything. For instance
echo new SelectBox(new ArrayIterator(array('foo', 'bar', 'baz')));
would give a neatly formatted
<select>
<option>Pick One</option>
<option>foo</option>
<option>bar</option>
<option>baz</option>
</select>