Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php  | 
            ||
| 27 | class Inspector extends Client  | 
            ||
| 28 | { | 
            ||
| 29 | /**  | 
            ||
| 30 | * getClientType  | 
            ||
| 31 | *  | 
            ||
| 32 | * @see ClientInterface  | 
            ||
| 33 | */  | 
            ||
| 34 | public function getClientType()  | 
            ||
| 38 | |||
| 39 | /**  | 
            ||
| 40 | * getClientIdentifier  | 
            ||
| 41 | *  | 
            ||
| 42 | * @see ClientInterface  | 
            ||
| 43 | */  | 
            ||
| 44 | public function getClientIdentifier()  | 
            ||
| 48 | |||
| 49 | /**  | 
            ||
| 50 | * getSchemas  | 
            ||
| 51 | *  | 
            ||
| 52 | * Return a list of available schemas in the current database.  | 
            ||
| 53 | *  | 
            ||
| 54 | * @access public  | 
            ||
| 55 | * @return \PommProject\Foundation\ConvertedResultIterator  | 
            ||
| 56 | */  | 
            ||
| 57 | public function getSchemas()  | 
            ||
| 58 |     { | 
            ||
| 59 | $sql = <<<SQL  | 
            ||
| 60 | select  | 
            ||
| 61 | n.nspname as "name",  | 
            ||
| 62 | n.oid as "oid",  | 
            ||
| 63 | d.description as "comment",  | 
            ||
| 64 | count(c) as "relations"  | 
            ||
| 65 | from pg_catalog.pg_namespace n  | 
            ||
| 66 | left join pg_catalog.pg_description d on n.oid = d.objoid  | 
            ||
| 67 | left join pg_catalog.pg_class c on  | 
            ||
| 68 |         c.relnamespace = n.oid and c.relkind in ('r', 'v') | 
            ||
| 69 | where :condition  | 
            ||
| 70 | group by 1, 2, 3  | 
            ||
| 71 | order by 1;  | 
            ||
| 72 | SQL;  | 
            ||
| 73 |         $condition = new Where('n.nspname !~ $* and n.nspname <> $*', ['^pg_', 'information_schema']); | 
            ||
| 74 | |||
| 75 | return $this->executeSql($sql, $condition);  | 
            ||
| 76 | }  | 
            ||
| 77 | |||
| 78 | /**  | 
            ||
| 79 | * getTableOid  | 
            ||
| 80 | *  | 
            ||
| 81 | * Return the table oid from PostgreSQL catalog. If no table is found, null  | 
            ||
| 82 | * is returned.  | 
            ||
| 83 | *  | 
            ||
| 84 | * @access public  | 
            ||
| 85 | * @param string $schema  | 
            ||
| 86 | * @param string $table  | 
            ||
| 87 | * @return int|null  | 
            ||
| 88 | */  | 
            ||
| 89 | View Code Duplication | public function getTableOid($schema, $table)  | 
            |
| 90 |     { | 
            ||
| 91 | $sql = <<<SQL  | 
            ||
| 92 | select  | 
            ||
| 93 | c.oid as oid  | 
            ||
| 94 | from  | 
            ||
| 95 | pg_catalog.pg_class c  | 
            ||
| 96 | left join pg_catalog.pg_namespace n on n.oid = c.relnamespace  | 
            ||
| 97 | where  | 
            ||
| 98 | :condition  | 
            ||
| 99 | SQL;  | 
            ||
| 100 | |||
| 101 |         $where = Where::create('n.nspname =  $*', [$schema]) | 
            ||
| 102 |             ->andWhere('c.relname = $*', [$table]) | 
            ||
| 103 | ;  | 
            ||
| 104 | |||
| 105 | $iterator = $this->executeSql($sql, $where);  | 
            ||
| 106 | |||
| 107 | return $iterator->isEmpty() ? null : $iterator->current()['oid'];  | 
            ||
| 108 | }  | 
            ||
| 109 | |||
| 110 | /**  | 
            ||
| 111 | * getTableFieldInformation  | 
            ||
| 112 | *  | 
            ||
| 113 | * Get table's field information. If no fields are found, null is  | 
            ||
| 114 | * returned.  | 
            ||
| 115 | *  | 
            ||
| 116 | * @access public  | 
            ||
| 117 | * @param int $oid  | 
            ||
| 118 | * @return \PommProject\Foundation\ConvertedResultIterator|null  | 
            ||
| 119 | */  | 
            ||
| 120 | public function getTableFieldInformation($oid)  | 
            ||
| 121 |     { | 
            ||
| 122 | $sql = <<<SQL  | 
            ||
| 123 | select  | 
            ||
| 124 | att.attname as "name",  | 
            ||
| 125 | case  | 
            ||
| 126 | when name.nspname = 'pg_catalog' then typ.typname  | 
            ||
| 127 |         else format('%s.%s', name.nspname, typ.typname) | 
            ||
| 128 | end as "type",  | 
            ||
| 129 | def.adsrc as "default",  | 
            ||
| 130 | att.attnotnull as "is_notnull",  | 
            ||
| 131 | dsc.description as "comment",  | 
            ||
| 132 | att.attnum as "position",  | 
            ||
| 133 | att.attnum = any(ind.indkey) as "is_primary"  | 
            ||
| 134 | from  | 
            ||
| 135 | pg_catalog.pg_attribute att  | 
            ||
| 136 | join pg_catalog.pg_type typ on att.atttypid = typ.oid  | 
            ||
| 137 | join pg_catalog.pg_class cla on att.attrelid = cla.oid  | 
            ||
| 138 | left join pg_catalog.pg_description dsc on cla.oid = dsc.objoid and att.attnum = dsc.objsubid  | 
            ||
| 139 | left join pg_catalog.pg_attrdef def on att.attrelid = def.adrelid and att.attnum = def.adnum  | 
            ||
| 140 | left join pg_catalog.pg_index ind on cla.oid = ind.indrelid and ind.indisprimary  | 
            ||
| 141 | left join pg_catalog.pg_namespace name on typ.typnamespace = name.oid  | 
            ||
| 142 | where  | 
            ||
| 143 | :condition  | 
            ||
| 144 | order by  | 
            ||
| 145 | att.attnum  | 
            ||
| 146 | SQL;  | 
            ||
| 147 |         $where = Where::create('att.attrelid = $*', [$oid]) | 
            ||
| 148 |             ->andWhere('att.attnum > 0') | 
            ||
| 149 |             ->andWhere('not att.attisdropped') | 
            ||
| 150 | ;  | 
            ||
| 151 | |||
| 152 | return $this->executeSql($sql, $where);  | 
            ||
| 153 | }  | 
            ||
| 154 | |||
| 155 | /**  | 
            ||
| 156 | * getSchemaOid  | 
            ||
| 157 | *  | 
            ||
| 158 | * Return the given schema oid, null if the schema is not found.  | 
            ||
| 159 | *  | 
            ||
| 160 | * @access public  | 
            ||
| 161 | * @param string $schema  | 
            ||
| 162 | * @param Where $where optional where clause.  | 
            ||
| 163 | * @return int|null  | 
            ||
| 164 | */  | 
            ||
| 165 | View Code Duplication | public function getSchemaOid($schema, Where $where = null)  | 
            |
| 166 |     { | 
            ||
| 167 | $condition =  | 
            ||
| 168 |             Where::create("s.nspname = $*", [$schema]) | 
            ||
| 169 | ->andWhere($where)  | 
            ||
| 170 | ;  | 
            ||
| 171 | $sql = <<<SQL  | 
            ||
| 172 | select  | 
            ||
| 173 | s.oid as oid  | 
            ||
| 174 | from  | 
            ||
| 175 | pg_catalog.pg_namespace s  | 
            ||
| 176 | where  | 
            ||
| 177 | :condition  | 
            ||
| 178 | SQL;  | 
            ||
| 179 | |||
| 180 | $iterator = $this->executeSql($sql, $condition);  | 
            ||
| 181 | |||
| 182 | return $iterator->isEmpty() ? null : $iterator->current()['oid'];  | 
            ||
| 183 | }  | 
            ||
| 184 | |||
| 185 | /**  | 
            ||
| 186 | * getPrimaryKey  | 
            ||
| 187 | *  | 
            ||
| 188 | * Get relation's primary key if any.  | 
            ||
| 189 | *  | 
            ||
| 190 | * @access public  | 
            ||
| 191 | * @param int $table_oid  | 
            ||
| 192 | * @return array|null  | 
            ||
| 193 | */  | 
            ||
| 194 | public function getPrimaryKey($table_oid)  | 
            ||
| 195 |     { | 
            ||
| 196 | $sql = <<<SQL  | 
            ||
| 197 | with  | 
            ||
| 198 | pk_field as (  | 
            ||
| 199 | select  | 
            ||
| 200 | att.attname as field  | 
            ||
| 201 | from  | 
            ||
| 202 | pg_catalog.pg_attribute att  | 
            ||
| 203 | join pg_catalog.pg_index ind on  | 
            ||
| 204 | att.attrelid = ind.indrelid and att.attnum = any(ind.indkey)  | 
            ||
| 205 | where  | 
            ||
| 206 | :condition  | 
            ||
| 207 | order by att.attnum asc  | 
            ||
| 208 | )  | 
            ||
| 209 | select array_agg(field) as fields from pk_field  | 
            ||
| 210 | SQL;  | 
            ||
| 211 | $condition =  | 
            ||
| 212 |             Where::create('ind.indrelid = $*', [$table_oid]) | 
            ||
| 213 |             ->andWhere('ind.indisprimary') | 
            ||
| 214 | ;  | 
            ||
| 215 | |||
| 216 | $pk = $this->executeSql($sql, $condition)->current();  | 
            ||
| 217 | |||
| 218 | return $pk['fields'][0] === null ? [] : array_reverse($pk['fields']);  | 
            ||
| 219 | }  | 
            ||
| 220 | |||
| 221 | /**  | 
            ||
| 222 | * getSchemaRelations  | 
            ||
| 223 | *  | 
            ||
| 224 | * Return information on relations in a given schema. An additional Where  | 
            ||
| 225 | * condition can be passed to filter against other criteria.  | 
            ||
| 226 | *  | 
            ||
| 227 | * @access public  | 
            ||
| 228 | * @param int $schema_oid  | 
            ||
| 229 | * @param Where $where  | 
            ||
| 230 | * @return \PommProject\Foundation\ConvertedResultIterator  | 
            ||
| 231 | */  | 
            ||
| 232 | public function getSchemaRelations($schema_oid, Where $where = null)  | 
            ||
| 233 |     { | 
            ||
| 234 |         $condition = Where::create('relnamespace = $*', [$schema_oid]) | 
            ||
| 235 |             ->andWhere(Where::createWhereIn('relkind', ['r', 'v', 'm', 'f'])) | 
            ||
| 236 | ->andWhere($where)  | 
            ||
| 237 | ;  | 
            ||
| 238 | |||
| 239 | $sql = <<<SQL  | 
            ||
| 240 | select  | 
            ||
| 241 | cl.relname as "name",  | 
            ||
| 242 | case  | 
            ||
| 243 | when cl.relkind = 'r' then 'table'  | 
            ||
| 244 | when cl.relkind = 'v' then 'view'  | 
            ||
| 245 | when cl.relkind = 'm' then 'materialized view'  | 
            ||
| 246 | when cl.relkind = 'f' then 'foreign table'  | 
            ||
| 247 | else 'other'  | 
            ||
| 248 | end as "type",  | 
            ||
| 249 | cl.oid as "oid",  | 
            ||
| 250 | des.description as "comment"  | 
            ||
| 251 | from  | 
            ||
| 252 | pg_catalog.pg_class cl  | 
            ||
| 253 | left join pg_catalog.pg_description des on  | 
            ||
| 254 | cl.oid = des.objoid and des.objsubid = 0  | 
            ||
| 255 | where :condition  | 
            ||
| 256 | order by name asc  | 
            ||
| 257 | SQL;  | 
            ||
| 258 | |||
| 259 | return $this->executeSql($sql, $condition);  | 
            ||
| 260 | }  | 
            ||
| 261 | |||
| 262 | /**  | 
            ||
| 263 | * getTableComment  | 
            ||
| 264 | *  | 
            ||
| 265 | * Return the comment on a table if set. Null otherwise.  | 
            ||
| 266 | *  | 
            ||
| 267 | * @access public  | 
            ||
| 268 | * @param int $table_oid  | 
            ||
| 269 | * @return string|null  | 
            ||
| 270 | */  | 
            ||
| 271 | View Code Duplication | public function getTableComment($table_oid)  | 
            |
| 272 |     { | 
            ||
| 273 | $sql = <<<SQL  | 
            ||
| 274 | select description from pg_catalog.pg_description where :condition  | 
            ||
| 275 | SQL;  | 
            ||
| 276 | |||
| 277 |         $where    = Where::create('objoid = $*', [$table_oid]); | 
            ||
| 278 | $iterator = $this->executeSql($sql, $where);  | 
            ||
| 279 | |||
| 280 | return $iterator->isEmpty() ? null : $iterator->current()['description'];  | 
            ||
| 281 | }  | 
            ||
| 282 | |||
| 283 | /**  | 
            ||
| 284 | * getTypeInformation  | 
            ||
| 285 | *  | 
            ||
| 286 | * Return the Oid of the given type name.  | 
            ||
| 287 | * It Additionally returns the type category.  | 
            ||
| 288 | *  | 
            ||
| 289 | * @access public  | 
            ||
| 290 | * @param string $type_name  | 
            ||
| 291 | * @param string $type_schema  | 
            ||
| 292 | * @return array|null  | 
            ||
| 293 | */  | 
            ||
| 294 | public function getTypeInformation($type_name, $type_schema = null)  | 
            ||
| 295 |     { | 
            ||
| 296 |         $condition = Where::create("t.typname = $*", [$type_name]); | 
            ||
| 297 | $sql = <<<SQL  | 
            ||
| 298 | select  | 
            ||
| 299 | t.oid as "oid",  | 
            ||
| 300 | t.typcategory as "category"  | 
            ||
| 301 | from  | 
            ||
| 302 | pg_catalog.pg_type t :join  | 
            ||
| 303 | where  | 
            ||
| 304 | :condition  | 
            ||
| 305 | SQL;  | 
            ||
| 306 | |||
| 307 |         if ($type_schema !== null) { | 
            ||
| 308 | $sql = strtr($sql, [':join' => 'join pg_namespace n on n.oid = t.typnamespace']);  | 
            ||
| 309 |             $condition->andWhere('n.nspname = $*', [$type_schema]); | 
            ||
| 310 |         } else { | 
            ||
| 311 | $sql = strtr($sql, [':join' => '']);  | 
            ||
| 312 | }  | 
            ||
| 313 | |||
| 314 | $iterator = $this->executeSql($sql, $condition);  | 
            ||
| 315 | |||
| 316 | return $iterator->isEmpty() ? null : $iterator->current();  | 
            ||
| 317 | }  | 
            ||
| 318 | |||
| 319 | /**  | 
            ||
| 320 | * getTypeCategory  | 
            ||
| 321 | *  | 
            ||
| 322 | * Get type category.  | 
            ||
| 323 | *  | 
            ||
| 324 | * @access public  | 
            ||
| 325 | * @param int $oid  | 
            ||
| 326 | * @return array|null  | 
            ||
| 327 | */  | 
            ||
| 328 | View Code Duplication | public function getTypeCategory($oid)  | 
            |
| 329 |     { | 
            ||
| 330 | $sql = <<<SQL  | 
            ||
| 331 | select  | 
            ||
| 332 | case  | 
            ||
| 333 | when n is null then t.type_name  | 
            ||
| 334 | else n.nspname||'.'||t.type_name  | 
            ||
| 335 | end as "name",  | 
            ||
| 336 | t.typcategory as "category"  | 
            ||
| 337 | from  | 
            ||
| 338 | pg_catalog.pg_type t  | 
            ||
| 339 | left join pg_namespace n on n.oid = t.typnamespace  | 
            ||
| 340 | where  | 
            ||
| 341 | :condition  | 
            ||
| 342 | SQL;  | 
            ||
| 343 |         $iterator = $this->executeSql($sql, Where::create('t.oid = $*', [$oid])); | 
            ||
| 344 | |||
| 345 | return $iterator->isEmpty() ? null : $iterator->current();  | 
            ||
| 346 | }  | 
            ||
| 347 | |||
| 348 | /**  | 
            ||
| 349 | * getTypeEnumValues  | 
            ||
| 350 | *  | 
            ||
| 351 | * Return all possible values from an enumerated type in its natural order.  | 
            ||
| 352 | *  | 
            ||
| 353 | * @access public  | 
            ||
| 354 | * @param int $oid  | 
            ||
| 355 | * @return array|null  | 
            ||
| 356 | */  | 
            ||
| 357 | public function getTypeEnumValues($oid)  | 
            ||
| 379 | |||
| 380 | /**  | 
            ||
| 381 | * getCompositeInformation  | 
            ||
| 382 | *  | 
            ||
| 383 | * Return the structure of a composite row.  | 
            ||
| 384 | *  | 
            ||
| 385 | * @access public  | 
            ||
| 386 | * @param int $oid  | 
            ||
| 387 | * @return \PommProject\Foundation\ConvertedResultIterator  | 
            ||
| 388 | */  | 
            ||
| 389 | public function getCompositeInformation($oid)  | 
            ||
| 406 | |||
| 407 | /**  | 
            ||
| 408 | * getVersion  | 
            ||
| 409 | *  | 
            ||
| 410 | * Return server version.  | 
            ||
| 411 | *  | 
            ||
| 412 | * @access public  | 
            ||
| 413 | * @throws FoundationException if invalid string.  | 
            ||
| 414 | * @return string  | 
            ||
| 415 | */  | 
            ||
| 416 | public function getVersion()  | 
            ||
| 425 | |||
| 426 | /**  | 
            ||
| 427 | * executeSql  | 
            ||
| 428 | *  | 
            ||
| 429 | * Launch query execution.  | 
            ||
| 430 | *  | 
            ||
| 431 | * @access protected  | 
            ||
| 432 | * @param string $sql  | 
            ||
| 433 | * @param Where $condition  | 
            ||
| 434 | * @return \PommProject\Foundation\ConvertedResultIterator  | 
            ||
| 435 | */  | 
            ||
| 436 | protected function executeSql($sql, Where $condition = null)  | 
            ||
| 447 | }  | 
            ||
| 448 |