2
\$\begingroup\$

I'm trying to sketch out a stripped down generic form validation example. I started by creating Plain Old PHP Objects (referred as POPOs) for form input with some classes such as a validator and html renderer class, that I then reduced to explicit functions.

The POPOs are useful in as much as you can comment and glance at expected properties. Looking at the code, one starts to wonder if I'd be better off with well defined arrays as inputs and not bother with the plain objects, as some of the object wrangling (the popo functions) are effectively just wrappers for array functions, and perhaps an array would just do.

Are these plain objects muddying the waters?

Any comments welcome.

<?php
class UserForm
{
    public $email;
}

class UserFormErrors extends UserForm
{}

function map_array_to_popo(array $array, $object)
{
    foreach($object as $key => $value)
        $object->$key = isset($array[$key])
            ? $array[$key]
            : null;

    return $object; 
}

function popo_has_empty_values($object)
{
    $array = (array) $object;

    return empty(array_filter($array));
}

function user_form_validate(UserForm $input)
{
    $errors = new UserFormErrors;
    if($input->email == '') {
        $errors->email[] = 'Email address is required.';
    } elseif(filter_var($input->email, FILTER_VALIDATE_EMAIL) === false) {
        $errors->email[] = 'Email address must be valid.';
    }

    return $errors;
}

function user_form_html_render(
        $action = '',
        UserForm $values, 
        UserFormErrors $errors
    )
{
    $escape = function($string) {
        return htmlspecialchars($string);
    };
    $error = function($messages) use ($escape) {
        return
            empty($messages)
            ? ''
            : '<ul><li>' . 
            implode('</li><li>', array_map($escape, $messages)) . 
            '</li></ul>';
    };
    ?>
    <form method='POST' action="<?= $action ?>">
        <label for="email">Email address:</label>
        <input type="text" name="email" id="email" value=
        "<?= $escape($values->email) ?>">
        <?= $error($errors->email) ?>
        <input type="submit">
    </form>
    <?php
}


if($_SERVER['REQUEST_METHOD'] == 'POST') {
    $input   = array_filter($_POST, 'is_string');
    $input   = array_map('trim', $input);
    $values  = map_array_to_popo($input, new UserForm);
    $errors  = user_form_validate($values);
    $valid   = popo_has_empty_values($errors);

    if($valid) {
        // Form values look good, do what you want with values.
        echo 'Email address entered is: ' . $values->email;
    } else {
        // Display form with data and errors
        user_form_html_render('', $values, $errors);
    }
} else {
    user_form_html_render('', new UserForm, new UserFormErrors);
}
\$\endgroup\$
  • \$\begingroup\$ Not wanting to detract from above, but a sketch of a non POPO version is here: pastebin.com/DM52nCFG \$\endgroup\$ – Progrock Nov 4 '18 at 11:39
1
\$\begingroup\$

Personally I don't see any value in introducing such objects. Especially given you are inclined to functional programming, which is a real trademark of your code, whereas such "POPOs" indeed look alien in it.

Either way, there is one plain mistake in the code, an Error by no means is a Form, so UserForm never can be a parent for the UserFormErrors. Even for sake of sparing you from writing a couple lines of code.

\$\endgroup\$
  • \$\begingroup\$ UserForm is badly named, perhaps UserFormInput and UserFormInputErrors would have been a better choice. \$\endgroup\$ – Progrock Nov 4 '18 at 10:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.