ramsey / collection Goto Github PK
View Code? Open in Web Editor NEW:card_index_dividers: A PHP library for representing and manipulating collections.
License: MIT License
:card_index_dividers: A PHP library for representing and manipulating collections.
License: MIT License
Refer to composer.json in ramsey/uuid for an example.
An AbstractCollection is extending AbstractArray, so a derivated class from AbstractCollection, like 'Collection' class should behave just as an AbstractArray. But is not because the constructors behave in different ways.
You are defining the AbstractArray constructor as receive an array to populate the object, but in AbstractCollection you define the constructor to receive the type of the collection.
I suggest to you to not break compatibility with AbstractArray and to let the objects be instantiated with an array of certain type by removing the AbstractCollection constructor or move it to Collection class. As you are using collectionType property it would be possible to just extend AbstractCollection and override this property (of changed to protected), so, when the FooCollection class in instantiated it will allow only members of the correct type and when the object is populated then it will enforce the type policy.
Of course this would break the compatibility with your current implementations of AbstractCollection and Collection, depending of what way did you use to set the type policy.
What do you think?
Hi! First and foremost, thanks for such an amazing library! I love it <3
There has been a PR (#87) that was merged past October, but a new release has not been done yet.
I don't want to be a PITA, but could we pretty please get a new revision?
Thank you again! :-D
Implements QueueInterface
.
This should define methods similar to those found on the Java Deque
interface.
Implements DoubleEndedQueueInterface
and extends Queue
.
https://travis-ci.org/ramsey/collection/builds/435792905
$ ./vendor/bin/phpstan analyse src tests --level=4
33/33 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ -------------------------------------------------------------------------
Line src/Map/TypedMap.php
------ -------------------------------------------------------------------------
119 PHPDoc tag @param for parameter $keyType with type mixed is not subtype
of native type string
119 PHPDoc tag @param for parameter $valueType with type mixed is not
subtype of native type string
121 Casting to string something that's already string.
122 Casting to string something that's already string.
------ -------------------------------------------------------------------------
------ -----------------------------------------------------------------------
Line src/QueueInterface.php
------ -----------------------------------------------------------------------
62 PHPDoc tag @return with type mixed is not subtype of native type bool
------ -----------------------------------------------------------------------
[ERROR] Found 5 errors
PhpStorm 2021.2 support for generics in PHP. But when I use foreach above collection PhpStomr does not recognize correct element type.
It is important when I want to change method names.
use Ramsey\Collection\AbstractCollection;
/**
* @extends AbstractCollection<Specific>
*/
class SpecificCollection extends AbstractCollection
{
public function getType(): string
{
return Specific::class;
}
}
class Specific
{
public function __construct(private string $code)
{}
public function getCode(): string
{
return $this->code;
}
}
class Test {
public function test1(): void
{
$collection = new SpecificCollection([new Specific('code1')]);
foreach ($collection as $item) {
$item->getCode(); // IDE do not recognize
}
}
/**
* @param SpecificCollection<Specific> $collection
*/
public function test2(SpecificCollection $collection): void
{
foreach ($collection as $item) {
$item->getCode(); // IDE do not recognize
}
}
/**
* @param SpecificCollection<Specific>|array<Specific> $collection
*/
public function test3(SpecificCollection $collection): void
{
foreach ($collection as $item) {
$item->getCode(); // IDE do not recognize
}
}
public function test4(SpecificCollection $collection): void
{
foreach ($collection as $item) {
/** @var Specific $item */
$item->getCode(); // Only this IDE recognize
}
}
public function test5(SpecificCollection $collection): void
{
foreach ($collection as $item) {
\assert($item instanceof Specific);
$item->getCode(); // Only this IDE recognize
}
}
}
I use in a Project the Collection Class with a Entity as Type. This work really good and is really straightforward.
But i think a Queue (FIFO Principle) would match my needs even better.
Therefore, my consideration of whether it is a good extension for this project.
Is this a benefit for this Project?
AbstractSet::add()
internally invokes AbstractCollection::add()
which forwards to AbstractSet::offsetSet()
. This flow causes AbstractCollection::contains()
to be invoked twice which, for extremely large collections, can significantly impact performance.
$tests = 10000;
$addCollection = new Ramsey\Collection\Set( 'int' );
$offsetSetCollection = new Ramsey\Collection\Set( 'int' );
$addTime = microtime( true );
for( $i = 0; $i < $tests; ++$i )
$addCollection->add( $i );
$addTime = microtime( true ) - $addTime;
$offsetSetTime = microtime( true );
for( $i = 0; $i < $tests; ++$i )
$offsetSetCollection[] = $i;
$offsetSetTime = microtime( true ) - $offsetSetTime;
// ~98% slower
var_dump( ( $addTime - $offsetSetTime ) / $offsetSetTime * 100 );
The library supports map()
on CollectionInterface, but there is no reduce()
.
Add CollectionInterface::reduce()
with the following signature:
/**
* @template TCarry
*
* @param callable(TCarry, T):TCarry $callback A callable to apply to each
* item of the collection to reduce it to a single value.
* @param TCarry|null $initial If provided, this is the initial value provided
* to the callback.
*
* @return TCarry
*/
public function reduce(callable $callback, mixed $initial = null): mixed;
Since this is a change to the interface, it requires a major version bump.
The package fzaninotto/faker is abandoned but Laravel maintainers create fork.
Replace abandoned package fzaninotto/faker with fakerphp/faker
The old package will no longer develop, receive bug fixes and work with PHP8
Replace composer dependency
Use alternative package
"require-dev": {
- "fzaninotto/faker": "^1.5",
+ "fakerphp/faker": "^1.9.1",
"jangregor/phpstan-prophecy": "^0.6",
On our projects we use strictly typed collections for Doctrine. I think it will be nice if AbstractCollection class can implement methods from Doctrine Collection interface.
I suggest to add methods to AbstractCollection and/or AbstractArray that will be compatible with Collection
public function removeElement($element)
public function containsKey($key)
public function get($key)
public function getKeys()
public function getValues()
public function set($key, $value)
public function key()
public function current()
public function next()
public function exists(Closure $p)
public function forAll(Closure $p)
public function partition(Closure $p)
public function indexOf($element)
public function slice($offset, $length = null)
Then user of the library can do something like this.
class ConcreteClassCollection extends Ramsey\Collection\AbstractCollection implements Doctrine\Common\Collections\Collection {
public function getType(): string
{
return ConcreteClass::class;
}
}
Maybe better approach is to add a trait?
class ConcreteClassCollection extends Ramsey\Collection\AbstractCollection implements Doctrine\Common\Collections\Collection {
use DoctrineCollectionTrait;
public function getType(): string
{
return ConcreteClass::class;
}
}
In my PR #44 I got feed back to use global namespace function calls . There are two things we can do here.
Opcode optimizations
For example: \is_array
instead of is_array
.
There is a list of functions that have an opcode benefit from those optimizations.
@see https://github.com/php/php-src/blob/master/Zend/zend_compile.c#L3803
Minor speed advantage without opcode change
Others might only have a minor advantage in speed like: \array_merge
vs array_merge
There is a benchmark comparing the global namespace call vs the function lookup
@see http://veewee.github.io/blog/optimizing-php-performance-by-fq-function-calls/
There a many repositories who already have this implemented.
Like PHP-CS-Fixer (PHP-CS-Fixer/PHP-CS-Fixer#3048)
Which might be a good addition to this repository to simply keep everything in check an even set the global namespace call for functions.
Since I didn't want to change the whole implementation with the mentioned PR I simply propose this here and would also file a PR if necessary.
When installing with composer 2 you get warning:
Package fzaninotto/faker is abandoned, you should avoid using it. No replacement was suggested.
Maybe the solution would be to switch to: https://github.com/fakerphp/faker
as mentioned in https://laravel-news.com/changes-coming-to-php-faker
Depends on #22
I propose adding new methods to AbstractCollection
to allow easy manipulation of the contents. We could then use these methods in place of using for/foreach loops and temporary variables for building new collections of data.
For example, given a class Foo and a typed Collection FooCollection:
Class Foo
{
public $id;
public $name;
public $createdAt;
public function __construct(int $id, string $name, DateTime $createdAt=null)
{
$this->id = $id;
$this->name = $name;
$this->createdAt = $createdAt;
}
}
Class FooCollection extends \Ramsey\Collection\AbstractCollection
{
public function getType()
{
return Foo::class;
}
}
And you have created a collection of those Foos:
$foos = [
new Foo(1, 'bar', new DateTime('now')),
new Foo(2, 'baz', new DateTime('1 week ago')),
new Foo(3, 'fizz', new DateTime('3 months ago')),
new Foo(4, 'buzz', new DateTime('2 days ago')),
];
$fooCollection = new FooCollection($foos);
And you have a method which was passed as an argument a FooCollection
, and you want to sort your collection by the createdAt time, you'd have to do something like this:
Class Bar{
public function doSomething(FooCollection $fooCollection){
$values = $fooCollection->toArray();
usort($values, function($a, $b){
return ($a->createdAt > $b->createdAt);
});
$fooCollection = new FooCollection($values);
//Do something else with the Foos.
}
}
I propose a sort method would be much simpler for users:
Class Bar{
public function doSomething(FooCollection $fooCollection){
$fooCollection->sort('createdAt');
//Do something else with the Foos.
}
}
/**
* Orders the items by a property or return value of the method in the class
* @param string property
* @param string order 'desc'/'asc'
* @return self
*/
sort($property, $order='asc')
$fooCollection->sort('createdAt', 'desc'); //sorts by property
$fooCollection->sort('getName'); //will call the method
/**
* Filters out items using a callback method
* @param function $callback
* @return self
*/
filter($callback)
$foos = [
new Foo(1, 'bar'),
new Foo(2, 'baz'),
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
];
$fooCollection = new FooCollection($foos);
//$fooCollection will now contain only the 'bar' and 'baz' foos.
$fooCollection->filter(function($foo){
return strlen($foo->name) <= 3;
});
/**
* Filters to items where the property or return value of a method equals the given value
* @param string property
* @param string value
* @return self
*/
where($property, $value)
$fooCollection->where('name', 'foo'); //accesses a property
$fooCollection->where('getName', 'foo'); //will call the method
/**
* Applies a callback method to each item
* @param function $callback
* @return self
*/
each($callback)
//Every Foo's name in the collection will be updated to end in '.beta'
$fooCollection->each(function($foo){
$foo->name .= '.beta';
});
/**
* Calculates the difference of two collections by comparing the objects - must be the same type
* @param AbstractCollection
*/
diff($collection)
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(2, 'baz'),
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $diffCollection should contain the 'bar', 'fizz' and 'buzz' Foos - the difference between the two.
$diffCollection = $someFoos->diff($moreFoos);
/**
* Calculates the intersection of two collections by comparing the objects - must be the same type
* @param AbstractCollection
*/
intersect($collection)
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(2, 'baz'),
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $intersectCollection should contain the 'baz' Foo - the intersection between the two.
$intersectCollection = $someFoos->intersect($moreFoos);
/**
* Filters out non-unique values either by comparing the objects, or an optional property/method accessor
* @param string property
*/
unique($property=null)
$foos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz') //Original "baz"
new Foo(3, 'baz'), //a duplicate "baz" with different id
new Foo(4, 'fizz'),
new Foo(5, 'buzz'), //Original "buzz"
new Foo(5, 'buzz'), //Exact copy of "buzz"
]);
// $uniqueFoos will contain 5 foos, the extra "buzz" having been removed.
$uniqueFoos = $uniqueFoos->unique();
// $uniqueFoos will contain 4 foos, the extra "buzz" and "baz" (#3) having been removed
$uniqueFoos = $uniqueFoos->unique('name');
/**
* Merges the two collections - must be the same type
* @param AbstractCollection
*/
merge($collection)
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $allFoos should contain all 4 foos
$allFoos = $someFoos->intersect($moreFoos);
/**
* Merges the two collections - must be the same type
* @param AbstractCollection
*/
merge($collection)
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $allFoos should contain all 4 foos
$allFoos = $someFoos->intersect($moreFoos);
/**
* Wrapper for array_column: returns the values from a single property/method
* @param string property
* @return array
*/
column($property)
$names = $fooCollection->column('name'); //accesses a property
$names = $fooCollection->column('getName'); //will call the method
/**
* Returns the first item in the collection
* @return stdClass
*/
first()
$firstFoo = $fooCollection->first();
In column()
, you can specify either a variable or a function name for the collection to column
with. Given that I am using a typed collection, is there a way for PHPStorm to understand and code complete in this case?
Depends on #14
Yes, when I need to split a CollectionInterface in two, I have to use CollectionInterface::filter twice with opposite callbacks, which is not very efficient.
I found out that Doctrine ArrayCollection is using a partition
method which doesn't itinerate twice in the collection. So it could be very useful to have this method in this project! I can submit a PR if needed.
I am not sure if it is better to implement the method in ArrayInterface or CollectionInterface (and related abstract method of course). What do you think?
Or maybe there is a way to do the same without modifying the project?
Thanks for your work ;)
I'm interested on push the TypedMap
class, is like a Map but keys and values are typed. This would follow the generics mimics of Map<K key, V value>
You could use the concrete class directly:
$map = new TypedMap('string', \My\Foo::class, $data);
$map['foo'] = new \My\Foo();
// this throw an exception since key is not a string
$map[1] = new \My\Foo();
// this throw an exception since value stdClass is not a \My\Foo
$map['baz'] = new \stdClass()
Or extending the AbstractTypedMap:
class FooTypedMap extends AbstractTypedMap
{
public function getKeyType()
{
return "int"
}
public function getValueType()
{
return Foo::class;
}
}
$map = new FooTypedMap([
100 => new Foo(),
200 => new Foo(2),
]);
Question: Should I place the class inside src/Map? Looks like this is the place of Maps since it contains the concrete class NamedParameterMap
.
As this contains the interface, an abstract class and a concrete implementation looks more organized on its own namespace Ramsey\Collection\TypedMap
that points to src/TypedMap
.
Thanks and please advice.
Depends on #14
I don't know this is a bug or a feature, but it looks strange for me.
I wrote a simple test case to reproduce the issue.
<?php
public function testDiffGenericCollection(): void
{
$bar1 = new Bar(1, 'a');
$bar2 = new Bar(2, 'b');
$barCollection1 = new Collection(Bar::class);
$barCollection2 = new Collection(Bar::class);
//Add 1 element to $barCollection1
$barCollection1->add($bar1);
//Add 2 elements to $barCollection2
$barCollection2->add($bar1);
$barCollection2->add($bar2);
$diffCollection = $barCollection1->diff($barCollection2);
$this->assertNotSame($diffCollection, $barCollection1);
$this->assertNotSame($diffCollection, $barCollection2);
$this->assertEquals([$bar2], $diffCollection->toArray());
// Make sure original collections are untouched
$this->assertEquals([$bar1], $barCollection1->toArray());
$this->assertEquals([$bar1, $bar2], $barCollection2->toArray());
}
And that test will fail because of:
1) Ramsey\Collection\Test\CollectionManipulationTest::testDiffGenericCollection
TypeError: Argument 1 passed to Ramsey\Collection\Collection::__construct() must be of the type string, array given, called in collection\src\AbstractCollection.php on line 294
Yes, because inside of AbstractCollection
294 we have this:
return new static(array_merge($diffAtoB, $diffBtoA));
I expect that type of collection will be saved and this test case should pass.
I expect something like this:
<?php
if (static::class === Collection::class) {
return new static(static::getType(), array_merge($diffAtoB, $diffBtoA));
}
return new static(array_merge($diffAtoB, $diffBtoA));
Maybe I'm wrong, but Generic collections should also be able to support
diff, intersect, merge etc. methods.
Using unserialize
without second parameter might can be targeted to use remote code execution.
More details about this here: https://github.com/kalessil/phpinspectionsea/blob/master/docs/security.md#exploiting-unserialize
Since we use the Serializable
interface it won't be possible to provide a second parameter.
So I would suggest two things:
For AbstractArray
we will prevent unserialize
to init any class. By setting allowed_classes
to false
. (Mentioned as a solution in the docs linked above).
In all child classes of AbstractArray
you can override the unserialize
method and set allowed_classes
to the type of the collection. So unserialize
will only initialize the known type of the collection.
For Example:
abstract class AbstractArray implements ArrayInterface
{
...
public function unserialize($serialized)
{
return unserialize($serialized, ['allowed_classes' => false]);
}
...
}
abstract class AbstractCollection extends AbstractArray
{
...
public function unserialize($serialized)
{
return unserialize($serialized, ['allowed_classes' => [$this->getType()]]);
}
...
}
No 😺
Integration with the Data Structure extension Ext Github Ext Docs
This would be a tremendous win for memory efficiency, in particular around the Map/Set Collection, but even more generally if you use a Vector, I would explain some more, but there is a nice medium post that does an even better job than I could here
It also has some nice builtin queues like:
It even has a polyfill (right now, this might go away in 2.0, fair warning)
One other (quick) suggestion, a Doctrine Collections style Collections library based on this, but with added support for Criteria and predefined Doctrine types 😀
Data Structures Extension, as noted.
For context, I deal a lot with shoveling data around from very large databases and I have found the ext-ds a life saver, but sometimes I need some plain PHP style collections (like those offered in this library) and even the short time i have used this library I have found it relatively straightforward to work with.
I'd be happy over the coming weeks to collaborate on this 👍
Just wanted to open the conversation 😄
Refer to https://github.com/phpstan/phpstan
Depends on #14
map
method in AbstractCollection
not working for Collection with type string
https://github.com/ramsey/collection/blob/master/src/AbstractCollection.php#L261
I have StringCollection
class StringCollection extends AbstractCollection
{
public function getType(): string
{
return 'string';
}
}
map
method doesn't work when I want to change elements case.
$stringCollection = new StringCollection(['foo', 'bar']);
$upperStringCollection = $stringCollection->map(function($item) {
return strtoupper($item);
});
print_r($upperStringCollection->toArray());
$upperStringCollection
valueArray
(
[0] => FOO
[1] => BAR
)
$upperStringCollection
valueArray
(
[0] => foo
[1] => bar
)
AbstractCollection::diff()
does not return the symmetric difference of 2 collections if their items are not already sorted using the same logical order.
This is due to the comparator function used in array_udiff which, as per official documentation, is supposed to return -1 ,0 or 1
but it only returns -1 or 0
.
array_udiff uses the comparator function to sort the elements of each array before comparing them to minimize the number of operations.
Create script foo.php
and add the following:
<?php
require_once 'vendor/autoload.php';
###########################
class Bar
{
public $name;
public function __construct(string $name)
{
$this->name = $name;
}
}
class BarCollection extends Ramsey\Collection\AbstractCollection
{
public function getType(): string
{
return Bar::class;
}
}
###########################
$bar1 = new Bar('one');
$bar2 = new Bar('two');
$collection1 = new BarCollection([$bar1]);
$collection2 = new BarCollection([$bar1, $bar2]);
$collection3 = new BarCollection([$bar2, $bar1]);
print_r($collection1->diff($collection2)); // This will print the correct result
echo '-----'.PHP_EOL;
print_r($collection1->diff($collection3)); // This will print the wrong result
Execute the script from a terminal:
$ php foo.php
See output similar to the following:
BarCollection Object
(
[data:protected] => Array
(
[0] => Bar Object
(
[name] => two
)
)
)
-----
BarCollection Object
(
[data:protected] => Array
(
[0] => Bar Object
(
[name] => one
)
[1] => Bar Object
(
[name] => two
)
)
)
BarCollection Object
(
[data:protected] => Array
(
[0] => Bar Object
(
[name] => two
)
)
)
-----
BarCollection Object
(
[data:protected] => Array
(
[0] => Bar Object
(
[name] => two
)
)
)
I'm happy to help with the resolution of this bug. Let me know if you are open to receive PRs.
ramsey/collection requires packages in composer.json which have been marked as abandonded by the author as of 20 days ago (March 15, 2020):
jakub-onderka/php-parallel-lint (see https://github.com/JakubOnderka/PHP-Parallel-Lint)
Recommended replacements:
https://github.com/php-parallel-lint/PHP-Parallel-Lint
I found this issue by loading up a new fresh copy of Laravel and it was complaining about the abandoned packages right away.
If you merge a Set containing [X, Y] with a Set containing [Y, Z], the result Set will be [X, Y, Y, Z]. Which is incorrect.
$set1 = new Set('string', ['X', 'Y']);
$set2 = new Set('string', ['Y', 'Z']);
$set3 = $set1->merge($set2);
foreach($set3 as $el)
{
echo($el . "\n");
}
Element 'Y' should only occur once in set3
Hello! I use ramsey/uuid
library in my code, and when I run my test on PHP 8.1 beta1 I'm getting the following error:
PHP Fatal error: During inheritance of ArrayAccess: Uncaught Return type of Ramsey\Collection\AbstractArray::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/collection/src/AbstractArray.php:87
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/collection/src/AbstractArray.php:30
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/collection/src/AbstractCollection.php:48
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/Builder/BuilderCollection.php:31
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/FeatureSet.php:435
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/FeatureSet.php:170
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/UuidFactory.php:108
Just extend \Ramsey\Collection\AbstractArray
and try to create instance under PHP 8.1 beta.
The instance is successfully created.
I need a collection that not only limits the type of data added, but also imposes restrictions on the data values. I browsed through the Wiki, ReadMe and tests but could not find any indication that this is possible. Any ideas?
When AbstractCollection
contains objects which have both a method and property with the same name, the value can never be extracted from the method through ValueExtractorTrait::extractValue()
.
class Person
{
public function __construct(private string $name)
{
}
public function name(): string
{
return $this->name;
}
}
$collection = new Ramsey\Collection\Collection();
$collection->add(new Person('Bob'));
print_r($collection->columns('name'));
The above triggers an error:
Error: Cannot access private property Person::$name
Array
(
[0] => Bob
)
This seems to be because ValueExtractorTrait::extractValue()
does not check if the property is actually accessible before trying to access it. It then proceeds to access it, causing the error, and then it never gets to checking whether a method exists with that same name which actually is accessible.
One option would be to verify accessibility before attempting to access the property or method, but this would require use of reflection with obvious performance downsides.
Alternatively if it is detected that both the method and property exist of the same name, one could use a try/catch block to attempt to access them and then only throw an exception if both attempts failed.
Finally another "fix" could be to swap the position of accessing the property with that of accessing the method, which would work for the above use case (which is probably the most common). However, this would then cause a similar issue for objects with a private method and public property with the same name.
Does not return correct value when using contasins with a collection of laravel models
Steps to reproduce the behavior (include code examples, if applicable):
Create script foo.php
and add the following:
<?php
require_once 'vendor/autoload.php';
$collection = new Ramsey\Collection\Collection(\App\User::class);
$model = \App\User::find(1);
$collection->add($model);
$model2 = \App\User::find(1);
$collection->contains($model2); //Return false
Execute the script from a terminal:
$ php foo.php
See output similar to the following:
The ouput the output must be true ...
Add any other context about the problem here.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.