As with any other SQL query with user-supplied data, to handle this in a truly safe way (or rather to push off the work where it belongs), use placeholders.
Yup, let's start with that goal and not lose sight of it - if the query text contains [user-supplied] data then the code is in violation of one of safemysql's (and safe SQL usage) tenants and the following is not necessarily true anymore!
[safemysql is] safe because every dynamic query part [or "bit of user data"] goes into query via placeholder.
The solution is then to build the query text with placeholders and the data array dynamically - but separately. At no time is the DQL (SQL syntax) and the data mixed. It is this separation (and the guarantee of the lower levels) that guarantees that there is no SQL Injection when this approach is followed.
$data = array();
$q = "SELECT * FROM table WHERE 1 = 1 ";
if($_POST['nameparts']){
$parts = explode(' ',$_POST['nameparts']);
foreach((array)$parts as $part){
$q .= " AND (`name` LIKE ?s OR `firstname` LIKE ?s )";
$data[] = '%' . $part . '%'; // add one for each replacement
$data[] = '%' . $part . '%';
}
if($_POST['age']) {
$q .= " AND `age` = ?i ";
$data[] = $_POST['age'];
}
}
And now we have the query text with placeholders and an array of the data to bind. Yippee, we are almost there! Now, create the array that will be passed and invoke the method supplying an array for the parameters.
$params = array($q);
$params = array_merge($params, $data);
$entries = call_user_func_array(array($dbs, 'getAll'), $params);
And, finished!