Native PHP function for hex string representation? - php

Is there a native PHP function for converting a string to its HEX representation that can be then eval()'d as a string, such as:
"ABC" => "\x41\x42\x43"
I know it can be done in several steps, I'm just wondering if I'm unnecessarily complicating something that could be done with a single, native function?

There is no native function, but you can use hex2bin to eval PHP code which is in hex, e.g.
eval(hex2bin("6563686f20706928293b")); # Output: 3.14159265359
eval(hex2bin(bin2hex("echo pi();"))); # Behind the scenes.
Or you can call the function which name is in hex string:
$ echo '<?$_=hex2bin(7069);die($_());' | php # 7069 = pi
3.14159265359

There is no built in PHP function to achieve the same results, but with some PHP-Fu you can do it in one line:
$str = 'ABC';
$str = '\x'.implode('\x', str_split(bin2hex($str), 2));
echo $str; // \x41\x42\x43
There is also a piece of code in the PHP docs which gives exactly the same results:
$str = 'ABC';
$field=bin2hex($str);
$field=chunk_split($field,2,"\\x");
$field= "\\x" . substr($field,0,-2);
echo $field; // \x41\x42\x43

$n = 1234;
echo printf("%x", $n); //should return the hexadecimal format of the number

Why don't you try something like this?
function strtohex($string) {
if (!empty($string)) {
$output = null;
$count = strlen($string, "UTF-8");
for ($i = 0; $i < $count; $i++) {
$output .= dechex(ord($string[$i]));
}
return $output;
}
}
For one-liners, the only alternative I can think about is the bin2hex() function, which is native in PHP and it offers the same.

Related

I want to convert alphabets to numbers, in this way: A=0, B=1, C=2... Z=25 using php

I want to write a function that take input as a string and convert alphabets to number and returns the converted numbers, in this way: A(a)=1, B(b)=2, C(c)=3... Z(z)=25 using php
thanks in advance
First, we make everything lowercase.
Then, using the ord function, we get the ascii code, and then substract 'a' from it.
function one_char_map($chr)
{
$chr=strtolower($chr);
return ord($chr)-ord('a');
}
function string_map($str)
{
return implode(array_map('one_char_map',str_split($str)));
}
echo string_map('abcD');//0123
Please try this :
function conv($alph=null){
return (!is_null($alph)?strpos("abcdefghijklmnopqrstuvwxyz", $alph):"Need String");
}
echo "<br /><br />";
echo conv("a");
EDIT :
$str = "abcDefghZ";
$out = "";
for($i=0;$i<strlen($str);$i++){
$out .= conv(strtolower($str[$i]));
}
echo $str."<br />".$out;
If you trying to roll your own hash function: DONT.
If you need to accept other characters from ASCII, use PHP's ord() function.
Try this:
This function returns the position, and optionally accepts a base integer to shift the numbers if you need to.
function alpha_ord($str, $base = 0) {
$pos = stripos(
'abcdefghijklmnopqrstuvwxyz',
$str{0}
);
if ($pos !== FALSE) {
$pos += $base;
}
return $pos;
}
print alpha_ord('A'); // 0
print alpha_ord('Z', 1); // 26
print alpha_ord('Z'); // 25
print alpha_ord('A', 65); // 65

PHP Update array index/key [duplicate]

If I had:
$string = "PascalCase";
I need
"pascal_case"
Does PHP offer a function for this purpose?
A shorter solution: Similar to the editor's one with a simplified regular expression and fixing the "trailing-underscore" problem:
$output = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $input));
PHP Demo |
Regex Demo
Note that cases like SimpleXML will be converted to simple_x_m_l using the above solution. That can also be considered a wrong usage of camel case notation (correct would be SimpleXml) rather than a bug of the algorithm since such cases are always ambiguous - even by grouping uppercase characters to one string (simple_xml) such algorithm will always fail in other edge cases like XMLHTMLConverter or one-letter words near abbreviations, etc. If you don't mind about the (rather rare) edge cases and want to handle SimpleXML correctly, you can use a little more complex solution:
$output = ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $input)), '_');
PHP Demo |
Regex Demo
Try this on for size:
$tests = array(
'simpleTest' => 'simple_test',
'easy' => 'easy',
'HTML' => 'html',
'simpleXML' => 'simple_xml',
'PDFLoad' => 'pdf_load',
'startMIDDLELast' => 'start_middle_last',
'AString' => 'a_string',
'Some4Numbers234' => 'some4_numbers234',
'TEST123String' => 'test123_string',
);
foreach ($tests as $test => $result) {
$output = from_camel_case($test);
if ($output === $result) {
echo "Pass: $test => $result\n";
} else {
echo "Fail: $test => $result [$output]\n";
}
}
function from_camel_case($input) {
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}
Output:
Pass: simpleTest => simple_test
Pass: easy => easy
Pass: HTML => html
Pass: simpleXML => simple_xml
Pass: PDFLoad => pdf_load
Pass: startMIDDLELast => start_middle_last
Pass: AString => a_string
Pass: Some4Numbers234 => some4_numbers234
Pass: TEST123String => test123_string
This implements the following rules:
A sequence beginning with a lowercase letter must be followed by lowercase letters and digits;
A sequence beginning with an uppercase letter can be followed by either:
one or more uppercase letters and digits (followed by either the end of the string or an uppercase letter followed by a lowercase letter or digit ie the start of the next sequence); or
one or more lowercase letters or digits.
A concise solution and can handle some tricky use cases:
function decamelize($string) {
return strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $string));
}
Can handle all these cases:
simpleTest => simple_test
easy => easy
HTML => html
simpleXML => simple_xml
PDFLoad => pdf_load
startMIDDLELast => start_middle_last
AString => a_string
Some4Numbers234 => some4_numbers234
TEST123String => test123_string
hello_world => hello_world
hello__world => hello__world
_hello_world_ => _hello_world_
hello_World => hello_world
HelloWorld => hello_world
helloWorldFoo => hello_world_foo
hello-world => hello-world
myHTMLFiLe => my_html_fi_le
aBaBaB => a_ba_ba_b
BaBaBa => ba_ba_ba
libC => lib_c
You can test this function here: http://syframework.alwaysdata.net/decamelize
The Symfony Serializer Component has a CamelCaseToSnakeCaseNameConverter that has two methods normalize() and denormalize(). These can be used as follows:
$nameConverter = new CamelCaseToSnakeCaseNameConverter();
echo $nameConverter->normalize('camelCase');
// outputs: camel_case
echo $nameConverter->denormalize('snake_case');
// outputs: snakeCase
Ported from Ruby's String#camelize and String#decamelize.
function decamelize($word) {
return preg_replace(
'/(^|[a-z])([A-Z])/e',
'strtolower(strlen("\\1") ? "\\1_\\2" : "\\2")',
$word
);
}
function camelize($word) {
return preg_replace('/(^|_)([a-z])/e', 'strtoupper("\\2")', $word);
}
One trick the above solutions may have missed is the 'e' modifier which causes preg_replace to evaluate the replacement string as PHP code.
Most solutions here feel heavy handed. Here's what I use:
$underscored = strtolower(
preg_replace(
["/([A-Z]+)/", "/_([A-Z]+)([A-Z][a-z])/"],
["_$1", "_$1_$2"],
lcfirst($camelCase)
)
);
"CamelCASE" is converted to "camel_case"
lcfirst($camelCase) will lower the first character (avoids 'CamelCASE' converted output to start with an underscore)
[A-Z] finds capital letters
+ will treat every consecutive uppercase as a word (avoids 'CamelCASE' to be converted to camel_C_A_S_E)
Second pattern and replacement are for ThoseSPECCases -> those_spec_cases instead of those_speccases
strtolower([…]) turns the output to lowercases
php does not offer a built in function for this afaik, but here is what I use
function uncamelize($camel,$splitter="_") {
$camel=preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $splitter.'$0', $camel));
return strtolower($camel);
}
the splitter can be specified in the function call, so you can call it like so
$camelized="thisStringIsCamelized";
echo uncamelize($camelized,"_");
//echoes "this_string_is_camelized"
echo uncamelize($camelized,"-");
//echoes "this-string-is-camelized"
I had a similar problem but couldn't find any answer that satisfies how to convert CamelCase to snake_case, while avoiding duplicate or redundant underscores _ for names with underscores, or all caps abbreviations.
Th problem is as follows:
CamelCaseClass => camel_case_class
ClassName_WithUnderscores => class_name_with_underscore
FAQ => faq
The solution I wrote is a simple two functions call, lowercase and search and replace for consecutive lowercase-uppercase letters:
strtolower(preg_replace("/([a-z])([A-Z])/", "$1_$2", $name));
"CamelCase" to "camel_case":
function camelToSnake($camel)
{
$snake = preg_replace('/[A-Z]/', '_$0', $camel);
$snake = strtolower($snake);
$snake = ltrim($snake, '_');
return $snake;
}
or:
function camelToSnake($camel)
{
$snake = preg_replace_callback('/[A-Z]/', function ($match){
return '_' . strtolower($match[0]);
}, $camel);
return ltrim($snake, '_');
}
If you are looking for a PHP 5.4 version and later answer here is the code:
function decamelize($word) {
return $word = preg_replace_callback(
"/(^|[a-z])([A-Z])/",
function($m) { return strtolower(strlen($m[1]) ? "$m[1]_$m[2]" : "$m[2]"); },
$word
);
}
function camelize($word) {
return $word = preg_replace_callback(
"/(^|_)([a-z])/",
function($m) { return strtoupper("$m[2]"); },
$word
);
}
You need to run a regex through it that matches every uppercase letter except if it is in the beginning and replace it with underscrore plus that letter. An utf-8 solution is this:
header('content-type: text/html; charset=utf-8');
$separated = preg_replace('%(?<!^)\p{Lu}%usD', '_$0', 'AaaaBbbbCcccDdddÁáááŐőőő');
$lower = mb_strtolower($separated, 'utf-8');
echo $lower; //aaaa_bbbb_cccc_dddd_áááá_őőőő
If you are not sure what case your string is, better to check it first, because this code assumes that the input is camelCase instead of underscore_Case or dash-Case, so if the latters have uppercase letters, it will add underscores to them.
The accepted answer from cletus is way too overcomplicated imho and it works only with latin characters. I find it a really bad solution and wonder why it was accepted at all. Converting TEST123String into test123_string is not necessarily a valid requirement. I rather kept it simple and separated ABCccc into a_b_cccc instead of ab_cccc because it does not lose information this way and the backward conversion will give the exact same string we started with. Even if you want to do it the other way it is relative easy to write a regex for it with positive lookbehind (?<!^)\p{Lu}\p{Ll}|(?<=\p{Ll})\p{Lu} or two regexes without lookbehind if you are not a regex expert. There is no need to split it up into substrings not to mention deciding between strtolower and lcfirst where using just strtolower would be completely fine.
Short solution:
$subject = "PascalCase";
echo strtolower(preg_replace('/\B([A-Z])/', '_$1', $subject));
Not fancy at all but simple and speedy as hell:
function uncamelize($str)
{
$str = lcfirst($str);
$lc = strtolower($str);
$result = '';
$length = strlen($str);
for ($i = 0; $i < $length; $i++) {
$result .= ($str[$i] == $lc[$i] ? '' : '_') . $lc[$i];
}
return $result;
}
echo uncamelize('HelloAWorld'); //hello_a_world
A version that doesn't use regex can be found in the Alchitect source:
decamelize($str, $glue='_')
{
$counter = 0;
$uc_chars = '';
$new_str = array();
$str_len = strlen($str);
for ($x=0; $x<$str_len; ++$x)
{
$ascii_val = ord($str[$x]);
if ($ascii_val >= 65 && $ascii_val <= 90)
{
$uc_chars .= $str[$x];
}
}
$tok = strtok($str, $uc_chars);
while ($tok !== false)
{
$new_char = chr(ord($uc_chars[$counter]) + 32);
$new_str[] = $new_char . $tok;
$tok = strtok($uc_chars);
++$counter;
}
return implode($new_str, $glue);
}
So here is a one-liner:
strtolower(preg_replace('/(?|([a-z\d])([A-Z])|([^\^])([A-Z][a-z]))/', '$1_$2', $string));
danielstjules/Stringy provieds a method to convert string from camelcase to snakecase.
s('TestUCase')->underscored(); // 'test_u_case'
Laravel 5.6 provides a very simple way of doing this:
/**
* Convert a string to snake case.
*
* #param string $value
* #param string $delimiter
* #return string
*/
public static function snake($value, $delimiter = '_'): string
{
if (!ctype_lower($value)) {
$value = strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
}
return $value;
}
What it does: if it sees that there is at least one capital letter in the given string, it uses a positive lookahead to search for any character (.) followed by a capital letter ((?=[A-Z])). It then replaces the found character with it's value followed by the separactor _.
If you are not using Composer for PHP you are wasting your time.
composer require doctrine/inflector
use Doctrine\Inflector\InflectorFactory;
// Couple ways to get class name:
// If inside a parent class
$class_name = get_called_class();
// Or just inside the class
$class_name = get_class();
// Or straight get a class name
$class_name = MyCustomClass::class;
// Or, of course, a string
$class_name = 'App\Libs\MyCustomClass';
// Take the name down to the base name:
$class_name = end(explode('\\', $class_name)));
$inflector = InflectorFactory::create()->build();
$inflector->tableize($class_name); // my_custom_class
https://github.com/doctrine/inflector/blob/master/docs/en/index.rst
Use Symfony String
composer require symfony/string
use function Symfony\Component\String\u;
u($string)->snake()->toString()
The direct port from rails (minus their special handling for :: or acronyms) would be
function underscore($word){
$word = preg_replace('#([A-Z\d]+)([A-Z][a-z])#','\1_\2', $word);
$word = preg_replace('#([a-z\d])([A-Z])#', '\1_\2', $word);
return strtolower(strtr($word, '-', '_'));
}
Knowing PHP, this will be faster than the manual parsing that's happening in other answers given here. The disadvantage is that you don't get to chose what to use as a separator between words, but that wasn't part of the question.
Also check the relevant rails source code
Note that this is intended for use with ASCII identifiers. If you need to do this with characters outside of the ASCII range, use the '/u' modifier for preg_matchand use mb_strtolower.
Here is my contribution to a six-year-old question with god knows how many answers...
It will convert all words in the provided string that are in camelcase to snakecase. For example "SuperSpecialAwesome and also FizBuzz καιΚάτιΑκόμα" will be converted to "super_special_awesome and also fizz_buzz και_κάτι_ακόμα".
mb_strtolower(
preg_replace_callback(
'/(?<!\b|_)\p{Lu}/u',
function ($a) {
return "_$a[0]";
},
'SuperSpecialAwesome'
)
);
Yii2 have the different function to make the word snake_case from CamelCase.
/**
* Converts any "CamelCased" into an "underscored_word".
* #param string $words the word(s) to underscore
* #return string
*/
public static function underscore($words)
{
return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words));
}
This is one of shorter ways:
function camel_to_snake($input)
{
return strtolower(ltrim(preg_replace('/([A-Z])/', '_\\1', $input), '_'));
}
function camel2snake($name) {
$str_arr = str_split($name);
foreach ($str_arr as $k => &$v) {
if (ord($v) >= 64 && ord($v) <= 90) { // A = 64; Z = 90
$v = strtolower($v);
$v = ($k != 0) ? '_'.$v : $v;
}
}
return implode('', $str_arr);
}
The worst answer on here was so close to being the best(use a framework). NO DON'T, just take a look at the source code. seeing what a well established framework uses would be a far more reliable approach(tried and tested). The Zend framework has some word filters which fit your needs. Source.
here is a couple of methods I adapted from the source.
function CamelCaseToSeparator($value,$separator = ' ')
{
if (!is_scalar($value) && !is_array($value)) {
return $value;
}
if (defined('PREG_BAD_UTF8_OFFSET_ERROR') && preg_match('/\pL/u', 'a') == 1) {
$pattern = ['#(?<=(?:\p{Lu}))(\p{Lu}\p{Ll})#', '#(?<=(?:\p{Ll}|\p{Nd}))(\p{Lu})#'];
$replacement = [$separator . '\1', $separator . '\1'];
} else {
$pattern = ['#(?<=(?:[A-Z]))([A-Z]+)([A-Z][a-z])#', '#(?<=(?:[a-z0-9]))([A-Z])#'];
$replacement = ['\1' . $separator . '\2', $separator . '\1'];
}
return preg_replace($pattern, $replacement, $value);
}
function CamelCaseToUnderscore($value){
return CamelCaseToSeparator($value,'_');
}
function CamelCaseToDash($value){
return CamelCaseToSeparator($value,'-');
}
$string = CamelCaseToUnderscore("CamelCase");
There is a library providing this functionality:
SnakeCaseFormatter::run('CamelCase'); // Output: "camel_case"
If you use Laravel framework, you can use just snake_case() method.
How to de-camelize without using regex:
function decamelize($str, $glue = '_') {
$capitals = [];
$replace = [];
foreach(str_split($str) as $index => $char) {
if(!ctype_upper($char)) {
continue;
}
$capitals[] = $char;
$replace[] = ($index > 0 ? $glue : '') . strtolower($char);
}
if(count($capitals) > 0) {
return str_replace($capitals, $replace, $str);
}
return $str;
}
An edit:
How would I do that in 2019:
PHP 7.3 and before:
function toSnakeCase($str, $glue = '_') {
return ltrim(
preg_replace_callback('/[A-Z]/', function ($matches) use ($glue) {
return $glue . strtolower($matches[0]);
}, $str),
$glue
);
}
And with PHP 7.4+:
function toSnakeCase($str, $glue = '_') {
return ltrim(preg_replace_callback('/[A-Z]/', fn($matches) => $glue . strtolower($matches[0]), $str), $glue);
}
If you're using the Laravel framework, a simpler built-in method exists:
$converted = Str::snake('fooBar'); // -> foo_bar
See documentation here:
https://laravel.com/docs/9.x/helpers#method-snake-case
The open source TurboCommons library contains a general purpose formatCase() method inside the StringUtils class, which lets you convert a string to lots of common case formats, like CamelCase, UpperCamelCase, LowerCamelCase, snake_case, Title Case, and many more.
https://github.com/edertone/TurboCommons
To use it, import the phar file to your project and:
use org\turbocommons\src\main\php\utils\StringUtils;
echo StringUtils::formatCase('camelCase', StringUtils::FORMAT_SNAKE_CASE);
// will output 'camel_Case'

PHP string challenge

I have an string such as
$string = "This is my test string {ABC}. This is test {XYZ}. I am new for PHP {PHP}".
Now I need to replace occurrence of string within {}, in such a way that output will be:
This is my test string {ABC 1}. This is test {XYZ 2}. I am new for PHP {PHP 3}".
I am looking to resolve this with recursive function but not getting expected result.
$i = 1;
echo preg_replace_callback('/\{(.+?)\}/', function (array $match) use (&$i) {
return sprintf('{%s %d}', $match[1], $i++);
}, $string);
The "trick" is to simply keep an external counter running, here $i, which is used in the anonymous callback via use (&$i).
There is no recursion here. Simply counting.
$result = preg_replace_callback("/\{([^}]*+)\}/",function($m) {
static $count = 0;
$count++;
return "{".$m[1]." ".$count."}";
},$string);
If you really need recursive :^ )
$string = "This is my test string {ABC}. This is test {XYZ}. I am new for PHP {PHP}";
function my_replace($string, $count = 1)
{
if ($string)
{
return preg_replace_callback('/\{(.+?)\}(.*)$/', function (array $match) use ($count)
{
return sprintf('{%s %d} %s', $match[1], $count, my_replace($match[2], $count + 1));
}, $string, 1);
}
}
echo my_replace($string);

PHP or Regex: string matches ALL characters in search pattern

I need to build a regex that will look for the occurrence of all characters in an inputted string.
For example, if the user inputs "equ" as the search parameter, "queen" and "obsequious" would match, but "qadaffi" and "tour" and "quail" would not.
Obviously I'm trying the basic /[equ]/ pattern and it's looking for "at least one of".
If there's a basic PHP function that would do this without regex, then that would be acceptable. But sad.
/[equ]/ is a character class which means it matches just one character. Try /.*equ.*/ instead. I haven't used the php matching functions, so the .*'s might be unnecessary.
Edit: Apparently they're definitely unnecessary, so just use /equ/.
yeah, agreed that simple for loop would be more efficient in your case.
assuming $query = "que"; and $input = "queen"; or anything else:
$matched = true;
$len = strlen($query); // or mb_strlen($query) if you have multibyte string in input
for ($i = 0; $i < $len; $i++){
if (!strstr($input, $query[$i])){
$matched = false;
break;
}
}
very primitive loop to begin with.
#sln
#jancha
I've implemented a timer to measure the speeds. Oddly, I'm finding that the regex is faster than the loop in my code. Is this right?
$haystack = "Obsequious";
$needle = array('e','q','u');
$regex = "/^(?=.*e)(?=.*q)(?=.*u)/";
function trial(){
GLOBAL $haystack;
GLOBAL $needle;
foreach ($needle as $n) {
if (!strpos($haystack, $n)) return false;
}
return true;
}
function trial2(){
GLOBAL $haystack;
GLOBAL $regex;
if (preg_match($regex, $haystack)) {
return true;
}
return false;
}
print time_trial("trial");
print time_trial("trial2");
function time_trial($function, $iterations=100000){
$before = microtime(true);
for ($i=0 ; $i<$iterations ; $i++) {
call_user_func($function);
}
$after = microtime(true);
$total = round($after-$before, 4);
return "Executed timed trial '$function' // $iterations iterations // $total seconds<br />\n";
}
Probably a regex is not the tool for this job. Not a php expert, but loop through each required character checking that it exists in the object string.
Otherwise, using a regex does about the same, but its slow and overkill:
/^(?=.*e)(?=.*q)(?=.*u)/

How to convert PascalCase to snake_case?

If I had:
$string = "PascalCase";
I need
"pascal_case"
Does PHP offer a function for this purpose?
A shorter solution: Similar to the editor's one with a simplified regular expression and fixing the "trailing-underscore" problem:
$output = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $input));
PHP Demo |
Regex Demo
Note that cases like SimpleXML will be converted to simple_x_m_l using the above solution. That can also be considered a wrong usage of camel case notation (correct would be SimpleXml) rather than a bug of the algorithm since such cases are always ambiguous - even by grouping uppercase characters to one string (simple_xml) such algorithm will always fail in other edge cases like XMLHTMLConverter or one-letter words near abbreviations, etc. If you don't mind about the (rather rare) edge cases and want to handle SimpleXML correctly, you can use a little more complex solution:
$output = ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $input)), '_');
PHP Demo |
Regex Demo
Try this on for size:
$tests = array(
'simpleTest' => 'simple_test',
'easy' => 'easy',
'HTML' => 'html',
'simpleXML' => 'simple_xml',
'PDFLoad' => 'pdf_load',
'startMIDDLELast' => 'start_middle_last',
'AString' => 'a_string',
'Some4Numbers234' => 'some4_numbers234',
'TEST123String' => 'test123_string',
);
foreach ($tests as $test => $result) {
$output = from_camel_case($test);
if ($output === $result) {
echo "Pass: $test => $result\n";
} else {
echo "Fail: $test => $result [$output]\n";
}
}
function from_camel_case($input) {
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}
Output:
Pass: simpleTest => simple_test
Pass: easy => easy
Pass: HTML => html
Pass: simpleXML => simple_xml
Pass: PDFLoad => pdf_load
Pass: startMIDDLELast => start_middle_last
Pass: AString => a_string
Pass: Some4Numbers234 => some4_numbers234
Pass: TEST123String => test123_string
This implements the following rules:
A sequence beginning with a lowercase letter must be followed by lowercase letters and digits;
A sequence beginning with an uppercase letter can be followed by either:
one or more uppercase letters and digits (followed by either the end of the string or an uppercase letter followed by a lowercase letter or digit ie the start of the next sequence); or
one or more lowercase letters or digits.
A concise solution and can handle some tricky use cases:
function decamelize($string) {
return strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $string));
}
Can handle all these cases:
simpleTest => simple_test
easy => easy
HTML => html
simpleXML => simple_xml
PDFLoad => pdf_load
startMIDDLELast => start_middle_last
AString => a_string
Some4Numbers234 => some4_numbers234
TEST123String => test123_string
hello_world => hello_world
hello__world => hello__world
_hello_world_ => _hello_world_
hello_World => hello_world
HelloWorld => hello_world
helloWorldFoo => hello_world_foo
hello-world => hello-world
myHTMLFiLe => my_html_fi_le
aBaBaB => a_ba_ba_b
BaBaBa => ba_ba_ba
libC => lib_c
You can test this function here: http://syframework.alwaysdata.net/decamelize
The Symfony Serializer Component has a CamelCaseToSnakeCaseNameConverter that has two methods normalize() and denormalize(). These can be used as follows:
$nameConverter = new CamelCaseToSnakeCaseNameConverter();
echo $nameConverter->normalize('camelCase');
// outputs: camel_case
echo $nameConverter->denormalize('snake_case');
// outputs: snakeCase
Ported from Ruby's String#camelize and String#decamelize.
function decamelize($word) {
return preg_replace(
'/(^|[a-z])([A-Z])/e',
'strtolower(strlen("\\1") ? "\\1_\\2" : "\\2")',
$word
);
}
function camelize($word) {
return preg_replace('/(^|_)([a-z])/e', 'strtoupper("\\2")', $word);
}
One trick the above solutions may have missed is the 'e' modifier which causes preg_replace to evaluate the replacement string as PHP code.
Most solutions here feel heavy handed. Here's what I use:
$underscored = strtolower(
preg_replace(
["/([A-Z]+)/", "/_([A-Z]+)([A-Z][a-z])/"],
["_$1", "_$1_$2"],
lcfirst($camelCase)
)
);
"CamelCASE" is converted to "camel_case"
lcfirst($camelCase) will lower the first character (avoids 'CamelCASE' converted output to start with an underscore)
[A-Z] finds capital letters
+ will treat every consecutive uppercase as a word (avoids 'CamelCASE' to be converted to camel_C_A_S_E)
Second pattern and replacement are for ThoseSPECCases -> those_spec_cases instead of those_speccases
strtolower([…]) turns the output to lowercases
php does not offer a built in function for this afaik, but here is what I use
function uncamelize($camel,$splitter="_") {
$camel=preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $splitter.'$0', $camel));
return strtolower($camel);
}
the splitter can be specified in the function call, so you can call it like so
$camelized="thisStringIsCamelized";
echo uncamelize($camelized,"_");
//echoes "this_string_is_camelized"
echo uncamelize($camelized,"-");
//echoes "this-string-is-camelized"
I had a similar problem but couldn't find any answer that satisfies how to convert CamelCase to snake_case, while avoiding duplicate or redundant underscores _ for names with underscores, or all caps abbreviations.
Th problem is as follows:
CamelCaseClass => camel_case_class
ClassName_WithUnderscores => class_name_with_underscore
FAQ => faq
The solution I wrote is a simple two functions call, lowercase and search and replace for consecutive lowercase-uppercase letters:
strtolower(preg_replace("/([a-z])([A-Z])/", "$1_$2", $name));
"CamelCase" to "camel_case":
function camelToSnake($camel)
{
$snake = preg_replace('/[A-Z]/', '_$0', $camel);
$snake = strtolower($snake);
$snake = ltrim($snake, '_');
return $snake;
}
or:
function camelToSnake($camel)
{
$snake = preg_replace_callback('/[A-Z]/', function ($match){
return '_' . strtolower($match[0]);
}, $camel);
return ltrim($snake, '_');
}
If you are looking for a PHP 5.4 version and later answer here is the code:
function decamelize($word) {
return $word = preg_replace_callback(
"/(^|[a-z])([A-Z])/",
function($m) { return strtolower(strlen($m[1]) ? "$m[1]_$m[2]" : "$m[2]"); },
$word
);
}
function camelize($word) {
return $word = preg_replace_callback(
"/(^|_)([a-z])/",
function($m) { return strtoupper("$m[2]"); },
$word
);
}
You need to run a regex through it that matches every uppercase letter except if it is in the beginning and replace it with underscrore plus that letter. An utf-8 solution is this:
header('content-type: text/html; charset=utf-8');
$separated = preg_replace('%(?<!^)\p{Lu}%usD', '_$0', 'AaaaBbbbCcccDdddÁáááŐőőő');
$lower = mb_strtolower($separated, 'utf-8');
echo $lower; //aaaa_bbbb_cccc_dddd_áááá_őőőő
If you are not sure what case your string is, better to check it first, because this code assumes that the input is camelCase instead of underscore_Case or dash-Case, so if the latters have uppercase letters, it will add underscores to them.
The accepted answer from cletus is way too overcomplicated imho and it works only with latin characters. I find it a really bad solution and wonder why it was accepted at all. Converting TEST123String into test123_string is not necessarily a valid requirement. I rather kept it simple and separated ABCccc into a_b_cccc instead of ab_cccc because it does not lose information this way and the backward conversion will give the exact same string we started with. Even if you want to do it the other way it is relative easy to write a regex for it with positive lookbehind (?<!^)\p{Lu}\p{Ll}|(?<=\p{Ll})\p{Lu} or two regexes without lookbehind if you are not a regex expert. There is no need to split it up into substrings not to mention deciding between strtolower and lcfirst where using just strtolower would be completely fine.
Short solution:
$subject = "PascalCase";
echo strtolower(preg_replace('/\B([A-Z])/', '_$1', $subject));
Not fancy at all but simple and speedy as hell:
function uncamelize($str)
{
$str = lcfirst($str);
$lc = strtolower($str);
$result = '';
$length = strlen($str);
for ($i = 0; $i < $length; $i++) {
$result .= ($str[$i] == $lc[$i] ? '' : '_') . $lc[$i];
}
return $result;
}
echo uncamelize('HelloAWorld'); //hello_a_world
A version that doesn't use regex can be found in the Alchitect source:
decamelize($str, $glue='_')
{
$counter = 0;
$uc_chars = '';
$new_str = array();
$str_len = strlen($str);
for ($x=0; $x<$str_len; ++$x)
{
$ascii_val = ord($str[$x]);
if ($ascii_val >= 65 && $ascii_val <= 90)
{
$uc_chars .= $str[$x];
}
}
$tok = strtok($str, $uc_chars);
while ($tok !== false)
{
$new_char = chr(ord($uc_chars[$counter]) + 32);
$new_str[] = $new_char . $tok;
$tok = strtok($uc_chars);
++$counter;
}
return implode($new_str, $glue);
}
So here is a one-liner:
strtolower(preg_replace('/(?|([a-z\d])([A-Z])|([^\^])([A-Z][a-z]))/', '$1_$2', $string));
danielstjules/Stringy provieds a method to convert string from camelcase to snakecase.
s('TestUCase')->underscored(); // 'test_u_case'
Laravel 5.6 provides a very simple way of doing this:
/**
* Convert a string to snake case.
*
* #param string $value
* #param string $delimiter
* #return string
*/
public static function snake($value, $delimiter = '_'): string
{
if (!ctype_lower($value)) {
$value = strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
}
return $value;
}
What it does: if it sees that there is at least one capital letter in the given string, it uses a positive lookahead to search for any character (.) followed by a capital letter ((?=[A-Z])). It then replaces the found character with it's value followed by the separactor _.
If you are not using Composer for PHP you are wasting your time.
composer require doctrine/inflector
use Doctrine\Inflector\InflectorFactory;
// Couple ways to get class name:
// If inside a parent class
$class_name = get_called_class();
// Or just inside the class
$class_name = get_class();
// Or straight get a class name
$class_name = MyCustomClass::class;
// Or, of course, a string
$class_name = 'App\Libs\MyCustomClass';
// Take the name down to the base name:
$class_name = end(explode('\\', $class_name)));
$inflector = InflectorFactory::create()->build();
$inflector->tableize($class_name); // my_custom_class
https://github.com/doctrine/inflector/blob/master/docs/en/index.rst
Use Symfony String
composer require symfony/string
use function Symfony\Component\String\u;
u($string)->snake()->toString()
The direct port from rails (minus their special handling for :: or acronyms) would be
function underscore($word){
$word = preg_replace('#([A-Z\d]+)([A-Z][a-z])#','\1_\2', $word);
$word = preg_replace('#([a-z\d])([A-Z])#', '\1_\2', $word);
return strtolower(strtr($word, '-', '_'));
}
Knowing PHP, this will be faster than the manual parsing that's happening in other answers given here. The disadvantage is that you don't get to chose what to use as a separator between words, but that wasn't part of the question.
Also check the relevant rails source code
Note that this is intended for use with ASCII identifiers. If you need to do this with characters outside of the ASCII range, use the '/u' modifier for preg_matchand use mb_strtolower.
Here is my contribution to a six-year-old question with god knows how many answers...
It will convert all words in the provided string that are in camelcase to snakecase. For example "SuperSpecialAwesome and also FizBuzz καιΚάτιΑκόμα" will be converted to "super_special_awesome and also fizz_buzz και_κάτι_ακόμα".
mb_strtolower(
preg_replace_callback(
'/(?<!\b|_)\p{Lu}/u',
function ($a) {
return "_$a[0]";
},
'SuperSpecialAwesome'
)
);
Yii2 have the different function to make the word snake_case from CamelCase.
/**
* Converts any "CamelCased" into an "underscored_word".
* #param string $words the word(s) to underscore
* #return string
*/
public static function underscore($words)
{
return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words));
}
This is one of shorter ways:
function camel_to_snake($input)
{
return strtolower(ltrim(preg_replace('/([A-Z])/', '_\\1', $input), '_'));
}
function camel2snake($name) {
$str_arr = str_split($name);
foreach ($str_arr as $k => &$v) {
if (ord($v) >= 64 && ord($v) <= 90) { // A = 64; Z = 90
$v = strtolower($v);
$v = ($k != 0) ? '_'.$v : $v;
}
}
return implode('', $str_arr);
}
The worst answer on here was so close to being the best(use a framework). NO DON'T, just take a look at the source code. seeing what a well established framework uses would be a far more reliable approach(tried and tested). The Zend framework has some word filters which fit your needs. Source.
here is a couple of methods I adapted from the source.
function CamelCaseToSeparator($value,$separator = ' ')
{
if (!is_scalar($value) && !is_array($value)) {
return $value;
}
if (defined('PREG_BAD_UTF8_OFFSET_ERROR') && preg_match('/\pL/u', 'a') == 1) {
$pattern = ['#(?<=(?:\p{Lu}))(\p{Lu}\p{Ll})#', '#(?<=(?:\p{Ll}|\p{Nd}))(\p{Lu})#'];
$replacement = [$separator . '\1', $separator . '\1'];
} else {
$pattern = ['#(?<=(?:[A-Z]))([A-Z]+)([A-Z][a-z])#', '#(?<=(?:[a-z0-9]))([A-Z])#'];
$replacement = ['\1' . $separator . '\2', $separator . '\1'];
}
return preg_replace($pattern, $replacement, $value);
}
function CamelCaseToUnderscore($value){
return CamelCaseToSeparator($value,'_');
}
function CamelCaseToDash($value){
return CamelCaseToSeparator($value,'-');
}
$string = CamelCaseToUnderscore("CamelCase");
There is a library providing this functionality:
SnakeCaseFormatter::run('CamelCase'); // Output: "camel_case"
If you use Laravel framework, you can use just snake_case() method.
How to de-camelize without using regex:
function decamelize($str, $glue = '_') {
$capitals = [];
$replace = [];
foreach(str_split($str) as $index => $char) {
if(!ctype_upper($char)) {
continue;
}
$capitals[] = $char;
$replace[] = ($index > 0 ? $glue : '') . strtolower($char);
}
if(count($capitals) > 0) {
return str_replace($capitals, $replace, $str);
}
return $str;
}
An edit:
How would I do that in 2019:
PHP 7.3 and before:
function toSnakeCase($str, $glue = '_') {
return ltrim(
preg_replace_callback('/[A-Z]/', function ($matches) use ($glue) {
return $glue . strtolower($matches[0]);
}, $str),
$glue
);
}
And with PHP 7.4+:
function toSnakeCase($str, $glue = '_') {
return ltrim(preg_replace_callback('/[A-Z]/', fn($matches) => $glue . strtolower($matches[0]), $str), $glue);
}
If you're using the Laravel framework, a simpler built-in method exists:
$converted = Str::snake('fooBar'); // -> foo_bar
See documentation here:
https://laravel.com/docs/9.x/helpers#method-snake-case
The open source TurboCommons library contains a general purpose formatCase() method inside the StringUtils class, which lets you convert a string to lots of common case formats, like CamelCase, UpperCamelCase, LowerCamelCase, snake_case, Title Case, and many more.
https://github.com/edertone/TurboCommons
To use it, import the phar file to your project and:
use org\turbocommons\src\main\php\utils\StringUtils;
echo StringUtils::formatCase('camelCase', StringUtils::FORMAT_SNAKE_CASE);
// will output 'camel_Case'

Categories