CakePHP 3.x: log as serialized array - cakephp

I'm writing my own parser log for CakePHP.
I only need one thing: that is not written a log "message" (as a string), but a serialized array with various log information (date, type, line, stack traces, etc.).
But I don't understand what method/class I should rewrite, although I have consulted the APIs. Can you help me?
EDIT:
For now I do the opposite: I read the logs (already written) and I transform them into an array with a regex.
My code:
$logs = array_map(function($log) {
preg_match('/^'.
'([\d\-]+\s[\d:]+)\s(Error: Fatal Error|Error|Notice: Notice|Warning: Warning)(\s\(\d+\))?:\s([^\n]+)\n'.
'(Exception Attributes:\s((.(?!Request|Referer|Stack|Trace))+)\n)?'.
'(Request URL:\s([^\n]+)\n)?'.
'(Referer URL:\s([^\n]+)\n)?'.
'(Stack Trace:\n(.+))?'.
'(Trace:\n(.+))?(.+)?'.
'/si', $log, $matches);
switch($matches[2]) {
case 'Error: Fatal Error':
$type = 'fatal';
break;
case 'Error':
$type = 'error';
break;
case 'Notice: Notice':
$type = 'notice';
break;
case 'Warning: Warning':
$type = 'warning';
break;
default:
$type = 'unknown';
break;
}
return (object) af([
'datetime' => \Cake\I18n\FrozenTime::parse($matches[1]),
'type' => $type,
'error' => $matches[4],
'attributes' => empty($matches[6]) ? NULL : $matches[6],
'url' => empty($matches[9]) ? NULL : $matches[9],
'referer' => empty($matches[11]) ? NULL : $matches[11],
'stack_trace' => empty($matches[13]) ? (empty($matches[16]) ? NULL : $matches[16]) : $matches[13],
'trace' => empty($matches[15]) ? NULL : $matches[15]
]);
}, af(preg_split('/[\r\n]{2,}/', $logs)));
For now I do the opposite: I read the logs (already written) and with a regex I transform them into an array.
The problem is this is terribly expensive. and that it would be better to do the opposite: to write directly to the logs as a serialized array.

I think what you want to do is write your own LogAdapter.
You simply create a class ArrayLog (extends BaseLog) as mentioned in the docs and configure cakePHP to use it. Within the log function you append the information like $level, $message and $context to a file as an array. This will result in a log file with several arrays that then can be split.
That being said, I would suggest to log to the database and read it out instead of parsing.

Ok, that's it!
(note that this code is absolutely experimental, I have yet to test it properly)
One interesting thing that I want to do: for each log, write to the serialized file and also simultaneously in a plan file.
This allows me either to read logs as a plain text file, or they can be manipulated using the serialized file.
use Cake\Log\Engine\FileLog;
class SerializedLog extends FileLog {
protected function _getLogAsArray($level, $message) {
$serialized['level'] = $level;
$serialized['datetime'] = date('Y-m-d H:i:s');
//Sets exception type and message
if(preg_match('/^(\[([^\]]+)\]\s)?(.+)/', $message, $matches)) {
if(!empty($matches[2]))
$serialized['exception'] = $matches[2];
$serialized['message'] = $matches[3];
}
//Sets the exception attributes
if(preg_match('/Exception Attributes:\s((.(?!Request URL|Referer URL|Stack Trace|Trace))+)/is', $message, $matches)) {
$serialized['attributes'] = $matches[1];
}
//Sets the request URL
if(preg_match('/^Request URL:\s(.+)$/mi', $message, $matches)) {
$serialized['request'] = $matches[1];
}
//Sets the referer URL
if(preg_match('/^Referer URL:\s(.+)$/mi', $message, $matches)) {
$serialized['referer'] = $matches[1];
}
//Sets the trace
if(preg_match('/(Stack )?Trace:\n(.+)$/is', $message, $matches)) {
$serialized['trace'] = $matches[2];
}
$serialized['full'] = date('Y-m-d H:i:s').' '.ucfirst($level).': '.$message;
return (object) $serialized;
}
public function log($level, $message, array $context = []) {
$message = $this->_format(trim($message), $context);
$filename = $this->_getFilename($level);
if (!empty($this->_size)) {
$this->_rotateFile($filename);
}
$pathname = $this->_path . $filename;
$mask = $this->_config['mask'];
//Gets the content of the existing logs and unserializes
$logs = #unserialize(#file_get_contents($pathname));
if(empty($logs) || !is_array($logs))
$logs = [];
//Adds the current log
$logs[] = $this->_getLogAsArray($level, $message);
//Serializes logs
$output = serialize($logs);
if (empty($mask)) {
return file_put_contents($pathname, $output);
}
$exists = file_exists($pathname);
$result = file_put_contents($pathname, $output);
static $selfError = false;
if (!$selfError && !$exists && !chmod($pathname, (int)$mask)) {
$selfError = true;
trigger_error(vsprintf(
'Could not apply permission mask "%s" on log file "%s"',
[$mask, $pathname]
), E_USER_WARNING);
$selfError = false;
}
return $result;
}
}

Related

PUT request with formData could not update using react.js and laravel

I am not receiving formdata in the Api endpoint which is being sent by fetch and PUT method.
I used the POST method and it worked out which i think is not recommended for updating.
I have react running on localhost:3000 and the laravel-API running on localhost:5000.
This is the route in API
Route::put('updateSlide/{id}', 'SlidesController#updateSlide');
This is what is in the controller
public function updateImage(Request $request, int $id)
{
$image = $this->slideRepo->findSlideById($id)->image;
if ($image) {
$result = Storage::disk('ucmp')->delete($image);
}
if ($request->hasFile('image') && $request->file('image') instanceof UploadedFile) {
return $this->slideRepo->saveCover($request->file('image'));
} // return response()->json($data);
// data is an array (note)
return null;
}
public function updateSlide(Request $request, int $id)
{
$imageUrl=$this->updateImage($request, $id);
return response()->json($this->slideRepo->updateSlide([
'caption' => $request['caption'],
'image' => $imageUrl,
'url' => $request['url']
],$id));
}
This is the function sending to fetch
export const updateSlideApi = (token, _slide, id) => {
return {
url: `${BASE_URL}/api/updateSlide/${id}`,
opt: API.requestOptions("PUT",token,null,{ body: _slide }, true)
};
};
In my header i do not have the content-type.
I expect json data from the laravel API function but am having an error, " Can only throw objects"
PHP does not parse the body of a PUT request.
Use POST and add parameter _method with value PUT.
PHP put has given me tough time, use this function will save your time parsing it else method:
function parsePutRequest()
{
// Fetch content and determine boundary
$raw_data = file_get_contents('php://input');
$boundary = substr($raw_data, 0, strpos($raw_data, "\r\n"));
// Fetch each part
$parts = array_slice(explode($boundary, $raw_data), 1);
$data = array();
foreach ($parts as $part) {
// If this is the last part, break
if ($part == "--\r\n") break;
// Separate content from headers
$part = ltrim($part, "\r\n");
list($raw_headers, $body) = explode("\r\n\r\n", $part, 2);
// Parse the headers list
$raw_headers = explode("\r\n", $raw_headers);
$headers = array();
foreach ($raw_headers as $header) {
list($name, $value) = explode(':', $header);
$headers[strtolower($name)] = ltrim($value, ' ');
}
// Parse the Content-Disposition to get the field name, etc.
if (isset($headers['content-disposition'])) {
$filename = null;
preg_match(
'/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/',
$headers['content-disposition'],
$matches
);
list(, $type, $name) = $matches;
isset($matches[4]) and $filename = $matches[4];
// handle your fields here
switch ($name) {
// this is a file upload
case 'userfile':
file_put_contents($filename, $body);
break;
// default for all other files is to populate $data
default:
$data[$name] = substr($body, 0, strlen($body) - 2);
break;
}
}
}
return $data;
}

row() return null for specific field

Im trying to save specific field from a record into a session, for my user-role. The problem here is i cannot take any other field except nama.
controller/verify_login
public function index(){
$this->load->model('verify_login_model');
$username = $this->input->post('username');
$password = $this->input->post('password');
$result = $this->verify_login_model->verify($username, $password);
if($result == $username) {
$name = $this->verify_login_model->getinfo($username);
$this->session->set_userdata('logged_in', TRUE);
$this->session->set_userdata('username', $username);
$this->session->set_userdata('name', $name);
$this->load->view('home_view');
} else {
redirect('login');
}
model/verify_login_model
function verify($username, $password){
$this->db->select('username', 'password');
$this->db->from('tb_user');
$this->db->where('username', $username);
$this->db->where('password', MD5($password));
$this->db->limit(1);
$query = $this->db->get();
if($query->num_rows()==1) {
return $username;
} else {
return false;
}
}
function getinfo($username) {
$this->db->select('nama', 'username');
$this->db->from('tb_userInfo');
$this->db->where('username', $username);
$this->db->limit(1);
$query = $this->db->get();
if($query->num_rows()==1) {
$result = $query->row();
return $result->nama;
} else {
return false;
}
}
view
<?php echo $this->session->userdata['name']?>
the var_dump($result) : object(stdClass)#22 (1) { ["nama"]=> string(4) "test" }
if i change the return $result->nama; to $result->username; i get error : Undefined property: stdClass::$username even tho im sure 200% there's username field in the table, and tried the query directly.
There's an error in your select statement, it must be
$this->db->select('nama,username');
You are separating each column, and that's not correct, all of the columns go in one string, that's why it tells you its undefined since the only column you are sending is nama and you're sending username as the second parameter for the select.
Here's a link on active record for CodeIgniter 2.2.0

cakephp how can i tell before function from an update

I am working on a CakePHP 2.x. The scenario is I am sending an encrypted and decrypted data to the database. So in order to do this I have written beforeSave function in each modal.
so right now the problem is whenever data is updated, the data is not going encrypted into db .. please anyone know how to i fix this issue
I am doing this in my controller. The update and save function:
foreach($data as $datas){
$count = $this->Contact->checkkey($datas['idUser'],$datas['key']);
if($count>0){
$this->Contact->updateContactAgainstkey($datas['name'],
$this->request->data['Contact']['mobileNo'],
$this->request->data['Contact']['other'],
$this->request->data['Contact']['email'],
$datas['key'],$datas['idUser']);
}else{
$this->Contact->create();
$this->Contact->save($this->request->data);
}
}
updateFunction in Model
public function updateContactAgainstkey($name,$mobileNo,
$other,$email,$key,$userid){
if($this->updateAll(
array('name' => "'$name'",
'mobileNo' => "'$mobileNo'",
'workNo' => "'$workNo'",
'homeNo' => "'$homeNo'",
'other' => "'$other'",
'email' => "'$email'",),
array('User_id'=>$userid,'key'=>$key))){
return true;
}else{
return false;
}
}
beforeSave function
public function beforeSave($options=array()) {
if ( isset ( $this -> data [ $this -> alias ] [ 'mobileNo' ] ) ) {
$this -> data [ $this -> alias ] [ 'mobileNo' ] = AllSecure::encrypt($this->data[$this->alias]['email']);
}
return true;
}
please help me if anyone know how to deal with this issue.
Try following code in model
public function updateAll($fields, $conditions = true) {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$created = FALSE;
$options = array();
if($db->update($this, $fields, null, $conditions)) {
$created = TRUE;
$this->Behaviors->trigger($this, 'afterSave', array($created, $options));
$this->afterSave($created);
$this->_clearCache();
$this->id = false;
return true;
}
return FALSE;
}
look here
http://nuts-and-bolts-of-cakephp.com/2010/01/27/make-updateall-fire-behavior-callbacks/
here better to use save function for updating data like:
$data=array();
$data['Contact']['mobileNo']=$this->request->data['Contact']['mobileNo'];
$data['Contact']['other']=$this->request->data['Contact']['other'];
$data['Contact']['other']=$this->request->data['Contact']['other'];
........... .............. ................
$this->Contact->id = "primerykey";
$this->Contact->save($data);
where $data contains all field that you want to update with value

How do I use fixed fields in CakeDC's csvUpload behavior in Util plugin

I am using the csvUpload behavior of the Utils plugin by CakeDC, on a CakePHP 2.2.1 install.
I have it working great it's processing a rather large csv successfully. However there are two fields in my table / Model that would be considered fixed, as they are based on ID's from from associated models that are not consistent. So I need to get these fixed values via variables which is easy enough.
So my question is, how do I use the fixed fields aspect of csvUpload? I have tried that following and many little variation, which obviously didn't work.
public function upload_csv($Id = null) {
$unique_add = 69;
if ( $this->request->is('POST') ) {
$records_count = $this->Model->find( 'count' );
try {
$fixed = array('Model' => array('random_id' => $Id, 'unique_add' => $unique_add));
$this->Model->importCSV($this->request->data['Model']['CsvFile']['tmp_name'], $fixed);
} catch (Exception $e) {
$import_errors = $this->Model->getImportErrors();
$this->set( 'import_errors', $import_errors );
$this->Session->setFlash( __('Error Importing') . ' ' . $this->request->data['Model']['CsvFile']['name'] . ', ' . __('column name mismatch.') );
$this->redirect( array('action'=>'import') );
}
$new_records_count = $this->Model->find( 'count' ) - $records_count;
$this->Session->setFlash(__('Successfully imported') . ' ' . $new_records_count . ' records from ' . $this->request->data['Model']['CsvFile']['name'] );
$this->redirect(array('plugin'=>'usermgmt', 'controller'=>'users', 'action'=>'dashboard'));
}
}
Any help would be greatly appreciated as I have only found 1 post concerning this behavior when I searching...
I made my custom method to achieve the same task. Define the following method in app\Plugin\Utils\Model\Behavior
public function getCSVData(Model &$Model, $file, $fixed = array())
{
$settings = array(
'delimiter' => ',',
'enclosure' => '"',
'hasHeader' => true
);
$this->setup($Model, $settings);
$handle = new SplFileObject($file, 'rb');
$header = $this->_getHeader($Model, $handle);
$db = $Model->getDataSource();
$db->begin($Model);
$saved = array();
$data = array();
$i = 0;
while (($row = $this->_getCSVLine($Model, $handle)) !== false)
{
foreach ($header as $k => $col)
{
// get the data field from Model.field
$col = str_replace('.', '-', trim($col));
if (strpos($col, '.') !== false)
{
list($model,$field) = explode('.', $col);
$data[$i][$model][$field] = (isset($row[$k])) ? $row[$k] : '';
}
else
{
$col = str_replace(' ','_', $col);
$data[$i][$Model->alias][$col] = (isset($row[$k])) ? $row[$k] : '';
}
}
$is_valid_row = false;
foreach($data[$i][$Model->alias] as $col => $value )
{
if(!empty($data[$i][$Model->alias][$col]))
{
$is_valid_row = true;
}
}
if($is_valid_row == true)
{
$i++;
$data = Set::merge($data, $fixed);
}
else
{
unset($data[$i]);
}
}
return $data;
}
And you can use it using:
$csv_data = $this->Model->getCSVData($this->request->data['Model']['CsvFile']['tmp_name'], $fixed);
Here $csv_data will contain an array of all of those records from the csv file which are not empty and with the fixed field in each record index.
So as I was telling Arun, I answered my own question and figured it out. I was looking to broad instead of really examining what was in front of me. I started running some debugging and figured it out.
First of all, $unique_add = 69 is seen as an int, duh. In order for it to be added to the csv it need to viewed as a string. So it simply becomes, $unique_add = '69'.
I couldn't enter the value of $Id directly into the fixed array. So I just had to perform a simple find to get the value I needed.
$needed_id = $this->Model->find('first', array(
'condition'=>array('Model.id'=>$Id)
)
);
$random_id = $needed_id['Model']['id'];
Hopefully this won't be needed to help anyone because hopefully no one else will make this silly mistake. But one plus... Now there's actually more than one post on the internet documenting the use of fixed fields in the CakeDC Utils plugin.

codeigniter file upload - optional?

I'm sure this is simple but I can't see how to make uploading a file with CI optional.
If you leave the file input box empty, the error "You didn't choose an upload file" appears.
The reason I want it to be optional is that my form edits a directory type listing, and I don't need to upload the image each time I edit the listing.
Is there a way to remove the "required" error handling on the file class
Use the following:
<?php if ( $_FILES AND $_FILES['field_name']['name'] )
{
// Upload the file
}
codeigniter file upload optionally ...works perfect..... :)
---------- controller ---------
function file()
{
$this->load->view('includes/template', $data);
}
function valid_file()
{
$this->form_validation->set_rules('userfile', 'File', 'trim|xss_clean');
if ($this->form_validation->run()==FALSE)
{
$this->file();
}
else
{
$config['upload_path'] = './documents/';
$config['allowed_types'] = 'gif|jpg|png|docx|doc|txt|rtf';
$config['max_size'] = '1000';
$config['max_width'] = '1024';
$config['max_height'] = '768';
$this->load->library('upload', $config);
if ( !$this->upload->do_upload('userfile',FALSE))
{
$this->form_validation->set_message('checkdoc', $data['error'] = $this->upload->display_errors());
if($_FILES['userfile']['error'] != 4)
{
return false;
}
}
else
{
return true;
}
}
i just use this lines which makes it optionally,
if($_FILES['userfile']['error'] != 4)
{
return false;
}
$_FILES['userfile']['error'] != 4 is for file required to upload.
you can u make it unneccessory by using $_FILES['userfile']['error'] != 4 , then it will pass this error for file required and
works great with other types of errors if any by using return false ,
hope it works for u ....
Use this code in the controller before calling do_upload()
if (is_uploaded_file($_FILES['field_name']['tmp_name'])) {
// your code here
}
Use This Code :-
$config['upload_path'] = 'assets/img/';
$config['allowed_types'] = 'gif|jpg|png|jpeg';
$this->load->library('upload', $config);
// Upload the file
if ($this->upload->do_upload('Image')){
$dataimage = $this->upload->data();
$data = array(
'image' => $dataimage['file_name'],
'UserName' => $this->input->post('UserName'),
'Password' => $this->input->post('Password'),
'xid' => $this->input->post('xid')
);
}
else{
/*$out['msg'] = show_err_msg($this->upload->display_errors());
echo json_encode($out);
exit();*/
$data = array(
'image' => NULL,
'UserName' => $this->input->post('UserName'),
'Password' => $this->input->post('Password'),
'xid' => $this->input->post('xid')
);
}

Resources