public function input($name, $label, $attr = array())
    {
        // generate an id if none exists
        if (empty($attr['id'])) {
            $attr['id'] = $this->transformNameForID($name);
        }

        // build the label
        if ($label !== false) {
            $labelAttr = [];
            if (is_array($label)) {
                $labelAttr = $label;
                $label = (! empty($label['value'])) ? $label['value'] : $name;
                unset($labelAttr['value']);
            }

            $labelAttr['for'] = $attr['id'];
            $label = $this->label($label, $labelAttr);
        } else {
            $label = '';
        }

        // remove any wrapper attributes
        // these will be dealt with later
        $wrapper = array();
        if (isset($attr['wrapper'])) {
            $wrapper = $attr['wrapper'];
            unset($attr['wrapper']);
        }

        // any before / between / after set
        // $attr passed by reference
        $injects = $this->getInjects($attr);

        // generate the actual field
        if (empty($attr['type'])) {
            $attr['type'] = 'text';
        }

        $field = $this->generateField(
            $name,
            $attr
        );

        // build the contents for the wrapper
        $contents = $this->buildField($attr['type'], $label, $field, $injects);

        // check if they actually want a wrapper
        if (isset($wrapper) && $wrapper === false) {
            return $contents;
        }

        // build up the wrapper info
        // should return the tag and attributes array
        $wrapperInfo = $this->getWrapper($wrapper, $attr);

        // return the final wrapper tag with the contents
        return $this->Html->tag(
            $wrapperInfo['tag'],
            $wrapperInfo['attr'],
            $contents,
            true
        );
    }