My first suggestion would be to use floated HTML elements instead of a table, which allows them to fill the available space instead of being hard-coded into a fixed number of table columns, e.g.:
<html>
<head>
<title>Test</title>
<style type='text/css'>
.container {
border: solid 2px blue;
padding: 0;
overflow: auto; /* this takes care of clearing the floats */
width: auto;
}
.test {
float: left;
margin: 0.5em;
padding: 0.5em;
border: solid 1px teal;
height: 8em;
width: 10em;
overflow: auto;
}
</style>
</head>
<body>
<h1>This Is a Test</h1>
<div class='container'>
<?php
for($i = 1; $i <= 10; $i++) {
$text = str_repeat("This is a test.", rand(1,5));
echo "<div class='test'><p>$text</p></div>\n";
}
?>
</div> <!-- container -->
<p>This is the end of the test, and should come after our repeating DIVs.</p>
</body>
</html>
Then just replace the for() loop in the above example with your loop on the query data (adjusting it to output the floating elements as above instead of table cells).
If you really want/need to use a table, the typical pattern is to use a counter variable that gets incremented on each loop iteration, and if it is evenly divisible by the desired number of columns, then you insert a "</tr>\n</tr> before the next <td>. This can get kind of fiddly though to keep the resulting HTML valid, and HTML tables in general can slow down page rendering in some cases, especially if you have nested tables.