FormToolsWithValidation.php

<?php

class FormToolsWithValidation
{
    protected $html = '';
    protected $doc;

    protected $html = '';
    protected $doc;
    
    public function __construct($formHtml,$hideXmlErrors=true)
    {
        $this->html = $formHtml;
        libxml_use_internal_errors($hideXmlErrors);
        $domDoc = new \DomDocument();
        $domDoc->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
        $domDoc->loadHtml($formHtml);
        libxml_use_internal_errors(false);
        $this->doc = $domDoc;
    }
    
    public function getValidSubmissions($userSubmissions,$ignore=[]){
        $inputs = $this->getInputs();

        $validSubmissions = [];
        foreach ($ignore as $index=>$ignoreKey){
            unset($userSubmissions[$ignoreKey]);
        }
        foreach ($inputs as $index=>$inputData){
            extract($inputData);
            $pass = $this->verify($inputData,$userSubmissions[$name]??null);
            if (!$pass){
                throw new \Exception("Input '{$name}' failed verification");
            }
            $validSubmissions[$name] = $userSubmissions[$name];
            unset($userSubmissions[$name]);
        }
        if (count($userSubmissions)>0){
            throw new \Exception("More data was sent to the server than was requested.");
        }
        return $validSubmissions;
    }

    protected function verify($inputData, $value)
    {
        //see $this->getInputs() to see what is extracted
        extract($inputData);
        if (empty($value) && $required) {
            return false;
        }

        if ($type == 'phone') {
            $clean = preg_replace('/[^0-9]/', '', $value);
            if (strlen($clean) == 10
                || strlen($clean) == 11) {
                return true;
            }
            return false;
        }
        if ($type == 'text') {
            return true;
        }
        if ($type == 'email') {
            $email = filter_var($value, FILTER_VALIDATE_EMAIL);
            if ($email === false) {
                return false;
            }

            return true;
        }

        return true;
    }

    public function getInputs()
    {
        $xPath = new DOMXpath($this->doc);
        $htmlInputs = $xPath->query('//*[@name]');
        $inputs = [];
        foreach ($htmlInputs as $input) {
            if ($input->tagName == 'form') {
                continue;
            }

            $data = [];
            $data['name'] = $input->getAttribute('name');
            $data['required'] = $input->hasAttribute('required');
            $data['type'] = $input->getAttribute('type');
            $data['tag'] = $input->tagName;
            $data['input'] = $input;
            if (isset($inputs[$data['name']])) {
                throw new \Exception("Input '" . $data['name'] . "' occurs twice on the form, which FormTools doesn't know how to handle.");
            }
            $inputs[$data['name']] = $data;
            continue;
        }
        return $inputs;
    }
    public function getFormName()
    {
        $forms = $this->doc->getElementsByTagName('form');
        if ($forms->count() > 1) {
            throw new \Exception("There are two forms... Form name cannot be retrieved.");
        } else if ($forms->count() == 0) {
            throw new \Exception("There are no forms. Cannot get form name.");
        }

        $name = $forms->item(0)->getAttribute('name');
        if ($name == null) {
            $name = null;
        }

        return $name;
    }

    public function insertPlaceholders($placeholders)
    {
        $xPath = new DOMXpath($this->doc);
        $htmlInputs = $xPath->query('//*[@name]');

        $formName = $this->getFormName();
        foreach ($htmlInputs as $input) {
            if ($input->tagName == 'form') {
                // $input->getAttribute('name');
                continue;
            }
            $name = $input->getAttribute('name');
            $ph = $placeholders[$name] ?? null;
            // var_dump($name,$ph);
            if ($ph === null) {
                continue;
            }

            $input->setAttribute('placeholder', $ph);
        }
    }

    public function nodeInnerHtml($node){
        $innerHTML = '';
        $children = $node->childNodes;
        foreach ($children as $child) {
            $innerHTML .= $child->ownerDocument->saveXML( $child );
        } 
        return $innerHTML;
    }
    public function innerBodyHTML(){
        // this was in my old FormsPlus code...
        $output = $this->doc->saveHtml($this->doc->childNodes[1]->childNodes[0]);
        $output = substr($output,strlen('<body>'),-strlen('</body>'));
        return $output;
    }
    public function __toString()
    {
        return $this->doc->saveHtml();
    }
    public function setFormInputValue($node,$value){
        $tagName = strtolower($node->tagName);
        $type = strtolower($node->getAttribute('type'));
        if ($tagName=='textarea'){
            $node->innerHTML = $value;
        } else if ($tagName=='input'){
            $node->setAttribute('value',$value);
        }
        return true;
    }

    public static function uploadFile($file, $destinationFolder, $validExts = ['jpg', 'png'], $maxMB = 15)
    {
        if (!is_array($file) || $file == []
            || $file['size'] == 0
            || $file['name'] == ''
            || $file['tmp_name'] == ''
            || !is_int($file['error'])) {
            return false;
        }

        try {
            if (!isset($file['error']) ||
                is_array($file['error'])
            ) {
                throw new RuntimeException('Invalid parameters.');
            }

            switch ($file['error']) {
                case UPLOAD_ERR_OK:
                    break;
                case UPLOAD_ERR_NO_FILE:
                    throw new RuntimeException('No file sent.');
                case UPLOAD_ERR_INI_SIZE:
                case UPLOAD_ERR_FORM_SIZE:
                    throw new RuntimeException('Exceeded filesize limit.');
                default:
                    throw new RuntimeException('Unknown errors.');
            }

            // You should also check filesize here.
            if ($file['size'] > ($maxMB * 1024 * 1024)) {
                throw new RuntimeException('Exceeded filesize limit.');
            }

            $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
            if (!in_array($ext, $validExts)) {
                // var_dump($ext);
                // var_dump($validExts);
                // var_dump($file);
                // exit;
                throw new RuntimeException('Invalid file format.');
            }

            if (!file_exists($destinationFolder)) {
                mkdir($destinationFolder, 0775, true);
            }

            $fileName = sha1_file($file['tmp_name']) . '.' . $ext;
            if (!move_uploaded_file(
                $file['tmp_name'],
                $destinationFolder . '/' . $fileName
            )
            ) {
                throw new RuntimeException('Failed to move uploaded file.');
            }

            return $fileName;

        } catch (RuntimeException $e) {

            throw $e;

        }
    }

}