diff --git a/README.md b/README.md
index 13b24f78bf84b83994dc016cdd0f83e718be95b7..cb431f67aeb2436bdd046e1cf54d8947681c2f12 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,7 @@ A PHP class that I use
 * for authentication of user logins
 * CRUD actions on ldap nodes
 
-Institute for Medical Education; University of Bern
-
-License: GNU GPL 3
-
-see [docs](docs/)
\ No newline at end of file
+👤 Author: Axel Hahn; Institute for Medical Education; University of Bern
+📄 Source: https://git-repo.iml.unibe.ch/iml-open-source/ldap-php-class
+📜 License: GNU GPL 3.0
+📗 Docs: https://os-docs.iml.unibe.ch/ldap-php-class/
diff --git a/docs/20_Usage.md b/docs/20_Usage.md
index 595e75933ccab564e3067ad5ce21e4d25a0c3161..6f6c14f70aa820fb7bc5a65d0c0576592ae12a80 100644
--- a/docs/20_Usage.md
+++ b/docs/20_Usage.md
@@ -11,12 +11,11 @@ require_once '[APPROOT]/classes/ldap.class.php';
 As an example I create a hash named $aConfig and save it as "inc_config.php".
 
 ```php
-$aConfig=[
+return [
       ...
 
        'ldap' => [
            'server'     => 'ldaps://ldap.example.com',
-           'port'       => 636,
            'DnLdapUser' => 'cn=Lookup,ou=Service,dc=some,dc=example.com',
            'PwLdapUser' => 'PasswordOfLookupUser',
 
@@ -32,16 +31,69 @@ $aConfig=[
 ];
 ```
 
-## Example: verify login data
+## initialize connection
 
 ```php
-require_once('inc_config.php');
+$aConfig = require_once('inc_config.php');
 require_once '[APPROOT]/classes/ldap.class.php';
 
-oLdap=new imlldap($aConfig['ldap']);
+$oLdap=new imlldap($aConfig['ldap']);
+```
+
+## Methods
+
+### Object handling
+
+* objAdd(string $sDn, array $aItem): bool
+* objGet(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"]): bool|array
+* objUpdate(string $sDn, array $aItem): bool
+* objDelete(string $sDn): bool
+
+### Attributes
+
+* objAddAttr(string $sDn, array $aItem): bool
+* objDeleteAttr(string $sDn, array $aItem): bool
+* objectAttributeExists(string $sDn, string $sAttribute): bool
+* objectAttributeAndValueExist(string $sDn, string $sAttribute, string $sAttrValue): bool - check only
+* objectAttributeAndValueMustExist(string $sDn, string $sAttribute, string $sAttrValue): bool - force the existence of attribute and value
+
+### User functions
+
+* userAdd(array $aItem, string $sDn = "")
+* getUserInfo(string $sUser, array $aAttributesToGet = ["*"]): bool|array
+* userDelete(string $sUserDn)
+* userUpdate(array $aItem)
+* setPassword(string $sUser, string $sPW): bool
+* verifyPassword(string $sUser, string $sPW): bool
+
+### Debugging
+
+Turn debugging on or off
 
-// set values from $_FORM or $_POST data of your login form here
-// The variable $bAuthenticated is true if authentication of the user was successful.
-$bAuthenticated=oLdap->verifyPassword($sUser, $sPassword);
+* debugOff()
+* debugOn()
 
+## Examples
+
+### read user attributes
+
+Use the username or an email address to get user data. The 2nd parameter defines the attributes to fetch (`["*"]` is default).
+
+```php
+$aUser = $oLdap->getUserInfo("john@example.com", []);
+$aUser = $oLdap->getUserInfo("john@example.com", ["memberof", "uid"]);        
+
+// simplify result array:
+print_r($oLdap->normalizeSearchentry($aUser));
+```
+
+### Example: search
+
+When using special chars in search then you can sanitize the search string.
+
+```php
+$sCn = 'John Smith (john)';
+$sSearchFilter = '(cn='.$oLdap->sanitizeFilter($sCn).')';
+$aResults = $oLdap->searchDn("<DN here>", $sSearchFilter, ["*"]);
+$oLdap->close();
 ```
diff --git a/docs/30_Methods.md b/docs/30_Methods.md
index 8fd210c8a505867f4d0ab2dbcca910dbddb347fa..86962bdc4e3e9a69283624b89a521e901e85bea3 100644
--- a/docs/30_Methods.md
+++ b/docs/30_Methods.md
@@ -5,19 +5,28 @@
 ---
 ## `class imlldap`
 
-IML LDAP CONNECTOR *<pre> 2022-02-22  ah  added objGet(), sanitizeFilter() <br> 2022-08-18  ah  mask password (showing 4 chars only) <br> 2022-08-22  ah  mhash is deprecated <br> 2022-08-26  ah  fix verifyPassword <br> </pre>
+IML LDAP CONNECTOR
+
+2022-02-22  ah  added objGet(), sanitizeFilter() 2022-08-18  ah  mask password (showing 4 chars only) 2022-08-22  ah  mhash is deprecated 2022-08-26  ah  fix verifyPassword 2024-07-11  ah  php8 only: use variable types
 
 ---
-## `private $_aLdap = array( 'server' => false, 'port' => false, 'DnLdapUser' => false, // ldap rdn oder dn 'PwLdapUser' => false, 'DnUserNode' => false, // ou=People... 'DnAppNode' => false, // cn=AppGroup... 'protoVersion' => 3, 'debugLevel' => 0, )`
+## `private array $_aLdap = [ 'server' => false, 'port' => false, 'DnLdapUser' => false, // ldap rdn oder dn 'PwLdapUser' => false, 'DnUserNode' => false, // ou=People... 'DnAppNode' => false, // cn=AppGroup... 'protoVersion' => 3, 'debugLevel' => 0, ]`
 
 ---
-## `private $_ldapConn = false`
+## `private object|bool $_ldapConn = false`
 
 ---
-## `private $_ldapBind = false`
+## `private object|bool $_ldapBind = false`
+
+ldap bind object - bind was done?
 
 ---
-## `public function __construct($aConfig = array())`
+## `var bool $bDebug = false`
+
+Flag if debug mode is on
+
+---
+## `public function __construct(array $aConfig = [])`
 
 constructor
 **Parameters:**
@@ -27,17 +36,17 @@ Var | Type | Desciption
 $aConfig | array | optional set ldap connection
 
 ---
-## `public function debugOn()`
+## `public function debugOn(): void`
 
 turn debug messages on; if this detail level is not enough, set a value with key debugLevel in ldap config array
 
 ---
-## `public function debugOff()`
+## `public function debugOff(): void`
 
 turn debug messages off
 
 ---
-## `private function _w($sText)`
+## `private function _w(string $sText): bool`
 
 write debug message if denugOn() was fired.
 
@@ -52,7 +61,7 @@ $sText | string | message text
   boolean
 
 ---
-## `private function _wLdaperror($sText = '')`
+## `private function _wLdaperror(string $sText = ''): bool`
 
 write last ldap error as debug
 
@@ -67,7 +76,7 @@ $sText | string | message text
   boolean
 
 ---
-## `public function setConfig($aConfig = array())`
+## `public function setConfig(array $aConfig = []): void`
 
 set a ldap config
 
@@ -87,17 +96,17 @@ Var | Type | Desciption
 $aConfig | array | new config items
 
 ---
-## `public function close()`
+## `public function close(): void`
 
 close an existing ldap connection
 
 ---
-## `public function connect()`
+## `public function connect(): void`
 
 connect to ldap
 
 ---
-## `public function bind($sUser = '', $sPw = '')`
+## `public function bind(string $sUser = '', string $sPw = ''): bool`
 
 ldap bind connects with a ldap user. If the ldap connection was not opened yet the connection will be established. If a binding exists it will be unbind
 
@@ -109,12 +118,12 @@ $sUser | string | optional: username (overrides _aLdap['DnLdapUser'])
 $sPw | string | optional: password (overrides _aLdap['PwLdapUser'])
 
 ---
-## `public function unbind()`
+## `public function unbind(): void`
 
 ldap unbind ... if a bind exists
 
 ---
-## `public function DnExists($sDn)`
+## `public function DnExists(string $sDn): bool`
 
 check if a DN already exists; return is true/ false
 **Parameters:**
@@ -128,37 +137,22 @@ $sDn | string | DN to check
   boolean
 
 ---
-## `public function normalizeSearchentry($aRecord)`
+## `public function normalizeSearchentry(array $aRecord): bool|array`
 
-get simpler array from ldap_get_entries after ldap_search
+get simpler array from ldap_get_entries after ldap_search If the given array doesn't contain the key "dn" it returns "false"
 
 **Parameters:**
 
 Var | Type | Desciption
 --  |--    |--
-$aRecord | array | singel result item
+$aRecord | array | single result item
 
 **Return:**
 
   array
 
 ---
-## `public function normalizeSearchresult($aLdapSearchresult)`
-
-get simpler array from ldap_get_entries after ldap_search
-
-**Parameters:**
-
-Var | Type | Desciption
---  |--    |--
-$aRecord | array | singel result item
-
-**Return:**
-
-  array
-
----
-## `static public function sanitizeFilter($s)`
+## `static public function sanitizeFilter(string $s): string`
 
 sanitize value to put into a search filter WARNING: the implementation is incomplete! I replaces the first N ascii chars only
 
@@ -176,9 +170,9 @@ $s | string | value to sanitize
   string
 
 ---
-## `public function searchDn($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*"), $bRecursive=true)`
+## `public function searchDn(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array`
 
-search in ldap directory and get result as array
+search in ldap directory and get result as array. It returns "false" on error: - no ldap connection - search failed
 
 **Parameters:**
 
@@ -191,10 +185,10 @@ $bRecursive | boolean | recusrive (uses ldap_search) or not (ldap_list)
 
 **Return:**
 
-  array
+  boolean|array
 
 ---
-## `public function searchUser($sSearchFilter='', $aAttributesToGet = array("*"), $bRecursive=true)`
+## `public function searchUser(string $sSearchFilter = '', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array`
 
 search for entries in in ldap user node and get result as array
 
@@ -208,10 +202,10 @@ $bRecursive | bool | flag: recursive search? default: true (=yes, recursive)
 
 **Return:**
 
-  array
+  boolean|array
 
 ---
-## `public function getUserInfo($sUser, $aAttributesToGet = array("*"))`
+## `public function getUserInfo(string $sUser, array $aAttributesToGet = ["*"]): bool|array`
 
 search user by a given username or email address. It returns false if the user does not exist or is not member of the group 'DnAppNode' (if it was set).
 
@@ -219,15 +213,15 @@ search user by a given username or email address. It returns false if the user d
 
 Var | Type | Desciption
 --  |--    |--
-$sUser | type | user id (uid) or email (mail) to search
-$aAttributesToGet | type | i.e. array("ou", "sn", "vorname", "mail", "uid", "memberOf")
+$sUser | string | user id (uid) or email (mail) to search
+$aAttributesToGet | array | i.e. ["ou", "sn", "vorname", "mail", "uid", "memberOf"]
 
 **Return:**
 
   boolean|array
 
 ---
-## `public function getUserDn($sUser)`
+## `public function getUserDn(string $sUser): bool|string`
 
 search for a DN entry with the lookup user by a given username or email address. It returns false if the user does not exist or is not member of the group 'DnAppNode' (if it was set).
 
@@ -235,14 +229,14 @@ search for a DN entry with the lookup user by a given username or email address.
 
 Var | Type | Desciption
 --  |--    |--
-$sUser | type | %s
+$sUser | string | %s
 
 **Return:**
 
   string
 
 ---
-## `public function setPassword($sUser, $sPW)`
+## `public function setPassword(string $sUser, string $sPW): bool`
 
 set a password for a given user; this requires a ldap bind with master/ admin account
 
@@ -258,7 +252,7 @@ $sPW | string | password
   boolean
 
 ---
-## `private function _getNTLMHash($Input)`
+## `private function _getNTLMHash(string $Input): string`
 
 get NTLM hash from a string taken from https://secure.php.net/manual/en/ref.hash.php
 
@@ -273,7 +267,7 @@ $Input | string | %s
   string
 
 ---
-## `public function setPasswordSamba($sUser, $sPW)`
+## `public function setPasswordSamba(string $sUser, string $sPW): bool`
 
 set a password for a given user for Samba this requires a ldap bind with master/ admin account see https://msdn.microsoft.com/en-us/library/cc223248.aspx see http://php.net/ldap-modify-batch  - last examle see https://secure.php.net/manual/en/ref.hash.php
 
@@ -289,23 +283,23 @@ $sPW | string | password
   boolean
 
 ---
-## `public function objAdd($sDn, $aItem)`
+## `public function objAdd(string $sDn, array $aItem): bool`
 
-update an ldap object this requires a ldap bind with master/ admin account
+update an ldap object this requires a ldap bind with master/ admin account It returns true if the action was successful
 
 **Parameters:**
 
 Var | Type | Desciption
 --  |--    |--
 $sDn | string | dn to update
-$aItem | string | array of new ldap properties
+$aItem | array | array of new ldap properties
 
 **Return:**
 
   boolean
 
 ---
-## `public function objAddAttr($sDn, $aItem)`
+## `public function objAddAttr(string $sDn, array $aItem): bool`
 
 update an ldap attribute this requires a ldap bind with master/ admin account
 
@@ -314,16 +308,16 @@ update an ldap attribute this requires a ldap bind with master/ admin account
 Var | Type | Desciption
 --  |--    |--
 $sDn | string | dn to update
-$aItem | string | array of new ldap properties
+$aItem | array | array of new ldap properties
 
 **Return:**
 
   boolean
 
 ---
-## `public function objGet($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*"))`
+## `public function objGet(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"]): bool|array`
 
-read attributes from ldap node with given DN (using ldap_read)
+read attributes from ldap node with given DN (using ldap_read) It returns "false" if the action was not successful - no ldap connection - DN or filter didn't match
 
 **Parameters:**
 
@@ -335,12 +329,12 @@ $aAttributesToGet | array | flat array of attributes to fetch
 
 **Return:**
 
-  array
+  boolean|array
 
 ---
-## `public function objUpdate($sDn, $aItem)`
+## `public function objUpdate(string $sDn, array $aItem): bool`
 
-update an ldap object with given key-value array if the attribute (key) does not exist it will be created. this requires a ldap bind with master/ admin account
+update an ldap object with given key-value array if the attribute (key) does not exist it will be created. this requires a ldap bind with master/ admin account It returns "false" if the action failed
 
 **Parameters:**
 
@@ -354,9 +348,9 @@ $aItem | array | updated entry
   boolean
 
 ---
-## `public function objDelete($sDn)`
+## `public function objDelete(string $sDn): bool`
 
-delete an ldap object this requires a ldap bind with master/ admin account
+delete an ldap object this requires a ldap bind with master/ admin account It returns "false" if the action failed
 
 **Parameters:**
 
@@ -369,25 +363,24 @@ $sDn | string | full DN to remove
   boolean
 
 ---
-## `public function objDeleteAttr($sDn, $aItem)`
-
-delete attributes of an ldap object this requires a ldap bind with master/ admin account
+## `public function objDeleteAttr(string $sDn, array $aItem): bool`
 
-TODO: Test me
+delete attributes of an ldap object this requires a ldap bind with master/ admin account It returns "false" if the action failed
 
+remove attribute "userPassword" of user $sUserDn: <code>$oLdap->objDeleteAttr($sUserDn, ['userPassword'=>[]]</code>
 **Parameters:**
 
 Var | Type | Desciption
 --  |--    |--
 $sDn | string | DN
-$aItem | string | item to remove
+$aItem | array | item to remove
 
 **Return:**
 
   boolean
 
 ---
-## `public function objectAttributeExists($sDn, $sAttribute)`
+## `public function objectAttributeExists(string $sDn, string $sAttribute): bool`
 
 check if an attribute exists in a DN
 
@@ -404,7 +397,7 @@ $sAttrValue | string | value to check
   boolean
 
 ---
-## `public function objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue)`
+## `public function objectAttributeAndValueExist(string $sDn, string $sAttribute, string $sAttrValue): bool`
 
 check if an attribute and value exist in a DN
 
@@ -421,7 +414,7 @@ $sAttrValue | string | value to check
   boolean
 
 ---
-## `public function objectAttributeAndValueMustExist($sDn, $sAttribute, $sAttrValue)`
+## `public function objectAttributeAndValueMustExist(string $sDn, string $sAttribute, string $sAttrValue): bool`
 
 check an attribute and value; it will be created if it does not exist this requires a ldap bind with master/ admin account
 
@@ -438,7 +431,7 @@ $sAttrValue | string | value to check
   boolean
 
 ---
-## `public function userAdd($aItem, $sDn = false)`
+## `public function userAdd(array $aItem, string $sDn = ""): bool`
 
 create a new user item this requires a ldap bind with master/ admin account
 
@@ -454,7 +447,7 @@ $sDn | string | optional DN where to create the user
   boolean
 
 ---
-## `public function userDelete($sUserDn)`
+## `public function userDelete(string $sUserDn): bool`
 
 delete a user this requires a ldap bind with master/ admin account
 
@@ -470,7 +463,7 @@ $sPW | string | new password to set
   boolean
 
 ---
-## `public function userUpdate($aItem)`
+## `public function userUpdate(array $aItem): bool`
 
 update an ldap object this requires a ldap bind with master/ admin account
 
@@ -485,7 +478,7 @@ $aItem | array | new user data to update
   boolean
 
 ---
-## `public function verifyPassword($sUser, $sPW)`
+## `public function verifyPassword(string $sUser, string $sPW): bool`
 
 verify user and password
 **Parameters:**
diff --git a/docs/_index.md b/docs/_index.md
index 074f0b26c1b2f2ed37a9648729d13c0cac2d8d16..2f382b7453ad16188037524529a4197f82838d93 100644
--- a/docs/_index.md
+++ b/docs/_index.md
@@ -5,12 +5,13 @@ A PHP class that I use
 * for authentication of user logins
 * CRUD actions on ldap nodes
 
-Institute for Medical Education; University of Bern
-
-License: GNU GPL 3
+👤 Author: Axel Hahn; Institute for Medical Education; University of Bern
+📄 Source: https://git-repo.iml.unibe.ch/iml-open-source/ldap-php-class
+📜 License: GNU GPL 3.0
+📗 Docs: https://os-docs.iml.unibe.ch/ldap-php-class/
 
 ## Requirements
 
-* PHP 7+
+* PHP 8
 * Php Ldap module
-* OpenLdap server to connect
+* OpenLdap server / Active Directory to connect
diff --git a/src/ldap.class.php b/src/ldap.class.php
index 82e4a57c387cfced3423599f0cb00f6763289b5d..67babe5eb26008d8c3f053d153183a48fcb3ef87 100755
--- a/src/ldap.class.php
+++ b/src/ldap.class.php
@@ -3,24 +3,26 @@
 /**
  * 
  * IML LDAP CONNECTOR
- *<pre>
- * 2022-02-22  ah  added objGet(), sanitizeFilter() <br>
- * 2022-08-18  ah  mask password (showing 4 chars only) <br>
- * 2022-08-22  ah  mhash is deprecated <br>
- * 2022-08-26  ah  fix verifyPassword <br>
- * </pre>
- * @author axel.hahn@iml.unibe.ch
+ *
+ * 2022-02-22  ah  added objGet(), sanitizeFilter()
+ * 2022-08-18  ah  mask password (showing 4 chars only)
+ * 2022-08-22  ah  mhash is deprecated
+ * 2022-08-26  ah  fix verifyPassword
+ * 2024-07-11  ah  php8 only: use variable types; update phpdocs
+ * 
+ * @author axel.hahn@unibe.ch
  */
-class imlldap {
+class imlldap
+{
 
     // ----------------------------------------------------------------------
     // vars
     // ----------------------------------------------------------------------
-    
+
     /**
      * @var array  options array for an ldap connection including some base settings and DNs
      */
-    private $_aLdap = array(
+    private array $_aLdap = [
         'server' => false,
         'port' => false,
         'DnLdapUser' => false, // ldap rdn oder dn
@@ -29,27 +31,34 @@ class imlldap {
         'DnAppNode' => false, // cn=AppGroup...
         'protoVersion' => 3,
         'debugLevel' => 0,
-    );
+    ];
     /**
      * @var object  current ldap connection  
      */
-    private $_ldapConn = false;
+    private object|bool $_ldapConn = false;
+
+    /**
+     * ldap bind object - bind was done?
+     * @var object|bool
+     */
+    private object|bool $_ldapBind = false;
 
     /**
-     * @var bool  bind was done?
+     * Flag if debug mode is on
+     * @var bool
      */
-    private $_ldapBind = false;
-    var $bDebug = false;
+    var bool $bDebug = false;
 
     // ----------------------------------------------------------------------
     // functions
     // ----------------------------------------------------------------------
-    
+
     /**
      * constructor
      * @param array  $aConfig  optional set ldap connection
      */
-    public function __construct($aConfig = array()) {
+    public function __construct(array $aConfig = [])
+    {
         if (!function_exists("ldap_connect")) {
             die(__CLASS__ . " ERROR: php-ldap module is not installed on this server.");
         }
@@ -58,7 +67,8 @@ class imlldap {
         }
     }
 
-    public function __destruct() {
+    public function __destruct()
+    {
         $this->close();
     }
 
@@ -72,7 +82,8 @@ class imlldap {
      * ldap config array
      * @see setConfig()
      */
-    public function debugOn() {
+    public function debugOn(): void
+    {
         $this->bDebug = true;
         if ($this->_aLdap['debugLevel']) {
             $this->_w(__FUNCTION__ . ' setting debug level ' . $this->_aLdap['debugLevel']);
@@ -83,7 +94,8 @@ class imlldap {
     /**
      * turn debug messages off
      */
-    public function debugOff() {
+    public function debugOff(): void
+    {
         $this->bDebug = false;
         ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 0);
     }
@@ -94,7 +106,8 @@ class imlldap {
      * @param string  $sText  message text
      * @return boolean
      */
-    private function _w($sText) {
+    private function _w(string $sText): bool
+    {
         if (!$this->bDebug) {
             return false;
         }
@@ -108,8 +121,9 @@ class imlldap {
      * @param string  $sText  message text
      * @return boolean
      */
-    private function _wLdaperror($sText = '') {
-        $this->_w(($sText ? $sText . ' - ' : '' ) . 'last LDAP-ERROR: ' . ldap_error($this->_ldapConn));
+    private function _wLdaperror(string $sText = ''): bool
+    {
+        $this->_w(($sText ? $sText . ' - ' : '') . 'last LDAP-ERROR: ' . ldap_error($this->_ldapConn));
         return true;
     }
 
@@ -130,7 +144,8 @@ class imlldap {
      *             'protoVersion' => 3
      *             'debugLevel'   => 0 // for debugging set higher 0 AND call debugOn()
      */
-    public function setConfig($aConfig = array()) {
+    public function setConfig(array $aConfig = []): void
+    {
         if (is_array($aConfig)) {
             foreach (array_keys($this->_aLdap) as $sKey) {
                 if (array_key_exists($sKey, $aConfig)) {
@@ -148,7 +163,8 @@ class imlldap {
     /**
      * close an existing ldap connection
      */
-    public function close() {
+    public function close(): void
+    {
         if ($this->_ldapConn) {
             $this->_w(__FUNCTION__ . ' closing connection.');
             ldap_close($this->_ldapConn);
@@ -162,7 +178,8 @@ class imlldap {
     /**
      * connect to ldap
      */
-    public function connect() {
+    public function connect(): void
+    {
 
         if (!array_key_exists('server', $this->_aLdap) || !$this->_aLdap['server']) {
             die(__CLASS__ . " ERROR: no ldap server was setup set. Use setConfig() first.");
@@ -173,7 +190,7 @@ class imlldap {
         }
 
         $this->_w(__FUNCTION__ . ' connect to ' . $this->_aLdap['server'] . ':' . $this->_aLdap['port']);
-        $this->_ldapConn = ldap_connect($this->_aLdap['server'], $this->_aLdap['port']);
+        $this->_ldapConn = ldap_connect($this->_aLdap['server']);
         if (!$this->_ldapConn) {
             $this->_wLdaperror(__FUNCTION__);
             die(__CLASS__ . " ERROR: ldap connect failed.");
@@ -201,10 +218,11 @@ class imlldap {
      * @param string  $sUser   optional: username (overrides _aLdap['DnLdapUser'])
      * @param string  $sPw     optional: password (overrides _aLdap['PwLdapUser'])
      */
-    public function bind($sUser = '', $sPw = '') {
-        if(!$sUser){
+    public function bind(string $sUser = '', string $sPw = ''): bool
+    {
+        if (!$sUser) {
             $sUser = $this->_aLdap['DnLdapUser'];
-            $sPw   = $this->_aLdap['PwLdapUser'];
+            $sPw = $this->_aLdap['PwLdapUser'];
         }
 
         if (!$this->_ldapConn) {
@@ -218,7 +236,7 @@ class imlldap {
             $this->_w(__FUNCTION__ . ' ERROR: no user was set as first param.');
             die("ERROR: no user was given to connect to ldap.");
         }
-        $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw,0,4).'**********');
+        $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw, 0, 4) . '**********');
 
         $this->_ldapBind = @ldap_bind($this->_ldapConn, $sUser, $sPw);
         if (!$this->_ldapBind) {
@@ -232,7 +250,8 @@ class imlldap {
     /**
      * ldap unbind ... if a bind exists
      */
-    public function unbind() {
+    public function unbind(): void
+    {
         if ($this->_ldapBind && !is_bool($this->_ldapBind)) {
             $this->_w(__FUNCTION__ . ' ...');
             ldap_unbind($this->_ldapBind);
@@ -251,30 +270,33 @@ class imlldap {
      * @param string  $sDn  DN to check
      * @return boolean
      */
-    public function DnExists($sDn) {
-        $aData = $this->searchDn($sDn, '(&(objectclass=top))', array("*"));
+    public function DnExists(string $sDn): bool
+    {
+        $aData = $this->searchDn($sDn, '(&(objectclass=top))', ["*"]);
         return is_array($aData);
     }
 
     /**
      * get simpler array from ldap_get_entries after ldap_search
+     * If the given array doesn't contain the key "dn" it returns "false"
      * 
-     * @param array  $aRecord  singel result item
+     * @param array  $aRecord  single result item
      * @return array
      */
-    public function normalizeSearchentry($aRecord) {
-        if (!is_array($aRecord) || !isset($aRecord['dn'])){
+    public function normalizeSearchentry(array $aRecord): bool|array
+    {
+        if (!is_array($aRecord) || !isset($aRecord['dn'])) {
             return false;
         }
-        $aItem = array();
+        $aItem = [];
         unset($aRecord['count']);
         foreach ($aRecord as $sAttr => $aData) {
             if (!is_integer($sAttr)) {
                 $value = $aData;
                 if (is_array($aData)) {
                     unset($aData['count']);
-                    $bUseArray=count($aData)>1 || array_search($sAttr, array('hieradata', 'member', 'memberof', 'objectclass'))!==false;
-                    if($bUseArray){
+                    $bUseArray = count($aData) > 1 || array_search($sAttr, ['hieradata', 'member', 'memberof', 'objectclass']) !== false;
+                    if ($bUseArray) {
                         sort($aData);
                     }
                     $value = $bUseArray ? $aData : $aData[0];
@@ -284,23 +306,6 @@ class imlldap {
         }
         return $aItem;
     }
-    /**
-     * get simpler array from ldap_get_entries after ldap_search
-     * 
-     * @param array  $aRecord  singel result item
-     * @return array
-     */
-    public function normalizeSearchresult($aLdapSearchresult) {
-        if (!is_array($aLdapSearchresult)){
-            return false;
-        }
-        $aReturn = array();
-        unset($aRecord['count']);
-        foreach ($aLdapSearchresult as $aRecord) {
-            $aReturn[]=$this->normalizeSearchentry($aRecord);
-        }
-        return $aReturn;
-    }
 
     /**
      * sanitize value to put into a search filter
@@ -315,51 +320,56 @@ class imlldap {
      * @param  string   $s  value to sanitize
      * @return string
      */
-    static public function sanitizeFilter($s){
+    static public function sanitizeFilter(string $s): string
+    {
 
         // helper array to replace special chars
-        $aReplace=array();
-        for($i=0; $i<65; $i++){
-            $val=dechex($i);
-            if ($val<10){
-                $val="0$val";
+        $aReplace = [];
+        for ($i = 0; $i < 65; $i++) {
+            $val = dechex($i);
+            if ($val < 10) {
+                $val = "0$val";
             }
-            $aReplace[chr($i)]='\\'.$val;
+            $aReplace[chr($i)] = '\\' . $val;
         }
 
-        $sReturn=$s;
-        $sReturn=str_replace(array_keys($aReplace), array_values($aReplace), $sReturn);
-        
+        $sReturn = $s;
+        $sReturn = str_replace(array_keys($aReplace), array_values($aReplace), $sReturn);
+
         return $sReturn;
     }
     /**
-     * search in ldap directory and get result as array
+     * search in ldap directory and get result as array.
+     * It returns "false" on error:
+     * - no ldap connection
+     * - search failed
      * 
      * @param string  $sDn               DN to search for
      * @param string  $sSearchFilter     filter in ldap filter syntax
      * @param array   $aAttributesToGet  flat array of attributes to fetch
      * @param boolean $bRecursive        recusrive (uses ldap_search) or not (ldap_list)
-     * @return array
+     * @return boolean|array
      */
-    public function searchDn($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*"), $bRecursive=true) {
+    public function searchDn(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array
+    {
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1).' recursive = '.($bRecursive ? 'yes' : 'no' ));
+        $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1) . ' recursive = ' . ($bRecursive ? 'yes' : 'no'));
 
         $oLdapSearch = $bRecursive
-                ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
-                : ldap_list  ($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
-                ;
+            ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
+            : ldap_list($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
+        ;
 
         if (!$oLdapSearch) {
             $this->_w(__FUNCTION__ . " !!!ERROR!!! filter $sSearchFilter failed ");
             return false;
         }
         $aItems = ldap_get_entries($this->_ldapConn, $oLdapSearch);
-        $this->_w(__FUNCTION__ . " count of returned items: ".count($aItems));
+        $this->_w(__FUNCTION__ . " count of returned items: " . count($aItems));
         // $this->_w(__FUNCTION__ . " <pre>".print_r($aItems,1).'</pre>');
         return $aItems;
     }
@@ -371,9 +381,10 @@ class imlldap {
      * @param array   $aAttributesToGet  flat array of attributes to fetch
      * @param bool    $bRecursive        flag: recursive search? default: true (=yes, recursive)
      * 
-     * @return array
+     * @return boolean|array
      */
-    public function searchUser($sSearchFilter='', $aAttributesToGet = array("*"), $bRecursive=true) {
+    public function searchUser(string $sSearchFilter = '', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array
+    {
         return $this->searchDn($this->_aLdap['DnUserNode'], $sSearchFilter, $aAttributesToGet, $bRecursive);
         /*
         if (!$this->_ldapBind) {
@@ -396,13 +407,14 @@ class imlldap {
      * It returns false if the user does not exist or is
      * not member of the group 'DnAppNode' (if it was set).
      * 
-     * @param string $sUser             user id (uid) or email (mail) to search
-     * @param array  $aAttributesToGet  i.e. array("ou", "sn", "vorname", "mail", "uid", "memberOf")
+     * @param string  $sUser             user id (uid) or email (mail) to search
+     * @param array   $aAttributesToGet  i.e. ["ou", "sn", "vorname", "mail", "uid", "memberOf"]
      * @return boolean|array
      */
-    public function getUserInfo($sUser, $aAttributesToGet = array("*")) {
+    public function getUserInfo(string $sUser, array $aAttributesToGet = ["*"]): bool|array
+    {
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -430,12 +442,13 @@ class imlldap {
      * email address. It returns false if the user does not exist or is
      * not member of the group 'DnAppNode' (if it was set).
      * 
-     * @param type $sUser
+     * @param string $sUser
      * @return string
      */
-    public function getUserDn($sUser) {
+    public function getUserDn(string $sUser): bool|string
+    {
         $this->_w(__FUNCTION__ . '(' . $sUser . ')');
-        $aItem = $this->getUserInfo($sUser, array("dn"));
+        $aItem = $this->getUserInfo($sUser, ["dn"]);
         if (is_array($aItem) && array_key_exists('dn', $aItem)) {
             $this->_w(__FUNCTION__ . ' OK: dn was found ' . $aItem['dn']);
             return $aItem['dn'];
@@ -452,15 +465,16 @@ class imlldap {
      * @param string  $sPW    password
      * @return boolean
      */
-    public function setPassword($sUser, $sPW) {
+    public function setPassword(string $sUser, string $sPW): bool
+    {
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
         $sDn = $this->getUserDn($sUser);
         if ($sDn) {
-            if (!ldap_mod_replace($this->_ldapConn, $sDn, array('userpassword' => "{MD5}" . base64_encode(pack("H*", md5($sPW)))))) {
+            if (!ldap_mod_replace($this->_ldapConn, $sDn, ['userpassword' => "{MD5}" . base64_encode(pack("H*", md5($sPW)))])) {
                 $this->_wLdaperror(__FUNCTION__);
                 return false;
             } else {
@@ -478,17 +492,18 @@ class imlldap {
      * @param string   $Input
      * @return string
      */
-    private function _getNTLMHash($Input) {
+    private function _getNTLMHash(string $Input): string
+    {
         // Convert the password from UTF8 to UTF16 (little endian)
         $Input = iconv('UTF-8', 'UTF-16LE', $Input);
 
         // Encrypt it with the MD4 hash
-        $MD4Hash=hash('md4',$Input);
+        $MD4Hash = hash('md4', $Input);
         // Make it uppercase, not necessary, but it's common to do so with NTLM hashes
         $NTLMHash = strtoupper($MD4Hash);
 
         // Return the result
-        return($NTLMHash);
+        return ($NTLMHash);
     }
 
     /**
@@ -502,15 +517,19 @@ class imlldap {
      * @param string  $sPW    password
      * @return boolean
      */
-    public function setPasswordSamba($sUser, $sPW) {
+    public function setPasswordSamba(string $sUser, string $sPW): bool
+    {
         $sDn = $this->getUserDn($sUser);
         if ($sDn) {
             $sPwField = 'sambaNTPassword';
             $sPwValue = $this->_getNTLMHash($sPW);
-            return $this->objUpdate($sDn, array(
-                        $sPwField => $sPwValue,
-                        'SambaPwdLastSet' => date('U'),
-            ));
+            return $this->objUpdate(
+                $sDn,
+                [
+                    $sPwField => $sPwValue,
+                    'SambaPwdLastSet' => date('U'),
+                ]
+            );
         }
         $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser);
         return false;
@@ -519,15 +538,17 @@ class imlldap {
     /**
      * update an ldap object
      * this requires a ldap bind with master/ admin account
+     * It returns true if the action was successful
      * 
      * @param string  $sDn     dn to update
      * @param array   $aItem   array of new ldap properties
      * @return boolean
      */
-    public function objAdd($sDn, $aItem) {
-        $this->_w(__FUNCTION__ . '("' . $sDn . '", <pre>['.print_r($aItem, 1).']</pre>)');
+    public function objAdd(string $sDn, array $aItem): bool
+    {
+        $this->_w(__FUNCTION__ . '("' . $sDn . '", <pre>[' . print_r($aItem, 1) . ']</pre>)');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -543,13 +564,14 @@ class imlldap {
      * this requires a ldap bind with master/ admin account
      * 
      * @param string  $sDn     dn to update
-     * @param string  $aItem   array of new ldap properties
+     * @param array   $aItem   array of new ldap properties
      * @return boolean
      */
-    public function objAddAttr($sDn, $aItem) {
+    public function objAddAttr(string $sDn, array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -567,23 +589,27 @@ class imlldap {
         return false;
     }
 
-     /**
+    /**
      * read attributes from ldap node with given DN (using ldap_read)
+     * It returns "false" if the action was not successful
+     * - no ldap connection
+     * - DN or filter didn't match
      * 
      * @param string  $sDn               DN to search for
      * @param string  $sSearchFilter     filter in ldap filter syntax
      * @param array   $aAttributesToGet  flat array of attributes to fetch
-     * @return array
+     * @return boolean|array
      */
-    public function objGet($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*")) {
+    public function objGet(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"]): bool|array
+    {
 
-        $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = '.$sSearchFilter.', atttr= '.print_r($aAttributesToGet, 1).' )');
+        $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = ' . $sSearchFilter . ', atttr= ' . print_r($aAttributesToGet, 1) . ' )');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        
+
         $oLdapResult = ldap_read($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet);
 
         if (!$oLdapResult) {
@@ -597,15 +623,17 @@ class imlldap {
      * update an ldap object with given key-value array
      * if the attribute (key) does not exist it will be created.
      * this requires a ldap bind with master/ admin account
+     * It returns "false" if the action failed
      * 
      * @param string  $sDn    full DN where to update the item
      * @param array   $aItem  updated entry
      * @return boolean
      */
-    public function objUpdate($sDn, $aItem) {
+    public function objUpdate(string $sDn, array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", ' . print_r($aItem, 1) . ')');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -624,14 +652,16 @@ class imlldap {
     /**
      * delete an ldap object
      * this requires a ldap bind with master/ admin account
+     * It returns "false" if the action failed
      * 
      * @param string  $sDn    full DN to remove 
      * @return boolean
      */
-    public function objDelete($sDn) {
+    public function objDelete(string $sDn): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '")');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -640,7 +670,8 @@ class imlldap {
             if (!ldap_delete($this->_ldapConn, $sDn)) {
                 $this->_wLdaperror(__FUNCTION__);
                 return false;
-            } return true;
+            }
+            return true;
         }
         $this->_w(__FUNCTION__ . ' missing parameter for DN');
         return false;
@@ -649,17 +680,21 @@ class imlldap {
     /**
      * delete attributes of an ldap object
      * this requires a ldap bind with master/ admin account
+     * It returns "false" if the action failed
      * 
-     * TODO: Test me
+     * @example:
+     * remove attribute "userPassword" of user $sUserDn:
+     * <code>$oLdap->objDeleteAttr($sUserDn, ['userPassword'=>[]]</code>
      * 
      * @param string  $sDn    DN
-     * @param string  $aItem  item to remove
+     * @param array   $aItem  item to remove
      * @return boolean
      */
-    public function objDeleteAttr($sDn, $aItem) {
+    public function objDeleteAttr(string $sDn, array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -669,7 +704,8 @@ class imlldap {
             if (!ldap_mod_del($this->_ldapConn, $sDn, $aItem)) {
                 $this->_wLdaperror(__FUNCTION__);
                 return false;
-            } return true;
+            }
+            return true;
         }
         $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) or item was not an array ' . print_r($aItem, 1));
         return false;
@@ -683,15 +719,16 @@ class imlldap {
      * @param string  $sAttrValue  value to check
      * @return boolean
      */
-    public function objectAttributeExists($sDn, $sAttribute) {
+    public function objectAttributeExists(string $sDn, string $sAttribute): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '")');
 
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        $aData = $this->searchDn($sDn, '(&(objectclass=top))', array($sAttribute));
+        $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]);
         $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)]));
         $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false'));
         return $return;
@@ -705,15 +742,16 @@ class imlldap {
      * @param string  $sAttrValue  value to check
      * @return boolean
      */
-    public function objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue) {
+    public function objectAttributeAndValueExist(string $sDn, string $sAttribute, string $sAttrValue): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")');
 
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        $aData = $this->searchDn($sDn, '(&(objectclass=top))', array($sAttribute));
+        $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]);
         $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)]) && array_search($sAttrValue, $aData[0][strtolower($sAttribute)]) !== false);
         $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false'));
         return $return;
@@ -728,7 +766,8 @@ class imlldap {
      * @param string  $sAttrValue  value to check
      * @return boolean
      */
-    public function objectAttributeAndValueMustExist($sDn, $sAttribute, $sAttrValue) {
+    public function objectAttributeAndValueMustExist(string $sDn, string $sAttribute, string $sAttrValue): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")');
         // return if it already exists
         if ($this->objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue)) {
@@ -737,7 +776,7 @@ class imlldap {
 
         // create it
         $this->_w(__FUNCTION__ . " create $sAttribute = $sAttrValue");
-        $return = $this->objAddAttr($sDn, array($sAttribute => $sAttrValue));
+        $return = $this->objAddAttr($sDn, [$sAttribute => $sAttrValue]);
         return $return;
     }
 
@@ -749,7 +788,8 @@ class imlldap {
      * @param string  $sDn    optional DN where to create the user
      * @return boolean
      */
-    public function userAdd($aItem, $sDn = false) {
+    public function userAdd(array $aItem, string $sDn = ""): bool
+    {
         if (!$sDn) {
             $sDn = 'cn=' . $aItem['cn'] . ',' . $this->_aLdap['DnUserNode'];
         }
@@ -769,7 +809,8 @@ class imlldap {
      * @param string  $sPW    new password to set
      * @return boolean
      */
-    public function userDelete($sUserDn) {
+    public function userDelete(string $sUserDn): bool
+    {
         $this->_w(__FUNCTION__ . '(' . $sUserDn . ')');
         return $this->objDelete($sUserDn);
     }
@@ -781,7 +822,8 @@ class imlldap {
      * @param array   $aItem  new user data to update
      * @return boolean
      */
-    public function userUpdate($aItem) {
+    public function userUpdate(array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '([array])');
         $sDn = $this->getUserDn($aItem['uid']);
         if ($sDn) {
@@ -801,7 +843,8 @@ class imlldap {
      * @param string  $sPW    password
      * @return boolean
      */
-    public function verifyPassword($sUser, $sPW) {
+    public function verifyPassword(string $sUser, string $sPW): bool
+    {
         $sDn = $this->getUserDn($sUser);
         if ($sDn) {
             return $this->bind($sDn, $sPW);