Your code isn't checking whether prepare returned successfully.
If the prepare encounters an error, it returns FALSE. instead of a statement object.
And a boolean FALSE value (the return from prepare) does not have a method/function bind_param.
$stmt = $conex->prepare("..."); if(!$stmt) { echo "error in prepare ". mysqli_error($conex) ; } else { // prepare returned a statement object so we can do a bind_param $stmt->bind_param(...);
To fix a syntax issue in the SQL statement that's causing an error in prepare, replace they keyword WHERE with keyword HAVING.
The predicates (conditions) in the WHERE clause are evaluated when rows are accessed. So the result of an aggregate function (e.g. COUNT() is not going to be available at the time those conditions in the WHERE are check. There's no way for that condition to be evaluated until after the rows are accessed, and the aggregate functions are evaluated. Predicates (conditions) in the HAVING clause get evaluated later, so it's valid to reference aggregates in a HAVING clause.
Just making that change to the SQL statement isn't likely going to get you the result you are looking for. Without a specification, we're just guessing what you are trying to return.
var_dump(). That will tell you what kind of datatype PHP sees it as being.WHEREis applied before the group, so you need to useGROUP BY id HAVING COUNT(id_ref) < ?rather than invokingWHERE.