- 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 |