- Copy dan paste file tersebut kedalam controller
- Tambahkan List dari controller, dan model yang ingin dibuat UML
- Jalankan controller
- File Akan otomatis ter-generate
- Install plugin PlantUML di VSCODE
- Install PlantUML dan gravizz
brew install graphviz
| <?php | |
| class extractor extends CI_Controller | |
| { | |
| private $workflow = false; | |
| private $exception_ucwords = ["oa_helper"]; | |
| private $sanitized = ["'", '"', ",", " ", ";", "(", ")"]; | |
| public function getRealpath($type, $name) | |
| { | |
| $dirs = [ | |
| "helper" => APPPATH . "helpers/", | |
| "model" => APPPATH . "models/", | |
| "library" => APPPATH . "libraries/", | |
| "controller" => APPPATH . "controllers/", | |
| ]; | |
| $path = $dirs[$type] . $name . ".php"; | |
| return realpath($path); | |
| } | |
| public function generateUmlClass($type, $name, $reference = null) | |
| { | |
| $uml = ""; | |
| $realpath = $this->getRealpath($type, $name); | |
| if (!$realpath) { | |
| // var_dump("$name : File not found"); | |
| return; | |
| } | |
| // if file not included | |
| if (!in_array($realpath, get_included_files())) { | |
| // var_dump($realpath); | |
| // var_dump(get_included_files()); | |
| // die; | |
| require_once $realpath; | |
| // ob_clean(); | |
| } | |
| if (!class_exists($name)) { | |
| // var_dump("$name : Class not found"); | |
| return; | |
| } | |
| // get methods and properties | |
| $class = new ReflectionClass($name); | |
| $methods = $class->getMethods(); | |
| $properties = $class->getProperties(); | |
| // get extends and implements | |
| $extends = $class->getParentClass(); | |
| $implements = $class->getInterfaces(); | |
| if ($extends) { | |
| $extends = $extends->getName(); | |
| } else { | |
| $extends = ""; | |
| } | |
| if ($extends == "Tte_msuratworkflow") { | |
| if ($this->workflow == false) { | |
| $this->workflow = true; | |
| $uml .= $this->generateUmlClass("model", "Tte_msuratworkflow"); | |
| } | |
| } | |
| if ($implements) { | |
| $implements = implode(",", $implements); | |
| } else { | |
| $implements = ""; | |
| } | |
| // get class name | |
| $className = "class " . $class->getName(); | |
| // combine class name and extends | |
| if ($extends) { | |
| $className = $className . " extends " . $extends; | |
| } | |
| if ($implements) { | |
| $className = $className . " implements " . $implements; | |
| } | |
| $uml .= "$className {\n"; | |
| if ($reference) { | |
| $value_load = $this->fetchLoaderFromFile($reference); | |
| $class_information = array_map([$this, 'parseClassInformation'], $value_load); | |
| $loaders = []; | |
| foreach ($class_information as $information) { | |
| foreach ($information['class'] as $class) { | |
| $loaders[] = [ | |
| // type | |
| $information['type'], | |
| // class | |
| $class, | |
| ]; | |
| } | |
| } | |
| $loaders = array_map("unserialize", array_unique(array_map("serialize", $loaders))); | |
| $loaders = array_map("unserialize", array_unique(array_map("serialize", $loaders))); | |
| if (!empty($loaders)) { | |
| foreach ($loaders as $value) { | |
| // $uml .= "\t #$value[1]:$value[0] = new $value[0]()\n"; | |
| $uml .= "\t #$value[0] $value[1]\n"; | |
| } | |
| } | |
| } | |
| // get properties | |
| if (!empty($properties)) { | |
| // php7.2 get type data from property | |
| foreach ($properties as $property) { | |
| $property_name = $property->getName(); | |
| // check visibility | |
| if ($property->isPrivate()) { | |
| $property_visibility = "-"; | |
| } elseif ($property->isProtected()) { | |
| $property_visibility = "#"; | |
| } else { | |
| $property_visibility = "+"; | |
| } | |
| $uml .= "\t $property_visibility$property_name\n"; | |
| } | |
| } | |
| $note = ""; | |
| // get methods | |
| if (!empty($methods)) { | |
| foreach ($methods as $method) { | |
| // get arguments | |
| $args = []; | |
| $params = $method->getParameters(); | |
| foreach ($params as $param) { | |
| $args[] = $param->getName(); | |
| } | |
| // get return type of method | |
| $return = $method->getReturnType() ? $method->getReturnType()->getName() . " " : ""; | |
| // check visibility | |
| $visibility = ""; | |
| if ($method->isPrivate()) { | |
| $visibility = "-"; | |
| } else if ($method->isProtected()) { | |
| $visibility = "#"; | |
| } else if ($method->isPublic()) { | |
| $visibility = "+"; | |
| } | |
| $method_name = $method->getName(); | |
| if($method_name == "load") continue; | |
| if (count($args) > 6) { | |
| $uml .= "\t $visibility" . $return . $method_name . "() \n"; | |
| // add note if method has more than 6 arguments | |
| $note .= "\tnote right of $name::$method_name\n"; | |
| $note .= "\t\tmethod has more than 6 arguments\n"; | |
| $note .= "\tend note\n"; | |
| } else { | |
| $uml .= "\t $visibility" . $return . $method_name . "(" . implode(", ", $args) . ") \n"; //disable argument | |
| } | |
| } | |
| } | |
| $uml .= "}\n"; | |
| $uml .= $note; | |
| return $uml; | |
| } | |
| /** | |
| * Mendapatkan baris yang mengandung kata load dari sebuah file, kecuali load view | |
| * | |
| * @param string $filepath Path menuju file yang ingin di extract menjadi class diagram | |
| * @return array | |
| */ | |
| function fetchLoaderFromFile($filepath) | |
| { | |
| $value_plain = file_get_contents($filepath); | |
| // Mengambil seluruh loader pada file | |
| $value_load = preg_grep("/load\->/", explode("\n", $value_plain)); | |
| // Mengecualikan load untuk view | |
| $value_load = preg_grep("/load\->view/", $value_load, PREG_GREP_INVERT); | |
| return $value_load; | |
| } | |
| /** | |
| * Fungsi digunakan untuk melakukan ekstraksi terhadap variable | |
| * pada sebuah loader | |
| */ | |
| function convertStringToListClass($string, $prefix, $suffix) | |
| { | |
| $dependencies_string = explode($prefix, $string)[1]; | |
| $dependencies_string = explode($suffix, $dependencies_string)[0]; | |
| $sanitized = $this->sanitized; | |
| // Remove the parentheses and split by ',' | |
| $dependencies = explode(',', $dependencies_string); | |
| foreach ($dependencies as $index => $value) { | |
| // menghapus double quote, single quote dan spasi | |
| $value = trim(str_replace($sanitized, '', $value)); | |
| // mengubah class menjadi ucword, mengikuti standart penamaan pada CI3 | |
| if (!in_array($value, $this->exception_ucwords)) { | |
| $value = ucwords($value); | |
| } | |
| $dependencies[$index] = $value; | |
| } | |
| return $dependencies; | |
| } | |
| /** | |
| * Mengubah string menjadi list array | |
| * | |
| * @param string $line baris kode yang akan di parse | |
| * @return array mengembalikan array dengan kunci ```type``` dan ```class``` | |
| */ | |
| function parseClassInformation($line) | |
| { | |
| // convert $this->CI to $this | |
| $line = str_replace('$this->CI', '$this', $line); | |
| $dependencies = explode("->", $line)[2]; | |
| $type = explode("(", $dependencies)[0]; | |
| $contain_string_array = strpos(strtolower($line), 'array'); | |
| $contain_array_symbol = strpos(strtolower($line), '['); | |
| if (is_int($contain_string_array)) { | |
| // Fungsi dibawah untuk meng-handle load model | |
| // dengan cara seperti berikut: | |
| // $this->load->model(array('Msurat_suratkeluar', 'Muser', 'Msurat', 'Mgroup', 'Tte_msurat')); | |
| $dependencies = $this->convertStringToListClass($line, "array(", ")"); | |
| } else if (is_int($contain_array_symbol)) { | |
| // Fungsi dibawah untuk meng-handle load model | |
| // dengan cara seperti berikut: | |
| // $this->load->model(['Msurat_suratkeluar', 'Muser', 'Msurat', 'Mgroup', 'Tte_msurat']); | |
| $dependencies = $this->convertStringToListClass($line, "[", "]"); | |
| } else { | |
| // Fungsi dibawah untuk meng-handle load model | |
| // dengan cara seperti berikut: | |
| // $this->load->model('Msurat_suratkeluar'); | |
| // $this->load->model('Msurat_suratkeluar', 'model'); | |
| $dependencies = explode("(", $dependencies)[1]; // 'Msurat_suratkeluar'); | |
| $dependencies = explode(")", $dependencies)[0]; // 'Msurat_suratkeluar' | |
| $dependencies = explode(",", $dependencies)[0]; // 'Msurat_suratkeluar' | |
| $dependencies = str_replace($this->sanitized, "", $dependencies); // Msurat_suratkeluar | |
| // mengubah class menjadi ucword, mengikuti standart penamaan pada CI3 | |
| if (!in_array($dependencies, $this->exception_ucwords)) { | |
| $dependencies = ucwords($dependencies); | |
| } | |
| $dependencies = [$dependencies]; // ubah menjadi array | |
| } | |
| return [ | |
| "type" => $type, | |
| "class" => $dependencies | |
| ]; | |
| } | |
| private function generateUml($list_components, $fileName) | |
| { | |
| ob_start(); | |
| // Variable untuk menampung seluruh file | |
| $list_files = []; | |
| foreach ($list_components as $component) { | |
| $filepath = $this->getRealpath($component[0], $component[1]); | |
| // Matikan program ketika file tidak dapat ditemukan | |
| if (!$filepath) die("404: File {$component[1]} tidak ditemukan"); | |
| $list_files[] = $filepath; | |
| } | |
| $list_loaders = []; | |
| foreach ($list_files as $value) { | |
| $class_loadstring = $this->fetchLoaderFromFile($value); | |
| $class_information = array_map([$this, 'parseClassInformation'], $class_loadstring); | |
| $loaders = []; | |
| foreach ($class_information as $information) { | |
| foreach ($information['class'] as $class) { | |
| $loaders[] = [ | |
| // type | |
| $information['type'], | |
| // class | |
| $class, | |
| // reference | |
| null // default reference is null | |
| ]; | |
| } | |
| } | |
| $loaders = array_map("unserialize", array_unique(array_map("serialize", $loaders))); | |
| $list_loaders[] = $loaders; | |
| } | |
| foreach ($list_components as $key => $component) { | |
| $component_with_null = array_merge($component, [null]); | |
| $component_type = $component_with_null[0]; | |
| $component_class = $component_with_null[1]; | |
| $list_loaders = array_map(function ($dependencies) use ($component_type, $component_class) { | |
| // Melakukan pengecekan apabila terdapat self reference, maka ubah nilai menjadi null | |
| $dependencies = array_map(function ($dependency) use ($component_type, $component_class) { | |
| $dependency_type = $dependency[0]; | |
| $dependency_class = $dependency[0]; | |
| // Kembalikan nilai null apabila terindinkasi self refenrece | |
| if ($dependency_type == $component_type && $dependency_class == $component_class) return null; | |
| return $dependency; | |
| }, $dependencies); | |
| // menghapus nilai null | |
| $dependencies = array_filter($dependencies); | |
| return $dependencies; | |
| }, $list_loaders); | |
| $list_loaders[$key][] = array_merge($component, [$list_files[$key]]); | |
| } | |
| // flatten array | |
| // var_dump($list_loaders);die; | |
| $flat_loaders = array_reduce($list_loaders, 'array_merge', array()); | |
| // menghapus data duplikat | |
| $flat_loaders = array_map("unserialize", array_unique(array_map("serialize", $flat_loaders))); | |
| $uml = "@startuml {$fileName}\n"; | |
| // initial class | |
| foreach ($flat_loaders as $value) { | |
| $uml .= $this->generateUmlClass($value[0], $value[1], $value[2]); | |
| } | |
| $uml .= "@enduml\n"; | |
| // create or replace file | |
| $path = FCPATH . "out/diagrams/{$fileName}.puml"; | |
| $file = fopen($path, "w") or die("Unable to open file!"); | |
| fwrite($file, $uml); | |
| fclose($file); | |
| ob_clean(); | |
| } | |
| public function index() | |
| { | |
| $list_of_uml = [ | |
| // "Surat Masuk Icon" => [ | |
| // ["controller", "Suratmasukiconplus"], | |
| // // ["model", "Iip_msuratmasuk"], | |
| // // ["model", "Msurat_suratkeluariconplus"], | |
| // ], | |
| // "Surat Keluar Icon" => [ | |
| // ["controller", "Suratkeluariconplus"], | |
| // // ["model", "Msurat_suratkeluariconplus"], | |
| // ], | |
| "Menu Integrasi" => [ | |
| ["controller", "Integrasi"], | |
| // ["model", "Iip_confsekretaris"], | |
| // ["library", "Plnintegration"], | |
| ], | |
| // "Iconplus_Service" => [ | |
| // // ["library", "Iconplus_integration"], | |
| // ["controller", "integration/Iconplus"], | |
| // ["controller", "integration/Provider"], | |
| // ["controller", "integration/Sync"], | |
| // // ["model", "Iip_maddress_book"], | |
| // // ["model", "Iip_marea"], | |
| // // ["model", "Iip_mcompany"], | |
| // // ["model", "Iip_morganization"], | |
| // // ["model", "Iip_msuratmasuk"], | |
| // ] | |
| // untuk membagi uml agar menjadi tidak terlalu kompleks | |
| // anda dapat memecahnya menjadi array baru | |
| // [], | |
| ]; | |
| foreach ($list_of_uml as $fileName => $value) { | |
| // check if file exist | |
| // if (!file_exists(FCPATH . "/diagrams/{$value[0][1]}.puml")) { | |
| try { | |
| $this->workflow = false; | |
| //code... | |
| $this->generateUml($value, $fileName); | |
| echo "$fileName. Generate UML {$value[0][1]} Success\n<br/>"; | |
| } catch (\Throwable $th) { | |
| // var_dump($value[0][1]); | |
| // die; | |
| //throw $th; | |
| } | |
| // } | |
| } | |
| } | |
| } |
| @startuml Iconplus_Service | |
| class Iconplus_integration { | |
| +CI | |
| +IPADDRESS | |
| +APPID | |
| +SHAREDKEY | |
| +__construct() | |
| +getToken() | |
| +request(url, data, method) | |
| +parseResponse(response) | |
| +userAms(idUnit) | |
| +pokokKegiatan(idUnit) | |
| +klasifikasi(idUnit) | |
| +allSuratMasuk(lastdate) | |
| +allOrganization() | |
| +allOrganizationDb() | |
| +perusahaan(idUnit) | |
| +perusahaanDb(idUnit) | |
| +area(idCompany) | |
| +areaDb(idCompany) | |
| +addressBook(personName, idUnit, companyCode, businessAreaCode) | |
| +downloadLampiran(nomorSurat, namaLampiran, lampiranPath) | |
| +cekSuratMasuk(nomorSurat, orgCode, updated_date) | |
| +detailSuratMasuk(nomorSurat) | |
| +kirimSuratMasuk(objects) | |
| } | |
| class Iip_morganization extends CI_Model { | |
| +sync(data) | |
| +all() | |
| +__construct() | |
| +__get(key) | |
| } | |
| class Iip_mcompany extends CI_Model { | |
| +sync(data) | |
| +all(idUnit) | |
| +__construct() | |
| +__get(key) | |
| } | |
| class Iip_marea extends CI_Model { | |
| +sync(data) | |
| +all(idCompany) | |
| +__construct() | |
| +__get(key) | |
| } | |
| class Iip_maddress_book extends CI_Model { | |
| +sync(data) | |
| +all() | |
| +__construct() | |
| +__get(key) | |
| } | |
| class Iip_msuratmasuk extends CI_Model { | |
| +__construct() | |
| +sync(data, dataAkses) | |
| +findByNomorSurat(nomor) | |
| +all() | |
| +getAllCount() | |
| +tracking_disposisi(period, myprofile) | |
| +show_tracking_disposisi(nomor_surat, myprofile) | |
| +paginate(period, myprofile) | |
| +getLastData() | |
| +getLastDateInserted() | |
| +can_disposisi(no_surat, myprofile) | |
| +first_hand_disposition(no_surat, myprofile) | |
| +disposisi() | |
| +__get(key) | |
| } | |
| note right of Iip_msuratmasuk::disposisi | |
| method has more than 6 arguments | |
| end note | |
| class Muser extends CI_Model { | |
| +__construct() | |
| +getmyprofile() | |
| +getprofile_by_posid(posid) | |
| +getprofile_by_para(children_group_id) | |
| -generate_query_profile(qwhere) | |
| +getprofile_by_attr(colname, value) | |
| +getprofile_by_nid(nid, all_array) | |
| +__get(key) | |
| } | |
| @enduml |