How to make alignment on console in php - php

I am trying to run a script through command prompt in PHP and trying to show the result in tabular form. But due to different character length of words I am not able to show the result properly align.
I want result like this
Book ISBN Department
Operating System 101 CS
C 102 CS
java 103 CS
Can anyone please help me to get this output like this in php on console.
Thanks in advance

If you don't want (or not allowed for some reason) to use libraries, you can use standard php printf / sprintf functions.
The problem with them that if you have values with variable and non-limited width, then you will have to decide if long values will be truncated or break table's layout.
First case:
// fixed width
$mask = "|%5.5s |%-30.30s | x |\n";
printf($mask, 'Num', 'Title');
printf($mask, '1', 'A value that fits the cell');
printf($mask, '2', 'A too long value the end of which will be cut off');
The output is
| Num |Title | x |
| 1 |A value that fits the cell | x |
| 2 |A too long value the end of wh | x |
Second case:
// only min-width of cells is set
$mask = "|%5s |%-30s | x |\n";
printf($mask, 'Num', 'Title');
printf($mask, '1', 'A value that fits the cell');
printf($mask, '2', 'A too long value that will brake the table');
And here we get
| Num |Title | x |
| 1 |A value that fits the cell | x |
| 2 |A too long value that will brake the table | x |
If neither of that satisfies your needs and you really need a table with flowing width columns, than you have to calculate maximum width of values in each column. But that is how PEAR::Console_Table exactly works.

You can use PEAR::Console_Table:
Console_Table helps you to display tabular data on a
terminal/shell/console.
Example:
require_once 'Console/Table.php';
$tbl = new Console_Table();
$tbl->setHeaders(array('Language', 'Year'));
$tbl->addRow(array('PHP', 1994));
$tbl->addRow(array('C', 1970));
$tbl->addRow(array('C++', 1983));
echo $tbl->getTable();
Output:
+----------+------+
| Language | Year |
+----------+------+
| PHP | 1994 |
| C | 1970 |
| C++ | 1983 |
+----------+------+

Your best option is to use the Pear Package Console_Table ( http://pear.php.net/package/Console_Table/ ).
To use - on a console you need to install the pear package, running:
pear install Console_Table
this should download the package and install. You can then use a sample script such as:
require_once 'Console/Table.php';
$tbl = new Console_Table();
$tbl->setHeaders(
array('Language', 'Year')
);
$tbl->addRow(array('PHP', 1994));
$tbl->addRow(array('C', 1970));
$tbl->addRow(array('C++', 1983));
echo $tbl->getTable();

You could try the recent simple PHP library ConsoleTable if you don't want to use the standard PHP functions printf/sprintf or the pear package PEAR::Console_Table.
Example:
require_once 'ConsoleTable.php';
$table = new LucidFrame\Console\ConsoleTable();
$table
->addHeader('Language')
->addHeader('Year')
->addRow()
->addColumn('PHP')
->addColumn(1994)
->addRow()
->addColumn('C++')
->addColumn(1983)
->addRow()
->addColumn('C')
->addColumn(1970)
->display()
;
Output:
+----------+------+
| Language | Year |
+----------+------+
| PHP | 1994 |
| C++ | 1983 |
| C | 1970 |
+----------+------+
See more example usages at its github page.

Too old, but i went trough the same now and used str_pad, just set the lenght as the size of your column and thats it
regards.

The CLIFramework table generator helps you get the job done very easily and it supports text alignment, text color, background color, text wrapping, text overflow handling.. etc
Here is the tutorial: https://github.com/c9s/CLIFramework/wiki/Using-Table-Component
Sample code: https://github.com/c9s/CLIFramework/blob/master/example/table.php
use CLIFramework\Component\Table\Table;
$table = new Table;
$table->setHeaders([ 'Published Date', 'Title', 'Description' ]);
$table->addRow(array(
"September 16, 2014",
"Title",
"Description",
29.5
));
$table->addRow(array(
"November 4, 2014",
"Hooked: How to Build Habit-Forming Products",
["Why do some products capture widespread attention whil..."],
99,
));
echo $table->render();

Just in case someone wants to do that in PHP I posted a gist on Github
https://gist.github.com/redestructa/2a7691e7f3ae69ec5161220c99e2d1b3
simply call:
$output = $tablePrinter->printLinesIntoArray($items, ['title', 'chilProp2']);
you may need to adapt the code if you are using a php version older than 7.2
after that call echo or writeLine depending on your environment.

Related

Sphinx: PDO exception with certain characters

I'm trying to get the Sphinx search server working with PDO, but it triggers a syntax error when using the MATCH() function in specific scenarios.
Ex.:
In my code I'm splitting the search query by space and then concatenate it using the | (OR) operator. If someone types test > 3, in the match function it would become (test | > | 3). This combination triggers a: Syntax error or access violation: 1064 main_idx: syntax error, unexpected '|' near ' > | 3'. I don't think it's an escape problem because the > character is not on the escape list and even if you try to escape it, it doesn't work. Is this a bug in the version of Sphinx i'm using? Or am I doing something wrong?
I'm using Sphinx version 2.2.11. It's actually a docker instance provided by this image: jamesrwhite/sphinx-alpine:2.2.11
The PHP version is 7.2.
This is my non-working code:
$searchQuery = "SELECT * FROM main_idx WHERE MATCH(:search)";
$dbh = new PDO('mysql:host=127.0.0.1;port=9306', 'root', 'root');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->prepare($searchQuery);
$stmt->bindValue('search', 'test | > | 3');
$stmt->execute();
Same code works perfectly fine if I'm using the MySQLi extension. It also works fine with PDO and Sphinx version 2.2.6. Something must've changed between 2.2.6 and 2.2.11. Anyone encountered this issue?
This behaviour is caused by this bug http://sphinxsearch.com/bugs/view.php?id=2305 and this fix https://github.com/sphinxsearch/sphinx/commit/d9923f76c7724fa8d05a3d328e26a664799841b7. In the previous revision ' > | ' was supported.
We at Manticore Search (fork of Sphinx) will check if the fix was correct and will make a better fix if that's not. Thanks for pointing this out.
Meanwhile you can use 2.2.8 from http://sphinxsearch.com/downloads/archive/ or build manually from the latest revision which supports the syntax (https://github.com/sphinxsearch/sphinx/commit/f33fa667fbfd2031ff072354ade4b050649fbd4e)
[UPDATE]
The fix is proper. It was wrong to not show the error about that in the previous versions as long as you DON'T have the spec. character (>) in your charset_table. To workaround this you can add > to your charset_table and then escape it in the search query, e.g.:
mysql> select * from idx_min where match('test | \\> | a');
+------+---------+----------+-------+------+
| id | doc | group_id | color | size |
+------+---------+----------+-------+------+
| 7 | dog > < | 5 | red | 3 |
+------+---------+----------+-------+------+
1 row in set (0.00 sec)
mysql> select * from idx_min where match('test | \\< | a');
+------+---------+----------+-------+------+
| id | doc | group_id | color | size |
+------+---------+----------+-------+------+
| 7 | dog > < | 5 | red | 3 |
+------+---------+----------+-------+------+
1 row in set (0.00 sec)
or
$stmt->bindValue('search', 'test | \\< | a');
in PDO.
There's still a little bug found though which is that if non-spec character is not in charset_table it doesn't generate an error. E.g.
mysql> select * from idx_min where match('test | j | a');
Empty set (0.00 sec)
works fine even though j is not in charset_table. I've filed a bug in our bug tracker https://github.com/manticoresoftware/manticoresearch/issues/156
Thanks again for helping to point this out.
say for exmple you want to do an exact match I like doing my exact matching like this...
...WHERE MATCH(column) AGAINST('happy I am') AND column LIKE '%happy I am%';
that will guarantee I match exactly what I want to match where as if I didn't include the AND LIKE... it would match happy OR I OR am

Where to define Field values for CakePHP tables

I have a CakePHP application that uses Fields to store values like 0, 1, 2.
Table:
+----+--------+----------------+
| id | status | title |
+----+--------+----------------+
| 1 | 2 | something new |
| 2 | 1 | nsfw |
| 3 | 1 | a potato |
| 5 | 0 | the real thing |
+----+--------+----------------+
Entity/ Array:
$data = [
0 => 'not published',
1 => 'published',
2 => 'draft',
9 => 'option',
];
// Some public methods to get the data..
Template Form dropdown:
+----+---------------+
| id | value |
+----+---------------+
| 0 | not published |
| 1 | published |
| 2 | draft |
| 9 | option |
+----+---------------+
What I use in template:
echo $this->Form->input('status', ['options' => $article->getArticleStatusList()]);
Example:
articles table with a status field. The default values are: 0 not published, 1 published, etc. Defining those in Entity/Article. There is an array with the default values, so in the template file I call an Entity method that uses the array for the options Form input.
Time ago I was using a configuration array for this.
Is this a good way to accomplish the task? It should be stored in a ini file? Or in a Table/Model?
All works but I want to follow the MVC pattern. Thanks.
Put them in src\Model\ArticleStatus.php. At least for me a status is a list of one or more things that don't change very often. No need to put them in a DB table. These lists are data and clearly belong into the model layer of the MVC pattern.
IMHO it is good practice to use constants for them because you'll do a lot checks in the code against these values. String values are prone to typos and hard to distinguish from other domains. For example if you have two tables using a status of the same name but with a different meaning the code can become tricky to understand and also a search and replace won't work very well because you'll change both types for both domains.
For example we have a countries table with a lot additional info per country but use a list of constants of our ~18 most used countries we have to do conditional checks on because of our business. So we have src\Model\Table\CountriesTable.php but as well src\Model\Country.php. The reason for this is it becomes much much more readable and easier to understand what goes in the the code if you can write Country::GERMANY instead of just using an id like 5. I personally consider it as very bad practice to use hard coded id's everywhere in the code.
if ($country === 41 && $status === 3)
vs
if ($country === Country::GERMANY && $status === ArticleStatus::PUBLISHED)
I think we can agree on that readable and easy to understand code is much better than typing a few characters less. Honestly, people whining about a few characters should learn to type faster. I consider it just as a bad excuse. ;) Also using an IDE will autocomplete the class constants any way for you. It won't do that for an integer.
Here is an example that would even allow you to generate your list with translated labels:
<?php
namespace App\Model;
class ArticleStatus {
const PUBLISHED = 'published';
const DRAFT = 'draft';
// Add more as you like
public static function getStatuses() {
return [
static::PUBLISHED ,
static::DRAFT
];
}
public static function getKeyValueList() {
return [
static::PUBLISHED => __d('app', 'Published'),
static::DRAFT=> __d('app', 'Draft')
];
}
}
Use it in your controller and set it to your view or directly use it in the view.

PHP/MySQL table update issue while checking condition in column

I have the above table: tblCompInfo, the product_id value is not 100% accurate and I need to fix it. I have total of 543847 total row with 25 different company and 12 different products.
now, The URL is 100% accurate and as you can see from the image I have highlighted with RED which means they are wrong and GREEN which is what it should be updated to.
TASK:
I need to update Product_id by parsing through URL and getting the INTEGER and checking it with product table, if its a product, assign the value else assign 0.
SOLUTION:
I got two solution in my head:
1. EXPORT the entire DATA to EXCEL CVS, change it and UPLOAD it to DATABASE. which means my entire week will be working with EXCEL only.
2. Since I have laravel framework: I can make a function in PHP and get the DATA company wise and UPDATE the table in a foreach loop with condition.
PROBLEM:
So, to make my life easy, I made the PHP function with a simple solution and it works BUT I get MEMORY ALLOCATION PROBLEM.
$companyID = ??;
$tblCompInfos = tblCompInfo::where('company_id', '=', $companyID)->get();
foreach($tblCompInfos as $tblCompInfo)
{
$actual_link = $tblCompInfo->url;
$pathlink = parse_url($actual_link, PHP_URL_PATH);
$product_id_from_url = preg_replace("/[^0-9]/", "" , $pathlink);
$FindIfItsInProductTable = Product::find($product_id_from_url);
$real_product_id = $FindIfItsInProductTable == null ? 0 : $product_id_from_url;
DB::table('tblCompInfo')->where('company_id', '=', $companyID)->where('url', '=', $tblCompInfo->url)->update(array(
'product_id' => $real_product_id,
));
echo $actual_link."-".$real_product_id."=".$tblCompInfo->product_id."<br>";
}
if it was a local server, I would have update my PHP.ini with more memory and do the job.
However, I have a LIVE server and it has to be done in the live server and I have no control or power over PHP.ini.
What to do? How can I do it easily that I will not get a memory issue?
Please help if anyone?
Try this :
UPDATE [table_name] SET product_id = CONVERT(SUBSTR(url, LOCATE('products/', url)+9, LOCATE('/compare',url)-LOCATE('products/', url)+9),UNSIGNED INTEGER)
But this will only works if every url field has suffix as /compare
if you use MariaDB you can use REGEXP_REPLACE to do the changes like
UPDATE your_table
SET url = REGEXP_REPLACE(url,'[0-9]+',Product_id)
WHERE Product_id > 0;
sample
MariaDB [your_schema]> SELECT REGEXP_REPLACE('http://example.com/products/12/compare','[0-9]+','99');
+--------------------------------------------------------------------+
| REGEXP_REPLACE('http://example.com/products/12/compare','[0-9]+','99') |
+--------------------------------------------------------------------+
| http://example.com/products/99/compare |
+--------------------------------------------------------------------+
1 row in set (0.00 sec)
MariaDB [your_schema]>
I have a pretty odd idea but it can work.
Look at that query :
SELECT
'http://example.com/products/12/compare' as url,
'http://example.com/products/' as check1,
'http://example.com/termsets/' as check2,
'http://example.com/products/12/compare' REGEXP 'http://example.com/products/' as regexp_check1, -- check 1
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) as test1,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) REGEXP "^[0-9]+$" as test1_only_num,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),2 ) as test11,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) REGEXP "^[0-9]+$" as test11_only_num,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),3 ) as test111,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) REGEXP "^[0-9]+$" as test111_only_num;
Result :
+----------------------------------------+------------------------------+------------------------------+---------------+-------+----------------+--------+-----------------+---------+------------------+
| url | check1 | check2 | regexp_check1 | test1 | test1_only_num | test11 | test11_only_num | test111 | test111_only_num |
+----------------------------------------+------------------------------+------------------------------+---------------+-------+----------------+--------+-----------------+---------+------------------+
| http://example.com/products/12/compare | http://example.com/products/ | http://example.com/termsets/ | 1 | 1 | 1 | 12 | 1 | 12/ | 0 |
+----------------------------------------+------------------------------+------------------------------+---------------+-------+----------------+--------+-----------------+---------+------------------+
Url, check1 and check2 are just to display the variables I'm using. It's a main ID, the query is not usable that way of course.
Logic with check1
You check with a REGEX if check1 is present in your URL. If yes, regexp_check1 is 1, else it's 0.
ONLY if regexp_check1 is 1, then you SUBSTRING your URL to take the part that is located AFTER the check1 sentence. You take the first character AFTER (test1), then the two characters AFTER (test11), the three characters AFTER (test111) etc.. until the max length your ID_PRODUCT can be (6 or 7 for example).
You REGEX the SUBSTR you isolated to check if they are numeric only (test1 is numeric, test11 is numeric only, test111 is not numeric only.
Then you know that the content of test11 is your ID
Then you do the same thing with check2 if regexp_check1 was 0, and with an eventual check3 (which would contain http://www.comadso.dk/products/ for example), and for every beginning you can have.
Maybe my idea is a shitty one, but hey if it's seem dumb but works, it's not dumb !

Box/Spout questions

This is my first time using Box/Spout library. I am using WAMP server.
My question is the following:
require_once('./spout-master/src/Spout/Autoloader/autoload.php');
use Box\Spout\Writer\WriterFactory;
use Box\Spout\Common\Type;
$filePath = 'test.xlsx';
$writer = WriterFactory::create(Type::XLSX);
$writer->openToFile($filePath);
[X]
$writer->addRow(['a'], $style);
$writer->close();
(1)
When I am running above code, I get the following error message:
Warning: rmdir(C:\WINDOWS\TEMP/xlsx560f58d588ceb): Permission denied in
C:\wamp\www\1300.revenue.com.my\public_html\spoutmaster\src\Spout\Common\Helper\FileSystemHelper.php on line 113
What is the errors means and how should I modify it to prevent this error message appeared?
(2) I want to make expected output like below:
But I didn't know how to write it on [X] part. How to write it in order to get the expected output?
It looks like the default temp folder used to generate the XLSX file cannot be deleted. You can verify it by checking the permissions on C:\WINDOWS\TEMP/xlsx560f58d588ceb.
To solve this issue, you can either manually fix the permissions on the temp folder (C:\WINDOWS\TEMP) or use another temporary folder, as specified here: https://github.com/box/spout#using-custom-temporary-folder
Regarding 2), there is no straightforward way to do this whith Spout. Spout does not support merging cells. The only thing you can do is:
| 1 | 2 | | 3 | |
|---|---|---|---|---|
| | A | B | A | B |
|---|---|---|---|---|
Or alternatively (if that makes more sense):
| 1 | 2 | 2 | 3 | 3 |
|---|---|---|---|---|
| 1 | A | B | A | B |
|---|---|---|---|---|
Either way, you'll have to format the rows as shown above: [[1,2,'',3',''], ['', 'A','B','A','B']] or [[1,2,2,3,3], [1, 'A','B','A','B']]

Mysql string check on equals is false for the same values

I have a problem with MySql
I have a table with parsed informations from websites. A strange string interpretation appear:
the query
select id, address from pagesjaunes_test where address = substr(address,1,length(address)-1)
return a set of values instead of none
at beginning I executed functions as:
address = replace(address, '\n', '')
address = replace(address, '\t', '')
address = replace(address, '\r', '')
address = replace(address, '\r\n', '')
address = trim(address)
but the problem still persist.
Values of field 'address' have some french chars , but the query returned also values that contains only alfanumeric english chars.
Another test: I tried to check the length of strings and ... the strlen() from PHP and LENGTH() from MYSQL display different results! Somewhere difference is by 2 chars, somewhere by 1 character without a specific "rule".
Visual I can't see any space or tabs or something else.
After I modified an address manualy(I deleted all string and I wrote it again), the problem is solved, but I have ~ 6000 values, so this is not a solution :)
What can be the problem?
I suppose that strings can have something as an "empty char", but how to detect and remove it?
Thanks
P.S.
the problem is not just length. I need to join this table with other one and using a condition that check if values from fields 'address' are equals. Even if the fields have the same collation and tables have the same collation, query returns that no addresses match
E.g.
For query:
SELECT p.address,char_length(p.address) , r.address, char_length(r.address)
FROM `pagesjaunes_test` p
LEFT JOIN restaurants r on p.name=r.name
WHERE
p.postal_code=r.postal_code
and p.address!=r.address
and p.phone=''
and p.cuisines=''
LIMIT 10
So: p.address!=r.address
The result is:
+-------------------------------------+------------------------+--------------------------+------------------------+
| address | char_length(p.address) | address | char_length(r.address) |
+-------------------------------------+------------------------+--------------------------+------------------------+
| Dupin Marc13 quai Grands Augustins | 34 | 13 quai Grands Augustins | 24 |
| 39 r Montpensier | 16 | 39 r Montpensier | 16 |
| 8 r Lord Byron | 14 | 3 r Balzac | 10 |
| 162 r Vaugirard | 15 | 162 r Vaugirard | 15 |
| 32 r Goutte d'Or | 16 | 32 r Goutte d'Or | 16 |
| 2 r Casimir Périer | 18 | 2 r Casimir Périer | 18 |
| 20 r Saussier Leroy | 19 | 20 r Saussier Leroy | 19 |
| Senes Douglas22 r Greneta | 25 | 22 r Greneta | 12 |
| Ngov Ly Mey44 r Tolbiac | 23 | 44 r Tolbiac | 12 |
| 33 r N-D de Nazareth | 20 | 33 r N-D de Nazareth | 20 |
+-------------------------------------+------------------------+--------------------------+------------------------+
As you see, "162 r Vaugirard", "20 r Saussier Leroy" contains only ASCII chars, have the same length but aren't equals!
Maybe have a look at the encoding of the mysql text fields - UTF8 encodes most of its characters with 2 bytes - only a small subset of UTF8 (ASCII characters for example) get encoded with one byte.
MySQL knows UTF8 and counts right.
PHP text functions aren't UTF8 aware and count the bytes itself.
So if PHP counts more than MYSQL, this is probably the cause and you could have a look at utf8decode.
br from Salzburg!
The official documentation says:
Returns the length of the string str, measured in bytes. A multi-byte character counts as multiple bytes. This means that for a string containing five two-byte characters, LENGTH() returns 10, whereas CHAR_LENGTH() returns 5.
So, use CHAR_LENGTH instead :)
select id, address from pagesjaunes_test
where address = substr(address, 1, char_length(address) - 1)
Finally, I found the problem. After changed collation to ascii_general_ci all non-ascii chars was transformed to "?". Some spaces also was replaced with "?". After check initial values, function ORD() from MySQL returned 160 (instead of 32) for these spaces. So,
UPDATE pagesjaunes_test SET address = TRIM(REPLACE(REPLACE(address, CHAR(160), ' '), ' ',' ')
resolved my question.

Categories